Merge "No need to explicitly enable TLS-PSK cipher suites." into lmp-dev
diff --git a/Android.mk b/Android.mk
index 8a50ae8..e2fa7d1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -118,9 +118,7 @@
 	core/java/android/content/IIntentReceiver.aidl \
 	core/java/android/content/IIntentSender.aidl \
 	core/java/android/content/IOnPrimaryClipChangedListener.aidl \
-        core/java/android/content/IPermissionResponseCallback.aidl \
 	core/java/android/content/IRestrictionsManager.aidl \
-        core/java/android/content/IRestrictionsProvider.aidl \
 	core/java/android/content/ISyncAdapter.aidl \
 	core/java/android/content/ISyncContext.aidl \
 	core/java/android/content/ISyncServiceAdapter.aidl \
diff --git a/api/current.txt b/api/current.txt
index 8076d47..f0f2ef9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -359,6 +359,7 @@
     field public static final int buttonTint = 16843889; // 0x1010471
     field public static final int buttonTintMode = 16843890; // 0x1010472
     field public static final int cacheColorHint = 16843009; // 0x1010101
+    field public static final int calendarTextColor = 16843934; // 0x101049e
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
@@ -447,6 +448,14 @@
     field public static final int dashWidth = 16843174; // 0x10101a6
     field public static final int data = 16842798; // 0x101002e
     field public static final int datePickerStyle = 16843612; // 0x101035c
+    field public static final int dateSelectorBackgroundColor = 16843928; // 0x1010498
+    field public static final int dateSelectorDayOfMonthTextAppearance = 16843930; // 0x101049a
+    field public static final int dateSelectorDayOfWeekBackgroundColor = 16843926; // 0x1010496
+    field public static final int dateSelectorDayOfWeekTextAppearance = 16843927; // 0x1010497
+    field public static final int dateSelectorMonthTextAppearance = 16843929; // 0x1010499
+    field public static final int dateSelectorYearListItemTextAppearance = 16843932; // 0x101049c
+    field public static final int dateSelectorYearListSelectedCircleColor = 16843933; // 0x101049d
+    field public static final int dateSelectorYearTextAppearance = 16843931; // 0x101049b
     field public static final int dateTextAppearance = 16843593; // 0x1010349
     field public static final int debuggable = 16842767; // 0x101000f
     field public static final int defaultValue = 16843245; // 0x10101ed
@@ -742,6 +751,8 @@
     field public static final int largeScreens = 16843398; // 0x1010286
     field public static final int largestWidthLimitDp = 16843622; // 0x1010366
     field public static final int launchMode = 16842781; // 0x101001d
+    field public static final int launchTaskBehindBackgroundAnimation = 16843923; // 0x1010493
+    field public static final int launchTaskBehindSourceAnimation = 16843924; // 0x1010494
     field public static final int layerType = 16843604; // 0x1010354
     field public static final int layout = 16842994; // 0x10100f2
     field public static final int layoutAnimation = 16842988; // 0x10100ec
@@ -996,6 +1007,7 @@
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
     field public static final int restrictedAccountType = 16843733; // 0x10103d5
+    field public static final int restrictionType = 16843925; // 0x1010495
     field public static final int reversible = 16843853; // 0x101044d
     field public static final int right = 16843183; // 0x10101af
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
@@ -3664,6 +3676,7 @@
 
   public class ActivityOptions {
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
+    method public static android.app.ActivityOptions makeLaunchTaskBehindAnimation();
     method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
     method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View, java.lang.String>...);
@@ -5292,6 +5305,8 @@
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
     method public android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle);
     method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
+    method public void enableSystemApp(android.content.ComponentName, java.lang.String);
+    method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
@@ -6575,11 +6590,10 @@
 
 package android.content {
 
-  public abstract class AbstractRestrictionsProvider extends android.app.Service {
+  public abstract class AbstractRestrictionsProvider extends android.content.BroadcastReceiver {
     ctor public AbstractRestrictionsProvider();
-    method public abstract android.os.Bundle getPermissionResponse(java.lang.String, java.lang.String);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract void requestPermission(java.lang.String, java.lang.String, android.os.Bundle);
+    method public void onReceive(android.content.Context, android.content.Intent);
+    method public abstract void requestPermission(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle);
   }
 
   public abstract class AbstractThreadedSyncAdapter {
@@ -7696,7 +7710,6 @@
     field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
     field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
     field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
-    field public static final int FLAG_ACTIVITY_LAUNCH_BEHIND = 4096; // 0x1000
     field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
     field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
     field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
@@ -7924,6 +7937,7 @@
   }
 
   public class RestrictionEntry implements android.os.Parcelable {
+    ctor public RestrictionEntry(int, java.lang.String);
     ctor public RestrictionEntry(java.lang.String, java.lang.String);
     ctor public RestrictionEntry(java.lang.String, boolean);
     ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
@@ -7958,31 +7972,34 @@
     field public static final int TYPE_INTEGER = 5; // 0x5
     field public static final int TYPE_MULTI_SELECT = 4; // 0x4
     field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_STRING = 6; // 0x6
   }
 
   public class RestrictionsManager {
     method public android.os.Bundle getApplicationRestrictions();
     method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
-    method public void getPermissionResponse(java.lang.String, android.content.RestrictionsManager.PermissionResponseCallback);
     method public boolean hasRestrictionsProvider();
     method public void notifyPermissionResponse(java.lang.String, android.os.Bundle);
     method public void requestPermission(java.lang.String, android.os.Bundle);
     field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
-    field public static final java.lang.String EXTRA_PACKAGE_NAME = "package_name";
-    field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "response";
+    field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.content.action.REQUEST_PERMISSION";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
+    field public static final java.lang.String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
+    field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
+    field public static final java.lang.String META_DATA_APP_RESTRICTIONS = "android.content.APP_RESTRICTIONS";
     field public static final java.lang.String REQUEST_KEY_APPROVE_LABEL = "android.request.approve_label";
     field public static final java.lang.String REQUEST_KEY_DATA = "android.request.data";
     field public static final java.lang.String REQUEST_KEY_DENY_LABEL = "android.request.deny_label";
-    field public static final java.lang.String REQUEST_KEY_DEVICE_NAME = "android.request.device";
     field public static final java.lang.String REQUEST_KEY_ICON = "android.request.icon";
     field public static final java.lang.String REQUEST_KEY_ID = "android.request.id";
     field public static final java.lang.String REQUEST_KEY_MESSAGE = "android.request.mesg";
-    field public static final java.lang.String REQUEST_KEY_REQUESTOR_NAME = "android.request.requestor";
+    field public static final java.lang.String REQUEST_KEY_NEW_REQUEST = "android.request.new_request";
     field public static final java.lang.String REQUEST_KEY_TITLE = "android.request.title";
+    field public static final java.lang.String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
     field public static final java.lang.String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval";
-    field public static final java.lang.String REQUEST_TYPE_QUESTION = "android.request.type.question";
     field public static final java.lang.String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode";
-    field public static final java.lang.String RESPONSE_KEY_ERROR_MESSAGE = "android.response.errormsg";
+    field public static final java.lang.String RESPONSE_KEY_MESSAGE = "android.response.msg";
     field public static final java.lang.String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp";
     field public static final java.lang.String RESPONSE_KEY_RESULT = "android.response.result";
     field public static final int RESULT_APPROVED = 1; // 0x1
@@ -7995,11 +8012,6 @@
     field public static final int RESULT_UNKNOWN_REQUEST = 4; // 0x4
   }
 
-  public static abstract class RestrictionsManager.PermissionResponseCallback {
-    ctor public RestrictionsManager.PermissionResponseCallback();
-    method public abstract void onResponse(android.os.Bundle);
-  }
-
   public class SearchRecentSuggestionsProvider extends android.content.ContentProvider {
     ctor public SearchRecentSuggestionsProvider();
     method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -8901,7 +8913,7 @@
     ctor public AssetFileDescriptor.AutoCloseOutputStream(android.content.res.AssetFileDescriptor) throws java.io.IOException;
   }
 
-  public final class AssetManager {
+  public final class AssetManager implements java.lang.AutoCloseable {
     method public void close();
     method public final java.lang.String[] getLocales();
     method public final java.lang.String[] list(java.lang.String) throws java.io.IOException;
@@ -14417,6 +14429,7 @@
     method public final void release();
     method public final void releaseOutputBuffer(int, boolean);
     method public final void releaseOutputBuffer(int, long);
+    method public final void reset();
     method public void setCallback(android.media.MediaCodec.Callback);
     method public final void setParameters(android.os.Bundle);
     method public final void setVideoScalingMode(int);
@@ -14957,6 +14970,7 @@
     method public void reset();
     method public void seekTo(int) throws java.lang.IllegalStateException;
     method public void selectTrack(int) throws java.lang.IllegalStateException;
+    method public void setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
     method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void setAudioStreamType(int);
     method public void setAuxEffectSendLevel(float);
@@ -28222,6 +28236,7 @@
     method public java.lang.String getId();
     method public android.telecomm.CallState getState();
     method public android.telecomm.StatusHints getStatusHints();
+    method public int getVideoState();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
@@ -36159,14 +36174,28 @@
     method public abstract void onReceivedIcon(java.lang.String, android.graphics.Bitmap);
   }
 
+  public abstract interface WebResourceRequest {
+    method public abstract java.lang.String getMethod();
+    method public abstract java.util.Map<java.lang.String, java.lang.String> getRequestHeaders();
+    method public abstract android.net.Uri getUrl();
+    method public abstract boolean hasUserGestureInsecure();
+    method public abstract boolean isForMainFrame();
+  }
+
   public class WebResourceResponse {
     ctor public WebResourceResponse(java.lang.String, java.lang.String, java.io.InputStream);
+    ctor public WebResourceResponse(java.lang.String, java.lang.String, int, java.lang.String, java.util.Map<java.lang.String, java.lang.String>, java.io.InputStream);
     method public java.io.InputStream getData();
     method public java.lang.String getEncoding();
     method public java.lang.String getMimeType();
+    method public java.lang.String getReasonPhrase();
+    method public java.util.Map<java.lang.String, java.lang.String> getResponseHeaders();
+    method public int getStatusCode();
     method public void setData(java.io.InputStream);
     method public void setEncoding(java.lang.String);
     method public void setMimeType(java.lang.String);
+    method public void setResponseHeaders(java.util.Map<java.lang.String, java.lang.String>);
+    method public void setStatusCodeAndReasonPhrase(int, java.lang.String);
   }
 
   public abstract class WebSettings {
@@ -36490,7 +36519,8 @@
     method public deprecated void onTooManyRedirects(android.webkit.WebView, android.os.Message, android.os.Message);
     method public void onUnhandledInputEvent(android.webkit.WebView, android.view.InputEvent);
     method public deprecated void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent);
-    method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, java.lang.String);
+    method public deprecated android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, java.lang.String);
+    method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, android.webkit.WebResourceRequest);
     method public boolean shouldOverrideKeyEvent(android.webkit.WebView, android.view.KeyEvent);
     method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
     field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
@@ -37154,8 +37184,16 @@
     ctor public DatePicker(android.content.Context, android.util.AttributeSet);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.content.res.ColorStateList getCalendarTextColor();
     method public android.widget.CalendarView getCalendarView();
     method public boolean getCalendarViewShown();
+    method public int getDateSelectorBackgroundColor();
+    method public int getDateSelectorDayOfMonthTextAppearance();
+    method public int getDateSelectorDayOfWeekBackgroundColor();
+    method public int getDateSelectorDayOfWeekTextAppearance();
+    method public int getDateSelectorMonthTextAppearance();
+    method public int getDateSelectorYearListItemTextAppearance();
+    method public int getDateSelectorYearTextAppearance();
     method public int getDayOfMonth();
     method public long getMaxDate();
     method public long getMinDate();
@@ -37163,7 +37201,15 @@
     method public boolean getSpinnersShown();
     method public int getYear();
     method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
+    method public void setCalendarTextColor(android.content.res.ColorStateList);
     method public void setCalendarViewShown(boolean);
+    method public void setDateSelectorBackgroundColor(int);
+    method public void setDateSelectorDayOfMonthTextAppearance(int);
+    method public void setDateSelectorDayOfWeekBackgroundColor(int);
+    method public void setDateSelectorDayOfWeekTextAppearance(int);
+    method public void setDateSelectorMonthTextAppearance(int);
+    method public void setDateSelectorYearListItemTextAppearance(int);
+    method public void setDateSelectorYearTextAppearance(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
     method public void setSpinnersShown(boolean);
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index bf2924c..bdfbde1 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -631,6 +631,9 @@
     public void setObjectValues(Object... values) {
         mValueType = values[0].getClass();
         mKeyframeSet = KeyframeSet.ofObject(values);
+        if (mEvaluator != null) {
+            mKeyframeSet.setEvaluator(mEvaluator);
+        }
     }
 
     /**
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 4a70e15..e2b5a84 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -30,7 +30,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
@@ -2196,13 +2195,21 @@
             return true;
         }
 
-        case MEDIA_RESOURCES_RELEASED: {
+        case MEDIA_RESOURCES_RELEASED_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
             mediaResourcesReleased(token);
             reply.writeNoException();
             return true;
         }
+
+        case NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            notifyLaunchTaskBehindComplete(token);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -5069,7 +5076,20 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        mRemote.transact(MEDIA_RESOURCES_RELEASED, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(MEDIA_RESOURCES_RELEASED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
+    public void notifyLaunchTaskBehindComplete(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION, data, reply,
+                IBinder.FLAG_ONEWAY);
         reply.readException();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index fafa948..4c02314 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -126,6 +126,8 @@
     public static final int ANIM_SCENE_TRANSITION = 5;
     /** @hide */
     public static final int ANIM_DEFAULT = 6;
+    /** @hide */
+    public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
 
     private String mPackageName;
     private int mAnimationType = ANIM_NONE;
@@ -432,6 +434,27 @@
         return opts;
     }
 
+    /**
+     * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
+     * presented to the user but will instead be only available through the recents task list.
+     * In addition, the new task wil be affiliated with the launching activity's task.
+     * Affiliated tasks are grouped together in the recents task list.
+     *
+     * <p>This behavior is not supported for activities with {@link
+     * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
+     * <code>singleInstance</code> or <code>singleTask</code>.
+     */
+    public static ActivityOptions makeLaunchTaskBehindAnimation() {
+        final ActivityOptions opts = new ActivityOptions();
+        opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND;
+        return opts;
+    }
+
+    /** @hide */
+    public boolean getLaunchTaskBehind() {
+        return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
+    }
+
     private ActivityOptions() {
     }
 
@@ -647,16 +670,15 @@
         if (mPackageName != null) {
             b.putString(KEY_PACKAGE_NAME, mPackageName);
         }
+        b.putInt(KEY_ANIM_TYPE, mAnimationType);
         switch (mAnimationType) {
             case ANIM_CUSTOM:
-                b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
                 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
                 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
                         != null ? mAnimationStartedListener.asBinder() : null);
                 break;
             case ANIM_SCALE_UP:
-                b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 b.putInt(KEY_ANIM_START_X, mStartX);
                 b.putInt(KEY_ANIM_START_Y, mStartY);
                 b.putInt(KEY_ANIM_START_WIDTH, mStartWidth);
@@ -664,7 +686,6 @@
                 break;
             case ANIM_THUMBNAIL_SCALE_UP:
             case ANIM_THUMBNAIL_SCALE_DOWN:
-                b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
                 b.putInt(KEY_ANIM_START_X, mStartX);
                 b.putInt(KEY_ANIM_START_Y, mStartY);
@@ -672,7 +693,6 @@
                         != null ? mAnimationStartedListener.asBinder() : null);
                 break;
             case ANIM_SCENE_TRANSITION:
-                b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 if (mTransitionReceiver != null) {
                     b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
                 }
@@ -683,6 +703,7 @@
                 b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
                 break;
         }
+
         return b;
     }
 
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 26c2c30..d7fb707 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -21,6 +21,7 @@
 import android.content.DialogInterface.OnClickListener;
 import android.os.Bundle;
 import android.text.format.DateUtils;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.DatePicker;
@@ -44,10 +45,11 @@
     private static final String DAY = "day";
 
     private final DatePicker mDatePicker;
-    private final OnDateSetListener mCallBack;
+    private final OnDateSetListener mDateSetListener;
     private final Calendar mCalendar;
 
     private boolean mTitleNeedsUpdate = true;
+    private boolean mIsCanceled = false;
 
     /**
      * The callback used to indicate the user is done filling in the date.
@@ -79,29 +81,36 @@
         this(context, 0, callBack, year, monthOfYear, dayOfMonth);
     }
 
+    static int resolveDialogTheme(Context context, int resid) {
+        if (resid == 0) {
+            TypedValue outValue = new TypedValue();
+            context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true);
+            return outValue.resourceId;
+        } else {
+            return resid;
+        }
+    }
+
     /**
      * @param context The context the dialog is to run in.
      * @param theme the theme to apply to this dialog
-     * @param callBack How the parent is notified that the date is set.
+     * @param listener How the parent is notified that the date is set.
      * @param year The initial year of the dialog.
      * @param monthOfYear The initial month of the dialog.
      * @param dayOfMonth The initial day of the dialog.
      */
     public DatePickerDialog(Context context,
             int theme,
-            OnDateSetListener callBack,
+            OnDateSetListener listener,
             int year,
             int monthOfYear,
             int dayOfMonth) {
-        super(context, theme);
+        super(context, resolveDialogTheme(context, theme));
 
-        mCallBack = callBack;
-
+        mDateSetListener = listener;
         mCalendar = Calendar.getInstance();
 
         Context themeContext = getContext();
-        setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_done), this);
-        setIcon(0);
 
         LayoutInflater inflater =
                 (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -109,20 +118,53 @@
         setView(view);
         setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
         mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
+
+        // Initialize state
+        mDatePicker.setLegacyMode(false, null);
+        mDatePicker.setShowDoneButton(true);
+        mDatePicker.setDismissCallback(new DatePicker.DatePickerDismissCallback() {
+            @Override
+            public void dismiss(DatePicker view, boolean isCancel, int year, int month, int dayOfMonth) {
+                mIsCanceled = isCancel;
+                if (!isCancel) {
+                    mDateSetListener.onDateSet(view, year, month, dayOfMonth);
+                }
+                DatePickerDialog.this.dismiss();
+            }
+        });
         mDatePicker.init(year, monthOfYear, dayOfMonth, this);
-        updateTitle(year, monthOfYear, dayOfMonth);
     }
 
     public void onClick(DialogInterface dialog, int which) {
         tryNotifyDateSet();
     }
 
+    @Override
+    public void cancel() {
+        mIsCanceled = true;
+        super.cancel();
+    }
+
+    @Override
+    protected void onStop() {
+        tryNotifyDateSet();
+        super.onStop();
+    }
+
     public void onDateChanged(DatePicker view, int year,
             int month, int day) {
         mDatePicker.init(year, month, day, this);
         updateTitle(year, month, day);
     }
 
+    private void tryNotifyDateSet() {
+        if (mDateSetListener != null && !mIsCanceled) {
+            mDatePicker.clearFocus();
+            mDateSetListener.onDateSet(mDatePicker, mDatePicker.getYear(),
+                    mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
+        }
+    }
+
     /**
      * Gets the {@link DatePicker} contained in this dialog.
      *
@@ -143,20 +185,6 @@
         mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
     }
 
-    private void tryNotifyDateSet() {
-        if (mCallBack != null) {
-            mDatePicker.clearFocus();
-            mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
-                    mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        tryNotifyDateSet();
-        super.onStop();
-    }
-
     private void updateTitle(int year, int month, int day) {
         if (!mDatePicker.getCalendarViewShown()) {
             mCalendar.set(Calendar.YEAR, year);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index b3a8a8a..c6921a2 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -444,6 +444,8 @@
     public boolean isBackgroundMediaPlaying(IBinder token) throws RemoteException;
     public void mediaResourcesReleased(IBinder token) throws RemoteException;
 
+    public void notifyLaunchTaskBehindComplete(IBinder token) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -750,5 +752,6 @@
     int IS_TOP_OF_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+224;
     int SET_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+225;
     int IS_BG_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+226;
-    int MEDIA_RESOURCES_RELEASED = IBinder.FIRST_CALL_TRANSACTION+227;
+    int MEDIA_RESOURCES_RELEASED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+227;
+    int NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+228;
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a3ffc00..5b92538 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -44,6 +44,9 @@
     void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
     boolean areNotificationsEnabledForPackage(String pkg, int uid);
 
+    void setPackagePriority(String pkg, int uid, int priority);
+    int getPackagePriority(String pkg, int uid);
+
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
     StatusBarNotification[] getActiveNotifications(String callingPkg);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7790640..4e7dac0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2243,7 +2243,7 @@
      * application running in the managed profile.
      *
      * <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be
-     * {@link Boolean}, {@link String}, or {@link String}[]. The recommended format for key strings
+     * boolean, int, String, or String[]. The recommended format for keys
      * is "com.example.packagename/example-setting" to avoid naming conflicts with library
      * components such as {@link android.webkit.WebView}.
      *
@@ -2524,6 +2524,45 @@
     }
 
     /**
+     * Called by profile or device owner to re-enable a system app that was disabled by default
+     * when the managed profile was created. This can only be called from a profile or device
+     * owner running within a managed profile.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName The package to be re-enabled in the current profile.
+     */
+    public void enableSystemApp(ComponentName admin, String packageName) {
+        if (mService != null) {
+            try {
+                mService.enableSystemApp(admin, packageName);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to install package: " + packageName);
+            }
+        }
+    }
+
+    /**
+     * Called by profile or device owner to re-enable system apps by intent that were disabled
+     * by default when the managed profile was created. This can only be called from a profile
+     * or device owner running within a managed profile.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param intent An intent matching the app(s) to be installed. All apps that resolve for this
+     *               intent will be re-enabled in the current profile.
+     * @return int The number of activities that matched the intent and were installed.
+     */
+    public int enableSystemApp(ComponentName admin, Intent intent) {
+        if (mService != null) {
+            try {
+                return mService.enableSystemAppWithIntent(admin, intent);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to install packages matching filter: " + intent);
+            }
+        }
+        return 0;
+    }
+
+    /**
      * Called by a profile owner to disable account management for a specific type of account.
      *
      * <p>The calling device admin must be a profile owner. If it is not, a
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c27d1cc..d36497e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -141,6 +141,9 @@
     boolean removeUser(in ComponentName who, in UserHandle userHandle);
     boolean switchUser(in ComponentName who, in UserHandle userHandle);
 
+    void enableSystemApp(in ComponentName admin, in String packageName);
+    int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
+
     void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
     String[] getAccountTypesWithManagementDisabled();
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 97e3fc5..faf8645 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -219,22 +219,6 @@
             "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
 
     /**
-     * Broadcast Action: Indicate BLE Advertising is started.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
-            "android.bluetooth.adapter.action.ADVERTISING_STARTED";
-
-    /**
-     * Broadcast Action: Indicated BLE Advertising is stopped.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
-            "android.bluetooth.adapter.action.ADVERTISING_STOPPED";
-
-    /**
      * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
      * intents to request the current scan mode. Possible values are:
      * {@link #SCAN_MODE_NONE},
@@ -403,8 +387,6 @@
     private IBluetooth mService;
 
     private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
-    private BluetoothAdvScanData mBluetoothAdvScanData = null;
-    private GattCallbackWrapper mAdvertisingGattCallback;
     private final Handler mHandler;  // Handler to post the advertise callback to run on main thread.
     private final Object mLock = new Object();
 
@@ -481,29 +463,6 @@
     }
 
     /**
-     * Returns a {@link BluetoothAdvScanData} object representing advertising data.
-     * Data will be reset when bluetooth service is turned off.
-     * @hide
-     */
-    public BluetoothAdvScanData getAdvScanData() {
-      try {
-          IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-          if (iGatt == null) {
-              // BLE is not supported
-              Log.e(TAG, "failed to start, iGatt null");
-              return null;
-          }
-          if (mBluetoothAdvScanData == null) {
-              mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD);
-          }
-          return mBluetoothAdvScanData;
-      } catch (RemoteException e) {
-          Log.e(TAG, "failed to get advScanData, error: " + e);
-          return null;
-      }
-    }
-
-    /**
      * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
      */
     public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
@@ -520,106 +479,6 @@
     }
 
     /**
-     * Interface for BLE advertising callback.
-     *
-     * @hide
-     */
-    public interface AdvertiseCallback {
-        /**
-         * Callback when advertise starts.
-         * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
-         */
-        void onAdvertiseStart(int status);
-        /**
-         * Callback when advertise stops.
-         * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
-         */
-        void onAdvertiseStop(int status);
-    }
-
-    /**
-     * Start BLE advertising using current {@link BluetoothAdvScanData}.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
-     *
-     * @param callback - {@link AdvertiseCallback}
-     * @return true if BLE advertising succeeds, false otherwise.
-     * @hide
-     */
-    public boolean startAdvertising(final AdvertiseCallback callback) {
-        if (getState() != STATE_ON) return false;
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            if (iGatt == null) {
-                // BLE is not supported.
-                return false;
-            }
-            // Restart/reset advertising packets if advertising is in progress.
-            if (isAdvertising()) {
-                // Invalid advertising callback.
-                if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) {
-                    Log.e(TAG, "failed to restart advertising, invalid callback");
-                    return false;
-                }
-                iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle);
-                // Run the callback from main thread.
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        // callback with status success.
-                        callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS);
-                    }
-                });
-                return true;
-            }
-            UUID uuid = UUID.randomUUID();
-            GattCallbackWrapper wrapper =
-                new GattCallbackWrapper(this, null, null, callback);
-            iGatt.registerClient(new ParcelUuid(uuid), wrapper);
-            if (!wrapper.advertiseStarted()) {
-                return false;
-            }
-            synchronized (mLock) {
-                mAdvertisingGattCallback = wrapper;
-            }
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Stop BLE advertising. The callback has to be the same one used for start advertising.
-     *
-     * @param callback - {@link AdvertiseCallback}
-     * @return true if BLE advertising stops, false otherwise.
-     * @hide
-     */
-    public boolean stopAdvertising(AdvertiseCallback callback) {
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            if (iGatt == null) {
-                // BLE is not supported
-                return false;
-            }
-            if (mAdvertisingGattCallback == null) {
-                // no callback.
-                return false;
-            }
-            // Make sure same callback is used for start and stop advertising.
-            if (callback != mAdvertisingGattCallback.mAdvertiseCallback) {
-                Log.e(TAG, "must use the same callback for star/stop advertising");
-                return false;
-            }
-            mAdvertisingGattCallback.stopAdvertising();
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
      * Return true if Bluetooth is currently enabled and ready for use.
      * <p>Equivalent to:
      * <code>getBluetoothState() == STATE_ON</code>
@@ -1076,23 +935,6 @@
     }
 
     /**
-     * Returns whether BLE is currently advertising.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
-     *
-     * @hide
-     */
-    public boolean isAdvertising() {
-        if (getState() != STATE_ON) return false;
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            return iGatt.isAdvertising();
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
      * Return the set of {@link BluetoothDevice} objects that are bonded
      * (paired) to the local adapter.
      * <p>If Bluetooth state is not {@link #STATE_ON}, this API
@@ -1537,8 +1379,6 @@
                 if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
                 synchronized (mManagerCallback) {
                     mService = null;
-                    // Reset bluetooth adv scan data when Gatt service is down.
-                    mBluetoothAdvScanData = null;
                     for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
                         try {
                             if (cb != null) {
@@ -1822,7 +1662,6 @@
         private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
         private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;
 
-        private final AdvertiseCallback mAdvertiseCallback;
         private final LeScanCallback mLeScanCb;
 
         // mLeHandle 0: not registered
@@ -1838,27 +1677,12 @@
             mLeScanCb = leScanCb;
             mScanFilter = uuid;
             mLeHandle = 0;
-            mAdvertiseCallback = null;
-        }
-
-        public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb,
-            UUID[] uuid, AdvertiseCallback callback) {
-          mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
-          mLeScanCb = leScanCb;
-          mScanFilter = uuid;
-          mLeHandle = 0;
-          mAdvertiseCallback = callback;
         }
 
         public boolean scanStarted() {
             return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT);
         }
 
-        public boolean advertiseStarted() {
-            // Wait for registeration callback.
-            return waitForRegisteration(1);
-        }
-
         private boolean waitForRegisteration(int maxWaitCount) {
             boolean started = false;
             synchronized(this) {
@@ -1878,27 +1702,6 @@
             return started;
         }
 
-        public void stopAdvertising() {
-            synchronized (this) {
-                if (mLeHandle <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
-                    return;
-                }
-                BluetoothAdapter adapter = mBluetoothAdapter.get();
-                if (adapter != null) {
-                    try {
-                        IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
-                        iGatt.stopAdvertising();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Failed to stop advertising" + e);
-                    }
-                } else {
-                    Log.e(TAG, "stopAdvertising, BluetoothAdapter is null");
-                }
-                notifyAll();
-            }
-        }
-
         public void stopLeScan() {
             synchronized(this) {
                 if (mLeHandle <= 0) {
@@ -1940,18 +1743,14 @@
                         BluetoothAdapter adapter = mBluetoothAdapter.get();
                         if (adapter != null) {
                             iGatt = adapter.getBluetoothManager().getBluetoothGatt();
-                            if (mAdvertiseCallback != null) {
-                                iGatt.startAdvertising(mLeHandle);
+                            if (mScanFilter == null) {
+                                iGatt.startScan(mLeHandle, false);
                             } else {
-                              if (mScanFilter == null) {
-                                  iGatt.startScan(mLeHandle, false);
-                              } else {
-                                  ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
-                                  for(int i = 0; i != uuids.length; ++i) {
-                                      uuids[i] = new ParcelUuid(mScanFilter[i]);
-                                  }
-                                  iGatt.startScanWithUuids(mLeHandle, false, uuids);
-                              }
+                                ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
+                                for(int i = 0; i != uuids.length; ++i) {
+                                    uuids[i] = new ParcelUuid(mScanFilter[i]);
+                                }
+                                iGatt.startScanWithUuids(mLeHandle, false, uuids);
                             }
                         } else {
                             Log.e(TAG, "onClientRegistered, BluetoothAdapter null");
@@ -2080,44 +1879,6 @@
         }
 
         public void onAdvertiseStateChange(int advertiseState, int status) {
-            Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status);
-            if (advertiseState == STATE_ADVERTISE_STARTED) {
-                if (status == ADVERTISE_CALLBACK_SUCCESS) {
-                    mAdvertiseCallback.onAdvertiseStart(status);
-                } else {
-                    // If status is unsuccessful and advertise state is started, it means stop
-                    // advertising fails.
-                    mAdvertiseCallback.onAdvertiseStop(status);
-                }
-            } else {
-                synchronized (this) {
-                    if (status == ADVERTISE_CALLBACK_SUCCESS) {
-                        BluetoothAdapter adapter = mBluetoothAdapter.get();
-                        if (adapter != null) {
-                            try {
-                                IBluetoothGatt iGatt =
-                                        adapter.getBluetoothManager().getBluetoothGatt();
-                                Log.d(TAG, "unregistering client " + mLeHandle);
-                                iGatt.unregisterClient(mLeHandle);
-                                // Reset advertise app handle.
-                                mLeHandle = -1;
-                                adapter.mAdvertisingGattCallback = null;
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Failed to unregister client" + e);
-                            }
-                        } else {
-                            Log.e(TAG, "cannot unregister client, BluetoothAdapter is null");
-                        }
-                    }
-                }
-                if (status == ADVERTISE_CALLBACK_SUCCESS) {
-                    mAdvertiseCallback.onAdvertiseStop(status);
-                } else{
-                    // If status is unsuccesful and advertise state is stopped, it means start
-                    // advertising fails.
-                    mAdvertiseCallback.onAdvertiseStart(status);
-                }
-            }
         }
 
         @Override
diff --git a/core/java/android/bluetooth/BluetoothAdvScanData.java b/core/java/android/bluetooth/BluetoothAdvScanData.java
deleted file mode 100644
index df2c256..0000000
--- a/core/java/android/bluetooth/BluetoothAdvScanData.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * This class provides the public APIs to set advertising and scan response data when BLE device
- * operates in peripheral mode. <br>
- * The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11
- * @hide
- */
-public final class BluetoothAdvScanData {
-
-  /**
-   * Available data types of {@link BluetoothAdvScanData}.
-   */
-  public static final int AD = 0;  // Advertising Data
-  public static final int SCAN_RESPONSE = 1;  // Scan Response
-  public static final int EIR = 2;  // Extended Inquiry Response
-
-  private static final String TAG = "BluetoothAdvScanData";
-
-  /**
-   * Data type of BluetoothAdvScanData.
-   */
-  private final int mDataType;
-  /**
-   * Bluetooth Gatt Service.
-   */
-  private IBluetoothGatt mBluetoothGatt;
-
-  /**
-   * @param mBluetoothGatt
-   * @param dataType
-   */
-  public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) {
-    this.mBluetoothGatt = mBluetoothGatt;
-    this.mDataType = dataType;
-  }
-
-  /**
-   * @return advertising data type.
-   */
-  public int getDataType() {
-    return mDataType;
-  }
-
-  /**
-   * Set manufactureCode and manufactureData.
-   * Returns true if manufacturer data is set, false if there is no enough room to set
-   * manufacturer data or the data is already set.
-   * @param manufacturerCode - unique identifier for the manufacturer
-   * @param manufacturerData - data associated with the specific manufacturer.
-   */
-  public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) {
-    if (mDataType != AD) return false;
-    try {
-      return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData);
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to set manufacturer id and data.", e);
-      return false;
-    }
-  }
-
-  /**
-   * Set service data.  Note the service data can only be set when the data type is {@code AD};
-   * @param serviceData
-   */
-  public boolean setServiceData(byte[] serviceData) {
-
-    if (mDataType != AD) return false;
-    if (serviceData == null) return false;
-    try {
-      return mBluetoothGatt.setAdvServiceData(serviceData);
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to set service data.", e);
-      return false;
-    }
-  }
-
-  /**
-   * Returns an immutable list of service uuids that will be advertised.
-   */
-  public List<ParcelUuid> getServiceUuids() {
-    try {
-      return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids());
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to get service uuids.", e);
-      return null;
-    }
-  }
-
-  /**
-   * Returns manufacturer data.
-   */
-  public byte[] getManufacturerData() {
-    if (mBluetoothGatt == null) return null;
-    try {
-      return mBluetoothGatt.getAdvManufacturerData();
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to get manufacturer data.", e);
-      return null;
-    }
-  }
-
-  /**
-   * Returns service data.
-   */
-  public byte[] getServiceData() {
-    if (mBluetoothGatt == null) return null;
-    try {
-      return mBluetoothGatt.getAdvServiceData();
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to get service data.", e);
-      return null;
-    }
-  }
-
-  /**
-   * Remove manufacturer data based on given manufacturer code.
-   * @param manufacturerCode
-   */
-  public void removeManufacturerCodeAndData(int manufacturerCode) {
-    if (mBluetoothGatt != null) {
-      try {
-        mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode);
-      } catch (RemoteException e) {
-        Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e);
-      }
-    }
-  }
-}
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 0f0eee6..6d4b9cd 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -48,15 +48,6 @@
     void unregisterClient(in int clientIf);
     void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
     void clientDisconnect(in int clientIf, in String address);
-    void startAdvertising(in int appIf);
-    void stopAdvertising();
-    boolean setAdvServiceData(in byte[] serviceData);
-    byte[] getAdvServiceData();
-    boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData);
-    byte[] getAdvManufacturerData();
-    List<ParcelUuid> getAdvServiceUuids();
-    void removeAdvManufacturerCodeAndData(int manufacturerCode);
-    boolean isAdvertising();
     void refreshDevice(in int clientIf, in String address);
     void discoverServices(in int clientIf, in String address);
     void readCharacteristic(in int clientIf, in String address, in int srvcType,
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index af218eb..18e3f54 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -64,7 +64,6 @@
                              in int charInstId, in ParcelUuid charUuid,
                              in byte[] value);
     void onReadRemoteRssi(in String address, in int rssi, in int status);
-    void onAdvertiseStateChange(in int advertiseState, in int status);
     void onMultiAdvertiseCallback(in int status);
     void onConfigureMTU(in String address, in int mtu, in int status);
     void onConnectionCongested(in String address, in boolean congested);
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index e232512..af79fcc 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -332,11 +332,6 @@
         }
 
         @Override
-        public void onAdvertiseStateChange(int advertiseState, int status) {
-            // no op
-        }
-
-        @Override
         public void onMultiAdvertiseCallback(int status) {
             // TODO: This logic needs to be re-visited to account
             //       for whether the scan has actually been started
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 7e87edc..220aa77 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -409,11 +409,6 @@
         }
 
         @Override
-        public void onAdvertiseStateChange(int advertiseState, int status) {
-            // no op
-        }
-
-        @Override
         public void onMultiAdvertiseCallback(int status) {
             // no op
         }
diff --git a/core/java/android/content/AbstractRestrictionsProvider.java b/core/java/android/content/AbstractRestrictionsProvider.java
index 1119478..3272970 100644
--- a/core/java/android/content/AbstractRestrictionsProvider.java
+++ b/core/java/android/content/AbstractRestrictionsProvider.java
@@ -16,78 +16,65 @@
 
 package android.content;
 
-import android.app.Service;
 import android.app.admin.DevicePolicyManager;
 import android.os.Bundle;
 import android.os.IBinder;
 
 /**
- * Abstract implementation of a Restrictions Provider Service. To implement a Restrictions Provider,
- * extend from this class and implement the abstract methods. Export this service in the
- * manifest. A profile owner device admin can then register this component as a Restrictions
- * Provider using {@link DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)}.
+ * Abstract implementation of a Restrictions Provider BroadcastReceiver. To implement a
+ * Restrictions Provider, extend from this class and implement the abstract methods.
+ * Export this receiver in the manifest. A profile owner device admin can then register this
+ * component as a Restrictions Provider using
+ * {@link DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)}.
  * <p>
  * The function of a Restrictions Provider is to transport permission requests from apps on this
  * device to an administrator (most likely on a remote device or computer) and deliver back
  * responses. The response should be sent back to the app via
  * {@link RestrictionsManager#notifyPermissionResponse(String, Bundle)}.
- * <p>
- * Apps can also query previously received responses using
- * {@link #getPermissionResponse(String, String)}. The period for which previously received
- * responses are available is left to the implementation of the Restrictions Provider.
+ *
+ * @see RestrictionsManager
  */
-public abstract class AbstractRestrictionsProvider extends Service {
+public abstract class AbstractRestrictionsProvider extends BroadcastReceiver {
 
     private static final String TAG = "AbstractRestrictionsProvider";
 
-    @Override
-    public final IBinder onBind(Intent intent) {
-        return new RestrictionsProviderWrapper().asBinder();
-    }
-
-    /**
-     * Checks to see if there is a response for a prior request and returns the response bundle if
-     * it exists. If there is no response yet or if the request is not known, the returned bundle
-     * should contain the response code in {@link RestrictionsManager#RESPONSE_KEY_RESULT}.
-     *
-     * @param packageName the application that is requesting a permission response.
-     * @param requestId the id of the request for which the response is needed.
-     * @return a bundle containing at a minimum the result of the request. It could contain other
-     * optional information such as error codes and cookies.
-     *
-     * @see RestrictionsManager#RESPONSE_KEY_RESULT
-     */
-    public abstract Bundle getPermissionResponse(String packageName, String requestId);
-
     /**
      * An asynchronous permission request made by an application for an operation that requires
      * authorization by a local or remote administrator other than the user. The Restrictions
-     * Provider must transfer the request to the administrator and deliver back a response, when
+     * Provider should transfer the request to the administrator and deliver back a response, when
      * available. The calling application is aware that the response could take an indefinite
      * amount of time.
+     * <p>
+     * If the request bundle contains the key {@link RestrictionsManager#REQUEST_KEY_NEW_REQUEST},
+     * then a new request must be sent. Otherwise the provider can look up any previous response
+     * to the same requestId and return the cached response.
      *
      * @param packageName the application requesting permission.
      * @param requestType the type of request, which determines the content and presentation of
      * the request data.
      * @param request the request data bundle containing at a minimum a request id.
      *
-     * @see RestrictionsManager#REQUEST_TYPE_QUESTION
+     * @see RestrictionsManager#REQUEST_TYPE_APPROVAL
      * @see RestrictionsManager#REQUEST_TYPE_LOCAL_APPROVAL
      * @see RestrictionsManager#REQUEST_KEY_ID
      */
-    public abstract void requestPermission(String packageName, String requestType, Bundle request);
+    public abstract void requestPermission(Context context,
+            String packageName, String requestType, Bundle request);
 
-    private class RestrictionsProviderWrapper extends IRestrictionsProvider.Stub {
+    /**
+     * Intercept standard Restrictions Provider broadcasts.  Implementations
+     * should not override this method; it is better to implement the
+     * convenience callbacks for each action.
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
 
-        @Override
-        public Bundle getPermissionResponse(String packageName, String requestId) {
-            return AbstractRestrictionsProvider.this
-                    .getPermissionResponse(packageName, requestId);
-        }
-
-        @Override
-        public void requestPermission(String packageName, String templateId, Bundle request) {
-            AbstractRestrictionsProvider.this.requestPermission(packageName, templateId, request);
+        if (RestrictionsManager.ACTION_REQUEST_PERMISSION.equals(action)) {
+            String packageName = intent.getStringExtra(RestrictionsManager.EXTRA_PACKAGE_NAME);
+            String requestType = intent.getStringExtra(RestrictionsManager.EXTRA_REQUEST_TYPE);
+            Bundle request = intent.getBundleExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE);
+            requestPermission(context, packageName, requestType, request);
         }
     }
 }
diff --git a/core/java/android/content/IPermissionResponseCallback.aidl b/core/java/android/content/IPermissionResponseCallback.aidl
deleted file mode 100644
index 8309768..0000000
--- a/core/java/android/content/IPermissionResponseCallback.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.content;
-
-import android.os.Bundle;
-
-/**
- * Callback for permission response queries.
- *
- * @hide
- */
- interface IPermissionResponseCallback {
-
-    void onResponse(in Bundle response);
-
-}
diff --git a/core/java/android/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl
index 49eb65b..b1c0a3a 100644
--- a/core/java/android/content/IRestrictionsManager.aidl
+++ b/core/java/android/content/IRestrictionsManager.aidl
@@ -17,7 +17,6 @@
 package android.content;
 
 import android.os.Bundle;
-import android.content.IPermissionResponseCallback;
 
 /**
  * Interface used by the RestrictionsManager
@@ -28,6 +27,4 @@
     boolean hasRestrictionsProvider();
     void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData);
     void notifyPermissionResponse(in String packageName, in Bundle response);
-    void getPermissionResponse(in String packageName, in String requestId,
-            in IPermissionResponseCallback callback);
 }
diff --git a/core/java/android/content/IRestrictionsProvider.aidl b/core/java/android/content/IRestrictionsProvider.aidl
deleted file mode 100644
index 4506b72..0000000
--- a/core/java/android/content/IRestrictionsProvider.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.content;
-
-import android.os.Bundle;
-
-/**
- * Interface to a restrictions provider service component.
- *
- * @hide
- */
- interface IRestrictionsProvider {
-
-    void requestPermission(in String packageName, in String requestType, in Bundle requestBundle);
-    Bundle getPermissionResponse(in String packageName, in String requestId);
-
-}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4153a02..5bf8a97 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3809,17 +3809,6 @@
      * {@link android.R.styleable#AndroidManifestActivity_autoRemoveFromRecents}.
      */
     public static final int FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS = 0x00002000;
-    /**
-     * If set along with FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
-     * presented to the user but will instead be only available through the recents task list.
-     * In addition, the new task wil be affiliated with the launching activity's task.
-     * Affiliated tasks are grouped together in the recents task list.
-     *
-     * <p>This behavior is not supported for activities with {@link
-     * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
-     * <code>singleInstance</code> or <code>singleTask</code>.
-     */
-    public static final int FLAG_ACTIVITY_LAUNCH_BEHIND = 0x00001000;
 
     /**
      * If set, when sending a broadcast only registered receivers will be
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 62f88a9..5341ea8 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -79,6 +79,13 @@
      */
     public static final int TYPE_INTEGER = 5;
 
+    /**
+     * A type of restriction. Use this for storing a string value.
+     * @see #setSelectedString
+     * @see #getSelectedString
+     */
+    public static final int TYPE_STRING = 6;
+
     /** The type of restriction. */
     private int mType;
 
@@ -107,6 +114,17 @@
     private String[] mCurrentValues;
 
     /**
+     * Constructor for specifying the type and key, with no initial value;
+     *
+     * @param type the restriction type.
+     * @param key the unique key for this restriction
+     */
+    public RestrictionEntry(int type, String key) {
+        mType = type;
+        mKey = key;
+    }
+
+    /**
      * Constructor for {@link #TYPE_CHOICE} type.
      * @param key the unique key for this restriction
      * @param selectedString the current value
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
index 5ef2dbc..5ae10cf 100644
--- a/core/java/android/content/RestrictionsManager.java
+++ b/core/java/android/content/RestrictionsManager.java
@@ -17,11 +17,24 @@
 package android.content;
 
 import android.app.admin.DevicePolicyManager;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Xml;
 
-import java.util.Collections;
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -32,50 +45,77 @@
  * <p>
  * Apps can expose a set of restrictions via an XML file specified in the manifest.
  * <p>
- * If the user has an active restrictions provider, dynamic requests can be made in
+ * If the user has an active Restrictions Provider, dynamic requests can be made in
  * addition to the statically imposed restrictions. Dynamic requests are app-specific
  * and can be expressed via a predefined set of request types.
  * <p>
  * The RestrictionsManager forwards the dynamic requests to the active
- * restrictions provider. The restrictions provider can respond back to requests by calling
+ * Restrictions Provider. The Restrictions Provider can respond back to requests by calling
  * {@link #notifyPermissionResponse(String, Bundle)}, when
  * a response is received from the administrator of the device or user.
  * The response is relayed back to the application via a protected broadcast,
  * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}.
  * <p>
- * Prior responses to requests can also be queried through calls to
- * {@link #getPermissionResponse(String, PermissionResponseCallback)}, if the provider
- * saves old responses.
- * <p>
  * Static restrictions are specified by an XML file referenced by a meta-data attribute
  * in the manifest. This enables applications as well as any web administration consoles
  * to be able to read the list of available restrictions from the apk.
  * <p>
  * The syntax of the XML format is as follows:
  * <pre>
- * &lt;restrictions&gt;
+ * &lt;?xml version="1.0" encoding="utf-8"?&gt;
+ * &lt;restrictions xmlns:android="http://schemas.android.com/apk/res/android" &gt;
  *     &lt;restriction
- *         android:key="&lt;key&gt;"
- *         android:restrictionType="boolean|string|integer|multi-select|null"
- *         ... /&gt;
+ *         android:key="string"
+ *         android:title="string resource"
+ *         android:restrictionType=["bool" | "string" | "integer"
+ *                                         | "choice" | "multi-select" | "hidden"]
+ *         android:description="string resource"
+ *         android:entries="string-array resource"
+ *         android:entryValues="string-array resource"
+ *         android:defaultValue="reference"
+ *         /&gt;
  *     &lt;restriction ... /&gt;
+ *     ...
  * &lt;/restrictions&gt;
  * </pre>
  * <p>
  * The attributes for each restriction depend on the restriction type.
+ * <p>
+ * <ul>
+ * <li><code>key</code>, <code>title</code> and <code>restrictionType</code> are mandatory.</li>
+ * <li><code>entries</code> and <code>entryValues</code> are required if <code>restrictionType
+ * </code> is <code>choice</code> or <code>multi-select</code>.</li>
+ * <li><code>defaultValue</code> is optional and its type depends on the
+ * <code>restrictionType</code></li>
+ * <li><code>hidden</code> type must have a <code>defaultValue</code> and will
+ * not be shown to the administrator. It can be used to pass along data that cannot be modified,
+ * such as a version code.</li>
+ * <li><code>description</code> is meant to describe the restriction in more detail to the
+ * administrator controlling the values, if the title is not sufficient.</li>
+ * </ul>
+ * <p>
+ * In your manifest's <code>application</code> section, add the meta-data tag to point to
+ * the restrictions XML file as shown below:
+ * <pre>
+ * &lt;application ... &gt;
+ *     &lt;meta-data android:name="android.content.APP_RESTRICTIONS"
+ *                   android:resource="@xml/app_restrictions" /&gt;
+ *     ...
+ * &lt;/application&gt;
+ * </pre>
  *
  * @see RestrictionEntry
  * @see AbstractRestrictionsProvider
+ * @see DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)
+ * @see DevicePolicyManager#setApplicationRestrictions(ComponentName, String, Bundle)
  */
 public class RestrictionsManager {
 
     private static final String TAG = "RestrictionsManager";
 
     /**
-     * Broadcast intent delivered when a response is received for a permission
-     * request. The response is not available for later query, so the receiver
-     * must persist and/or immediately act upon the response. The application
-     * should not interrupt the user by coming to the foreground if it isn't
+     * Broadcast intent delivered when a response is received for a permission request. The
+     * application should not interrupt the user by coming to the foreground if it isn't
      * currently in the foreground. It can either post a notification informing
      * the user of the response or wait until the next time the user launches the app.
      * <p>
@@ -89,9 +129,32 @@
             "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
 
     /**
+     * Broadcast intent sent to the Restrictions Provider to handle a permission request from
+     * an app. It will have the following extras: {@link #EXTRA_PACKAGE_NAME},
+     * {@link #EXTRA_REQUEST_TYPE} and {@link #EXTRA_REQUEST_BUNDLE}. The Restrictions Provider
+     * will handle the request and respond back to the RestrictionsManager, when a response is
+     * available, by calling {@link #notifyPermissionResponse}.
+     * <p>
+     * The BroadcastReceiver must require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN}
+     * permission to ensure that only the system can send the broadcast.
+     */
+    public static final String ACTION_REQUEST_PERMISSION =
+            "android.content.action.REQUEST_PERMISSION";
+
+    /**
      * The package name of the application making the request.
      */
-    public static final String EXTRA_PACKAGE_NAME = "package_name";
+    public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
+
+    /**
+     * The request type passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     */
+    public static final String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
+
+    /**
+     * The request bundle passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     */
+    public static final String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
 
     /**
      * Contains a response from the administrator for specific request.
@@ -101,7 +164,7 @@
      * <li>{@link #RESPONSE_KEY_RESULT}: The response result.</li>
      * </ul>
      */
-    public static final String EXTRA_RESPONSE_BUNDLE = "response";
+    public static final String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
 
     /**
      * Request type for a simple question, with a possible title and icon.
@@ -113,7 +176,7 @@
      * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
      * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}.
      */
-    public static final String REQUEST_TYPE_QUESTION = "android.request.type.question";
+    public static final String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
 
     /**
      * Request type for a local password challenge. This is a way for an app to ask
@@ -204,26 +267,19 @@
     public static final String REQUEST_KEY_DENY_LABEL = "android.request.deny_label";
 
     /**
-     * Key for requestor's name contained in the request bundle. This value is not specified by
-     * the application. It is automatically inserted into the Bundle by the Restrictions Provider
-     * before it is sent to the administrator.
+     * Key for issuing a new request, contained in the request bundle. If this is set to true,
+     * the Restrictions Provider must make a new request. If it is false or not specified, then
+     * the Restrictions Provider can return a cached response that has the same requestId, if
+     * available. If there's no cached response, it will issue a new one to the administrator.
      * <p>
-     * Type: String
+     * Type: boolean
      */
-    public static final String REQUEST_KEY_REQUESTOR_NAME = "android.request.requestor";
+    public static final String REQUEST_KEY_NEW_REQUEST = "android.request.new_request";
 
     /**
-     * Key for requestor's device name contained in the request bundle. This value is not specified
-     * by the application. It is automatically inserted into the Bundle by the Restrictions Provider
-     * before it is sent to the administrator.
-     * <p>
-     * Type: String
-     */
-    public static final String REQUEST_KEY_DEVICE_NAME = "android.request.device";
-
-    /**
-     * Key for the response in the response bundle sent to the application, for a permission
-     * request.
+     * Key for the response result in the response bundle sent to the application, for a permission
+     * request. It indicates the status of the request. In some cases an additional message might
+     * be available in {@link #RESPONSE_KEY_MESSAGE}, to be displayed to the user.
      * <p>
      * Type: int
      * <p>
@@ -249,8 +305,8 @@
     public static final int RESULT_NO_RESPONSE = 3;
 
     /**
-     * Response result value indicating that the request is unknown, when returned through a
-     * call to #getPendingResponse
+     * Response result value indicating that the request is unknown, when it's not a new
+     * request.
      */
     public static final int RESULT_UNKNOWN_REQUEST = 4;
 
@@ -258,7 +314,7 @@
      * Response result value indicating an error condition. Additional error code might be available
      * in the response bundle, for the key {@link #RESPONSE_KEY_ERROR_CODE}. There might also be
      * an associated error message in the response bundle, for the key
-     * {@link #RESPONSE_KEY_ERROR_MESSAGE}.
+     * {@link #RESPONSE_KEY_MESSAGE}.
      */
     public static final int RESULT_ERROR = 5;
 
@@ -294,11 +350,11 @@
     public static final String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode";
 
     /**
-     * Key for the optional error message in the response bundle sent to the application.
+     * Key for the optional message in the response bundle sent to the application.
      * <p>
      * Type: String
      */
-    public static final String RESPONSE_KEY_ERROR_MESSAGE = "android.response.errormsg";
+    public static final String RESPONSE_KEY_MESSAGE = "android.response.msg";
 
     /**
      * Key for the optional timestamp of when the administrator responded to the permission
@@ -308,23 +364,19 @@
      */
     public static final String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp";
 
+    /**
+     * Name of the meta-data entry in the manifest that points to the XML file containing the
+     * application's available restrictions.
+     * @see #getManifestRestrictions(String)
+     */
+    public static final String META_DATA_APP_RESTRICTIONS = "android.content.APP_RESTRICTIONS";
+
+    private static final String TAG_RESTRICTION = "restriction";
+
     private final Context mContext;
     private final IRestrictionsManager mService;
 
     /**
-     * Callback object for returning a response for a request.
-     *
-     * @see #getPermissionResponse
-     */
-    public static abstract class PermissionResponseCallback {
-        /**
-         * Contains the response
-         * @param response
-         */
-        public abstract void onResponse(Bundle response);
-    }
-
-    /**
      * @hide
      */
     public RestrictionsManager(Context context, IRestrictionsManager service) {
@@ -350,11 +402,10 @@
     }
 
     /**
-     * Called by an application to check if there is an active restrictions provider. If
-     * there isn't, {@link #getPermissionResponse(String, PermissionResponseCallback)}
-     * and {@link #requestPermission(String, Bundle)} are not available.
+     * Called by an application to check if there is an active Restrictions Provider. If
+     * there isn't, {@link #requestPermission(String, Bundle)} is not available.
      *
-     * @return whether there is an active restrictions provider.
+     * @return whether there is an active Restrictions Provider.
      */
     public boolean hasRestrictionsProvider() {
         try {
@@ -374,13 +425,24 @@
      *
      * @param requestType The type of request. The type could be one of the
      * predefined types specified here or a custom type that the specific
-     * restrictions provider might understand. For custom types, the type name should be
+     * Restrictions Provider might understand. For custom types, the type name should be
      * namespaced to avoid collisions with predefined types and types specified by
-     * other restrictions providers.
+     * other Restrictions Providers.
      * @param request A Bundle containing the data corresponding to the specified request
      * type. The keys for the data in the bundle depend on the request type.
+     *
+     * @throws IllegalArgumentException if any of the required parameters are missing.
      */
     public void requestPermission(String requestType, Bundle request) {
+        if (requestType == null) {
+            throw new NullPointerException("requestType cannot be null");
+        }
+        if (request == null) {
+            throw new NullPointerException("request cannot be null");
+        }
+        if (!request.containsKey(REQUEST_KEY_ID)) {
+            throw new IllegalArgumentException("REQUEST_KEY_ID must be specified");
+        }
         try {
             if (mService != null) {
                 mService.requestPermission(mContext.getPackageName(), requestType, request);
@@ -391,41 +453,27 @@
     }
 
     /**
-     * Called by an application to query for any available response from the restrictions provider
-     * for the given requestId. The call returns immediately and the response will be returned
-     * via the provided callback. This does not initiate a new request and does not wait
-     * for a response to be received. It merely returns any previously received response
-     * or indicates if there was no available response. If there are multiple responses
-     * available for the same request ID, the most recent one is returned.
+     * Called by the Restrictions Provider to deliver a response to an application.
      *
-     * @param requestId The ID of the original request made via
-     * {@link #requestPermission(String, Bundle)}. It's possible to also query for responses
-     * to requests made on a different device with the same requestId, if the Restrictions
-     * Provider happens to sync responses across devices with the same account managed by the
-     * restrictions provider.
-     * @param callback The response is returned via the callback object. Cannot be null.
-     */
-    public void getPermissionResponse(String requestId, PermissionResponseCallback callback) {
-        if (requestId == null || callback == null) {
-            throw new NullPointerException("requestId or callback cannot be null");
-        }
-        try {
-            if (mService != null) {
-                mService.getPermissionResponse(mContext.getPackageName(), requestId,
-                        new PermissionResponseCallbackWrapper(callback));
-            }
-        } catch (RemoteException re) {
-            Log.w(TAG, "Couldn't reach service");
-        }
-    }
-
-    /**
-     * Called by the restrictions provider to deliver a response to an application.
-     *
-     * @param packageName the application to deliver the response to.
+     * @param packageName the application to deliver the response to. Cannot be null.
      * @param response the Bundle containing the response status, request ID and other information.
+     *                 Cannot be null.
+     *
+     * @throws IllegalArgumentException if any of the required parameters are missing.
      */
     public void notifyPermissionResponse(String packageName, Bundle response) {
+        if (packageName == null) {
+            throw new NullPointerException("packageName cannot be null");
+        }
+        if (response == null) {
+            throw new NullPointerException("request cannot be null");
+        }
+        if (!response.containsKey(REQUEST_KEY_ID)) {
+            throw new IllegalArgumentException("REQUEST_KEY_ID must be specified");
+        }
+        if (!response.containsKey(RESPONSE_KEY_RESULT)) {
+            throw new IllegalArgumentException("RESPONSE_KEY_RESULT must be specified");
+        }
         try {
             if (mService != null) {
                 mService.notifyPermissionResponse(packageName, response);
@@ -444,22 +492,118 @@
      * in the manifest, or null if none was specified.
      */
     public List<RestrictionEntry> getManifestRestrictions(String packageName) {
-        // TODO:
-        return null;
+        ApplicationInfo appInfo = null;
+        try {
+            appInfo = mContext.getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.GET_META_DATA);
+        } catch (NameNotFoundException pnfe) {
+            throw new IllegalArgumentException("No such package " + packageName);
+        }
+        if (appInfo == null || !appInfo.metaData.containsKey(META_DATA_APP_RESTRICTIONS)) {
+            return null;
+        }
+
+        XmlResourceParser xml =
+                appInfo.loadXmlMetaData(mContext.getPackageManager(), META_DATA_APP_RESTRICTIONS);
+        List<RestrictionEntry> restrictions = loadManifestRestrictions(packageName, xml);
+
+        return restrictions;
     }
 
-    private static class PermissionResponseCallbackWrapper
-            extends IPermissionResponseCallback.Stub {
+    private List<RestrictionEntry> loadManifestRestrictions(String packageName,
+            XmlResourceParser xml) {
+        Context appContext;
+        try {
+            appContext = mContext.createPackageContext(packageName, 0 /* flags */);
+        } catch (NameNotFoundException nnfe) {
+            return null;
+        }
+        ArrayList<RestrictionEntry> restrictions = new ArrayList<RestrictionEntry>();
+        RestrictionEntry restriction;
 
-        private PermissionResponseCallback mCallback;
-
-        PermissionResponseCallbackWrapper(PermissionResponseCallback callback) {
-            mCallback = callback;
+        try {
+            int tagType = xml.next();
+            while (tagType != XmlPullParser.END_DOCUMENT) {
+                if (tagType == XmlPullParser.START_TAG) {
+                    if (xml.getName().equals(TAG_RESTRICTION)) {
+                        AttributeSet attrSet = Xml.asAttributeSet(xml);
+                        if (attrSet != null) {
+                            TypedArray a = appContext.obtainStyledAttributes(attrSet,
+                                    com.android.internal.R.styleable.RestrictionEntry);
+                            restriction = loadRestriction(appContext, a);
+                            if (restriction != null) {
+                                restrictions.add(restriction);
+                            }
+                        }
+                    }
+                }
+                tagType = xml.next();
+            }
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "Reading restriction metadata for " + packageName, e);
+            return null;
+        } catch (IOException e) {
+            Log.w(TAG, "Reading restriction metadata for " + packageName, e);
+            return null;
         }
 
-        @Override
-        public void onResponse(Bundle response) {
-            mCallback.onResponse(response);
+        return restrictions;
+    }
+
+    private RestrictionEntry loadRestriction(Context appContext, TypedArray a) {
+        String key = a.getString(R.styleable.RestrictionEntry_key);
+        int restrictionType = a.getInt(
+                R.styleable.RestrictionEntry_restrictionType, -1);
+        String title = a.getString(R.styleable.RestrictionEntry_title);
+        String description = a.getString(R.styleable.RestrictionEntry_description);
+        int entries = a.getResourceId(R.styleable.RestrictionEntry_entries, 0);
+        int entryValues = a.getResourceId(R.styleable.RestrictionEntry_entryValues, 0);
+
+        if (restrictionType == -1) {
+            Log.w(TAG, "restrictionType cannot be omitted");
+            return null;
         }
+
+        if (key == null) {
+            Log.w(TAG, "key cannot be omitted");
+            return null;
+        }
+
+        RestrictionEntry restriction = new RestrictionEntry(restrictionType, key);
+        restriction.setTitle(title);
+        restriction.setDescription(description);
+        if (entries != 0) {
+            restriction.setChoiceEntries(appContext, entries);
+        }
+        if (entryValues != 0) {
+            restriction.setChoiceValues(appContext, entryValues);
+        }
+        // Extract the default value based on the type
+        switch (restrictionType) {
+            case RestrictionEntry.TYPE_NULL: // hidden
+            case RestrictionEntry.TYPE_STRING:
+            case RestrictionEntry.TYPE_CHOICE:
+                restriction.setSelectedString(
+                        a.getString(R.styleable.RestrictionEntry_defaultValue));
+                break;
+            case RestrictionEntry.TYPE_INTEGER:
+                restriction.setIntValue(
+                        a.getInt(R.styleable.RestrictionEntry_defaultValue, 0));
+                break;
+            case RestrictionEntry.TYPE_MULTI_SELECT:
+                int resId = a.getResourceId(R.styleable.RestrictionEntry_defaultValue, 0);
+                if (resId != 0) {
+                    restriction.setAllSelectedStrings(
+                            appContext.getResources().getStringArray(resId));
+                }
+                break;
+            case RestrictionEntry.TYPE_BOOLEAN:
+                restriction.setSelectedState(
+                        a.getBoolean(R.styleable.RestrictionEntry_defaultValue, false));
+                break;
+            default:
+                Log.w(TAG, "Unknown restriction type " + restrictionType);
+        }
+        return restriction;
     }
 }
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 8f0c249..49ffef2 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -185,7 +185,9 @@
     public ConfigurationInfo[] configPreferences;
 
     /**
-     * The features that this application has said it requires.
+     * Features that this application has requested.
+     *
+     * @see FeatureInfo#FLAG_REQUIRED
      */
     public FeatureInfo[] reqFeatures;
 
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 348a7ad..df82d26 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -42,17 +42,17 @@
  * intervention to complete the installation.
  * <p>
  * Sessions can install brand new apps, upgrade existing apps, or add new splits
- * onto an existing app.
+ * into an existing app.
  * <p>
- * Apps packaged into multiple split APKs always consist of a single "base" APK
+ * Apps packaged as multiple split APKs always consist of a single "base" APK
  * (with a {@code null} split name) and zero or more "split" APKs (with unique
  * split names). Any subset of these APKs can be installed together, as long as
  * the following constraints are met:
  * <ul>
  * <li>All APKs must have the exact same package name, version code, and signing
  * certificates.
- * <li>All installations must contain a single base APK.
  * <li>All APKs must have unique split names.
+ * <li>All installations must contain a single base APK.
  * </ul>
  */
 public class PackageInstaller {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 052c2ca..37df29a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1639,21 +1639,19 @@
     public abstract String[] canonicalToCurrentPackageNames(String[] names);
 
     /**
-     * Return a "good" intent to launch a front-door activity in a package,
-     * for use for example to implement an "open" button when browsing through
-     * packages.  The current implementation will look first for a main
-     * activity in the category {@link Intent#CATEGORY_INFO}, next for a
-     * main activity in the category {@link Intent#CATEGORY_LAUNCHER}, or return
-     * null if neither are found.
-     *
-     * <p>Throws {@link NameNotFoundException} if a package with the given
-     * name cannot be found on the system.
+     * Returns a "good" intent to launch a front-door activity in a package.
+     * This is used, for example, to implement an "open" button when browsing
+     * through packages.  The current implementation looks first for a main
+     * activity in the category {@link Intent#CATEGORY_INFO}, and next for a
+     * main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
+     * <code>null</code> if neither are found.
      *
      * @param packageName The name of the package to inspect.
      *
-     * @return Returns either a fully-qualified Intent that can be used to
-     * launch the main activity in the package, or null if the package does
-     * not contain such an activity.
+     * @return A fully-qualified {@link Intent} that can be used to launch the
+     * main activity in the package. Returns <code>null</code> if the package
+     * does not contain such an activity, or if <em>packageName</em> is not
+     * recognized. 
      */
     public abstract Intent getLaunchIntentForPackage(String packageName);
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7bfe55d..43c2b15 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -82,9 +82,22 @@
 import java.util.zip.ZipEntry;
 
 /**
- * Package archive parsing
+ * Parser for package files (APKs) on disk. This supports apps packaged either
+ * as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
+ * APKs in a single directory.
+ * <p>
+ * Apps packaged as multiple APKs always consist of a single "base" APK (with a
+ * {@code null} split name) and zero or more "split" APKs (with unique split
+ * names). Any subset of those split APKs are a valid install, as long as the
+ * following constraints are met:
+ * <ul>
+ * <li>All APKs must have the exact same package name, version code, and signing
+ * certificates.
+ * <li>All APKs must have unique split names.
+ * <li>All installations must contain a single base APK.
+ * </ul>
  *
- * {@hide}
+ * @hide
  */
 public class PackageParser {
     private static final boolean DEBUG_JAR = false;
@@ -92,6 +105,7 @@
     private static final boolean DEBUG_BACKUP = false;
 
     // TODO: switch outError users to PackageParserException
+    // TODO: refactor "codePath" to "apkPath"
 
     /** File name in an APK for the Android manifest. */
     private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
@@ -247,6 +261,7 @@
         /** Paths of any split APKs, ordered by parsed splitName */
         public final String[] splitCodePaths;
 
+        public final boolean coreApp;
         public final boolean multiArch;
 
         private PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
@@ -259,6 +274,7 @@
             this.codePath = codePath;
             this.baseCodePath = baseApk.codePath;
             this.splitCodePaths = splitCodePaths;
+            this.coreApp = baseApk.coreApp;
             this.multiArch = baseApk.multiArch;
         }
 
@@ -283,11 +299,12 @@
         public final int installLocation;
         public final VerifierInfo[] verifiers;
         public final Signature[] signatures;
+        public final boolean coreApp;
         public final boolean multiArch;
 
         public ApkLite(String codePath, String packageName, String splitName, int versionCode,
                 int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
-                boolean multiArch) {
+                boolean coreApp, boolean multiArch) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -295,6 +312,7 @@
             this.installLocation = installLocation;
             this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
             this.signatures = signatures;
+            this.coreApp = coreApp;
             this.multiArch = multiArch;
         }
     }
@@ -322,6 +340,11 @@
         mSeparateProcesses = procs;
     }
 
+    /**
+     * Flag indicating this parser should only consider apps with
+     * {@code coreApp} manifest attribute to be valid apps. This is useful when
+     * creating a minimalist boot environment.
+     */
     public void setOnlyCoreApps(boolean onlyCoreApps) {
         mOnlyCoreApps = onlyCoreApps;
     }
@@ -401,7 +424,7 @@
             pi.gids = gids;
         }
         if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) {
-            int N = p.configPreferences.size();
+            int N = p.configPreferences != null ? p.configPreferences.size() : 0;
             if (N > 0) {
                 pi.configPreferences = new ConfigurationInfo[N];
                 p.configPreferences.toArray(pi.configPreferences);
@@ -591,6 +614,17 @@
         }
     }
 
+    /**
+     * Parse only lightweight details about the package at the given location.
+     * Automatically detects if the package is a monolithic style (single APK
+     * file) or cluster style (directory of APKs).
+     * <p>
+     * This performs sanity checking on cluster style packages, such as
+     * requiring identical package name and version codes, a single base APK,
+     * and unique split names.
+     *
+     * @see PackageParser#parsePackage(File, int)
+     */
     public static PackageLite parsePackageLite(File packageFile, int flags)
             throws PackageParserException {
         if (packageFile.isDirectory()) {
@@ -677,6 +711,20 @@
         return new PackageLite(codePath, baseApk, splitNames, splitCodePaths);
     }
 
+    /**
+     * Parse the package at the given location. Automatically detects if the
+     * package is a monolithic style (single APK file) or cluster style
+     * (directory of APKs).
+     * <p>
+     * This performs sanity checking on cluster style packages, such as
+     * requiring identical package name and version codes, a single base APK,
+     * and unique split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(Package, int)}.
+     *
+     * @see #parsePackageLite(File, int)
+     */
     public Package parsePackage(File packageFile, int flags) throws PackageParserException {
         if (packageFile.isDirectory()) {
             return parseClusterPackage(packageFile, flags);
@@ -697,6 +745,11 @@
     private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
         final PackageLite lite = parseClusterPackageLite(packageDir, 0);
 
+        if (mOnlyCoreApps && !lite.coreApp) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Not a coreApp: " + packageDir);
+        }
+
         final File baseApk = new File(lite.baseCodePath);
         final Package pkg = parseBaseApk(baseApk, flags);
         if (pkg == null) {
@@ -705,12 +758,13 @@
         }
 
         if (!ArrayUtils.isEmpty(lite.splitNames)) {
+            final int num = lite.splitNames.length;
             pkg.splitNames = lite.splitNames;
             pkg.splitCodePaths = lite.splitCodePaths;
+            pkg.splitFlags = new int[num];
 
-            for (String splitCodePath : lite.splitCodePaths) {
-                final File splitApk = new File(splitCodePath);
-                parseSplitApk(pkg, splitApk, flags);
+            for (int i = 0; i < num; i++) {
+                parseSplitApk(pkg, i, flags);
             }
         }
 
@@ -730,115 +784,195 @@
      */
     @Deprecated
     public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
-        final Package pkg = parseBaseApk(apkFile, flags);
-        if (pkg == null) {
-            throw new PackageParserException(mParseError, "Failed to parse " + apkFile);
+        if (mOnlyCoreApps) {
+            final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
+            if (!lite.coreApp) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "Not a coreApp: " + apkFile);
+            }
         }
 
+        final Package pkg = parseBaseApk(apkFile, flags);
         pkg.codePath = apkFile.getAbsolutePath();
         return pkg;
     }
 
-    private Package parseBaseApk(File apkFile, int flags) {
-        final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
+    private Package parseBaseApk(File apkFile, int flags) throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
 
         mParseError = PackageManager.INSTALL_SUCCEEDED;
-
-        final String apkPath = apkFile.getAbsolutePath();
         mArchiveSourcePath = apkFile.getAbsolutePath();
-        if (!apkFile.isFile()) {
-            Slog.w(TAG, "Skipping dir: " + apkPath);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-            return null;
-        }
-        if (!isApkFile(apkFile) && (flags & PARSE_MUST_BE_APK) != 0) {
-            if ((flags&PARSE_IS_SYSTEM) == 0) {
-                // We expect to have non-.apk files in the system dir,
-                // so don't warn about them.
-                Slog.w(TAG, "Skipping non-package file: " + apkPath);
-            }
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-            return null;
+
+        if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkFile(apkFile)) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                    "Invalid package file: " + apkPath);
         }
 
-        if (DEBUG_JAR)
-            Slog.d(TAG, "Scanning package: " + apkPath);
+        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
 
-        XmlResourceParser parser = null;
-        AssetManager assmgr = null;
+        AssetManager assets = null;
         Resources res = null;
-        boolean assetError = true;
+        XmlResourceParser parser = null;
         try {
-            assmgr = new AssetManager();
-            int cookie = assmgr.addAssetPath(apkPath);
-            if (cookie != 0) {
-                res = new Resources(assmgr, mMetrics, null);
-                assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                        Build.VERSION.RESOURCES_SDK_INT);
-                parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
-                assetError = false;
-            } else {
-                Slog.w(TAG, "Failed adding asset path:" + apkPath);
+            assets = new AssetManager();
+            int cookie = assets.addAssetPath(apkPath);
+            if (cookie == 0) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Failed adding asset path: " + apkPath);
             }
-        } catch (Exception e) {
-            Slog.w(TAG, "Unable to read AndroidManifest.xml of " + apkPath, e);
-        }
-        if (assetError) {
-            if (assmgr != null) assmgr.close();
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
-            return null;
-        }
-        String[] errorText = new String[1];
-        Package pkg = null;
-        Exception errorException = null;
-        try {
-            // XXXX todo: need to figure out correct configuration.
-            pkg = parseBaseApk(res, parser, flags, trustedOverlay, errorText);
-        } catch (Exception e) {
-            errorException = e;
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
-        }
 
-        if (pkg == null) {
-            // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
-            // just means to skip this app so don't make a fuss about it.
-            if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
-                if (errorException != null) {
-                    Slog.w(TAG, apkPath, errorException);
-                } else {
-                    Slog.w(TAG, apkPath + " (at "
-                            + parser.getPositionDescription()
-                            + "): " + errorText[0]);
-                }
-                if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
-                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-                }
+            res = new Resources(assets, mMetrics, null);
+            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    Build.VERSION.RESOURCES_SDK_INT);
+            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+
+            final String[] outError = new String[1];
+            final Package pkg = parseBaseApk(res, parser, flags, outError);
+            if (pkg == null) {
+                throw new PackageParserException(mParseError,
+                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
             }
-            parser.close();
-            assmgr.close();
-            return null;
+
+            pkg.baseCodePath = apkPath;
+            pkg.mSignatures = null;
+
+            // TODO: Remove this when the WebView can load resources dynamically. b/11505352
+            pkg.usesOptionalLibraries = ArrayUtils.add(pkg.usesOptionalLibraries,
+                    "com.android.webview");
+
+            return pkg;
+
+        } catch (PackageParserException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+            IoUtils.closeQuietly(assets);
         }
-
-        parser.close();
-        assmgr.close();
-
-        pkg.baseCodePath = apkPath;
-        pkg.mSignatures = null;
-
-        // TODO: Remove this when the WebView can load resources dynamically. b/11505352
-        if (pkg.usesOptionalLibraries == null) {
-            pkg.usesOptionalLibraries = new ArrayList<String>();
-        }
-        pkg.usesOptionalLibraries.add("com.android.webview");
-
-        return pkg;
     }
 
-    private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException {
-        final String splitCodePath = apkFile.getAbsolutePath();
-        mArchiveSourcePath = apkFile.getAbsolutePath();
+    private void parseSplitApk(Package pkg, int splitIndex, int flags)
+            throws PackageParserException {
+        final String apkPath = pkg.splitCodePaths[splitIndex];
+        final File apkFile = new File(apkPath);
 
-        // TODO: expand split APK parsing
+        mParseError = PackageManager.INSTALL_SUCCEEDED;
+        mArchiveSourcePath = apkPath;
+
+        if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkFile(apkFile)) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                    "Invalid package file: " + apkPath);
+        }
+
+        if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+
+        AssetManager assets = null;
+        Resources res = null;
+        XmlResourceParser parser = null;
+        try {
+            assets = new AssetManager();
+            int cookie = assets.addAssetPath(apkPath);
+            if (cookie == 0) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Failed adding asset path: " + apkPath);
+            }
+
+            res = new Resources(assets, mMetrics, null);
+            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    Build.VERSION.RESOURCES_SDK_INT);
+            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+
+            final String[] outError = new String[1];
+            pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError);
+            if (pkg == null) {
+                throw new PackageParserException(mParseError,
+                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
+            }
+
+        } catch (PackageParserException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+            IoUtils.closeQuietly(assets);
+        }
+    }
+
+    /**
+     * Parse the manifest of a <em>split APK</em>.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable
+     * of doing, so many valid features of a base APK have been carefully
+     * omitted here.
+     */
+    private Package parseSplitApk(Package pkg, Resources res, XmlResourceParser parser, int flags,
+            int splitIndex, String[] outError) throws XmlPullParserException, IOException,
+            PackageParserException {
+        AttributeSet attrs = parser;
+
+        // We parsed manifest tag earlier; just skip past it
+        parsePackageSplitNames(parser, attrs, flags);
+
+        mParseInstrumentationArgs = null;
+        mParseActivityArgs = null;
+        mParseServiceArgs = null;
+        mParseProviderArgs = null;
+
+        int type;
+
+        boolean foundApp = false;
+
+        int outerDepth = parser.getDepth();
+        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("application")) {
+                if (foundApp) {
+                    if (RIGID_PARSER) {
+                        outError[0] = "<manifest> has more than one <application>";
+                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                        return null;
+                    } else {
+                        Slog.w(TAG, "<manifest> has more than one <application>");
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                }
+
+                foundApp = true;
+                if (!parseSplitApplication(pkg, res, parser, attrs, flags, splitIndex, outError)) {
+                    return null;
+                }
+
+            } else if (RIGID_PARSER) {
+                outError[0] = "Bad element under <manifest>: "
+                    + parser.getName();
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                return null;
+
+            } else {
+                Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+                        + " at " + mArchiveSourcePath + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            }
+        }
+
+        if (!foundApp) {
+            outError[0] = "<manifest> does not contain an <application>";
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
+        }
+
+        return pkg;
     }
 
     /**
@@ -1001,14 +1135,14 @@
             throws PackageParserException {
         final String apkPath = apkFile.getAbsolutePath();
 
-        AssetManager assmgr = null;
+        AssetManager assets = null;
         XmlResourceParser parser = null;
         try {
-            assmgr = new AssetManager();
-            assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            assets = new AssetManager();
+            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                     Build.VERSION.RESOURCES_SDK_INT);
 
-            int cookie = assmgr.addAssetPath(apkPath);
+            int cookie = assets.addAssetPath(apkPath);
             if (cookie == 0) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                         "Failed to parse " + apkPath);
@@ -1017,8 +1151,8 @@
             final DisplayMetrics metrics = new DisplayMetrics();
             metrics.setToDefaults();
 
-            final Resources res = new Resources(assmgr, metrics, null);
-            parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+            final Resources res = new Resources(assets, metrics, null);
+            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
 
             // Only collect certificates on the manifest; does not validate
             // signatures across remainder of package.
@@ -1036,8 +1170,8 @@
             throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                     "Failed to parse " + apkPath, e);
         } finally {
-            if (parser != null) parser.close();
-            if (assmgr != null) assmgr.close();
+            IoUtils.closeQuietly(parser);
+            IoUtils.closeQuietly(assets);
         }
     }
 
@@ -1118,8 +1252,10 @@
 
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
         int versionCode = 0;
-        int numFound = 0;
+        boolean coreApp = false;
         boolean multiArch = false;
+
+        int numFound = 0;
         for (int i = 0; i < attrs.getAttributeCount(); i++) {
             String attr = attrs.getAttributeName(i);
             if (attr.equals("installLocation")) {
@@ -1129,8 +1265,11 @@
             } else if (attr.equals("versionCode")) {
                 versionCode = attrs.getAttributeIntValue(i, 0);
                 numFound++;
+            } else if (attr.equals("coreApp")) {
+                coreApp = attrs.getAttributeBooleanValue(i, false);
+                numFound++;
             }
-            if (numFound >= 2) {
+            if (numFound >= 3) {
                 break;
             }
         }
@@ -1165,7 +1304,7 @@
         }
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
-                installLocation, verifiers, signatures, multiArch);
+                installLocation, verifiers, signatures, coreApp, multiArch);
     }
 
     /**
@@ -1180,8 +1319,16 @@
         return new Signature(sig);
     }
 
+    /**
+     * Parse the manifest of a <em>base APK</em>.
+     * <p>
+     * When adding new features, carefully consider if they should also be
+     * supported by split APKs.
+     */
     private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
-            boolean trustedOverlay, String[] outError) throws XmlPullParserException, IOException {
+            String[] outError) throws XmlPullParserException, IOException {
+        final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
+
         AttributeSet attrs = parser;
 
         mParseInstrumentationArgs = null;
@@ -1202,14 +1349,6 @@
 
         int type;
 
-        if (mOnlyCoreApps) {
-            boolean core = attrs.getAttributeBooleanValue(null, "coreApp", false);
-            if (!core) {
-                mParseError = PackageManager.INSTALL_SUCCEEDED;
-                return null;
-            }
-        }
-
         if (!TextUtils.isEmpty(splitName)) {
             outError[0] = "Expected base APK, but found split " + splitName;
             mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
@@ -1290,7 +1429,7 @@
                 }
 
                 foundApp = true;
-                if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
+                if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
                     return null;
                 }
             } else if (tagName.equals("overlay")) {
@@ -1362,7 +1501,7 @@
                     cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
                 }
                 sa.recycle();
-                pkg.configPreferences.add(cPref);
+                pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
 
                 XmlUtils.skipCurrentTag(parser);
 
@@ -1385,15 +1524,12 @@
                     fi.flags |= FeatureInfo.FLAG_REQUIRED;
                 }
                 sa.recycle();
-                if (pkg.reqFeatures == null) {
-                    pkg.reqFeatures = new ArrayList<FeatureInfo>();
-                }
-                pkg.reqFeatures.add(fi);
-                
+                pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
+
                 if (fi.name == null) {
                     ConfigurationInfo cPref = new ConfigurationInfo();
                     cPref.reqGlEsVersion = fi.reqGlEsVersion;
-                    pkg.configPreferences.add(cPref);
+                    pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
                 }
 
                 XmlUtils.skipCurrentTag(parser);
@@ -2204,7 +2340,14 @@
         return a;
     }
 
-    private boolean parseApplication(Package owner, Resources res,
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>base APK</em> manifest.
+     * <p>
+     * When adding new features, carefully consider if they should also be
+     * supported by split APKs.
+     */
+    private boolean parseBaseApplication(Package owner, Resources res,
             XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
         throws XmlPullParserException, IOException {
         final ApplicationInfo ai = owner.applicationInfo;
@@ -2324,7 +2467,7 @@
             ai.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE;
         }
 
-        boolean hardwareAccelerated = sa.getBoolean(
+        owner.baseHardwareAccelerated = sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
                 owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
 
@@ -2449,7 +2592,7 @@
             String tagName = parser.getName();
             if (tagName.equals("activity")) {
                 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
-                        hardwareAccelerated);
+                        owner.baseHardwareAccelerated);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2515,11 +2658,9 @@
                 sa.recycle();
 
                 if (lname != null) {
-                    if (owner.libraryNames == null) {
-                        owner.libraryNames = new ArrayList<String>();
-                    }
-                    if (!owner.libraryNames.contains(lname)) {
-                        owner.libraryNames.add(lname.intern());
+                    lname = lname.intern();
+                    if (!ArrayUtils.contains(owner.libraryNames, lname)) {
+                        owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);
                     }
                 }
 
@@ -2540,19 +2681,150 @@
                 sa.recycle();
 
                 if (lname != null) {
+                    lname = lname.intern();
                     if (req) {
-                        if (owner.usesLibraries == null) {
-                            owner.usesLibraries = new ArrayList<String>();
-                        }
-                        if (!owner.usesLibraries.contains(lname)) {
-                            owner.usesLibraries.add(lname.intern());
-                        }
+                        owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
                     } else {
-                        if (owner.usesOptionalLibraries == null) {
-                            owner.usesOptionalLibraries = new ArrayList<String>();
-                        }
-                        if (!owner.usesOptionalLibraries.contains(lname)) {
-                            owner.usesOptionalLibraries.add(lname.intern());
+                        owner.usesOptionalLibraries = ArrayUtils.add(
+                                owner.usesOptionalLibraries, lname);
+                    }
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals("uses-package")) {
+                // Dependencies for app installers; we don't currently try to
+                // enforce this.
+                XmlUtils.skipCurrentTag(parser);
+
+            } else {
+                if (!RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <application>: " + tagName
+                            + " at " + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <application>: " + tagName;
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>split APK</em> manifest.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable
+     * of doing, so many valid features of a base APK have been carefully
+     * omitted here.
+     */
+    private boolean parseSplitApplication(Package owner, Resources res, XmlPullParser parser,
+            AttributeSet attrs, int flags, int splitIndex, String[] outError)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestApplication);
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_hasCode, true)) {
+            owner.splitFlags[splitIndex] |= ApplicationInfo.FLAG_HAS_CODE;
+        }
+
+        final int innerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals("activity")) {
+                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
+                        owner.baseHardwareAccelerated);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.activities.add(a);
+
+            } else if (tagName.equals("receiver")) {
+                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.receivers.add(a);
+
+            } else if (tagName.equals("service")) {
+                Service s = parseService(owner, res, parser, attrs, flags, outError);
+                if (s == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.services.add(s);
+
+            } else if (tagName.equals("provider")) {
+                Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
+                if (p == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.providers.add(p);
+
+            } else if (tagName.equals("activity-alias")) {
+                Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.activities.add(a);
+
+            } else if (parser.getName().equals("meta-data")) {
+                // note: application meta-data is stored off to the side, so it can
+                // remain null in the primary copy (we like to avoid extra copies because
+                // it can be large)
+                if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
+                        outError)) == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+            } else if (tagName.equals("uses-library")) {
+                sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary);
+
+                // Note: don't allow this value to be a reference to a resource
+                // that may change.
+                String lname = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
+                boolean req = sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
+                        true);
+
+                sa.recycle();
+
+                if (lname != null) {
+                    lname = lname.intern();
+                    if (req) {
+                        // Upgrade to treat as stronger constraint
+                        owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
+                        owner.usesOptionalLibraries = ArrayUtils.remove(
+                                owner.usesOptionalLibraries, lname);
+                    } else {
+                        // Ignore if someone already defined as required
+                        if (!ArrayUtils.contains(owner.usesLibraries, lname)) {
+                            owner.usesOptionalLibraries = ArrayUtils.add(
+                                    owner.usesOptionalLibraries, lname);
                         }
                     }
                 }
@@ -3873,6 +4145,11 @@
         /** Paths of any split APKs, ordered by parsed splitName */
         public String[] splitCodePaths;
 
+        /** Flags of any split APKs; ordered by parsed splitName */
+        public int[] splitFlags;
+
+        public boolean baseHardwareAccelerated;
+
         // For now we only support one application per package.
         public final ApplicationInfo applicationInfo = new ApplicationInfo();
 
@@ -3941,15 +4218,10 @@
         // Whether an operation is currently pending on this package
         public boolean mOperationPending;
 
-        /*
-         *  Applications hardware preferences
-         */
-        public final ArrayList<ConfigurationInfo> configPreferences =
-                new ArrayList<ConfigurationInfo>();
+        // Applications hardware preferences
+        public ArrayList<ConfigurationInfo> configPreferences = null;
 
-        /*
-         *  Applications requested features
-         */
+        // Applications requested features
         public ArrayList<FeatureInfo> reqFeatures = null;
 
         public int installLocation;
@@ -3998,6 +4270,25 @@
             return paths;
         }
 
+        /**
+         * Filtered set of {@link #getAllCodePaths()} that excludes
+         * resource-only APKs.
+         */
+        public List<String> getAllCodePathsExcludingResourceOnly() {
+            ArrayList<String> paths = new ArrayList<>();
+            if ((applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+                paths.add(baseCodePath);
+            }
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                for (int i = 0; i < splitCodePaths.length; i++) {
+                    if ((splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+                        paths.add(splitCodePaths[i]);
+                    }
+                }
+            }
+            return paths;
+        }
+
         public void setPackageName(String newName) {
             packageName = newName;
             applicationInfo.packageName = newName;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 0c04401..3a30123 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -33,7 +33,7 @@
  * files that have been bundled with the application as a simple stream of
  * bytes.
  */
-public final class AssetManager {
+public final class AssetManager implements AutoCloseable {
     /* modes used when opening an asset */
 
     /**
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 9625578..38ddede 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1711,7 +1711,7 @@
 
             String locale = null;
             if (mConfiguration.locale != null) {
-                locale = adjustLanguageTag(localeToLanguageTag(mConfiguration.locale));
+                locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
             }
             int width, height;
             if (mMetrics.widthPixels >= mMetrics.heightPixels) {
@@ -1800,12 +1800,6 @@
         }
     }
 
-    // Locale.toLanguageTag() is not available in Java6. LayoutLib overrides
-    // this method to enable users to use Java6.
-    private String localeToLanguageTag(Locale locale) {
-        return locale.toLanguageTag();
-    }
-
     /**
      * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
      * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4c73e6a..eadfa73 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -2423,6 +2423,19 @@
             return Camera.this;
         }
 
+
+        /**
+         * Value equality check.
+         *
+         * @hide
+         */
+        public boolean same(Parameters other) {
+            if (this == other) {
+                return true;
+            }
+            return other != null && Parameters.this.mMap.equals(other.mMap);
+        }
+
         /**
          * Writes the current Parameters to the log.
          * @hide
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4b1659f..4976a48 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -131,6 +131,7 @@
     }
 
     private final CameraMetadataNative mProperties;
+    private List<CameraCharacteristics.Key<?>> mKeys;
     private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
     private List<CaptureResult.Key<?>> mAvailableResultKeys;
 
@@ -194,8 +195,20 @@
      */
     @Override
     public List<Key<?>> getKeys() {
-        // Force the javadoc for this function to show up on the CameraCharacteristics page
-        return super.getKeys();
+        // List of keys is immutable; cache the results after we calculate them
+        if (mKeys != null) {
+            return mKeys;
+        }
+
+        int[] filterTags = get(REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+        if (filterTags == null) {
+            throw new AssertionError("android.request.availableCharacteristicsKeys must be non-null"
+                    + " in the characteristics");
+        }
+
+        mKeys = Collections.unmodifiableList(
+                getKeysStatic(getClass(), getKeyClass(), this, filterTags));
+        return mKeys;
     }
 
     /**
@@ -218,8 +231,13 @@
             Object crKey = CaptureRequest.Key.class;
             Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
 
-            mAvailableRequestKeys = Collections.unmodifiableList(
-                    getAvailableKeyList(CaptureRequest.class, crKeyTyped));
+            int[] filterTags = get(REQUEST_AVAILABLE_REQUEST_KEYS);
+            if (filterTags == null) {
+                throw new AssertionError("android.request.availableRequestKeys must be non-null "
+                        + "in the characteristics");
+            }
+            mAvailableRequestKeys =
+                    getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags);
         }
         return mAvailableRequestKeys;
     }
@@ -244,8 +262,12 @@
             Object crKey = CaptureResult.Key.class;
             Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
 
-            mAvailableResultKeys = Collections.unmodifiableList(
-                    getAvailableKeyList(CaptureResult.class, crKeyTyped));
+            int[] filterTags = get(REQUEST_AVAILABLE_RESULT_KEYS);
+            if (filterTags == null) {
+                throw new AssertionError("android.request.availableResultKeys must be non-null "
+                        + "in the characteristics");
+            }
+            mAvailableResultKeys = getAvailableKeyList(CaptureResult.class, crKeyTyped, filterTags);
         }
         return mAvailableResultKeys;
     }
@@ -266,7 +288,7 @@
      * @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata
      */
     private <TKey> List<TKey>
-    getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass) {
+    getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags) {
 
         if (metadataClass.equals(CameraMetadata.class)) {
             throw new AssertionError(
@@ -277,7 +299,7 @@
         }
 
         List<TKey> staticKeyList = CameraCharacteristics.<TKey>getKeysStatic(
-                metadataClass, keyClass, /*instance*/null);
+                metadataClass, keyClass, /*instance*/null, filterTags);
         return Collections.unmodifiableList(staticKeyList);
     }
 
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index fa35f44..36b1089 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,11 +16,13 @@
 
 package android.hardware.camera2;
 
+import android.hardware.camera2.impl.CameraMetadataNative;
 import android.util.Log;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -101,7 +103,8 @@
     @SuppressWarnings("unchecked")
     public List<TKey> getKeys() {
         Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
-        return Collections.unmodifiableList(getKeysStatic(thisClass, getKeyClass(), this));
+        return Collections.unmodifiableList(
+                getKeysStatic(thisClass, getKeyClass(), this, /*filterTags*/null));
     }
 
     /**
@@ -111,14 +114,25 @@
      * <p>
      * Optionally, if {@code instance} is not null, then filter out any keys with null values.
      * </p>
+     *
+     * <p>
+     * Optionally, if {@code filterTags} is not {@code null}, then filter out any keys
+     * whose native {@code tag} is not in {@code filterTags}. The {@code filterTags} array will be
+     * sorted as a side effect.
+     * </p>
      */
      /*package*/ @SuppressWarnings("unchecked")
     static <TKey> ArrayList<TKey> getKeysStatic(
              Class<?> type, Class<TKey> keyClass,
-             CameraMetadata<TKey> instance) {
+             CameraMetadata<TKey> instance,
+             int[] filterTags) {
 
         if (VERBOSE) Log.v(TAG, "getKeysStatic for " + type);
 
+        if (filterTags != null) {
+            Arrays.sort(filterTags);
+        }
+
         ArrayList<TKey> keyList = new ArrayList<TKey>();
 
         Field[] fields = type.getDeclaredFields();
@@ -137,7 +151,15 @@
                 }
 
                 if (instance == null || instance.getProtected(key) != null) {
-                    keyList.add(key);
+                    if (shouldKeyBeAdded(key, filterTags)) {
+                        keyList.add(key);
+
+                        if (VERBOSE) {
+                            Log.v(TAG, "getKeysStatic - key was added - " + key);
+                        }
+                    } else if (VERBOSE) {
+                        Log.v(TAG, "getKeysStatic - key was filtered - " + key);
+                    }
                 }
             }
         }
@@ -145,6 +167,39 @@
         return keyList;
     }
 
+    @SuppressWarnings("rawtypes")
+    private static <TKey> boolean shouldKeyBeAdded(TKey key, int[] filterTags) {
+        if (key == null) {
+            throw new NullPointerException("key must not be null");
+        }
+
+        CameraMetadataNative.Key nativeKey;
+
+        /*
+         * Get the native key from the public api key
+         */
+        if (key instanceof CameraCharacteristics.Key) {
+            nativeKey = ((CameraCharacteristics.Key)key).getNativeKey();
+        } else if (key instanceof CaptureResult.Key) {
+            nativeKey = ((CaptureResult.Key)key).getNativeKey();
+        } else if (key instanceof CaptureRequest.Key) {
+            nativeKey = ((CaptureRequest.Key)key).getNativeKey();
+        } else {
+            // Reject fields that aren't a key
+            throw new IllegalArgumentException("key type must be that of a metadata key");
+        }
+
+        // No filtering necessary
+        if (filterTags == null) {
+            return true;
+        }
+
+        int keyTag = nativeKey.getTag();
+
+        // non-negative result is returned iff the value is in the array
+        return Arrays.binarySearch(filterTags, keyTag) >= 0;
+    }
+
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * The enum values below this point are generated from metadata
      * definitions in /system/media/camera/docs. Do not modify by hand or
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 6de5c25..ebab563 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -56,7 +56,6 @@
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Size;
 
 import com.android.internal.util.Preconditions;
@@ -79,7 +78,7 @@
         private final Class<T> mType;
         private final TypeReference<T> mTypeReference;
         private final String mName;
-
+        private final int mHash;
         /**
          * Visible for testing only.
          *
@@ -95,6 +94,7 @@
             mName = name;
             mType = type;
             mTypeReference = TypeReference.createSpecializedTypeReference(type);
+            mHash = mName.hashCode() ^ mTypeReference.hashCode();
         }
 
         /**
@@ -113,6 +113,7 @@
             mName = name;
             mType = (Class<T>)typeReference.getRawType();
             mTypeReference = typeReference;
+            mHash = mName.hashCode() ^ mTypeReference.hashCode();
         }
 
         /**
@@ -137,7 +138,7 @@
          */
         @Override
         public final int hashCode() {
-            return mName.hashCode() ^ mTypeReference.hashCode();
+            return mHash;
         }
 
         /**
@@ -156,6 +157,10 @@
                 return true;
             }
 
+            if (o == null || this.hashCode() != o.hashCode()) {
+                return false;
+            }
+
             Key<?> lhs;
 
             if (o instanceof CaptureResult.Key) {
@@ -337,11 +342,11 @@
     public <T> T get(Key<T> key) {
         Preconditions.checkNotNull(key, "key must not be null");
 
-        Pair<T, Boolean> override = getOverride(key);
-        if (override.second) {
-            return override.first;
+        // Check if key has been overridden to use a wrapper class on the java side.
+        GetCommand g = sGetCommandMap.get(key);
+        if (g != null) {
+            return (T) g.getValue(this, key);
         }
-
         return getBase(key);
     }
 
@@ -371,7 +376,9 @@
      * type to the key.
      */
     public <T> void set(Key<T> key, T value) {
-        if (setOverride(key, value)) {
+        SetCommand s = sSetCommandMap.get(key);
+        if (s != null) {
+            s.setValue(this, value);
             return;
         }
 
@@ -449,44 +456,119 @@
         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
         return marshaler.unmarshal(buffer);
     }
-    // Need overwrite some metadata that has different definitions between native
-    // and managed sides.
-    @SuppressWarnings("unchecked")
-    private <T> Pair<T, Boolean> getOverride(Key<T> key) {
-        T value = null;
-        boolean override = true;
 
-        if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
-            value = (T) getAvailableFormats();
-        } else if (key.equals(CaptureResult.STATISTICS_FACES)) {
-            value = (T) getFaces();
-        } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
-            value = (T) getFaceRectangles();
-        } else if (key.equals(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)) {
-            value = (T) getStreamConfigurationMap();
-        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) {
-            value = (T) getMaxRegions(key);
-        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB)) {
-            value = (T) getMaxRegions(key);
-        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) {
-            value = (T) getMaxRegions(key);
-        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW)) {
-            value = (T) getMaxNumOutputs(key);
-        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC)) {
-            value = (T) getMaxNumOutputs(key);
-        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING)) {
-            value = (T) getMaxNumOutputs(key);
-        } else if (key.equals(CaptureRequest.TONEMAP_CURVE)) {
-            value = (T) getTonemapCurve();
-        } else if (key.equals(CaptureResult.JPEG_GPS_LOCATION)) {
-            value = (T) getGpsLocation();
-        } else if (key.equals(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP)) {
-            value = (T) getLensShadingMap();
-        } else {
-            override = false;
-        }
-
-        return Pair.create(value, override);
+    // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden
+    // metadata.
+    private static final HashMap<Key<?>, GetCommand> sGetCommandMap =
+            new HashMap<Key<?>, GetCommand>();
+    static {
+        sGetCommandMap.put(
+                CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getAvailableFormats();
+                    }
+                });
+        sGetCommandMap.put(
+                CaptureResult.STATISTICS_FACES.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getFaceRectangles();
+                    }
+                });
+        sGetCommandMap.put(
+                CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getFaces();
+                    }
+                });
+        sGetCommandMap.put(
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getStreamConfigurationMap();
+                    }
+                });
+        sGetCommandMap.put(
+                CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMaxRegions(key);
+                    }
+                });
+        sGetCommandMap.put(
+                CameraCharacteristics.CONTROL_MAX_REGIONS_AWB.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMaxRegions(key);
+                    }
+                });
+        sGetCommandMap.put(
+                CameraCharacteristics.CONTROL_MAX_REGIONS_AF.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMaxRegions(key);
+                    }
+                });
+        sGetCommandMap.put(
+                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMaxNumOutputs(key);
+                    }
+                });
+        sGetCommandMap.put(
+                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMaxNumOutputs(key);
+                    }
+                });
+        sGetCommandMap.put(
+                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMaxNumOutputs(key);
+                    }
+                });
+        sGetCommandMap.put(
+                CaptureRequest.TONEMAP_CURVE.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getTonemapCurve();
+                    }
+                });
+        sGetCommandMap.put(
+                CaptureResult.JPEG_GPS_LOCATION.getNativeKey(), new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getGpsLocation();
+                    }
+                });
+        sGetCommandMap.put(
+                CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getLensShadingMap();
+                    }
+                });
     }
 
     private int[] getAvailableFormats() {
@@ -759,19 +841,37 @@
         writeValues(tag, values);
     }
 
-    // Set the camera metadata override.
-    private <T> boolean setOverride(Key<T> key, T value) {
-        if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
-            return setAvailableFormats((int[]) value);
-        } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
-            return setFaceRectangles((Rect[]) value);
-        } else if (key.equals(CaptureRequest.TONEMAP_CURVE)) {
-            return setTonemapCurve((TonemapCurve) value);
-        } else if (key.equals(CaptureResult.JPEG_GPS_LOCATION)) {
-            return setGpsLocation((Location) value);
-        }
-        // For other keys, set() falls back to setBase().
-        return false;
+    // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden
+    // metadata.
+    private static final HashMap<Key<?>, SetCommand> sSetCommandMap =
+            new HashMap<Key<?>, SetCommand>();
+    static {
+        sSetCommandMap.put(CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAvailableFormats((int[]) value);
+            }
+        });
+        sSetCommandMap.put(CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setFaceRectangles((Rect[]) value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setTonemapCurve((TonemapCurve) value);
+            }
+        });
+        sSetCommandMap.put(CaptureResult.JPEG_GPS_LOCATION.getNativeKey(), new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setGpsLocation((Location) value);
+            }
+        });
     }
 
     private boolean setAvailableFormats(int[] value) {
diff --git a/core/java/android/hardware/camera2/impl/GetCommand.java b/core/java/android/hardware/camera2/impl/GetCommand.java
new file mode 100644
index 0000000..a3c677a
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/GetCommand.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+/**
+ * Getter interface for use with Command pattern metadata value getters.
+ */
+public interface GetCommand {
+
+    /**
+     * Get the value from the given {@link CameraMetadataNative} object.
+     *
+     * @param metadata the {@link CameraMetadataNative} object to get the value from.
+     * @param key the {@link CameraMetadataNative.Key} to look up.
+     * @param <T> the type of the value.
+     * @return the value for a given {@link CameraMetadataNative.Key}.
+     */
+    public <T> T getValue(CameraMetadataNative metadata, CameraMetadataNative.Key<T> key);
+}
diff --git a/core/java/android/hardware/camera2/impl/SetCommand.java b/core/java/android/hardware/camera2/impl/SetCommand.java
new file mode 100644
index 0000000..82a01b2
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/SetCommand.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+/**
+ * Setter interface for use with Command pattern metadata value setters.
+ */
+public interface SetCommand {
+
+    /**
+     * Set the value in the given metadata.
+     *
+     * @param metadata {@link CameraMetadataNative} to set value in.
+     * @param value value to set.
+     * @param <T> type of the value to set.
+     */
+    public <T> void setValue(/*inout*/CameraMetadataNative metadata,
+                             T value);
+}
diff --git a/core/java/android/hardware/camera2/legacy/BurstHolder.java b/core/java/android/hardware/camera2/legacy/BurstHolder.java
index e35eb50..c141c51 100644
--- a/core/java/android/hardware/camera2/legacy/BurstHolder.java
+++ b/core/java/android/hardware/camera2/legacy/BurstHolder.java
@@ -17,6 +17,9 @@
 package android.hardware.camera2.legacy;
 
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.util.Log;
+import android.view.Surface;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -25,8 +28,8 @@
  * Immutable container for a burst of capture results.
  */
 public class BurstHolder {
-
-    private final ArrayList<CaptureRequest> mRequests;
+    private static final String TAG = "BurstHolder";
+    private final ArrayList<RequestHolder.Builder> mRequestBuilders;
     private final boolean mRepeating;
     private final int mRequestId;
 
@@ -38,7 +41,13 @@
      * @param requests a {@link java.util.List} of {@link CaptureRequest}s in this burst.
      */
     public BurstHolder(int requestId, boolean repeating, List<CaptureRequest> requests) {
-        mRequests = new ArrayList<CaptureRequest>(requests);
+        mRequestBuilders = new ArrayList<RequestHolder.Builder>();
+        int i = 0;
+        for (CaptureRequest r : requests) {
+            mRequestBuilders.add(new RequestHolder.Builder(requestId, /*subsequenceId*/i,
+                    /*request*/r, repeating));
+            ++i;
+        }
         mRepeating = repeating;
         mRequestId = requestId;
     }
@@ -61,7 +70,7 @@
      * Return the number of requests in this burst sequence.
      */
     public int getNumberOfRequests() {
-        return mRequests.size();
+        return mRequestBuilders.size();
     }
 
     /**
@@ -73,8 +82,8 @@
     public List<RequestHolder> produceRequestHolders(long frameNumber) {
         ArrayList<RequestHolder> holders = new ArrayList<RequestHolder>();
         int i = 0;
-        for (CaptureRequest r : mRequests) {
-            holders.add(new RequestHolder(mRequestId, i, r, mRepeating, frameNumber + i));
+        for (RequestHolder.Builder b : mRequestBuilders) {
+            holders.add(b.build(frameNumber + i));
             ++i;
         }
         return holders;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index a10a2af..88f95e1 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -28,15 +28,12 @@
 import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.utils.ListUtils;
-import android.hardware.camera2.utils.ParamsUtils;
 import android.util.Log;
-import android.util.Rational;
 import android.util.Size;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.android.internal.util.Preconditions.*;
 import static android.hardware.camera2.CaptureResult.*;
 
 /**
@@ -46,6 +43,36 @@
     private static final String TAG = "LegacyResultMapper";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
+    private LegacyRequest mCachedRequest = null;
+    private CameraMetadataNative mCachedResult = null;
+
+    /**
+     * Generate capture result metadata from the legacy camera request.
+     *
+     * <p>This method caches and reuses the result from the previous call to this method if
+     * the {@code parameters} of the subsequent {@link LegacyRequest} passed to this method
+     * have not changed.</p>
+     *
+     * @param legacyRequest a non-{@code null} legacy request containing the latest parameters
+     * @param timestamp the timestamp to use for this result in nanoseconds.
+     *
+     * @return {@link CameraMetadataNative} object containing result metadata.
+     */
+    public CameraMetadataNative cachedConvertResultMetadata(
+            LegacyRequest legacyRequest, long timestamp) {
+        if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) {
+            CameraMetadataNative newResult = new CameraMetadataNative(mCachedResult);
+
+            // sensor.timestamp
+            newResult.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
+            return newResult;
+        }
+
+        mCachedRequest = legacyRequest;
+        mCachedResult = convertResultMetadata(mCachedRequest, timestamp);
+        return mCachedResult;
+    }
+
     /**
      * Generate capture result metadata from the legacy camera request.
      *
diff --git a/core/java/android/hardware/camera2/legacy/RequestHolder.java b/core/java/android/hardware/camera2/legacy/RequestHolder.java
index e674736..9f27093 100644
--- a/core/java/android/hardware/camera2/legacy/RequestHolder.java
+++ b/core/java/android/hardware/camera2/legacy/RequestHolder.java
@@ -23,6 +23,8 @@
 
 import java.util.Collection;
 
+import static com.android.internal.util.Preconditions.*;
+
 /**
  * Immutable container for a single capture request and associated information.
  */
@@ -34,14 +36,131 @@
     private final int mRequestId;
     private final int mSubsequeceId;
     private final long mFrameNumber;
+    private final boolean mHasJpegTargets;
+    private final boolean mHasPreviewTargets;
 
-    RequestHolder(int requestId, int subsequenceId, CaptureRequest request, boolean repeating,
-                  long frameNumber) {
+    /**
+     * Returns true if the given surface requires jpeg buffers.
+     *
+     * @param s a {@link android.view.Surface} to check.
+     * @return true if the surface requires a jpeg buffer.
+     */
+    public static boolean jpegType(Surface s)
+            throws LegacyExceptionUtils.BufferQueueAbandonedException {
+        return LegacyCameraDevice.detectSurfaceType(s) ==
+                CameraMetadataNative.NATIVE_JPEG_FORMAT;
+    }
+
+    /**
+     * Returns true if the given surface requires non-jpeg buffer types.
+     *
+     * <p>
+     * "Jpeg buffer" refers to the buffers returned in the jpeg
+     * {@link android.hardware.Camera.PictureCallback}.  Non-jpeg buffers are created using a tee
+     * of the preview stream drawn to the surface
+     * set via {@link android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder)} or
+     * equivalent methods.
+     * </p>
+     * @param s a {@link android.view.Surface} to check.
+     * @return true if the surface requires a non-jpeg buffer type.
+     */
+    public static boolean previewType(Surface s)
+            throws LegacyExceptionUtils.BufferQueueAbandonedException {
+        return LegacyCameraDevice.detectSurfaceType(s) !=
+                CameraMetadataNative.NATIVE_JPEG_FORMAT;
+    }
+
+    /**
+     * Returns true if any of the surfaces targeted by the contained request require jpeg buffers.
+     */
+    private static boolean requestContainsJpegTargets(CaptureRequest request) {
+        for (Surface s : request.getTargets()) {
+            try {
+                if (jpegType(s)) {
+                    return true;
+                }
+            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+                Log.w(TAG, "Surface abandoned, skipping...", e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if any of the surfaces targeted by the contained request require a
+     * non-jpeg buffer type.
+     */
+    private static boolean requestContainsPreviewTargets(CaptureRequest request) {
+        for (Surface s : request.getTargets()) {
+            try {
+                if (previewType(s)) {
+                    return true;
+                }
+            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+                Log.w(TAG, "Surface abandoned, skipping...", e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * A builder class for {@link RequestHolder} objects.
+     *
+     * <p>
+     * This allows per-request queries to be cached for repeating {@link CaptureRequest} objects.
+     * </p>
+     */
+    public final static class Builder {
+        private final int mRequestId;
+        private final int mSubsequenceId;
+        private final CaptureRequest mRequest;
+        private final boolean mRepeating;
+        private final boolean mHasJpegTargets;
+        private final boolean mHasPreviewTargets;
+
+        /**
+         * Construct a new {@link Builder} to generate {@link RequestHolder} objects.
+         *
+         * @param requestId the ID to set in {@link RequestHolder} objects.
+         * @param subsequenceId the sequence ID to set in {@link RequestHolder} objects.
+         * @param request the original {@link CaptureRequest} to set in {@link RequestHolder}
+         *                objects.
+         * @param repeating {@code true} if the request is repeating.
+         */
+        public Builder(int requestId, int subsequenceId, CaptureRequest request,
+                       boolean repeating) {
+            checkNotNull(request, "request must not be null");
+            mRequestId = requestId;
+            mSubsequenceId = subsequenceId;
+            mRequest = request;
+            mRepeating = repeating;
+            mHasJpegTargets = requestContainsJpegTargets(mRequest);
+            mHasPreviewTargets = requestContainsPreviewTargets(mRequest);
+        }
+
+        /**
+         * Build a new {@link RequestHolder} using with parameters generated from this
+         *      {@link Builder}.
+         *
+         * @param frameNumber the {@code framenumber} to generate in the {@link RequestHolder}.
+         * @return a {@link RequestHolder} constructed with the {@link Builder}'s parameters.
+         */
+        public RequestHolder build(long frameNumber) {
+            return new RequestHolder(mRequestId, mSubsequenceId, mRequest, mRepeating, frameNumber,
+                    mHasJpegTargets, mHasPreviewTargets);
+        }
+    }
+
+    private RequestHolder(int requestId, int subsequenceId, CaptureRequest request,
+                          boolean repeating, long frameNumber, boolean hasJpegTargets,
+                          boolean hasPreviewTargets) {
         mRepeating = repeating;
         mRequest = request;
         mRequestId = requestId;
         mSubsequeceId = subsequenceId;
         mFrameNumber = frameNumber;
+        mHasJpegTargets = hasJpegTargets;
+        mHasPreviewTargets = hasPreviewTargets;
     }
 
     /**
@@ -90,86 +209,15 @@
      * Returns true if any of the surfaces targeted by the contained request require jpeg buffers.
      */
     public boolean hasJpegTargets() {
-        for (Surface s : getHolderTargets()) {
-            try {
-                if (jpegType(s)) {
-                    return true;
-                }
-            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
-                Log.w(TAG, "Surface abandoned, skipping...", e);
-            }
-        }
-        return false;
+        return mHasJpegTargets;
     }
 
     /**
      * Returns true if any of the surfaces targeted by the contained request require a
      * non-jpeg buffer type.
      */
-    public boolean hasPreviewTargets() {
-        for (Surface s : getHolderTargets()) {
-            try {
-                if (previewType(s)) {
-                    return true;
-                }
-            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
-                Log.w(TAG, "Surface abandoned, skipping...", e);
-            }
-        }
-        return false;
+    public boolean hasPreviewTargets(){
+        return mHasPreviewTargets;
     }
 
-    /**
-     * Return the first surface targeted by the contained request that requires a
-     * non-jpeg buffer type.
-     */
-    public Surface getFirstPreviewTarget() {
-        for (Surface s : getHolderTargets()) {
-            try {
-                if (previewType(s)) {
-                    return s;
-                }
-            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
-                Log.w(TAG, "Surface abandoned, skipping...", e);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns true if the given surface requires jpeg buffers.
-     *
-     * @param s a {@link Surface} to check.
-     * @return true if the surface requires a jpeg buffer.
-     */
-    public static boolean jpegType(Surface s)
-            throws LegacyExceptionUtils.BufferQueueAbandonedException {
-        if (LegacyCameraDevice.detectSurfaceType(s) ==
-                CameraMetadataNative.NATIVE_JPEG_FORMAT) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the given surface requires non-jpeg buffer types.
-     *
-     * <p>
-     * "Jpeg buffer" refers to the buffers returned in the jpeg
-     * {@link android.hardware.Camera.PictureCallback}.  Non-jpeg buffers are created using a tee
-     * of the preview stream drawn to the surface
-     * set via {@link android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder)} or
-     * equivalent methods.
-     * </p>
-     * @param s a {@link Surface} to check.
-     * @return true if the surface requires a non-jpeg buffer type.
-     */
-    public static boolean previewType(Surface s)
-            throws LegacyExceptionUtils.BufferQueueAbandonedException {
-        if (LegacyCameraDevice.detectSurfaceType(s) !=
-                CameraMetadataNative.NATIVE_JPEG_FORMAT) {
-            return true;
-        }
-        return false;
-    }
 }
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 7d1be3b..cc7a90e 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -207,15 +207,17 @@
                 @Override
                 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                     RequestHolder holder = mInFlightPreview;
+
+                    if (DEBUG) {
+                        mPrevCounter.countAndLog();
+                    }
+
                     if (holder == null) {
                         mGLThreadManager.queueNewFrame(null);
                         Log.w(TAG, "Dropping preview frame.");
                         return;
                     }
 
-                    if (DEBUG) {
-                        mPrevCounter.countAndLog();
-                    }
                     mInFlightPreview = null;
 
                     if (holder.hasPreviewTargets()) {
@@ -278,7 +280,6 @@
         startPreview();
     }
 
-
     private void configureOutputs(Collection<Surface> outputs) throws IOException {
         stopPreview();
         if (mGLThreadManager != null) {
@@ -395,6 +396,7 @@
             mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
         }
 
+        mCamera.setParameters(mParams);
         // TODO: configure the JPEG surface with some arbitrary size
         // using LegacyCameraDevice.nativeConfigureSurface
     }
@@ -530,6 +532,7 @@
 
     private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
         private boolean mCleanup = false;
+        private LegacyResultMapper mMapper = new LegacyResultMapper();
 
         @Override
         public boolean handleMessage(Message msg) {
@@ -540,6 +543,10 @@
             if (DEBUG) {
                 Log.d(TAG, "Request thread handling message:" + msg.what);
             }
+            long startTime = 0;
+            if (DEBUG) {
+                startTime = SystemClock.elapsedRealtimeNanos();
+            }
             switch (msg.what) {
                 case MSG_CONFIGURE_OUTPUTS:
                     ConfigureHolder config = (ConfigureHolder) msg.obj;
@@ -553,6 +560,10 @@
                         throw new IOError(e);
                     }
                     config.condition.open();
+                    if (DEBUG) {
+                        long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
+                        Log.d(TAG, "Configure took " + totalTime + " ns");
+                    }
                     break;
                 case MSG_SUBMIT_CAPTURE_REQUEST:
                     Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
@@ -573,6 +584,8 @@
                             nextBurst.first.produceRequestHolders(nextBurst.second);
                     for (RequestHolder holder : requests) {
                         CaptureRequest request = holder.getRequest();
+
+                        boolean paramsChanged = false;
                         if (mLastRequest == null || mLastRequest.captureRequest != request) {
 
                             // The intermediate buffer is sometimes null, but we always need
@@ -587,9 +600,13 @@
                             // Parameters are mutated as a side-effect
                             LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
 
-                            mParams = legacyRequest.parameters;
-                            mCamera.setParameters(mParams);
+                            if (!mParams.same(legacyRequest.parameters)) {
+                                mParams = legacyRequest.parameters;
+                                mCamera.setParameters(mParams);
+                                paramsChanged = true;
+                            }
                         }
+
                         mDeviceState.setCaptureStart(holder);
                         long timestamp = 0;
                         try {
@@ -616,16 +633,29 @@
                             // TODO: err handling
                             throw new IOError(e);
                         }
+
                         if (timestamp == 0) {
                             timestamp = SystemClock.elapsedRealtimeNanos();
                         }
-                        // Update parameters to the latest that we think the camera is using
-                        mLastRequest.setParameters(mCamera.getParameters());
-                        CameraMetadataNative result =
-                                LegacyResultMapper.convertResultMetadata(mLastRequest, timestamp);
+
+                        if (paramsChanged) {
+                            if (DEBUG) {
+                                Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
+                            }
+                            mParams = mCamera.getParameters();
+
+                            // Update parameters to the latest that we think the camera is using
+                            mLastRequest.setParameters(mParams);
+                        }
+
+
+                        CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
+                                mLastRequest, timestamp);
                         mDeviceState.setCaptureResult(holder, result);
                     }
                     if (DEBUG) {
+                        long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
+                        Log.d(TAG, "Capture request took " + totalTime + " ns");
                         mRequestCounter.countAndLog();
                     }
                     break;
diff --git a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
index 92d9057..ba821e4 100644
--- a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
+++ b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
@@ -81,17 +81,18 @@
                     break;
                 }
             }
-        }
 
-        if (marshaler == null) {
-            throw new UnsupportedOperationException(
-                     "Could not find marshaler that matches the requested " +
-                     "combination of type reference " +
-                     typeToken + " and native type " +
-                     MarshalHelpers.toStringNativeType(nativeType));
-        }
+            if (marshaler == null) {
+                throw new UnsupportedOperationException(
+                        "Could not find marshaler that matches the requested " +
+                                "combination of type reference " +
+                                typeToken + " and native type " +
+                                MarshalHelpers.toStringNativeType(nativeType));
+            }
 
-        sMarshalerMap.put(marshalToken, marshaler);
+            // Only put when no cached version exists to avoid +0.5ms lookup per call.
+            sMarshalerMap.put(marshalToken, marshaler);
+        }
 
         return marshaler;
     }
@@ -100,10 +101,12 @@
         public MarshalToken(TypeReference<T> typeReference, int nativeType) {
             this.typeReference = typeReference;
             this.nativeType = nativeType;
+            this.hash = typeReference.hashCode() ^ nativeType;
         }
 
         final TypeReference<T> typeReference;
         final int nativeType;
+        private final int hash;
 
         @Override
         public boolean equals(Object other) {
@@ -118,7 +121,7 @@
 
         @Override
         public int hashCode() {
-            return typeReference.hashCode() ^ nativeType;
+            return hash;
         }
     }
 
diff --git a/core/java/android/hardware/camera2/utils/CloseableLock.java b/core/java/android/hardware/camera2/utils/CloseableLock.java
index af55055..9ac89c8 100644
--- a/core/java/android/hardware/camera2/utils/CloseableLock.java
+++ b/core/java/android/hardware/camera2/utils/CloseableLock.java
@@ -110,7 +110,9 @@
     @Override
     public void close() {
         if (mClosed) {
-            log("close - already closed; ignoring");
+            if (VERBOSE) {
+                log("close - already closed; ignoring");
+            }
             return;
         }
 
@@ -139,7 +141,9 @@
             mLock.unlock();
         }
 
-        log("close - completed");
+        if (VERBOSE) {
+            log("close - completed");
+        }
     }
 
     /**
@@ -169,7 +173,9 @@
 
             // Lock is already closed, all further acquisitions will fail
             if (mClosed) {
-                log("acquire lock early aborted (already closed)");
+                if (VERBOSE) {
+                    log("acquire lock early aborted (already closed)");
+                }
                 return null;
             }
 
@@ -187,7 +193,9 @@
 
                 // Did another thread #close while we were waiting? Unblock immediately.
                 if (mClosed) {
-                    log("acquire lock unblocked aborted (already closed)");
+                    if (VERBOSE) {
+                        log("acquire lock unblocked aborted (already closed)");
+                    }
                     return null;
                 }
             }
@@ -200,7 +208,9 @@
             mLock.unlock();
         }
 
-        log("acquired lock (local own count = " + ownedLocks + "");
+        if (VERBOSE) {
+            log("acquired lock (local own count = " + ownedLocks + ")");
+        }
         return new ScopedLock();
     }
 
@@ -231,7 +241,9 @@
 
             // Lock is already closed, all further acquisitions will fail
             if (mClosed) {
-                log("acquire exclusive lock early aborted (already closed)");
+                if (VERBOSE) {
+                    log("acquire exclusive lock early aborted (already closed)");
+                }
                 return null;
             }
 
@@ -254,7 +266,9 @@
 
              // Did another thread #close while we were waiting? Unblock immediately.
                 if (mClosed) {
-                    log("acquire exclusive lock unblocked aborted (already closed)");
+                    if (VERBOSE) {
+                        log("acquire exclusive lock unblocked aborted (already closed)");
+                    }
                     return null;
                 }
             }
@@ -267,7 +281,9 @@
             mLock.unlock();
         }
 
-        log("acquired exclusive lock (local own count = " + ownedLocks + "");
+        if (VERBOSE) {
+            log("acquired exclusive lock (local own count = " + ownedLocks + ")");
+        }
         return new ScopedLock();
     }
 
@@ -318,13 +334,13 @@
             mLock.unlock();
         }
 
-        log("released lock (local lock count " + ownedLocks + ")");
+        if (VERBOSE) {
+             log("released lock (local lock count " + ownedLocks + ")");
+        }
     }
 
     private void log(String what) {
-        if (VERBOSE) {
-            Log.v(TAG + "[" + mName + "]", what);
-        }
+        Log.v(TAG + "[" + mName + "]", what);
     }
 
 }
diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java
index d0c919c..24ce124 100644
--- a/core/java/android/hardware/camera2/utils/TypeReference.java
+++ b/core/java/android/hardware/camera2/utils/TypeReference.java
@@ -46,6 +46,7 @@
  */
 public abstract class TypeReference<T> {
     private final Type mType;
+    private final int mHash;
 
     /**
      * Create a new type reference for {@code T}.
@@ -73,6 +74,7 @@
             throw new IllegalArgumentException(
                     "Including a type variable in a type reference is not allowed");
         }
+        mHash = mType.hashCode();
     }
 
     /**
@@ -84,11 +86,11 @@
 
     private TypeReference(Type type) {
         mType = type;
-
         if (containsTypeVariable(mType)) {
             throw new IllegalArgumentException(
                     "Including a type variable in a type reference is not allowed");
         }
+        mHash = mType.hashCode();
     }
 
     private static class SpecializedTypeReference<T> extends TypeReference<T> {
@@ -249,7 +251,7 @@
      */
     @Override
     public int hashCode() {
-        return mType.hashCode();
+        return mHash;
     }
 
     /**
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c22c4b6..aab7b1f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -120,6 +120,16 @@
     public static final int PROCESS_STATE = 12;
 
     /**
+     * A constant indicating a sync timer
+     */
+    public static final int SYNC = 13;
+
+    /**
+     * A constant indicating a job timer
+     */
+    public static final int JOB = 14;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -142,7 +152,7 @@
     /**
      * Bump the version on this if the checkin format changes.
      */
-    private static final int BATTERY_STATS_CHECKIN_VERSION = 8;
+    private static final int BATTERY_STATS_CHECKIN_VERSION = 9;
     
     private static final long BYTES_PER_KB = 1024;
     private static final long BYTES_PER_MB = 1048576; // 1024^2
@@ -157,6 +167,8 @@
     private static final String FOREGROUND_DATA = "fg";
     private static final String STATE_TIME_DATA = "st";
     private static final String WAKELOCK_DATA = "wl";
+    private static final String SYNC_DATA = "sy";
+    private static final String JOB_DATA = "jb";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
     private static final String WAKEUP_REASON_DATA = "wr";
     private static final String NETWORK_DATA = "nt";
@@ -273,6 +285,20 @@
         public abstract Map<String, ? extends Wakelock> getWakelockStats();
 
         /**
+         * Returns a mapping containing sync statistics.
+         *
+         * @return a Map from Strings to Timer objects.
+         */
+        public abstract Map<String, ? extends Timer> getSyncStats();
+
+        /**
+         * Returns a mapping containing scheduled job statistics.
+         *
+         * @return a Map from Strings to Timer objects.
+         */
+        public abstract Map<String, ? extends Timer> getJobStats();
+
+        /**
          * The statistics associated with a particular wake lock.
          */
         public static abstract class Wakelock {
@@ -660,13 +686,19 @@
         public static final int EVENT_FOREGROUND = 0x0002;
         // Event is about an application package that is at the top of the screen.
         public static final int EVENT_TOP = 0x0003;
-        // Event is about an application package that is at the top of the screen.
+        // Event is about active sync operations.
         public static final int EVENT_SYNC = 0x0004;
         // Events for all additional wake locks aquired/release within a wake block.
         // These are not generated by default.
         public static final int EVENT_WAKE_LOCK = 0x0005;
+        // Event is about an application executing a scheduled job.
+        public static final int EVENT_JOB = 0x0006;
+        // Events for users running.
+        public static final int EVENT_USER_RUNNING = 0x0007;
+        // Events for foreground user.
+        public static final int EVENT_USER_FOREGROUND = 0x0008;
         // Number of event types.
-        public static final int EVENT_COUNT = 0x0006;
+        public static final int EVENT_COUNT = 0x0009;
         // Mask to extract out only the type part of the event.
         public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
 
@@ -680,6 +712,14 @@
         public static final int EVENT_SYNC_FINISH = EVENT_SYNC | EVENT_FLAG_FINISH;
         public static final int EVENT_WAKE_LOCK_START = EVENT_WAKE_LOCK | EVENT_FLAG_START;
         public static final int EVENT_WAKE_LOCK_FINISH = EVENT_WAKE_LOCK | EVENT_FLAG_FINISH;
+        public static final int EVENT_JOB_START = EVENT_JOB | EVENT_FLAG_START;
+        public static final int EVENT_JOB_FINISH = EVENT_JOB | EVENT_FLAG_FINISH;
+        public static final int EVENT_USER_RUNNING_START = EVENT_USER_RUNNING | EVENT_FLAG_START;
+        public static final int EVENT_USER_RUNNING_FINISH = EVENT_USER_RUNNING | EVENT_FLAG_FINISH;
+        public static final int EVENT_USER_FOREGROUND_START =
+                EVENT_USER_FOREGROUND | EVENT_FLAG_START;
+        public static final int EVENT_USER_FOREGROUND_FINISH =
+                EVENT_USER_FOREGROUND | EVENT_FLAG_FINISH;
 
         // For CMD_EVENT.
         public int eventCode;
@@ -1269,11 +1309,11 @@
     };
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
-            "null", "proc", "fg", "top", "sync", "wake_lock_in"
+            "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg"
     };
 
     public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
-            "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl"
+            "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf"
     };
 
     /**
@@ -1857,9 +1897,9 @@
                 screenOnTime / 1000, phoneOnTime / 1000, wifiOnTime / 1000,
                 wifiRunningTime / 1000, bluetoothOnTime / 1000,
                 mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
-                fullWakeLockTimeTotal, partialWakeLockTimeTotal,
-                0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which),
-                getMobileRadioActiveAdjustedTime(which), interactiveTime / 1000,
+                fullWakeLockTimeTotal / 1000, partialWakeLockTimeTotal / 1000,
+                0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which) / 1000,
+                getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000,
                 lowPowerModeEnabledTime / 1000);
         
         // Dump screen brightness stats
@@ -2080,10 +2120,9 @@
                 }
             }
             
-            Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
+            Map<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
             if (wakelocks.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
-                        : wakelocks.entrySet()) {
+                for (Map.Entry<String, ? extends Uid.Wakelock> ent : wakelocks.entrySet()) {
                     Uid.Wakelock wl = ent.getValue();
                     String linePrefix = "";
                     sb.setLength(0);
@@ -2105,6 +2144,32 @@
                 }
             }
 
+            Map<String, ? extends Timer> syncs = u.getSyncStats();
+            if (syncs.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : syncs.entrySet()) {
+                    Timer timer = ent.getValue();
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    if (totalTime != 0) {
+                        dumpLine(pw, uid, category, SYNC_DATA, ent.getKey(), totalTime, count);
+                    }
+                }
+            }
+
+            Map<String, ? extends Timer> jobs = u.getJobStats();
+            if (jobs.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
+                    Timer timer = ent.getValue();
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    if (totalTime != 0) {
+                        dumpLine(pw, uid, category, JOB_DATA, ent.getKey(), totalTime, count);
+                    }
+                }
+            }
+
             SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
             int NSE = sensors.size();
             for (int ise=0; ise<NSE; ise++) {
@@ -2937,8 +3002,7 @@
             if (wakelocks.size() > 0) {
                 long totalFull = 0, totalPartial = 0, totalWindow = 0;
                 int count = 0;
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
-                    : wakelocks.entrySet()) {
+                for (Map.Entry<String, ? extends Uid.Wakelock> ent : wakelocks.entrySet()) {
                     Uid.Wakelock wl = ent.getValue();
                     String linePrefix = ": ";
                     sb.setLength(0);
@@ -2998,6 +3062,56 @@
                 }
             }
 
+            Map<String, ? extends Timer> syncs = u.getSyncStats();
+            if (syncs.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : syncs.entrySet()) {
+                    Timer timer = ent.getValue();
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    Sync ");
+                    sb.append(ent.getKey());
+                    sb.append(": ");
+                    if (totalTime != 0) {
+                        formatTimeMs(sb, totalTime);
+                        sb.append("realtime (");
+                        sb.append(count);
+                        sb.append(" times)");
+                    } else {
+                        sb.append("(not used)");
+                    }
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+
+            Map<String, ? extends Timer> jobs = u.getJobStats();
+            if (syncs.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
+                    Timer timer = ent.getValue();
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    Job ");
+                    sb.append(ent.getKey());
+                    sb.append(": ");
+                    if (totalTime != 0) {
+                        formatTimeMs(sb, totalTime);
+                        sb.append("realtime (");
+                        sb.append(count);
+                        sb.append(" times)");
+                    } else {
+                        sb.append("(not used)");
+                    }
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+
             SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
             int NSE = sensors.size();
             for (int ise=0; ise<NSE; ise++) {
@@ -3260,7 +3374,6 @@
         int oldTemp = -1;
         int oldVolt = -1;
         long lastTime = -1;
-        long firstTime = -1;
 
         void reset() {
             oldState = oldState2 = 0;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 8fc3c5a..cc09653 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -76,6 +76,7 @@
 
     private static final String EXIT_CONDITION_TAG = "exitCondition";
     private static final String EXIT_CONDITION_ATT_ID = "id";
+    private static final String EXIT_CONDITION_ATT_COMPONENT = "component";
 
     public boolean allowCalls;
     public boolean allowMessages;
@@ -89,6 +90,7 @@
     public ComponentName[] conditionComponents;
     public Uri[] conditionIds;
     public Uri exitConditionId;
+    public ComponentName exitConditionComponent;
 
     public ZenModeConfig() { }
 
@@ -114,6 +116,7 @@
         }
         allowFrom = source.readInt();
         exitConditionId = source.readParcelable(null);
+        exitConditionComponent = source.readParcelable(null);
     }
 
     @Override
@@ -144,6 +147,7 @@
         }
         dest.writeInt(allowFrom);
         dest.writeParcelable(exitConditionId, 0);
+        dest.writeParcelable(exitConditionComponent, 0);
     }
 
     @Override
@@ -160,6 +164,7 @@
             .append(",conditionIds=")
             .append(conditionIds == null ? null : TextUtils.join(",", conditionIds))
             .append(",exitConditionId=").append(exitConditionId)
+            .append(",exitConditionComponent=").append(exitConditionComponent)
             .append(']').toString();
     }
 
@@ -191,7 +196,8 @@
                 && other.sleepEndMinute == sleepEndMinute
                 && Objects.deepEquals(other.conditionComponents, conditionComponents)
                 && Objects.deepEquals(other.conditionIds, conditionIds)
-                && Objects.equals(other.exitConditionId, exitConditionId);
+                && Objects.equals(other.exitConditionId, exitConditionId)
+                && Objects.equals(other.exitConditionComponent, exitConditionComponent);
     }
 
     @Override
@@ -199,7 +205,7 @@
         return Objects.hash(allowCalls, allowMessages, allowFrom, sleepMode,
                 sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute,
                 Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds),
-                exitConditionId);
+                exitConditionId, exitConditionComponent);
     }
 
     public boolean isValid() {
@@ -289,6 +295,8 @@
                     }
                 } else if (EXIT_CONDITION_TAG.equals(tag)) {
                     rt.exitConditionId = safeUri(parser, EXIT_CONDITION_ATT_ID);
+                    rt.exitConditionComponent =
+                            safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT);
                 }
             }
         }
@@ -325,9 +333,11 @@
                 out.endTag(null, CONDITION_TAG);
             }
         }
-        if (exitConditionId != null) {
+        if (exitConditionId != null && exitConditionComponent != null) {
             out.startTag(null, EXIT_CONDITION_TAG);
             out.attribute(null, EXIT_CONDITION_ATT_ID, exitConditionId.toString());
+            out.attribute(null, EXIT_CONDITION_ATT_COMPONENT,
+                    exitConditionComponent.flattenToString());
             out.endTag(null, EXIT_CONDITION_TAG);
         }
         out.endTag(null, ZEN_TAG);
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index c6005b9..363f97f 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -160,7 +160,7 @@
     @Override
     public TransitionSet setDuration(long duration) {
         super.setDuration(duration);
-        if (mDuration >= 0) {
+        if (mDuration >= 0 && mTransitions != null) {
             int numTransitions = mTransitions.size();
             for (int i = 0; i < numTransitions; ++i) {
                 mTransitions.get(i).setDuration(duration);
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index 6aae84d..f5d515d 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -39,4 +39,20 @@
             throw new IOException(e.getMessage().substring(PREFIX_IO.length()));
         }
     }
+
+    public static String getCompleteMessage(String msg, Throwable t) {
+        final StringBuilder builder = new StringBuilder();
+        if (msg != null) {
+            builder.append(msg).append(": ");
+        }
+        builder.append(t.getMessage());
+        while ((t = t.getCause()) != null) {
+            builder.append(": ").append(t.getMessage());
+        }
+        return builder.toString();
+    }
+
+    public static String getCompleteMessage(Throwable t) {
+        return getCompleteMessage(null, t);
+    }
 }
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 26f47f9..cc090ad 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -46,6 +46,12 @@
     public static final int CLOCK_TICK = 4;
 
     /**
+     * The user has pressed either a day or month or year date of a Calendar.
+     * @hide
+     */
+    public static final int CALENDAR_DATE = 5;
+
+    /**
      * This is a private constant.  Feel free to renumber as desired.
      * @hide
      */
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index ae59bbc..a61d771 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -80,7 +80,7 @@
     void removeWindowToken(IBinder token);
     void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
-            int configChanges, boolean voiceInteraction);
+            int configChanges, boolean voiceInteraction, boolean launchTaskBehind);
     void setAppGroupId(IBinder token, int groupId);
     void setAppOrientation(IApplicationToken token, int requestedOrientation);
     int getAppOrientation(IApplicationToken token);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index fb8ce15..e2ebf6e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -81,6 +81,9 @@
     // applied as translation when updating the root render node.
     private int mInsetTop, mInsetLeft;
 
+    // Whether the surface has insets. Used to protect opacity.
+    private boolean mHasInsets;
+
     // Light and shadow properties specified by the theme.
     private final float mLightY;
     private final float mLightZ;
@@ -187,12 +190,17 @@
         final float lightX = width / 2.0f;
         mWidth = width;
         mHeight = height;
-        if (surfaceInsets != null) {
+        if (surfaceInsets != null && !surfaceInsets.isEmpty()) {
+            mHasInsets = true;
             mInsetLeft = surfaceInsets.left;
             mInsetTop = surfaceInsets.top;
             mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
             mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
+
+            // If the surface has insets, it can't be opaque.
+            setOpaque(false);
         } else {
+            mHasInsets = false;
             mInsetLeft = 0;
             mInsetTop = 0;
             mSurfaceWidth = width;
@@ -204,7 +212,7 @@
 
     @Override
     void setOpaque(boolean opaque) {
-        nSetOpaque(mNativeProxy, opaque);
+        nSetOpaque(mNativeProxy, opaque && !mHasInsets);
     }
 
     @Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a09a061..fdbc619 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4852,8 +4852,8 @@
      */
     private void manageFocusHotspot(boolean focused, View v) {
         final Rect r = new Rect();
-        if (!focused && v != null && mAttachInfo != null) {
-            v.getBoundsOnScreen(r);
+        if (v != null && mAttachInfo != null) {
+            v.getHotspotBounds(r);
             final int[] location = mAttachInfo.mTmpLocation;
             getLocationOnScreen(location);
             r.offset(-location[0], -location[1]);
@@ -4867,6 +4867,22 @@
     }
 
     /**
+     * Populates <code>outRect</code> with the hotspot bounds. By default,
+     * the hotspot bounds are identical to the screen bounds.
+     *
+     * @param outRect rect to populate with hotspot bounds
+     * @hide Only for internal use by views and widgets.
+     */
+    public void getHotspotBounds(Rect outRect) {
+        final Drawable background = getBackground();
+        if (background != null) {
+            background.getHotspotBounds(outRect);
+        } else {
+            getBoundsOnScreen(outRect);
+        }
+    }
+
+    /**
      * Request that a rectangle of this view be visible on the screen,
      * scrolling if necessary just enough.
      *
diff --git a/core/java/android/webkit/WebResourceRequest.java b/core/java/android/webkit/WebResourceRequest.java
new file mode 100644
index 0000000..dc7c808
--- /dev/null
+++ b/core/java/android/webkit/WebResourceRequest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.net.Uri;
+
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * Encompasses parameters to the {@link WebViewClient#shouldInterceptRequest} method.
+ */
+public interface WebResourceRequest {
+    /**
+     * Gets the URL for which the resource request was made.
+     *
+     * @return the URL for which the resource request was made.
+     */
+    Uri getUrl();
+
+    /**
+     * Gets whether the request was made for the main frame.
+     *
+     * @return whether the request was made for the main frame. Will be false for iframes,
+     *         for example.
+     */
+    boolean isForMainFrame();
+
+    /**
+     * Gets whether a gesture was associated with the request.
+     * <p>
+     * <strong>IMPORTANT:</strong>
+     * This should not be used to implement any form of security. It is possible for the content
+     * to spoof this.
+     *
+     * @return whether a gesture was associated with the request.
+     */
+    boolean hasUserGestureInsecure();
+
+    /**
+     * Gets the method associated with the request, for example "GET".
+     *
+     * @return the method associated with the request.
+     */
+    String getMethod();
+
+    /**
+     * Gets the headers associated with the request. These are represented as a mapping of header
+     * name to header value.
+     *
+     * @return the headers associated with the request.
+     */
+    Map<String, String> getRequestHeaders();
+}
diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java
index f21e2b4..ad6e9aa 100644
--- a/core/java/android/webkit/WebResourceResponse.java
+++ b/core/java/android/webkit/WebResourceResponse.java
@@ -17,6 +17,7 @@
 package android.webkit;
 
 import java.io.InputStream;
+import java.util.Map;
 
 /**
  * Encapsulates a resource response. Applications can return an instance of this
@@ -24,9 +25,11 @@
  * response when the WebView requests a particular resource.
  */
 public class WebResourceResponse {
-    // Accessed by jni, do not rename without modifying the jni code.
     private String mMimeType;
     private String mEncoding;
+    private int mStatusCode;
+    private String mReasonPhrase;
+    private Map<String, String> mResponseHeaders;
     private InputStream mInputStream;
 
     /**
@@ -47,6 +50,28 @@
     }
 
     /**
+     * Constructs a resource response with the given parameters. Callers must
+     * implement {@link InputStream#read(byte[]) InputStream.read(byte[])} for
+     * the input stream.
+     *
+     * @param mimeType the resource response's MIME type, for example text/html
+     * @param encoding the resource response's encoding
+     * @param statusCode the status code needs to be in the ranges [100, 299], [400, 599].
+     *                   Causing a redirect by specifying a 3xx code is not supported.
+     * @param reasonPhrase the phrase describing the status code, for example "OK". Must be non-null
+     *                     and not empty.
+     * @param responseHeaders the resource response's headers represented as a mapping of header
+     *                        name -> header value.
+     * @param data the input stream that provides the resource response's data
+     */
+    public WebResourceResponse(String mimeType, String encoding, int statusCode,
+            String reasonPhrase, Map<String, String> responseHeaders, InputStream data) {
+        this(mimeType, encoding, data);
+        setStatusCodeAndReasonPhrase(statusCode, reasonPhrase);
+        setResponseHeaders(responseHeaders);
+    }
+
+    /**
      * Sets the resource response's MIME type, for example text/html.
      *
      * @param mimeType the resource response's MIME type
@@ -84,7 +109,73 @@
     }
 
     /**
-     * Sets the input stream that provides the resource respone's data. Callers
+     * Sets the resource response's status code and reason phrase.
+     *
+     * @param statusCode the status code needs to be in the ranges [100, 299], [400, 599].
+     *                   Causing a redirect by specifying a 3xx code is not supported.
+     * @param reasonPhrase the phrase describing the status code, for example "OK". Must be non-null
+     *                     and not empty.
+     */
+    public void setStatusCodeAndReasonPhrase(int statusCode, String reasonPhrase) {
+        if (statusCode < 100)
+            throw new IllegalArgumentException("statusCode can't be less than 100.");
+        if (statusCode > 599)
+            throw new IllegalArgumentException("statusCode can't be greater than 599.");
+        if (statusCode > 299 && statusCode < 400)
+            throw new IllegalArgumentException("statusCode can't be in the [300, 399] range.");
+        if (reasonPhrase == null)
+            throw new IllegalArgumentException("reasonPhrase can't be null.");
+        if (reasonPhrase.trim().isEmpty())
+            throw new IllegalArgumentException("reasonPhrase can't be empty.");
+        for (int i = 0; i < reasonPhrase.length(); i++) {
+            int c = reasonPhrase.charAt(i);
+            if (c > 0x7F) {
+                throw new IllegalArgumentException(
+                        "reasonPhrase can't contain non-ASCII characters.");
+            }
+        }
+        mStatusCode = statusCode;
+        mReasonPhrase = reasonPhrase;
+    }
+
+    /**
+     * Gets the resource response's status code.
+     *
+     * @return the resource response's status code.
+     */
+    public int getStatusCode() {
+        return mStatusCode;
+    }
+
+    /**
+     * Gets the description of the resource response's status code.
+     *
+     * @return the description of the resource response's status code.
+     */
+    public String getReasonPhrase() {
+        return mReasonPhrase;
+    }
+
+    /**
+     * Sets the headers for the resource response.
+     *
+     * @param headers mapping of header name -> header value.
+     */
+    public void setResponseHeaders(Map<String, String> headers) {
+        mResponseHeaders = headers;
+    }
+
+    /**
+     * Gets the headers for the resource response.
+     *
+     * @return the headers for the resource response.
+     */
+    public Map<String, String> getResponseHeaders() {
+        return mResponseHeaders;
+    }
+
+    /**
+     * Sets the input stream that provides the resource response's data. Callers
      * must implement {@link InputStream#read(byte[]) InputStream.read(byte[])}.
      *
      * @param data the input stream that provides the resource response's data
@@ -94,7 +185,7 @@
     }
 
     /**
-     * Gets the input stream that provides the resource respone's data.
+     * Gets the input stream that provides the resource response's data.
      *
      * @return the input stream that provides the resource response's data
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 290a574..adf4803 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2471,4 +2471,16 @@
         mProvider.getViewDelegate().preDispatchDraw(canvas);
         super.dispatchDraw(canvas);
     }
+
+    @Override
+    public void onStartTemporaryDetach() {
+        super.onStartTemporaryDetach();
+        mProvider.getViewDelegate().onStartTemporaryDetach();
+    }
+
+    @Override
+    public void onFinishTemporaryDetach() {
+        super.onFinishTemporaryDetach();
+        mProvider.getViewDelegate().onFinishTemporaryDetach();
+    }
 }
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 62b80c4..d52dd60 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -96,13 +96,36 @@
      * @return A {@link android.webkit.WebResourceResponse} containing the
      *         response information or null if the WebView should load the
      *         resource itself.
+     * @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest)
+     *             shouldInterceptRequest(WebView, WebResourceRequest)} instead.
      */
+    @Deprecated
     public WebResourceResponse shouldInterceptRequest(WebView view,
             String url) {
         return null;
     }
 
     /**
+     * Notify the host application of a resource request and allow the
+     * application to return the data.  If the return value is null, the WebView
+     * will continue to load the resource as usual.  Otherwise, the return
+     * response and data will be used.  NOTE: This method is called on a thread
+     * other than the UI thread so clients should exercise caution
+     * when accessing private data or the view system.
+     *
+     * @param view The {@link android.webkit.WebView} that is requesting the
+     *             resource.
+     * @param request Object containing the details of the request.
+     * @return A {@link android.webkit.WebResourceResponse} containing the
+     *         response information or null if the WebView should load the
+     *         resource itself.
+     */
+    public WebResourceResponse shouldInterceptRequest(WebView view,
+            WebResourceRequest request) {
+        return shouldInterceptRequest(view, request.getUrl().toString());
+    }
+
+    /**
      * Notify the host application that there have been an excessive number of
      * HTTP redirects. As the host application if it would like to continue
      * trying to load the resource. The default behavior is to send the cancel
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 5081ff5..b6fd363 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -360,6 +360,10 @@
         public void setLayerType(int layerType, Paint paint);
 
         public void preDispatchDraw(Canvas canvas);
+
+        public void onStartTemporaryDetach();
+
+        public void onFinishTemporaryDetach();
     }
 
     interface ScrollDelegate {
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 2c1a77c..8f49fb8 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -17,7 +17,9 @@
 package android.widget;
 
 import android.annotation.Widget;
+import android.app.UiModeManager;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.os.Parcel;
@@ -49,6 +51,8 @@
 
 import libcore.icu.ICU;
 
+import static android.os.Build.VERSION_CODES.L;
+
 /**
  * This class is a widget for selecting a date. The date can be selected by a
  * year, month, and day spinners or a {@link CalendarView}. The set of spinners
@@ -70,6 +74,15 @@
  * @attr ref android.R.styleable#DatePicker_minDate
  * @attr ref android.R.styleable#DatePicker_spinnersShown
  * @attr ref android.R.styleable#DatePicker_calendarViewShown
+ * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekBackgroundColor
+ * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorBackgroundColor
+ * @attr ref android.R.styleable#DatePicker_dateSelectorMonthTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfMonthTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorYearTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorYearListItemTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorYearListSelectedCircleColor
+ * @attr ref android.R.styleable#DatePicker_calendarTextColor
  */
 @Widget
 public class DatePicker extends FrameLayout {
@@ -78,6 +91,10 @@
 
     private DatePickerDelegate mDelegate;
 
+    private int mDefStyleAttr;
+    private int mDefStyleRes;
+    private Context mContext;
+
     /**
      * The callback used to indicate the user changes\d the date.
      */
@@ -110,7 +127,65 @@
     public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        mDelegate = new LegacyDatePickerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
+        mContext = context;
+        mDefStyleAttr = defStyleAttr;
+        mDefStyleRes = defStyleRes;
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
+                mDefStyleAttr, mDefStyleRes);
+
+        // Create the correct UI delegate. Default is the legacy one.
+        final boolean isLegacyMode = a.getBoolean(R.styleable.DatePicker_legacyMode,
+                isLegacyMode());
+
+        a.recycle();
+
+        setLegacyMode(isLegacyMode, attrs);
+    }
+
+    private boolean isLegacyMode() {
+        UiModeManager uiModeManager =
+                (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
+        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+            return true;
+        }
+        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
+        return targetSdkVersion < L;
+    }
+
+    private DatePickerDelegate createLegacyUIDelegate(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        return new LegacyDatePickerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    private DatePickerDelegate createNewUIDelegate(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        return new android.widget.DatePickerDelegate(this, context, attrs, defStyleAttr,
+                defStyleRes);
+    }
+
+    /**
+     * @hide
+     */
+    public void setLegacyMode(boolean isLegacyMode, AttributeSet attrs) {
+        removeAllViewsInLayout();
+        mDelegate = isLegacyMode ?
+                createLegacyUIDelegate(mContext, attrs, mDefStyleAttr, mDefStyleRes) :
+                createNewUIDelegate(mContext, attrs, mDefStyleAttr, mDefStyleRes);
+    }
+
+    /**
+     * @hide
+     */
+    public void setShowDoneButton(boolean showDoneButton) {
+        mDelegate.setShowDoneButton(showDoneButton);
+    }
+
+    /**
+     * @hide
+     */
+    public void setDismissCallback(DatePickerDismissCallback callback) {
+        mDelegate.setDismissCallback(callback);
     }
 
     /**
@@ -129,7 +204,7 @@
     }
 
     /**
-     * Updates the current date.
+     * Update the current date.
      *
      * @param year The year.
      * @param month The month which is <strong>starting from zero</strong>.
@@ -171,7 +246,7 @@
      * @return The minimal supported date.
      */
     public long getMinDate() {
-        return mDelegate.getMinDate();
+        return mDelegate.getMinDate().getTimeInMillis();
     }
 
     /**
@@ -196,7 +271,7 @@
      * @return The maximal supported date.
      */
     public long getMaxDate() {
-        return mDelegate.getMaxDate();
+        return mDelegate.getMaxDate().getTimeInMillis();
     }
 
     /**
@@ -300,6 +375,182 @@
         mDelegate.setSpinnersShown(shown);
     }
 
+    /**
+     * Sets the background color for the date selector's day of week.
+     *
+     * @param color The background color.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekBackgroundColor
+     */
+    public void setDateSelectorDayOfWeekBackgroundColor(int color) {
+        mDelegate.setDateSelectorDayOfWeekBackgroundColor(color);
+    }
+
+    /**
+     * Gets the background color for the date selector's day of week.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekBackgroundColor
+     */
+    public int getDateSelectorDayOfWeekBackgroundColor() {
+        return mDelegate.getDateSelectorDayOfWeekBackgroundColor();
+    }
+
+    /**
+     * Sets the text appearance for the date selector's day of week.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekTextAppearance
+     */
+    public void setDateSelectorDayOfWeekTextAppearance(int resId) {
+        mDelegate.setDateSelectorDayOfWeekTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the date selector's day of week.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekTextAppearance
+     */
+    public int getDateSelectorDayOfWeekTextAppearance() {
+        return mDelegate.getDateSelectorDayOfWeekTextAppearance();
+    }
+
+    /**
+     * Sets the background color for the date selector's.
+     *
+     * @param color The background color.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorBackgroundColor
+     */
+    public void setDateSelectorBackgroundColor(int color) {
+        mDelegate.setDateSelectorBackgroundColor(color);
+    }
+
+    /**
+     * Gets the background color for the date selector's.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorBackgroundColor
+     */
+    public int getDateSelectorBackgroundColor() {
+        return mDelegate.getDateSelectorBackgroundColor();
+    }
+
+    /**
+     * Sets the text appearance for the date selector's month.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorMonthTextAppearance
+     */
+    public void setDateSelectorMonthTextAppearance(int resId) {
+        mDelegate.setDateSelectorMonthTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the date selector's month.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorMonthTextAppearance
+     */
+    public int getDateSelectorMonthTextAppearance() {
+        return mDelegate.getDateSelectorMonthTextAppearance();
+    }
+
+    /**
+     * Sets the text appearance for the date selector's day of month.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfMonthTextAppearance
+     */
+    public void setDateSelectorDayOfMonthTextAppearance(int resId) {
+        mDelegate.setDateSelectorDayOfMonthTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the date selector's day of month.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfMonthTextAppearance
+     */
+    public int getDateSelectorDayOfMonthTextAppearance() {
+        return mDelegate.getDateSelectorDayOfMonthTextAppearance();
+    }
+
+    /**
+     * Sets the text appearance for the date selector's year.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorYearTextAppearance
+     */
+    public void setDateSelectorYearTextAppearance(int resId) {
+        mDelegate.setDateSelectorYearTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the date selector's year.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorYearTextAppearance
+     */
+    public int getDateSelectorYearTextAppearance() {
+        return mDelegate.getDateSelectorYearTextAppearance();
+    }
+
+    /**
+     * Sets the text appearance for the year list item.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorYearListItemTextAppearance
+     */
+    public void setDateSelectorYearListItemTextAppearance(int resId) {
+        mDelegate.setDateSelectorYearListItemTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the year list item.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorYearListItemTextAppearance
+     */
+    public int getDateSelectorYearListItemTextAppearance() {
+        return mDelegate.getDateSelectorYearListItemTextAppearance();
+    }
+
+    /**
+     * Sets the text color state list for the calendar.
+     *
+     * @param colors The text color state list.
+     *
+     * @attr ref android.R.styleable#DatePicker_calendarTextColor
+     */
+    public void setCalendarTextColor(ColorStateList colors) {
+        mDelegate.setCalendarTextColor(colors);
+    }
+
+    /**
+     * Gets the text color state list for the calendar.
+     *
+     * @return The text color state list for the calendar.
+     *
+     * @attr ref android.R.styleable#DatePicker_calendarTextColor
+     */
+    public ColorStateList getCalendarTextColor() {
+        return mDelegate.getCalendarTextColors();
+    }
+
     // Override so we are in complete control of save / restore for this widget.
     @Override
     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
@@ -323,6 +574,8 @@
      * A delegate interface that defined the public API of the DatePicker. Allows different
      * DatePicker implementations. This would need to be implemented by the DatePicker delegates
      * for the real behavior.
+     *
+     * @hide
      */
     interface DatePickerDelegate {
         void init(int year, int monthOfYear, int dayOfMonth,
@@ -335,15 +588,42 @@
         int getDayOfMonth();
 
         void setMinDate(long minDate);
-        long getMinDate();
+        Calendar getMinDate();
 
         void setMaxDate(long maxDate);
-        long getMaxDate();
+        Calendar getMaxDate();
 
         void setEnabled(boolean enabled);
         boolean isEnabled();
 
-        CalendarView getCalendarView ();
+        void setDateSelectorDayOfWeekBackgroundColor(int color);
+        int getDateSelectorDayOfWeekBackgroundColor();
+
+        void setDateSelectorDayOfWeekTextAppearance(int resId);
+        int getDateSelectorDayOfWeekTextAppearance();
+
+        void setDateSelectorBackgroundColor(int color);
+        int getDateSelectorBackgroundColor();
+
+        void setDateSelectorMonthTextAppearance(int resId);
+        int getDateSelectorMonthTextAppearance();
+
+        void setDateSelectorDayOfMonthTextAppearance(int resId);
+        int getDateSelectorDayOfMonthTextAppearance();
+
+        void setDateSelectorYearTextAppearance(int resId);
+        int getDateSelectorYearTextAppearance();
+
+        void setDateSelectorYearListItemTextAppearance(int resId);
+        int getDateSelectorYearListItemTextAppearance();
+
+        void setDateSelectorYearListSelectedCircleColor(int color);
+        int getDateSelectorYearListSelectedCircleColor();
+
+        void setCalendarTextColor(ColorStateList colors);
+        ColorStateList getCalendarTextColors();
+
+        CalendarView getCalendarView();
 
         void setCalendarViewShown(boolean shown);
         boolean getCalendarViewShown();
@@ -351,6 +631,9 @@
         void setSpinnersShown(boolean shown);
         boolean getSpinnersShown();
 
+        void setShowDoneButton(boolean showDoneButton);
+        void setDismissCallback(DatePickerDismissCallback callback);
+
         void onConfigurationChanged(Configuration newConfig);
 
         void dispatchRestoreInstanceState(SparseArray<Parcelable> container);
@@ -366,7 +649,7 @@
     /**
      * An abstract class which can be used as a start for DatePicker implementations
      */
-    abstract static class AbstractTimePickerDelegate implements DatePickerDelegate {
+    abstract static class AbstractDatePickerDelegate implements DatePickerDelegate {
         // The delegator
         protected DatePicker mDelegator;
 
@@ -379,7 +662,7 @@
         // Callbacks
         protected  OnDateChangedListener mOnDateChangedListener;
 
-        public AbstractTimePickerDelegate(DatePicker delegator, Context context) {
+        public AbstractDatePickerDelegate(DatePicker delegator, Context context) {
             mDelegator = delegator;
             mContext = context;
 
@@ -396,9 +679,18 @@
     }
 
     /**
+     * A callback interface for dismissing the DatePicker when included into a Dialog
+     *
+     * @hide
+     */
+    public static interface DatePickerDismissCallback {
+        void dismiss(DatePicker view, boolean isCancel, int year, int month, int dayOfMonth);
+    }
+
+    /**
      * A delegate implementing the basic DatePicker
      */
-    private static class LegacyDatePickerDelegate extends AbstractTimePickerDelegate {
+    private static class LegacyDatePickerDelegate extends AbstractDatePickerDelegate {
 
         private static final String DATE_FORMAT = "MM/dd/yyyy";
 
@@ -466,7 +758,7 @@
             String minDate = attributesArray.getString(R.styleable.DatePicker_minDate);
             String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate);
             int layoutResourceId = attributesArray.getResourceId(
-                    R.styleable.DatePicker_internalLayout, R.layout.date_picker);
+                    R.styleable.DatePicker_legacyLayout, R.layout.date_picker_legacy);
             attributesArray.recycle();
 
             LayoutInflater inflater = (LayoutInflater) context
@@ -643,8 +935,10 @@
         }
 
         @Override
-        public long getMinDate() {
-            return mCalendarView.getMinDate();
+        public Calendar getMinDate() {
+            final Calendar minDate = Calendar.getInstance();
+            minDate.setTimeInMillis(mCalendarView.getMinDate());
+            return minDate;
         }
 
         @Override
@@ -664,8 +958,10 @@
         }
 
         @Override
-        public long getMaxDate() {
-            return mCalendarView.getMaxDate();
+        public Calendar getMaxDate() {
+            final Calendar maxDate = Calendar.getInstance();
+            maxDate.setTimeInMillis(mCalendarView.getMaxDate());
+            return maxDate;
         }
 
         @Override
@@ -683,6 +979,87 @@
         }
 
         @Override
+        public void setDateSelectorDayOfWeekBackgroundColor(int color) {
+        }
+
+        @Override
+        public int getDateSelectorDayOfWeekBackgroundColor() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorDayOfWeekTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorDayOfWeekTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorBackgroundColor(int color) {
+        }
+
+        @Override
+        public int getDateSelectorBackgroundColor() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorMonthTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorMonthTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorDayOfMonthTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorDayOfMonthTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorYearTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorYearTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorYearListItemTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorYearListItemTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorYearListSelectedCircleColor(int color) {
+        }
+
+        @Override
+        public int getDateSelectorYearListSelectedCircleColor() {
+            return 0;
+        }
+
+        @Override
+        public void setCalendarTextColor(ColorStateList colors) {
+        }
+
+        @Override
+        public ColorStateList getCalendarTextColors() {
+            return ColorStateList.valueOf(0);
+        }
+
+        @Override
         public CalendarView getCalendarView() {
             return mCalendarView;
         }
@@ -708,6 +1085,16 @@
         }
 
         @Override
+        public void setShowDoneButton(boolean showDoneButton) {
+            // Nothing to do
+        }
+
+        @Override
+        public void setDismissCallback(DatePickerDismissCallback callback) {
+            // Nothing to do
+        }
+
+        @Override
         public void onConfigurationChanged(Configuration newConfig) {
             setCurrentLocale(newConfig.locale);
         }
diff --git a/core/java/android/widget/DatePickerController.java b/core/java/android/widget/DatePickerController.java
new file mode 100644
index 0000000..6a074da
--- /dev/null
+++ b/core/java/android/widget/DatePickerController.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import java.util.Calendar;
+
+/**
+ * Controller class to communicate among the various components of the date picker dialog.
+ *
+ * @hide
+ */
+interface DatePickerController {
+
+    void onYearSelected(int year);
+
+    void onDayOfMonthSelected(int year, int month, int day);
+
+    void registerOnDateChangedListener(OnDateChangedListener listener);
+
+    void unregisterOnDateChangedListener(OnDateChangedListener listener);
+
+    Calendar getSelectedDay();
+
+    int getFirstDayOfWeek();
+
+    int getMinYear();
+    int getMaxYear();
+
+    int getMinMonth();
+    int getMaxMonth();
+
+    int getMinDay();
+    int getMaxDay();
+
+    void setMinDate(long minDate);
+    Calendar getMinDate();
+
+    void setMaxDate(long maxDate);
+    Calendar getMaxDate();
+
+    void tryVibrate();
+}
diff --git a/core/java/android/widget/DatePickerDelegate.java b/core/java/android/widget/DatePickerDelegate.java
new file mode 100644
index 0000000..31044d4
--- /dev/null
+++ b/core/java/android/widget/DatePickerDelegate.java
@@ -0,0 +1,979 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+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.os.Parcel;
+import android.os.Parcelable;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.HapticFeedbackConstants;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+
+import com.android.internal.R;
+import com.android.internal.widget.AccessibleDateAnimator;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+
+/**
+ * A delegate for picking up a date (day / month / year).
+ */
+class DatePickerDelegate extends DatePicker.AbstractDatePickerDelegate implements
+        View.OnClickListener, DatePickerController {
+
+    private static final int UNINITIALIZED = -1;
+    private static final int MONTH_AND_DAY_VIEW = 0;
+    private static final int YEAR_VIEW = 1;
+
+    private static final int DEFAULT_START_YEAR = 1900;
+    private static final int DEFAULT_END_YEAR = 2100;
+
+    private static final int PULSE_ANIMATOR_DURATION = 544;
+
+    private static final int ANIMATION_DURATION = 300;
+    private static final int ANIMATION_DELAY = 650;
+
+    private static final int MONTH_INDEX = 0;
+    private static final int DAY_INDEX = 1;
+    private static final int YEAR_INDEX = 2;
+
+    private SimpleDateFormat mYearFormat = new SimpleDateFormat("y", Locale.getDefault());
+    private SimpleDateFormat mDayFormat = new SimpleDateFormat("d", Locale.getDefault());
+
+    private TextView mDayOfWeekView;
+    private LinearLayout mDateLayout;
+    private LinearLayout mMonthAndDayLayout;
+    private TextView mSelectedMonthTextView;
+    private TextView mSelectedDayTextView;
+    private TextView mSelectedYearView;
+    private DayPickerView mDayPickerView;
+    private YearPickerView mYearPickerView;
+
+    private ViewGroup mLayoutButtons;
+
+    private boolean mIsEnabled = true;
+
+    // Accessibility strings.
+    private String mDayPickerDescription;
+    private String mSelectDay;
+    private String mYearPickerDescription;
+    private String mSelectYear;
+
+    private AccessibleDateAnimator mAnimator;
+
+    private DatePicker.OnDateChangedListener mDateChangedListener;
+
+    private boolean mDelayAnimation = true;
+
+    private int mCurrentView = UNINITIALIZED;
+
+    private Calendar mCurrentDate;
+    private Calendar mTempDate;
+    private Calendar mMinDate;
+    private Calendar mMaxDate;
+
+    // For showing the done button when in a Dialog
+    private Button mDoneButton;
+    private boolean mShowDoneButton;
+    private DatePicker.DatePickerDismissCallback mDismissCallback;
+
+    private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
+
+    private int mDayOfWeekTextAppearanceResId;
+    private int mMonthTextAppearanceResId;
+    private int mDayOfMonthTextAppearanceResId;
+    private int mYearTextAppearanceResId;
+
+    private int mYearListItemTextAppearanceResId;
+
+    private int mDayOfWeekBackgroundColor;
+    private int mMonthAndDayBackgroundColor;
+
+    private ColorStateList mCalendarTextColors;
+
+    public DatePickerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+
+        super(delegator, context);
+
+        final Locale locale = Locale.getDefault();
+        mMinDate = getCalendarForLocale(mMinDate, locale);
+        mMaxDate = getCalendarForLocale(mMaxDate, locale);
+        mTempDate = getCalendarForLocale(mMaxDate, locale);
+
+        mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
+
+        mMinDate.set(DEFAULT_START_YEAR, 1, 1);
+        mMaxDate.set(DEFAULT_END_YEAR, 12, 31);
+
+        // process style attributes
+        final TypedArray a = mContext.obtainStyledAttributes(attrs,
+                R.styleable.DatePicker, defStyleAttr, defStyleRes);
+
+        final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+
+        final int layoutResourceId = a.getResourceId(
+                R.styleable.DatePicker_internalLayout, R.layout.date_picker_holo);
+
+        View mainView = inflater.inflate(layoutResourceId, null);
+        mDelegator.addView(mainView);
+
+        mDayOfWeekView = (TextView) mainView.findViewById(R.id.date_picker_header);
+        mDateLayout = (LinearLayout) mainView.findViewById(R.id.day_picker_selector_layout);
+        mMonthAndDayLayout = (LinearLayout) mainView.findViewById(
+                R.id.date_picker_month_and_day_layout);
+        mMonthAndDayLayout.setOnClickListener(this);
+        mSelectedMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_month);
+        mSelectedDayTextView = (TextView) mainView.findViewById(R.id.date_picker_day);
+        mSelectedYearView = (TextView) mainView.findViewById(R.id.date_picker_year);
+        mSelectedYearView.setOnClickListener(this);
+
+        // Use Theme attributes if possible
+        mDayOfWeekTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorDayOfWeekTextAppearance, -1);
+        if (mDayOfWeekTextAppearanceResId != -1) {
+            mDayOfWeekView.setTextAppearance(context, mDayOfWeekTextAppearanceResId);
+        }
+
+        mMonthTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorMonthTextAppearance, -1);
+        if (mMonthTextAppearanceResId != -1) {
+            mSelectedMonthTextView.setTextAppearance(context, mMonthTextAppearanceResId);
+        }
+
+        mDayOfMonthTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorDayOfMonthTextAppearance, -1);
+        if (mDayOfMonthTextAppearanceResId != -1) {
+            mSelectedDayTextView.setTextAppearance(context, mDayOfMonthTextAppearanceResId);
+        }
+
+        mYearTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorYearTextAppearance, -1);
+        if (mYearTextAppearanceResId != -1) {
+            mSelectedYearView.setTextAppearance(context, mYearTextAppearanceResId);
+        }
+
+        Resources res = mDelegator.getResources();
+
+        mDayOfWeekBackgroundColor = a.getColor(
+                R.styleable.DatePicker_dateSelectorDayOfWeekBackgroundColor,
+                res.getColor(
+                        R.color.datepicker_default_header_dayofweek_background_color_holo_light));
+        mDayOfWeekView.setBackgroundColor(mDayOfWeekBackgroundColor);
+
+        mMonthAndDayBackgroundColor = a.getColor(R.styleable.DatePicker_dateSelectorBackgroundColor,
+                res.getColor(R.color.datepicker_default_header_selector_background_holo_light));
+        mMonthAndDayLayout.setBackgroundColor(mMonthAndDayBackgroundColor);
+
+        mDayPickerView = new DayPickerView(mContext, this);
+        mYearPickerView = new YearPickerView(mContext);
+        mYearPickerView.init(this);
+
+        ColorStateList colors = a.getColorStateList(R.styleable.DatePicker_calendarTextColor);
+        setCalendarTextColor(colors);
+
+        mDayPickerDescription = res.getString(R.string.day_picker_description);
+        mSelectDay = res.getString(R.string.select_day);
+        mYearPickerDescription = res.getString(R.string.year_picker_description);
+        mSelectYear = res.getString(R.string.select_year);
+
+        mAnimator = (AccessibleDateAnimator) mainView.findViewById(R.id.animator);
+        mAnimator.addView(mDayPickerView);
+        mAnimator.addView(mYearPickerView);
+        mAnimator.setDateMillis(mCurrentDate.getTimeInMillis());
+        Animation animation = new AlphaAnimation(0.0f, 1.0f);
+        animation.setDuration(ANIMATION_DURATION);
+        mAnimator.setInAnimation(animation);
+        Animation animation2 = new AlphaAnimation(1.0f, 0.0f);
+        animation2.setDuration(ANIMATION_DURATION);
+        mAnimator.setOutAnimation(animation2);
+
+        mLayoutButtons = (ViewGroup) mainView.findViewById(R.id.layout_buttons);
+        mDoneButton = (Button) mainView.findViewById(R.id.done);
+        mDoneButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                tryVibrate();
+                if (mDismissCallback != null) {
+                    mDismissCallback.dismiss(mDelegator, false, mCurrentDate.get(Calendar.YEAR),
+                            mCurrentDate.get(Calendar.MONTH),
+                            mCurrentDate.get(Calendar.DAY_OF_MONTH));
+                }
+            }
+        });
+
+        updateDisplay(false);
+        setCurrentView(MONTH_AND_DAY_VIEW);
+    }
+
+    /**
+     * Gets a calendar for locale bootstrapped with the value of a given calendar.
+     *
+     * @param oldCalendar The old calendar.
+     * @param locale The locale.
+     */
+    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
+        if (oldCalendar == null) {
+            return Calendar.getInstance(locale);
+        } else {
+            final long currentTimeMillis = oldCalendar.getTimeInMillis();
+            Calendar newCalendar = Calendar.getInstance(locale);
+            newCalendar.setTimeInMillis(currentTimeMillis);
+            return newCalendar;
+        }
+    }
+
+    /**
+     * Compute the array representing the order of Month / Day / Year views in their layout.
+     * Will be used for I18N purpose as the order of them depends on the Locale.
+     */
+    private int[] getMonthDayYearIndexes(String pattern) {
+        int[] result = new int[3];
+
+        final String filteredPattern = pattern.replaceAll("'.*?'", "");
+
+        final int dayIndex = filteredPattern.indexOf('d');
+        final int monthMIndex = filteredPattern.indexOf("M");
+        final int monthIndex = (monthMIndex != -1) ? monthMIndex : filteredPattern.indexOf("L");
+        final int yearIndex = filteredPattern.indexOf("y");
+
+        if (yearIndex < monthIndex) {
+            result[YEAR_INDEX] = 0;
+
+            if (monthIndex < dayIndex) {
+                result[MONTH_INDEX] = 1;
+                result[DAY_INDEX] = 2;
+            } else {
+                result[MONTH_INDEX] = 2;
+                result[DAY_INDEX] = 1;
+            }
+        } else {
+            result[YEAR_INDEX] = 2;
+
+            if (monthIndex < dayIndex) {
+                result[MONTH_INDEX] = 0;
+                result[DAY_INDEX] = 1;
+            } else {
+                result[MONTH_INDEX] = 1;
+                result[DAY_INDEX] = 0;
+            }
+        }
+        return result;
+    }
+
+    private void updateDisplay(boolean announce) {
+        if (mDayOfWeekView != null) {
+            mDayOfWeekView.setText(mCurrentDate.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG,
+                    Locale.getDefault()));
+        }
+        final String bestDateTimePattern =
+                DateFormat.getBestDateTimePattern(mCurrentLocale, "yMMMd");
+
+        // Compute indices of Month, Day and Year views
+        int[] viewIndices = getMonthDayYearIndexes(bestDateTimePattern);
+
+        // Restart from a clean state
+        mMonthAndDayLayout.removeAllViews();
+        mDateLayout.removeView(mSelectedYearView);
+
+        // Position the Year View at the correct location
+        if (viewIndices[YEAR_INDEX] == 0) {
+            mDateLayout.addView(mSelectedYearView, 0);
+        } else {
+            mDateLayout.addView(mSelectedYearView, 1);
+        }
+
+        // Position Day and Month Views
+        if (viewIndices[MONTH_INDEX] > viewIndices[DAY_INDEX]) {
+            // Day View is first
+            mMonthAndDayLayout.addView(mSelectedDayTextView);
+            mMonthAndDayLayout.addView(mSelectedMonthTextView);
+        } else {
+            // Month View is first
+            mMonthAndDayLayout.addView(mSelectedMonthTextView);
+            mMonthAndDayLayout.addView(mSelectedDayTextView);
+        }
+
+        mSelectedMonthTextView.setText(mCurrentDate.getDisplayName(Calendar.MONTH, Calendar.SHORT,
+                Locale.getDefault()).toUpperCase(Locale.getDefault()));
+        mSelectedDayTextView.setText(mDayFormat.format(mCurrentDate.getTime()));
+        mSelectedYearView.setText(mYearFormat.format(mCurrentDate.getTime()));
+
+        // Accessibility.
+        long millis = mCurrentDate.getTimeInMillis();
+        mAnimator.setDateMillis(millis);
+        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
+        String monthAndDayText = DateUtils.formatDateTime(mContext, millis, flags);
+        mMonthAndDayLayout.setContentDescription(monthAndDayText);
+
+        if (announce) {
+            flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
+            String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
+            mAnimator.announceForAccessibility(fullDateText);
+        }
+        updatePickers();
+    }
+
+    private void setCurrentView(final int viewIndex) {
+        long millis = mCurrentDate.getTimeInMillis();
+
+        switch (viewIndex) {
+            case MONTH_AND_DAY_VIEW:
+                ObjectAnimator pulseAnimator = getPulseAnimator(mMonthAndDayLayout, 0.9f,
+                        1.05f);
+                if (mDelayAnimation) {
+                    pulseAnimator.setStartDelay(ANIMATION_DELAY);
+                    mDelayAnimation = false;
+                }
+                mDayPickerView.onDateChanged();
+                if (mCurrentView != viewIndex) {
+                    mMonthAndDayLayout.setSelected(true);
+                    mSelectedYearView.setSelected(false);
+                    mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW);
+                    mCurrentView = viewIndex;
+                }
+                pulseAnimator.start();
+
+                int flags = DateUtils.FORMAT_SHOW_DATE;
+                String dayString = DateUtils.formatDateTime(mContext, millis, flags);
+                mAnimator.setContentDescription(mDayPickerDescription + ": " + dayString);
+                mAnimator.announceForAccessibility(mSelectDay);
+                break;
+            case YEAR_VIEW:
+                pulseAnimator = getPulseAnimator(mSelectedYearView, 0.85f, 1.1f);
+                if (mDelayAnimation) {
+                    pulseAnimator.setStartDelay(ANIMATION_DELAY);
+                    mDelayAnimation = false;
+                }
+                mYearPickerView.onDateChanged();
+                if (mCurrentView != viewIndex) {
+                    mMonthAndDayLayout.setSelected(false);
+                    mSelectedYearView.setSelected(true);
+                    mAnimator.setDisplayedChild(YEAR_VIEW);
+                    mCurrentView = viewIndex;
+                }
+                pulseAnimator.start();
+
+                CharSequence yearString = mYearFormat.format(millis);
+                mAnimator.setContentDescription(mYearPickerDescription + ": " + yearString);
+                mAnimator.announceForAccessibility(mSelectYear);
+                break;
+        }
+    }
+
+    @Override
+    public void init(int year, int monthOfYear, int dayOfMonth,
+            DatePicker.OnDateChangedListener callBack) {
+        mDateChangedListener = callBack;
+        mCurrentDate.set(Calendar.YEAR, year);
+        mCurrentDate.set(Calendar.MONTH, monthOfYear);
+        mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+        updateDisplay(false);
+    }
+
+    @Override
+    public void updateDate(int year, int month, int dayOfMonth) {
+        mCurrentDate.set(Calendar.YEAR, year);
+        mCurrentDate.set(Calendar.MONTH, month);
+        mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+        mDateChangedListener.onDateChanged(mDelegator, year, month, dayOfMonth);
+        updateDisplay(false);
+    }
+
+    @Override
+    public int getYear() {
+        return mCurrentDate.get(Calendar.YEAR);
+    }
+
+    @Override
+    public int getMonth() {
+        return mCurrentDate.get(Calendar.MONTH);
+    }
+
+    @Override
+    public int getDayOfMonth() {
+        return mCurrentDate.get(Calendar.DAY_OF_MONTH);
+    }
+
+    @Override
+    public void setMinDate(long minDate) {
+        mTempDate.setTimeInMillis(minDate);
+        if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
+                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
+            return;
+        }
+        if (mCurrentDate.before(mTempDate)) {
+            mCurrentDate.setTimeInMillis(minDate);
+            updatePickers();
+            updateDisplay(false);
+        }
+        mMinDate.setTimeInMillis(minDate);
+        mDayPickerView.goTo(getSelectedDay(), false, true, true);
+    }
+
+    @Override
+    public Calendar getMinDate() {
+        return mMinDate;
+    }
+
+    @Override
+    public void setMaxDate(long maxDate) {
+        mTempDate.setTimeInMillis(maxDate);
+        if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
+                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
+            return;
+        }
+        if (mCurrentDate.after(mTempDate)) {
+            mCurrentDate.setTimeInMillis(maxDate);
+            updatePickers();
+            updateDisplay(false);
+        }
+        mMaxDate.setTimeInMillis(maxDate);
+        mDayPickerView.goTo(getSelectedDay(), false, true, true);
+    }
+
+    @Override
+    public Calendar getMaxDate() {
+        return mMaxDate;
+    }
+
+    @Override
+    public int getFirstDayOfWeek() {
+        return mCurrentDate.getFirstDayOfWeek();
+    }
+
+    @Override
+    public int getMinYear() {
+        return mMinDate.get(Calendar.YEAR);
+    }
+
+    @Override
+    public int getMaxYear() {
+        return mMaxDate.get(Calendar.YEAR);
+    }
+
+    @Override
+    public int getMinMonth() {
+        return mMinDate.get(Calendar.MONTH);
+    }
+
+    @Override
+    public int getMaxMonth() {
+        return mMaxDate.get(Calendar.MONTH);
+    }
+
+    @Override
+    public int getMinDay() {
+        return mMinDate.get(Calendar.DAY_OF_MONTH);
+    }
+
+    @Override
+    public int getMaxDay() {
+        return mMaxDate.get(Calendar.DAY_OF_MONTH);
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        mMonthAndDayLayout.setEnabled(enabled);
+        mSelectedYearView.setEnabled(enabled);
+        mAnimator.setEnabled(enabled);
+        mIsEnabled = enabled;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    @Override
+    public void setDateSelectorDayOfWeekBackgroundColor(int color) {
+        if (mDayOfWeekBackgroundColor != color) {
+            mDayOfWeekBackgroundColor = color;
+            mDayOfWeekView.setBackgroundColor(color);
+        }
+    }
+
+    @Override
+    public int getDateSelectorDayOfWeekBackgroundColor() {
+        return mDayOfWeekBackgroundColor;
+    }
+
+    @Override
+    public void setDateSelectorDayOfWeekTextAppearance(int resId) {
+        if (mDayOfWeekTextAppearanceResId != resId && resId > 0) {
+            mDayOfWeekTextAppearanceResId = resId;
+            mDayOfWeekView.setTextAppearance(mContext, resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorDayOfWeekTextAppearance() {
+        return mDayOfWeekTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorBackgroundColor(int color) {
+        if (mMonthAndDayBackgroundColor != color) {
+            mMonthAndDayBackgroundColor = color;
+            mMonthAndDayLayout.setBackgroundColor(color);
+        }
+    }
+
+    @Override
+    public int getDateSelectorBackgroundColor() {
+        return mMonthAndDayBackgroundColor;
+    }
+
+    @Override
+    public void setDateSelectorMonthTextAppearance(int resId) {
+        if (mMonthTextAppearanceResId != resId && resId > 0) {
+            mMonthTextAppearanceResId = resId;
+            mSelectedMonthTextView.setTextAppearance(mContext, resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorMonthTextAppearance() {
+        return mMonthTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorDayOfMonthTextAppearance(int resId) {
+        if (mDayOfMonthTextAppearanceResId != resId && resId > 0) {
+            mDayOfMonthTextAppearanceResId = resId;
+            mSelectedDayTextView.setTextAppearance(mContext, resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorDayOfMonthTextAppearance() {
+        return mDayOfMonthTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorYearTextAppearance(int resId) {
+        if (mYearTextAppearanceResId != resId && resId > 0) {
+            mYearTextAppearanceResId = resId;
+            mSelectedYearView.setTextAppearance(mContext, resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorYearTextAppearance() {
+        return mYearTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorYearListItemTextAppearance(int resId) {
+        if (mYearListItemTextAppearanceResId != resId) {
+            mYearListItemTextAppearanceResId = resId;
+            mYearPickerView.setItemTextAppearance(resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorYearListItemTextAppearance() {
+        return mYearListItemTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorYearListSelectedCircleColor(int color) {
+        mYearPickerView.setYearSelectedCircleColor(color);
+    }
+
+    @Override
+    public int getDateSelectorYearListSelectedCircleColor() {
+        return mYearPickerView.getYearSelectedCircleColor();
+    }
+
+    @Override
+    public void setCalendarTextColor(ColorStateList colors) {
+        if (colors == null) {
+            return;
+        }
+        if (mCalendarTextColors == null || !mCalendarTextColors.equals(colors)) {
+            mCalendarTextColors = colors;
+            mDayPickerView.setCalendarTextColor(colors);
+        }
+    }
+
+    @Override
+    public ColorStateList getCalendarTextColors() {
+        return mCalendarTextColors;
+    }
+
+    @Override
+    public CalendarView getCalendarView() {
+        throw new UnsupportedOperationException(
+                "CalendarView does not exists for the new DatePicker");
+    }
+
+    @Override
+    public void setCalendarViewShown(boolean shown) {
+        // No-op for compatibility with the old DatePicker.
+    }
+
+    @Override
+    public boolean getCalendarViewShown() {
+        return false;
+    }
+
+    @Override
+    public void setSpinnersShown(boolean shown) {
+        // No-op for compatibility with the old DatePicker.
+    }
+
+    @Override
+    public boolean getSpinnersShown() {
+        return false;
+    }
+
+    @Override
+    public void setShowDoneButton(boolean showDoneButton) {
+        mShowDoneButton = showDoneButton;
+        updateDoneButtonVisibility();
+    }
+
+    private void updateDoneButtonVisibility() {
+        mLayoutButtons.setVisibility(mShowDoneButton ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void setDismissCallback(DatePicker.DatePickerDismissCallback callback) {
+        mDismissCallback = callback;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        mYearFormat = new SimpleDateFormat("y", newConfig.locale);
+        mDayFormat = new SimpleDateFormat("d", newConfig.locale);
+    }
+
+    @Override
+    public void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+        // Nothing to do
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState(Parcelable superState) {
+        final int year = mCurrentDate.get(Calendar.YEAR);
+        final int month = mCurrentDate.get(Calendar.MONTH);
+        final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+
+        int listPosition = -1;
+        int listPositionOffset = -1;
+
+        if (mCurrentView == MONTH_AND_DAY_VIEW) {
+            listPosition = mDayPickerView.getMostVisiblePosition();
+        } else if (mCurrentView == YEAR_VIEW) {
+            listPosition = mYearPickerView.getFirstVisiblePosition();
+            listPositionOffset = mYearPickerView.getFirstPositionOffset();
+        }
+
+        return new SavedState(superState, year, month, day, mMinDate.getTimeInMillis(),
+                mMaxDate.getTimeInMillis(), mCurrentView, listPosition, listPositionOffset);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+
+        mCurrentDate.set(ss.getSelectedDay(), ss.getSelectedMonth(), ss.getSelectedYear());
+        mCurrentView = ss.getCurrentView();
+        mMinDate.setTimeInMillis(ss.getMinDate());
+        mMaxDate.setTimeInMillis(ss.getMaxDate());
+
+        updateDisplay(false);
+        setCurrentView(mCurrentView);
+
+        final int listPosition = ss.getListPosition();
+        if (listPosition != -1) {
+            if (mCurrentView == MONTH_AND_DAY_VIEW) {
+                mDayPickerView.postSetSelection(listPosition);
+            } else if (mCurrentView == YEAR_VIEW) {
+                mYearPickerView.postSetSelectionFromTop(listPosition, ss.getListPositionOffset());
+            }
+        }
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        onPopulateAccessibilityEvent(event);
+        return true;
+    }
+
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        event.getText().add(mCurrentDate.getTime().toString());
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        event.setClassName(DatePicker.class.getName());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        info.setClassName(DatePicker.class.getName());
+    }
+
+    @Override
+    public void onYearSelected(int year) {
+        adjustDayInMonthIfNeeded(mCurrentDate.get(Calendar.MONTH), year);
+        mCurrentDate.set(Calendar.YEAR, year);
+        updatePickers();
+        setCurrentView(MONTH_AND_DAY_VIEW);
+        updateDisplay(true);
+        updateDoneButtonEnableState();
+    }
+
+    // If the newly selected month / year does not contain the currently selected day number,
+    // change the selected day number to the last day of the selected month or year.
+    //      e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
+    //      e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
+    private void adjustDayInMonthIfNeeded(int month, int year) {
+        int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+        int daysInMonth = getDaysInMonth(month, year);
+        if (day > daysInMonth) {
+            mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
+        }
+    }
+
+    public static int getDaysInMonth(int month, int year) {
+        switch (month) {
+            case Calendar.JANUARY:
+            case Calendar.MARCH:
+            case Calendar.MAY:
+            case Calendar.JULY:
+            case Calendar.AUGUST:
+            case Calendar.OCTOBER:
+            case Calendar.DECEMBER:
+                return 31;
+            case Calendar.APRIL:
+            case Calendar.JUNE:
+            case Calendar.SEPTEMBER:
+            case Calendar.NOVEMBER:
+                return 30;
+            case Calendar.FEBRUARY:
+                return (year % 4 == 0) ? 29 : 28;
+            default:
+                throw new IllegalArgumentException("Invalid Month");
+        }
+    }
+
+    @Override
+    public void onDayOfMonthSelected(int year, int month, int day) {
+        mCurrentDate.set(Calendar.YEAR, year);
+        mCurrentDate.set(Calendar.MONTH, month);
+        mCurrentDate.set(Calendar.DAY_OF_MONTH, day);
+        updatePickers();
+        updateDisplay(true);
+        updateDoneButtonEnableState();
+    }
+
+    private void updateDoneButtonEnableState() {
+        if (mShowDoneButton) {
+            final boolean enabled = mCurrentDate.equals(mMinDate) ||
+                    mCurrentDate.equals(mMaxDate) ||
+                    (mCurrentDate.after(mMinDate) && mCurrentDate.before(mMaxDate));
+            mDoneButton.setEnabled(enabled);
+        }
+    }
+
+    private void updatePickers() {
+        Iterator<OnDateChangedListener> iterator = mListeners.iterator();
+        while (iterator.hasNext()) {
+            iterator.next().onDateChanged();
+        }
+    }
+
+    @Override
+    public void registerOnDateChangedListener(OnDateChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void unregisterOnDateChangedListener(OnDateChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    @Override
+    public Calendar getSelectedDay() {
+        return mCurrentDate;
+    }
+
+    @Override
+    public void tryVibrate() {
+        mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
+    }
+
+    @Override
+    public void onClick(View v) {
+        tryVibrate();
+        if (v.getId() == R.id.date_picker_year) {
+            setCurrentView(YEAR_VIEW);
+        } else if (v.getId() == R.id.date_picker_month_and_day_layout) {
+            setCurrentView(MONTH_AND_DAY_VIEW);
+        }
+    }
+
+    /**
+     * Class for managing state storing/restoring.
+     */
+    private static class SavedState extends View.BaseSavedState {
+
+        private final int mSelectedYear;
+        private final int mSelectedMonth;
+        private final int mSelectedDay;
+        private final long mMinDate;
+        private final long mMaxDate;
+        private final int mCurrentView;
+        private final int mListPosition;
+        private final int mListPositionOffset;
+
+        /**
+         * Constructor called from {@link DatePicker#onSaveInstanceState()}
+         */
+        private SavedState(Parcelable superState, int year, int month, int day,
+                long minDate, long maxDate, int currentView, int listPosition,
+                int listPositionOffset) {
+            super(superState);
+            mSelectedYear = year;
+            mSelectedMonth = month;
+            mSelectedDay = day;
+            mMinDate = minDate;
+            mMaxDate = maxDate;
+            mCurrentView = currentView;
+            mListPosition = listPosition;
+            mListPositionOffset = listPositionOffset;
+        }
+
+        /**
+         * Constructor called from {@link #CREATOR}
+         */
+        private SavedState(Parcel in) {
+            super(in);
+            mSelectedYear = in.readInt();
+            mSelectedMonth = in.readInt();
+            mSelectedDay = in.readInt();
+            mMinDate = in.readLong();
+            mMaxDate = in.readLong();
+            mCurrentView = in.readInt();
+            mListPosition = in.readInt();
+            mListPositionOffset = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(mSelectedYear);
+            dest.writeInt(mSelectedMonth);
+            dest.writeInt(mSelectedDay);
+            dest.writeLong(mMinDate);
+            dest.writeLong(mMaxDate);
+            dest.writeInt(mCurrentView);
+            dest.writeInt(mListPosition);
+            dest.writeInt(mListPositionOffset);
+        }
+
+        public int getSelectedDay() {
+            return mSelectedDay;
+        }
+
+        public int getSelectedMonth() {
+            return mSelectedMonth;
+        }
+
+        public int getSelectedYear() {
+            return mSelectedYear;
+        }
+
+        public long getMinDate() {
+            return mMinDate;
+        }
+
+        public long getMaxDate() {
+            return mMaxDate;
+        }
+
+        public int getCurrentView() {
+            return mCurrentView;
+        }
+
+        public int getListPosition() {
+            return mListPosition;
+        }
+
+        public int getListPositionOffset() {
+            return mListPositionOffset;
+        }
+
+        @SuppressWarnings("all")
+        // suppress unused and hiding
+        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
+
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    /**
+     * Render an animator to pulsate a view in place.
+     * @param labelToAnimate the view to pulsate.
+     * @return The animator object. Use .start() to begin.
+     */
+    public static ObjectAnimator getPulseAnimator(View labelToAnimate, float decreaseRatio,
+                                                  float increaseRatio) {
+        Keyframe k0 = Keyframe.ofFloat(0f, 1f);
+        Keyframe k1 = Keyframe.ofFloat(0.275f, decreaseRatio);
+        Keyframe k2 = Keyframe.ofFloat(0.69f, increaseRatio);
+        Keyframe k3 = Keyframe.ofFloat(1f, 1f);
+
+        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe(View.SCALE_X, k0, k1, k2, k3);
+        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe(View.SCALE_Y, k0, k1, k2, k3);
+        ObjectAnimator pulseAnimator =
+                ObjectAnimator.ofPropertyValuesHolder(labelToAnimate, scaleX, scaleY);
+        pulseAnimator.setDuration(PULSE_ANIMATOR_DURATION);
+
+        return pulseAnimator;
+    }
+}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
new file mode 100644
index 0000000..c44bd46
--- /dev/null
+++ b/core/java/android/widget/DayPickerView.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * This displays a list of months in a calendar format with selectable days.
+ */
+class DayPickerView extends ListView implements AbsListView.OnScrollListener,
+        OnDateChangedListener {
+
+    private static final String TAG = "DayPickerView";
+
+    // How long the GoTo fling animation should last
+    private static final int GOTO_SCROLL_DURATION = 250;
+
+    // How long to wait after receiving an onScrollStateChanged notification before acting on it
+    private static final int SCROLL_CHANGE_DELAY = 40;
+
+    private static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator
+
+    private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
+
+    // These affect the scroll speed and feel
+    private float mFriction = 1.0f;
+
+    // highlighted time
+    private Calendar mSelectedDay = Calendar.getInstance();
+    private SimpleMonthAdapter mAdapter;
+
+    private Calendar mTempDay = Calendar.getInstance();
+
+    // which month should be displayed/highlighted [0-11]
+    private int mCurrentMonthDisplayed;
+    // used for tracking what state listview is in
+    private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+    // used for tracking what state listview is in
+    private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+
+    private DatePickerController mController;
+    private boolean mPerformingScroll;
+
+    private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this);
+
+    public DayPickerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public DayPickerView(Context context, DatePickerController controller) {
+        super(context);
+        init();
+        setController(controller);
+    }
+
+    public void setController(DatePickerController controller) {
+        if (mController != null) {
+            mController.unregisterOnDateChangedListener(this);
+        }
+        mController = controller;
+        mController.registerOnDateChangedListener(this);
+        setUpAdapter();
+        setAdapter(mAdapter);
+        onDateChanged();
+    }
+
+    public void init() {
+        setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+        setDrawSelectorOnTop(false);
+
+        setUpListView();
+    }
+
+    public void onChange() {
+        setUpAdapter();
+        setAdapter(mAdapter);
+    }
+
+    /**
+     * Creates a new adapter if necessary and sets up its parameters. Override
+     * this method to provide a custom adapter.
+     */
+    protected void setUpAdapter() {
+        if (mAdapter == null) {
+            mAdapter = new SimpleMonthAdapter(getContext(), mController);
+        } else {
+            mAdapter.setSelectedDay(mSelectedDay);
+            mAdapter.notifyDataSetChanged();
+        }
+        // refresh the view with the new parameters
+        mAdapter.notifyDataSetChanged();
+    }
+
+    /*
+     * Sets all the required fields for the list view. Override this method to
+     * set a different list view behavior.
+     */
+    protected void setUpListView() {
+        // Transparent background on scroll
+        setCacheColorHint(0);
+        // No dividers
+        setDivider(null);
+        // Items are clickable
+        setItemsCanFocus(true);
+        // The thumb gets in the way, so disable it
+        setFastScrollEnabled(false);
+        setVerticalScrollBarEnabled(false);
+        setOnScrollListener(this);
+        setFadingEdgeLength(0);
+        // Make the scrolling behavior nicer
+        setFriction(ViewConfiguration.getScrollFriction() * mFriction);
+    }
+
+    private int getDiffMonths(Calendar start, Calendar end){
+        final int diffYears = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
+        final int diffMonths = end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
+        return diffMonths;
+    }
+
+    private int getPositionFromDay(Calendar day) {
+        final int diffMonthMax = getDiffMonths(mController.getMinDate(), mController.getMaxDate());
+        int diffMonth = getDiffMonths(mController.getMinDate(), day);
+
+        if (diffMonth < 0 ) {
+            diffMonth = 0;
+        } else if (diffMonth > diffMonthMax) {
+            diffMonth = diffMonthMax;
+        }
+
+        return diffMonth;
+    }
+
+    /**
+     * This moves to the specified time in the view. If the time is not already
+     * in range it will move the list so that the first of the month containing
+     * the time is at the top of the view. If the new time is already in view
+     * the list will not be scrolled unless forceScroll is true. This time may
+     * optionally be highlighted as selected as well.
+     *
+     * @param day The day to move to
+     * @param animate Whether to scroll to the given time or just redraw at the
+     *            new location
+     * @param setSelected Whether to set the given time as selected
+     * @param forceScroll Whether to recenter even if the time is already
+     *            visible
+     * @return Whether or not the view animated to the new location
+     */
+    public boolean goTo(Calendar day, boolean animate, boolean setSelected,
+                        boolean forceScroll) {
+
+        // Set the selected day
+        if (setSelected) {
+            mSelectedDay.setTimeInMillis(day.getTimeInMillis());
+        }
+
+        mTempDay.setTimeInMillis(day.getTimeInMillis());
+        final int position = getPositionFromDay(day);
+
+        View child;
+        int i = 0;
+        int top = 0;
+        // Find a child that's completely in the view
+        do {
+            child = getChildAt(i++);
+            if (child == null) {
+                break;
+            }
+            top = child.getTop();
+        } while (top < 0);
+
+        // Compute the first and last position visible
+        int selectedPosition;
+        if (child != null) {
+            selectedPosition = getPositionForView(child);
+        } else {
+            selectedPosition = 0;
+        }
+
+        if (setSelected) {
+            mAdapter.setSelectedDay(mSelectedDay);
+        }
+
+        // Check if the selected day is now outside of our visible range
+        // and if so scroll to the month that contains it
+        if (position != selectedPosition || forceScroll) {
+            setMonthDisplayed(mTempDay);
+            mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
+            if (animate) {
+                smoothScrollToPositionFromTop(
+                        position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION);
+                return true;
+            } else {
+                postSetSelection(position);
+            }
+        } else if (setSelected) {
+            setMonthDisplayed(mSelectedDay);
+        }
+        return false;
+    }
+
+    public void postSetSelection(final int position) {
+        clearFocus();
+        post(new Runnable() {
+
+            @Override
+            public void run() {
+                setSelection(position);
+            }
+        });
+        onScrollStateChanged(this, OnScrollListener.SCROLL_STATE_IDLE);
+    }
+
+    /**
+     * Updates the title and selected month if the view has moved to a new
+     * month.
+     */
+    @Override
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+                         int totalItemCount) {
+        SimpleMonthView child = (SimpleMonthView) view.getChildAt(0);
+        if (child == null) {
+            return;
+        }
+
+        mPreviousScrollState = mCurrentScrollState;
+    }
+
+    /**
+     * Sets the month displayed at the top of this view based on time. Override
+     * to add custom events when the title is changed.
+     */
+    protected void setMonthDisplayed(Calendar date) {
+        if (mCurrentMonthDisplayed != date.get(Calendar.MONTH)) {
+            mCurrentMonthDisplayed = date.get(Calendar.MONTH);
+            invalidateViews();
+        }
+    }
+
+    @Override
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+        // use a post to prevent re-entering onScrollStateChanged before it
+        // exits
+        mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
+    }
+
+    void setCalendarTextColor(ColorStateList colors) {
+        mAdapter.setCalendarTextColor(colors);
+    }
+
+    protected class ScrollStateRunnable implements Runnable {
+        private int mNewState;
+        private View mParent;
+
+        ScrollStateRunnable(View view) {
+            mParent = view;
+        }
+
+        /**
+         * Sets up the runnable with a short delay in case the scroll state
+         * immediately changes again.
+         *
+         * @param view The list view that changed state
+         * @param scrollState The new state it changed to
+         */
+        public void doScrollStateChange(AbsListView view, int scrollState) {
+            mParent.removeCallbacks(this);
+            mNewState = scrollState;
+            mParent.postDelayed(this, SCROLL_CHANGE_DELAY);
+        }
+
+        @Override
+        public void run() {
+            mCurrentScrollState = mNewState;
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG,
+                        "new scroll state: " + mNewState + " old state: " + mPreviousScrollState);
+            }
+            // Fix the position after a scroll or a fling ends
+            if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
+                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE
+                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+                mPreviousScrollState = mNewState;
+                int i = 0;
+                View child = getChildAt(i);
+                while (child != null && child.getBottom() <= 0) {
+                    child = getChildAt(++i);
+                }
+                if (child == null) {
+                    // The view is no longer visible, just return
+                    return;
+                }
+                int firstPosition = getFirstVisiblePosition();
+                int lastPosition = getLastVisiblePosition();
+                boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1;
+                final int top = child.getTop();
+                final int bottom = child.getBottom();
+                final int midpoint = getHeight() / 2;
+                if (scroll && top < LIST_TOP_OFFSET) {
+                    if (bottom > midpoint) {
+                        smoothScrollBy(top, GOTO_SCROLL_DURATION);
+                    } else {
+                        smoothScrollBy(bottom, GOTO_SCROLL_DURATION);
+                    }
+                }
+            } else {
+                mPreviousScrollState = mNewState;
+            }
+        }
+    }
+
+    /**
+     * Gets the position of the view that is most prominently displayed within the list view.
+     */
+    public int getMostVisiblePosition() {
+        final int firstPosition = getFirstVisiblePosition();
+        final int height = getHeight();
+
+        int maxDisplayedHeight = 0;
+        int mostVisibleIndex = 0;
+        int i=0;
+        int bottom = 0;
+        while (bottom < height) {
+            View child = getChildAt(i);
+            if (child == null) {
+                break;
+            }
+            bottom = child.getBottom();
+            int displayedHeight = Math.min(bottom, height) - Math.max(0, child.getTop());
+            if (displayedHeight > maxDisplayedHeight) {
+                mostVisibleIndex = i;
+                maxDisplayedHeight = displayedHeight;
+            }
+            i++;
+        }
+        return firstPosition + mostVisibleIndex;
+    }
+
+    @Override
+    public void onDateChanged() {
+        goTo(mController.getSelectedDay(), false, true, true);
+    }
+
+    /**
+     * Attempts to return the date that has accessibility focus.
+     *
+     * @return The date that has accessibility focus, or {@code null} if no date
+     *         has focus.
+     */
+    private Calendar findAccessibilityFocus() {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child instanceof SimpleMonthView) {
+                final Calendar focus = ((SimpleMonthView) child).getAccessibilityFocus();
+                if (focus != null) {
+                    return focus;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Attempts to restore accessibility focus to a given date. No-op if
+     * {@code day} is {@code null}.
+     *
+     * @param day The date that should receive accessibility focus
+     * @return {@code true} if focus was restored
+     */
+    private boolean restoreAccessibilityFocus(Calendar day) {
+        if (day == null) {
+            return false;
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child instanceof SimpleMonthView) {
+                if (((SimpleMonthView) child).restoreAccessibilityFocus(day)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    protected void layoutChildren() {
+        final Calendar focusedDay = findAccessibilityFocus();
+        super.layoutChildren();
+        if (mPerformingScroll) {
+            mPerformingScroll = false;
+        } else {
+            restoreAccessibilityFocus(focusedDay);
+        }
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setItemCount(-1);
+    }
+
+    private String getMonthAndYearString(Calendar day) {
+        StringBuffer sbuf = new StringBuffer();
+        sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
+        sbuf.append(" ");
+        sbuf.append(mYearFormat.format(day.getTime()));
+        return sbuf.toString();
+    }
+
+    /**
+     * Necessary for accessibility, to ensure we support "scrolling" forward and backward
+     * in the month list.
+     */
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+        info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+    }
+
+    /**
+     * When scroll forward/backward events are received, announce the newly scrolled-to month.
+     */
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (action != AccessibilityNodeInfo.ACTION_SCROLL_FORWARD &&
+                action != AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+            return super.performAccessibilityAction(action, arguments);
+        }
+
+        // Figure out what month is showing.
+        int firstVisiblePosition = getFirstVisiblePosition();
+        int month = firstVisiblePosition % 12;
+        int year = firstVisiblePosition / 12 + mController.getMinYear();
+        Calendar day = Calendar.getInstance();
+        day.set(year, month, 1);
+
+        // Scroll either forward or backward one month.
+        if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
+            day.add(Calendar.MONTH, 1);
+            if (day.get(Calendar.MONTH) == 12) {
+                day.set(Calendar.MONTH, 0);
+                day.add(Calendar.YEAR, 1);
+            }
+        } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+            View firstVisibleView = getChildAt(0);
+            // If the view is fully visible, jump one month back. Otherwise, we'll just jump
+            // to the first day of first visible month.
+            if (firstVisibleView != null && firstVisibleView.getTop() >= -1) {
+                // There's an off-by-one somewhere, so the top of the first visible item will
+                // actually be -1 when it's at the exact top.
+                day.add(Calendar.MONTH, -1);
+                if (day.get(Calendar.MONTH) == -1) {
+                    day.set(Calendar.MONTH, 11);
+                    day.add(Calendar.YEAR, -1);
+                }
+            }
+        }
+
+        // Go to that month.
+        announceForAccessibility(getMonthAndYearString(day));
+        goTo(day, true, false, true);
+        mPerformingScroll = true;
+        return true;
+    }
+}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 3758d86..57b8dcb 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -21,7 +21,6 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.RectF;
 
 import android.content.Context;
 import android.graphics.Canvas;
@@ -106,7 +105,6 @@
     private float mPullDistance;
     
     private final Rect mBounds = new Rect();
-    private final RectF mArcRect = new RectF();
     private final Paint mPaint = new Paint();
     private float mRadius;
     private float mBaseGlowHeight;
@@ -318,11 +316,9 @@
 
         final int count = canvas.save();
 
-        final float y = mBounds.height();
-        final float centerY = y - mRadius;
         final float centerX = mBounds.centerX();
+        final float centerY = mBounds.height() - mRadius;
 
-        mArcRect.set(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius);
         canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0);
 
         final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
@@ -330,7 +326,7 @@
 
         canvas.clipRect(mBounds);
         canvas.translate(translateX, 0);
-        canvas.drawArc(mArcRect, 45, 90, false, mPaint);
+        canvas.drawCircle(centerX, centerY, mRadius, mPaint);
         canvas.restoreToCount(count);
 
         boolean oneLastFrame = false;
diff --git a/core/java/android/widget/OnDateChangedListener.java b/core/java/android/widget/OnDateChangedListener.java
new file mode 100644
index 0000000..29be888
--- /dev/null
+++ b/core/java/android/widget/OnDateChangedListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+/**
+ * The callback used to notify other date picker components of a change in the selected date.
+ *
+ */
+interface OnDateChangedListener {
+
+    public void onDateChanged();
+}
+
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
new file mode 100644
index 0000000..53d0839
--- /dev/null
+++ b/core/java/android/widget/SimpleMonthAdapter.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Calendar;
+import java.util.HashMap;
+
+/**
+ * An adapter for a list of {@link android.widget.SimpleMonthView} items.
+ */
+class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayClickListener {
+    private static final String TAG = "SimpleMonthAdapter";
+
+    private final Context mContext;
+    private final DatePickerController mController;
+    private Calendar mSelectedDay;
+
+    private ColorStateList mCalendarTextColors;
+
+    public SimpleMonthAdapter(Context context, DatePickerController controller) {
+        mContext = context;
+        mController = controller;
+        init();
+        setSelectedDay(mController.getSelectedDay());
+    }
+
+    /**
+     * Updates the selected day and related parameters.
+     *
+     * @param day The day to highlight
+     */
+    public void setSelectedDay(Calendar day) {
+        if (mSelectedDay != day) {
+            mSelectedDay = day;
+            notifyDataSetChanged();
+        }
+    }
+
+    void setCalendarTextColor(ColorStateList colors) {
+        mCalendarTextColors = colors;
+    }
+
+    /**
+     * Set up the gesture detector and selected time
+     */
+    protected void init() {
+        mSelectedDay = Calendar.getInstance();
+    }
+
+    @Override
+    public int getCount() {
+        final int diffYear = mController.getMaxYear() - mController.getMinYear();
+        final int diffMonth = 1 + mController.getMaxMonth() - mController.getMinMonth()
+                + 12 * diffYear;
+        return diffMonth;
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return null;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        SimpleMonthView v;
+        HashMap<String, Integer> drawingParams = null;
+        if (convertView != null) {
+            v = (SimpleMonthView) convertView;
+            // We store the drawing parameters in the view so it can be recycled
+            drawingParams = (HashMap<String, Integer>) v.getTag();
+        } else {
+            v = new SimpleMonthView(mContext);
+            // Set up the new view
+            AbsListView.LayoutParams params = new AbsListView.LayoutParams(
+                    AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT);
+            v.setLayoutParams(params);
+            v.setClickable(true);
+            v.setOnDayClickListener(this);
+            if (mCalendarTextColors != null) {
+                v.setTextColor(mCalendarTextColors);
+            }
+        }
+        if (drawingParams == null) {
+            drawingParams = new HashMap<String, Integer>();
+        } else {
+            drawingParams.clear();
+        }
+        final int currentMonth = position + mController.getMinMonth();
+        final int month = currentMonth % 12;
+        final int year = currentMonth / 12 + mController.getMinYear();
+
+        int selectedDay = -1;
+        if (isSelectedDayInMonth(year, month)) {
+            selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
+        }
+
+        // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
+        // height/number of weeks before being displayed.
+        v.reuse();
+
+        final int enabledDayRangeStart;
+        if (mController.getMinMonth() == month && mController.getMinYear() == year) {
+            enabledDayRangeStart = mController.getMinDay();
+        } else {
+            enabledDayRangeStart = 1;
+        }
+
+        final int enabledDayRangeEnd;
+        if (mController.getMaxMonth() == month && mController.getMaxYear() == year) {
+            enabledDayRangeEnd = mController.getMaxDay();
+        } else {
+            enabledDayRangeEnd = 31;
+        }
+
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_SELECTED_DAY, selectedDay);
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_YEAR, year);
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_MONTH, month);
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_WEEK_START, mController.getFirstDayOfWeek());
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_ENABLEDDAYRANGE_START, enabledDayRangeStart);
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_ENABLEDDAYRANGE_END, enabledDayRangeEnd);
+        v.setMonthParams(drawingParams);
+        v.invalidate();
+        return v;
+    }
+
+    private boolean isSelectedDayInMonth(int year, int month) {
+        return mSelectedDay.get(Calendar.YEAR) == year && mSelectedDay.get(Calendar.MONTH) == month;
+    }
+
+    @Override
+    public void onDayClick(SimpleMonthView view, Calendar day) {
+        if (day != null) {
+            onDayTapped(day);
+        }
+    }
+
+    /**
+     * Maintains the same hour/min/sec but moves the day to the tapped day.
+     *
+     * @param day The day that was tapped
+     */
+    protected void onDayTapped(Calendar day) {
+        mController.tryVibrate();
+        mController.onDayOfMonthSelected(day.get(Calendar.YEAR), day.get(Calendar.MONTH),
+                day.get(Calendar.DAY_OF_MONTH));
+        setSelectedDay(day);
+    }
+}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
new file mode 100644
index 0000000..7589711
--- /dev/null
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.internal.R;
+import com.android.internal.widget.ExploreByTouchHelper;
+
+import java.security.InvalidParameterException;
+import java.util.Calendar;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * A calendar-like view displaying a specified month and the appropriate selectable day numbers
+ * within the specified month.
+ */
+class SimpleMonthView extends View {
+    private static final String TAG = "SimpleMonthView";
+
+    /**
+     * These params can be passed into the view to control how it appears.
+     * {@link #VIEW_PARAMS_WEEK} is the only required field, though the default
+     * values are unlikely to fit most layouts correctly.
+     */
+    /**
+     * This sets the height of this week in pixels
+     */
+    static final String VIEW_PARAMS_HEIGHT = "height";
+    /**
+     * This specifies the position (or weeks since the epoch) of this week,
+     * calculated using
+     */
+    static final String VIEW_PARAMS_MONTH = "month";
+    /**
+     * This specifies the position (or weeks since the epoch) of this week,
+     * calculated using
+     */
+    static final String VIEW_PARAMS_YEAR = "year";
+    /**
+     * This sets one of the days in this view as selected {@link Time#SUNDAY}
+     * through {@link Time#SATURDAY}.
+     */
+    static final String VIEW_PARAMS_SELECTED_DAY = "selected_day";
+    /**
+     * Which day the week should start on. {@link Time#SUNDAY} through
+     * {@link Time#SATURDAY}.
+     */
+    static final String VIEW_PARAMS_WEEK_START = "week_start";
+    /**
+     * First enabled day.
+     */
+    static final String VIEW_PARAMS_ENABLEDDAYRANGE_START = "enabled_day_range_start";
+    /**
+     * Last enabled day.
+     */
+    static final String VIEW_PARAMS_ENABLEDDAYRANGE_END = "enabled_day_range_end";
+
+    private static int DEFAULT_HEIGHT = 32;
+    private static int MIN_HEIGHT = 10;
+
+    private static final int DEFAULT_SELECTED_DAY = -1;
+    private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
+    private static final int DEFAULT_NUM_DAYS = 7;
+    private static final int DEFAULT_NUM_ROWS = 6;
+    private static final int MAX_NUM_ROWS = 6;
+
+    private static final int SELECTED_CIRCLE_ALPHA = 60;
+
+    private static int DAY_SEPARATOR_WIDTH = 1;
+
+    private int mMiniDayNumberTextSize;
+    private int mMonthLabelTextSize;
+    private int mMonthDayLabelTextSize;
+    private int mMonthHeaderSize;
+    private int mDaySelectedCircleSize;
+
+    // used for scaling to the device density
+    private static float mScale = 0;
+
+    // affects the padding on the sides of this view
+    private int mPadding = 0;
+
+    private String mDayOfWeekTypeface;
+    private String mMonthTitleTypeface;
+
+    private Paint mDayNumberPaint;
+    private Paint mDayNumberDisabledPaint;
+    private Paint mDayNumberSelectedPaint;
+
+    private Paint mMonthTitlePaint;
+    private Paint mMonthDayLabelPaint;
+
+    private final Formatter mFormatter;
+    private final StringBuilder mStringBuilder;
+
+    private int mMonth;
+    private int mYear;
+
+    // Quick reference to the width of this view, matches parent
+    private int mWidth;
+
+    // The height this view should draw at in pixels, set by height param
+    private int mRowHeight = DEFAULT_HEIGHT;
+
+    // If this view contains the today
+    private boolean mHasToday = false;
+
+    // Which day is selected [0-6] or -1 if no day is selected
+    private int mSelectedDay = -1;
+
+    // Which day is today [0-6] or -1 if no day is today
+    private int mToday = DEFAULT_SELECTED_DAY;
+
+    // Which day of the week to start on [0-6]
+    private int mWeekStart = DEFAULT_WEEK_START;
+
+    // How many days to display
+    private int mNumDays = DEFAULT_NUM_DAYS;
+
+    // The number of days + a spot for week number if it is displayed
+    private int mNumCells = mNumDays;
+
+    private int mDayOfWeekStart = 0;
+
+    // First enabled day
+    private int mEnabledDayStart = 1;
+
+    // Last enabled day
+    private int mEnabledDayEnd = 31;
+
+    private final Calendar mCalendar = Calendar.getInstance();
+    private final Calendar mDayLabelCalendar = Calendar.getInstance();
+
+    private final MonthViewTouchHelper mTouchHelper;
+
+    private int mNumRows = DEFAULT_NUM_ROWS;
+
+    // Optional listener for handling day click actions
+    private OnDayClickListener mOnDayClickListener;
+
+    // Whether to prevent setting the accessibility delegate
+    private boolean mLockAccessibilityDelegate;
+
+    private int mNormalTextColor;
+    private int mDisabledTextColor;
+    private int mSelectedDayColor;
+
+    public SimpleMonthView(Context context) {
+        this(context, null);
+    }
+
+    public SimpleMonthView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.datePickerStyle);
+    }
+
+    public SimpleMonthView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs);
+
+        final Resources res = context.getResources();
+
+        mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface);
+        mMonthTitleTypeface = res.getString(R.string.sans_serif);
+
+        mStringBuilder = new StringBuilder(50);
+        mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
+
+        mMiniDayNumberTextSize = res.getDimensionPixelSize(R.dimen.datepicker_day_number_size);
+        mMonthLabelTextSize = res.getDimensionPixelSize(R.dimen.datepicker_month_label_size);
+        mMonthDayLabelTextSize = res.getDimensionPixelSize(
+                R.dimen.datepicker_month_day_label_text_size);
+        mMonthHeaderSize = res.getDimensionPixelOffset(
+                R.dimen.datepicker_month_list_item_header_height);
+        mDaySelectedCircleSize = res.getDimensionPixelSize(
+                R.dimen.datepicker_day_number_select_circle_radius);
+
+        mRowHeight = (res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height)
+                - mMonthHeaderSize) / MAX_NUM_ROWS;
+
+        // Set up accessibility components.
+        mTouchHelper = new MonthViewTouchHelper(this);
+        setAccessibilityDelegate(mTouchHelper);
+        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        mLockAccessibilityDelegate = true;
+
+        // Sets up any standard paints that will be used
+        initView();
+    }
+
+    void setTextColor(ColorStateList colors) {
+        final Resources res = getContext().getResources();
+
+        mNormalTextColor = colors.getColorForState(ENABLED_STATE_SET,
+                res.getColor(R.color.datepicker_default_normal_text_color_holo_light));
+        mMonthTitlePaint.setColor(mNormalTextColor);
+        mMonthDayLabelPaint.setColor(mNormalTextColor);
+
+        mDisabledTextColor = colors.getColorForState(EMPTY_STATE_SET,
+                res.getColor(R.color.datepicker_default_disabled_text_color_holo_light));
+        mDayNumberDisabledPaint.setColor(mDisabledTextColor);
+
+        mSelectedDayColor = colors.getColorForState(ENABLED_SELECTED_STATE_SET,
+                res.getColor(R.color.holo_blue_light));
+        mDayNumberSelectedPaint.setColor(mSelectedDayColor);
+        mDayNumberSelectedPaint.setAlpha(SELECTED_CIRCLE_ALPHA);
+    }
+
+    @Override
+    public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
+        // Workaround for a JB MR1 issue where accessibility delegates on
+        // top-level ListView items are overwritten.
+        if (!mLockAccessibilityDelegate) {
+            super.setAccessibilityDelegate(delegate);
+        }
+    }
+
+    public void setOnDayClickListener(OnDayClickListener listener) {
+        mOnDayClickListener = listener;
+    }
+
+    @Override
+    public boolean dispatchHoverEvent(MotionEvent event) {
+        // First right-of-refusal goes the touch exploration helper.
+        if (mTouchHelper.dispatchHoverEvent(event)) {
+            return true;
+        }
+        return super.dispatchHoverEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_UP:
+                final int day = getDayFromLocation(event.getX(), event.getY());
+                if (day >= 0) {
+                    onDayClick(day);
+                }
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * Sets up the text and style properties for painting.
+     */
+    private void initView() {
+        mMonthTitlePaint = new Paint();
+        mMonthTitlePaint.setAntiAlias(true);
+        mMonthTitlePaint.setColor(mNormalTextColor);
+        mMonthTitlePaint.setTextSize(mMonthLabelTextSize);
+        mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface, Typeface.BOLD));
+        mMonthTitlePaint.setTextAlign(Align.CENTER);
+        mMonthTitlePaint.setStyle(Style.FILL);
+        mMonthTitlePaint.setFakeBoldText(true);
+
+        mMonthDayLabelPaint = new Paint();
+        mMonthDayLabelPaint.setAntiAlias(true);
+        mMonthDayLabelPaint.setColor(mNormalTextColor);
+        mMonthDayLabelPaint.setTextSize(mMonthDayLabelTextSize);
+        mMonthDayLabelPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.NORMAL));
+        mMonthDayLabelPaint.setTextAlign(Align.CENTER);
+        mMonthDayLabelPaint.setStyle(Style.FILL);
+        mMonthDayLabelPaint.setFakeBoldText(true);
+
+        mDayNumberSelectedPaint = new Paint();
+        mDayNumberSelectedPaint.setAntiAlias(true);
+        mDayNumberSelectedPaint.setColor(mSelectedDayColor);
+        mDayNumberSelectedPaint.setAlpha(SELECTED_CIRCLE_ALPHA);
+        mDayNumberSelectedPaint.setTextAlign(Align.CENTER);
+        mDayNumberSelectedPaint.setStyle(Style.FILL);
+        mDayNumberSelectedPaint.setFakeBoldText(true);
+
+        mDayNumberPaint = new Paint();
+        mDayNumberPaint.setAntiAlias(true);
+        mDayNumberPaint.setTextSize(mMiniDayNumberTextSize);
+        mDayNumberPaint.setTextAlign(Align.CENTER);
+        mDayNumberPaint.setStyle(Style.FILL);
+        mDayNumberPaint.setFakeBoldText(false);
+
+        mDayNumberDisabledPaint = new Paint();
+        mDayNumberDisabledPaint.setAntiAlias(true);
+        mDayNumberDisabledPaint.setColor(mDisabledTextColor);
+        mDayNumberDisabledPaint.setTextSize(mMiniDayNumberTextSize);
+        mDayNumberDisabledPaint.setTextAlign(Align.CENTER);
+        mDayNumberDisabledPaint.setStyle(Style.FILL);
+        mDayNumberDisabledPaint.setFakeBoldText(false);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        drawMonthTitle(canvas);
+        drawWeekDayLabels(canvas);
+        drawDays(canvas);
+    }
+
+    /**
+     * Sets all the parameters for displaying this week. The only required
+     * parameter is the week number. Other parameters have a default value and
+     * will only update if a new value is included, except for focus month,
+     * which will always default to no focus month if no value is passed in. See
+     * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters.
+     *
+     * @param params A map of the new parameters, see
+     *            {@link #VIEW_PARAMS_HEIGHT}
+     */
+    void setMonthParams(HashMap<String, Integer> params) {
+        if (!params.containsKey(VIEW_PARAMS_MONTH) && !params.containsKey(VIEW_PARAMS_YEAR)) {
+            throw new InvalidParameterException(
+                    "You must specify the month and year for this view");
+        }
+        setTag(params);
+        // We keep the current value for any params not present
+        if (params.containsKey(VIEW_PARAMS_HEIGHT)) {
+            mRowHeight = params.get(VIEW_PARAMS_HEIGHT);
+            if (mRowHeight < MIN_HEIGHT) {
+                mRowHeight = MIN_HEIGHT;
+            }
+        }
+        if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) {
+            mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY);
+        }
+
+        // Allocate space for caching the day numbers and focus values
+        mMonth = params.get(VIEW_PARAMS_MONTH);
+        mYear = params.get(VIEW_PARAMS_YEAR);
+
+        // Figure out what day today is
+        final Time today = new Time(Time.getCurrentTimezone());
+        today.setToNow();
+        mHasToday = false;
+        mToday = -1;
+
+        mCalendar.set(Calendar.MONTH, mMonth);
+        mCalendar.set(Calendar.YEAR, mYear);
+        mCalendar.set(Calendar.DAY_OF_MONTH, 1);
+        mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK);
+
+        if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
+            mWeekStart = params.get(VIEW_PARAMS_WEEK_START);
+        } else {
+            mWeekStart = mCalendar.getFirstDayOfWeek();
+        }
+
+        if (params.containsKey(VIEW_PARAMS_ENABLEDDAYRANGE_START)) {
+            mEnabledDayStart = params.get(VIEW_PARAMS_ENABLEDDAYRANGE_START);
+        }
+        if (params.containsKey(VIEW_PARAMS_ENABLEDDAYRANGE_END)) {
+            mEnabledDayEnd = params.get(VIEW_PARAMS_ENABLEDDAYRANGE_END);
+        }
+
+        mNumCells = getDaysInMonth(mMonth, mYear);
+        for (int i = 0; i < mNumCells; i++) {
+            final int day = i + 1;
+            if (sameDay(day, today)) {
+                mHasToday = true;
+                mToday = day;
+            }
+        }
+        mNumRows = calculateNumRows();
+
+        // Invalidate cached accessibility information.
+        mTouchHelper.invalidateRoot();
+    }
+
+    private static int getDaysInMonth(int month, int year) {
+        switch (month) {
+            case Calendar.JANUARY:
+            case Calendar.MARCH:
+            case Calendar.MAY:
+            case Calendar.JULY:
+            case Calendar.AUGUST:
+            case Calendar.OCTOBER:
+            case Calendar.DECEMBER:
+                return 31;
+            case Calendar.APRIL:
+            case Calendar.JUNE:
+            case Calendar.SEPTEMBER:
+            case Calendar.NOVEMBER:
+                return 30;
+            case Calendar.FEBRUARY:
+                return (year % 4 == 0) ? 29 : 28;
+            default:
+                throw new IllegalArgumentException("Invalid Month");
+        }
+    }
+
+    public void reuse() {
+        mNumRows = DEFAULT_NUM_ROWS;
+        requestLayout();
+    }
+
+    private int calculateNumRows() {
+        int offset = findDayOffset();
+        int dividend = (offset + mNumCells) / mNumDays;
+        int remainder = (offset + mNumCells) % mNumDays;
+        return (dividend + (remainder > 0 ? 1 : 0));
+    }
+
+    private boolean sameDay(int day, Time today) {
+        return mYear == today.year &&
+                mMonth == today.month &&
+                day == today.monthDay;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows
+                + mMonthHeaderSize);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mWidth = w;
+
+        // Invalidate cached accessibility information.
+        mTouchHelper.invalidateRoot();
+    }
+
+    private String getMonthAndYearString() {
+        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
+                | DateUtils.FORMAT_NO_MONTH_DAY;
+        mStringBuilder.setLength(0);
+        long millis = mCalendar.getTimeInMillis();
+        return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
+                Time.getCurrentTimezone()).toString();
+    }
+
+    private void drawMonthTitle(Canvas canvas) {
+        int x = (mWidth + 2 * mPadding) / 2;
+        int y = (mMonthHeaderSize - mMonthDayLabelTextSize) / 2 + (mMonthLabelTextSize / 3);
+        canvas.drawText(getMonthAndYearString(), x, y, mMonthTitlePaint);
+    }
+
+    private void drawWeekDayLabels(Canvas canvas) {
+        int y = mMonthHeaderSize - (mMonthDayLabelTextSize / 2);
+        int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
+
+        for (int i = 0; i < mNumDays; i++) {
+            int calendarDay = (i + mWeekStart) % mNumDays;
+            int x = (2 * i + 1) * dayWidthHalf + mPadding;
+            mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
+            canvas.drawText(mDayLabelCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT,
+                    Locale.getDefault()).toUpperCase(Locale.getDefault()), x, y,
+                    mMonthDayLabelPaint);
+        }
+    }
+
+    /**
+     * Draws the month days.
+     */
+    private void drawDays(Canvas canvas) {
+        int y = (((mRowHeight + mMiniDayNumberTextSize) / 2) - DAY_SEPARATOR_WIDTH)
+                + mMonthHeaderSize;
+        int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
+        int j = findDayOffset();
+        for (int day = 1; day <= mNumCells; day++) {
+            int x = (2 * j + 1) * dayWidthHalf + mPadding;
+            if (mSelectedDay == day) {
+                canvas.drawCircle(x, y - (mMiniDayNumberTextSize / 3), mDaySelectedCircleSize,
+                        mDayNumberSelectedPaint);
+            }
+
+            if (mHasToday && mToday == day) {
+                mDayNumberPaint.setColor(mSelectedDayColor);
+            } else {
+                mDayNumberPaint.setColor(mNormalTextColor);
+            }
+            final Paint paint = (day < mEnabledDayStart || day > mEnabledDayEnd) ?
+                    mDayNumberDisabledPaint : mDayNumberPaint;
+            canvas.drawText(String.format("%d", day), x, y, paint);
+            j++;
+            if (j == mNumDays) {
+                j = 0;
+                y += mRowHeight;
+            }
+        }
+    }
+
+    private int findDayOffset() {
+        return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
+                - mWeekStart;
+    }
+
+    /**
+     * Calculates the day that the given x position is in, accounting for week
+     * number. Returns the day or -1 if the position wasn't in a day.
+     *
+     * @param x The x position of the touch event
+     * @return The day number, or -1 if the position wasn't in a day
+     */
+    private int getDayFromLocation(float x, float y) {
+        int dayStart = mPadding;
+        if (x < dayStart || x > mWidth - mPadding) {
+            return -1;
+        }
+        // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
+        int row = (int) (y - mMonthHeaderSize) / mRowHeight;
+        int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding));
+
+        int day = column - findDayOffset() + 1;
+        day += row * mNumDays;
+        if (day < 1 || day > mNumCells) {
+            return -1;
+        }
+        return day;
+    }
+
+    /**
+     * 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 onDayClick(int day) {
+        if (mOnDayClickListener != null) {
+            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 The date that has accessibility focus, or {@code null} if no date
+     *         has focus
+     */
+    Calendar getAccessibilityFocus() {
+        final int day = mTouchHelper.getFocusedVirtualView();
+        Calendar date = null;
+        if (day >= 0) {
+            date = Calendar.getInstance();
+            date.set(mYear, mMonth, day);
+        }
+        return date;
+    }
+
+    /**
+     * Clears accessibility focus within the view. No-op if the view does not
+     * contain accessibility focus.
+     */
+    public void clearAccessibilityFocus() {
+        mTouchHelper.clearFocusedVirtualView();
+    }
+
+    /**
+     * Attempts to restore accessibility focus to the specified date.
+     *
+     * @param day The date which should receive focus
+     * @return {@code false} if the date is not valid for this month view, or
+     *         {@code true} if the date received focus
+     */
+    boolean restoreAccessibilityFocus(Calendar day) {
+        if ((day.get(Calendar.YEAR) != mYear) || (day.get(Calendar.MONTH) != mMonth) ||
+                (day.get(Calendar.DAY_OF_MONTH) > mNumCells)) {
+            return false;
+        }
+        mTouchHelper.setFocusedVirtualView(day.get(Calendar.DAY_OF_MONTH));
+        return true;
+    }
+
+    /**
+     * Provides a virtual view hierarchy for interfacing with an accessibility
+     * service.
+     */
+    private class MonthViewTouchHelper extends ExploreByTouchHelper {
+        private static final String DATE_FORMAT = "dd MMMM yyyy";
+
+        private final Rect mTempRect = new Rect();
+        private final Calendar mTempCalendar = Calendar.getInstance();
+
+        public MonthViewTouchHelper(View host) {
+            super(host);
+        }
+
+        public void setFocusedVirtualView(int virtualViewId) {
+            getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
+                    virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+        }
+
+        public void clearFocusedVirtualView() {
+            final int focusedVirtualView = getFocusedVirtualView();
+            if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) {
+                getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
+                        focusedVirtualView,
+                        AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+                        null);
+            }
+        }
+
+        @Override
+        protected int getVirtualViewAt(float x, float y) {
+            final int day = getDayFromLocation(x, y);
+            if (day >= 0) {
+                return day;
+            }
+            return ExploreByTouchHelper.INVALID_ID;
+        }
+
+        @Override
+        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
+            for (int day = 1; day <= mNumCells; day++) {
+                virtualViewIds.add(day);
+            }
+        }
+
+        @Override
+        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+            event.setContentDescription(getItemDescription(virtualViewId));
+        }
+
+        @Override
+        protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
+            getItemBounds(virtualViewId, mTempRect);
+
+            node.setContentDescription(getItemDescription(virtualViewId));
+            node.setBoundsInParent(mTempRect);
+            node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+
+            if (virtualViewId == mSelectedDay) {
+                node.setSelected(true);
+            }
+
+        }
+
+        @Override
+        protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
+                Bundle arguments) {
+            switch (action) {
+                case AccessibilityNodeInfo.ACTION_CLICK:
+                    onDayClick(virtualViewId);
+                    return true;
+            }
+
+            return false;
+        }
+
+        /**
+         * Calculates the bounding rectangle of a given time object.
+         *
+         * @param day The day to calculate bounds for
+         * @param rect The rectangle in which to store the bounds
+         */
+        private void getItemBounds(int day, Rect rect) {
+            final int offsetX = mPadding;
+            final int offsetY = mMonthHeaderSize;
+            final int cellHeight = mRowHeight;
+            final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays);
+            final int index = ((day - 1) + findDayOffset());
+            final int row = (index / mNumDays);
+            final int column = (index % mNumDays);
+            final int x = (offsetX + (column * cellWidth));
+            final int y = (offsetY + (row * cellHeight));
+
+            rect.set(x, y, (x + cellWidth), (y + cellHeight));
+        }
+
+        /**
+         * Generates a description for a given time object. Since this
+         * description will be spoken, the components are ordered by descending
+         * specificity as DAY MONTH YEAR.
+         *
+         * @param day The day to generate a description for
+         * @return A description of the time object
+         */
+        private CharSequence getItemDescription(int day) {
+            mTempCalendar.set(mYear, mMonth, day);
+            final CharSequence date = DateFormat.format(DATE_FORMAT,
+                    mTempCalendar.getTimeInMillis());
+
+            if (day == mSelectedDay) {
+                return getContext().getString(R.string.item_is_selected, date);
+            }
+
+            return date;
+        }
+    }
+
+    /**
+     * Handles callbacks when the user clicks on a time object.
+     */
+    public interface OnDayClickListener {
+        public void onDayClick(SimpleMonthView view, Calendar day);
+    }
+}
diff --git a/core/java/android/widget/TextViewWithCircularIndicator.java b/core/java/android/widget/TextViewWithCircularIndicator.java
new file mode 100644
index 0000000..22d770c
--- /dev/null
+++ b/core/java/android/widget/TextViewWithCircularIndicator.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+
+import com.android.internal.R;
+
+class TextViewWithCircularIndicator extends TextView {
+
+    private static final int SELECTED_CIRCLE_ALPHA = 60;
+
+    private final Paint mCirclePaint = new Paint();
+
+    private final String mItemIsSelectedText;
+    private int mCircleColor;
+    private boolean mDrawIndicator;
+
+    public TextViewWithCircularIndicator(Context context) {
+        this(context, null);
+    }
+
+    public TextViewWithCircularIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TextViewWithCircularIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TextViewWithCircularIndicator(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+
+        super(context, attrs);
+        Resources res = context.getResources();
+
+        // Use Theme attributes if possible
+        final TypedArray a = mContext.obtainStyledAttributes(attrs,
+                R.styleable.DatePicker, defStyleAttr, defStyleRes);
+
+        final int resId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorYearListItemTextAppearance, -1);
+        if (resId != -1) {
+            setTextAppearance(context, resId);
+        }
+
+        mItemIsSelectedText = res.getString(R.string.item_is_selected);
+
+        a.recycle();
+
+        init();
+    }
+
+    private void init() {
+        mCirclePaint.setTypeface(Typeface.create(mCirclePaint.getTypeface(), Typeface.BOLD));
+        mCirclePaint.setAntiAlias(true);
+        mCirclePaint.setTextAlign(Paint.Align.CENTER);
+        mCirclePaint.setStyle(Paint.Style.FILL);
+    }
+
+    public void setCircleColor(int color) {
+        if (color != mCircleColor) {
+            mCircleColor = color;
+            mCirclePaint.setColor(mCircleColor);
+            mCirclePaint.setAlpha(SELECTED_CIRCLE_ALPHA);
+            requestLayout();
+        }
+    }
+
+    public void setDrawIndicator(boolean drawIndicator) {
+        mDrawIndicator = drawIndicator;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mDrawIndicator) {
+            final int width = getWidth();
+            final int height = getHeight();
+            int radius = Math.min(width, height) / 2;
+            canvas.drawCircle(width / 2, height / 2, radius, mCirclePaint);
+        }
+    }
+
+    @Override
+    public CharSequence getContentDescription() {
+        CharSequence itemText = getText();
+        if (mDrawIndicator) {
+            return String.format(mItemIsSelectedText, itemText);
+        } else {
+            return itemText;
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/widget/TimePickerDelegate.java b/core/java/android/widget/TimePickerDelegate.java
index bf3971c..cd89667 100644
--- a/core/java/android/widget/TimePickerDelegate.java
+++ b/core/java/android/widget/TimePickerDelegate.java
@@ -1389,8 +1389,8 @@
         final Keyframe k2 = Keyframe.ofFloat(0.69f, increaseRatio);
         final Keyframe k3 = Keyframe.ofFloat(1f, 1f);
 
-        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe("scaleX", k0, k1, k2, k3);
-        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe("scaleY", k0, k1, k2, k3);
+        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe(View.SCALE_X, k0, k1, k2, k3);
+        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe(View.SCALE_Y, k0, k1, k2, k3);
         ObjectAnimator pulseAnimator =
                 ObjectAnimator.ofPropertyValuesHolder(labelToAnimate, scaleX, scaleY);
         pulseAnimator.setDuration(PULSE_ANIMATOR_DURATION);
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
new file mode 100644
index 0000000..bac9320
--- /dev/null
+++ b/core/java/android/widget/YearPickerView.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+
+import java.util.Calendar;
+
+import com.android.internal.R;
+
+/**
+ * Displays a selectable list of years.
+ */
+class YearPickerView extends ListView implements AdapterView.OnItemClickListener,
+        OnDateChangedListener {
+    private static final String TAG = "YearPickerView";
+
+    private DatePickerController mController;
+    private YearAdapter mAdapter;
+    private int mViewSize;
+    private int mChildSize;
+    private int mSelectedPosition = -1;
+    private int mYearSelectedCircleColor;
+
+    public YearPickerView(Context context) {
+        this(context, null);
+    }
+
+    public YearPickerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public YearPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public YearPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        ViewGroup.LayoutParams frame = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
+                LayoutParams.WRAP_CONTENT);
+        setLayoutParams(frame);
+
+        Resources res = context.getResources();
+        mViewSize = res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height);
+        mChildSize = res.getDimensionPixelOffset(R.dimen.datepicker_year_label_height);
+
+        setVerticalFadingEdgeEnabled(true);
+        setFadingEdgeLength(mChildSize / 3);
+
+        final int paddingTop = res.getDimensionPixelSize(
+                R.dimen.datepicker_year_picker_padding_top);
+        setPadding(0, paddingTop, 0, 0);
+
+        // Use Theme attributes if possible
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.DatePicker, defStyleAttr, defStyleRes);
+
+        final int colorResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorYearListSelectedCircleColor,
+                R.color.datepicker_default_circle_background_color_holo_light);
+        mYearSelectedCircleColor = res.getColor(colorResId);
+
+        a.recycle();
+
+        setOnItemClickListener(this);
+        setDividerHeight(0);
+    }
+
+    public void init(DatePickerController controller) {
+        mController = controller;
+        mController.registerOnDateChangedListener(this);
+
+        mAdapter = new YearAdapter(getContext(), R.layout.year_label_text_view);
+        updateAdapterData();
+        setAdapter(mAdapter);
+
+        onDateChanged();
+    }
+
+    public void setYearSelectedCircleColor(int color) {
+        if (color != mYearSelectedCircleColor) {
+            mYearSelectedCircleColor = color;
+        }
+        requestLayout();
+    }
+
+    public int getYearSelectedCircleColor()  {
+        return mYearSelectedCircleColor;
+    }
+
+    private void updateAdapterData() {
+        mAdapter.clear();
+        final int maxYear = mController.getMaxYear();
+        for (int year = mController.getMinYear(); year <= maxYear; year++) {
+            mAdapter.add(year);
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        mController.tryVibrate();
+        if (position != mSelectedPosition) {
+            mSelectedPosition = position;
+            mAdapter.notifyDataSetChanged();
+        }
+        mController.onYearSelected(mAdapter.getItem(position));
+    }
+
+    void setItemTextAppearance(int resId) {
+        mAdapter.setItemTextAppearance(resId);
+    }
+
+    private class YearAdapter extends ArrayAdapter<Integer> {
+        int mItemTextAppearanceResId;
+
+        public YearAdapter(Context context, int resource) {
+            super(context, resource);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextViewWithCircularIndicator v = (TextViewWithCircularIndicator)
+                    super.getView(position, convertView, parent);
+            v.setTextAppearance(getContext(), mItemTextAppearanceResId);
+            v.requestLayout();
+            int year = getItem(position);
+            boolean selected = mController.getSelectedDay().get(Calendar.YEAR) == year;
+            v.setDrawIndicator(selected);
+            if (selected) {
+                v.setCircleColor(mYearSelectedCircleColor);
+            }
+            return v;
+        }
+
+        public void setItemTextAppearance(int resId) {
+            mItemTextAppearanceResId = resId;
+        }
+    }
+
+    public void postSetSelectionCentered(final int position) {
+        postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
+    }
+
+    public void postSetSelectionFromTop(final int position, final int offset) {
+        post(new Runnable() {
+
+            @Override
+            public void run() {
+                setSelectionFromTop(position, offset);
+                requestLayout();
+            }
+        });
+    }
+
+    public int getFirstPositionOffset() {
+        final View firstChild = getChildAt(0);
+        if (firstChild == null) {
+            return 0;
+        }
+        return firstChild.getTop();
+    }
+
+    @Override
+    public void onDateChanged() {
+        updateAdapterData();
+        mAdapter.notifyDataSetChanged();
+        postSetSelectionCentered(
+                mController.getSelectedDay().get(Calendar.YEAR) - mController.getMinYear());
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            event.setFromIndex(0);
+            event.setToIndex(0);
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 98a5843..901d6e6 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -47,6 +47,10 @@
     void noteProcessStart(String name, int uid);
     void noteProcessState(String name, int uid, int state);
     void noteProcessFinish(String name, int uid);
+    void noteSyncStart(String name, int uid);
+    void noteSyncFinish(String name, int uid);
+    void noteJobStart(String name, int uid);
+    void noteJobFinish(String name, int uid);
 
     void noteStartWakelock(int uid, int pid, String name, String historyName,
             int type, boolean unimportantForLogging);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6ca048e..dec94f2 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -91,7 +91,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 109 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 112 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -2109,6 +2109,11 @@
             return;
         }
 
+        if (dataSize == 0) {
+            // The history is currently empty; we need it to start with a time stamp.
+            cur.currentTime = System.currentTimeMillis();
+            addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_RESET, cur);
+        }
         addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
     }
 
@@ -2362,6 +2367,50 @@
                 elapsedRealtime);
     }
 
+    public void noteSyncStartLocked(String name, int uid) {
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        getUidStatsLocked(uid).noteStartSyncLocked(name, elapsedRealtime);
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_START, name, uid, 0)) {
+            return;
+        }
+        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SYNC_START, name, uid);
+    }
+
+    public void noteSyncFinishLocked(String name, int uid) {
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        getUidStatsLocked(uid).noteStopSyncLocked(name, elapsedRealtime);
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_FINISH, name, uid, 0)) {
+            return;
+        }
+        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SYNC_FINISH, name, uid);
+    }
+
+    public void noteJobStartLocked(String name, int uid) {
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        getUidStatsLocked(uid).noteStartJobLocked(name, elapsedRealtime);
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_START, name, uid, 0)) {
+            return;
+        }
+        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_START, name, uid);
+    }
+
+    public void noteJobFinishLocked(String name, int uid) {
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        getUidStatsLocked(uid).noteStopJobLocked(name, elapsedRealtime);
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_FINISH, name, uid, 0)) {
+            return;
+        }
+        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_FINISH, name, uid);
+    }
+
     private void requestWakelockCpuUpdate() {
         if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
             Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
@@ -3833,6 +3882,16 @@
         final ArrayMap<String, Wakelock> mWakelockStats = new ArrayMap<String, Wakelock>();
 
         /**
+         * The statistics we have collected for this uid's syncs.
+         */
+        final ArrayMap<String, StopwatchTimer> mSyncStats = new ArrayMap<String, StopwatchTimer>();
+
+        /**
+         * The statistics we have collected for this uid's jobs.
+         */
+        final ArrayMap<String, StopwatchTimer> mJobStats = new ArrayMap<String, StopwatchTimer>();
+
+        /**
          * The statistics we have collected for this uid's sensor activations.
          */
         final SparseArray<Sensor> mSensorStats = new SparseArray<Sensor>();
@@ -3872,6 +3931,16 @@
         }
 
         @Override
+        public Map<String, ? extends BatteryStats.Timer> getSyncStats() {
+            return mSyncStats;
+        }
+
+        @Override
+        public Map<String, ? extends BatteryStats.Timer> getJobStats() {
+            return mJobStats;
+        }
+
+        @Override
         public SparseArray<? extends BatteryStats.Uid.Sensor> getSensorStats() {
             return mSensorStats;
         }
@@ -4396,6 +4465,24 @@
                     active = true;
                 }
             }
+            for (int is=mSyncStats.size()-1; is>=0; is--) {
+                StopwatchTimer timer = mSyncStats.valueAt(is);
+                if (timer.reset(false)) {
+                    mSyncStats.removeAt(is);
+                    timer.detach();
+                } else {
+                    active = true;
+                }
+            }
+            for (int ij=mJobStats.size()-1; ij>=0; ij--) {
+                StopwatchTimer timer = mJobStats.valueAt(ij);
+                if (timer.reset(false)) {
+                    mJobStats.removeAt(ij);
+                    timer.detach();
+                } else {
+                    active = true;
+                }
+            }
             for (int ise=mSensorStats.size()-1; ise>=0; ise--) {
                 Sensor s = mSensorStats.valueAt(ise);
                 if (s.reset()) {
@@ -4497,6 +4584,22 @@
                 wakelock.writeToParcelLocked(out, elapsedRealtimeUs);
             }
 
+            int NS = mSyncStats.size();
+            out.writeInt(NS);
+            for (int is=0; is<NS; is++) {
+                out.writeString(mSyncStats.keyAt(is));
+                StopwatchTimer timer = mSyncStats.valueAt(is);
+                Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs);
+            }
+
+            int NJ = mJobStats.size();
+            out.writeInt(NJ);
+            for (int ij=0; ij<NJ; ij++) {
+                out.writeString(mJobStats.keyAt(ij));
+                StopwatchTimer timer = mJobStats.valueAt(ij);
+                Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs);
+            }
+
             int NSE = mSensorStats.size();
             out.writeInt(NSE);
             for (int ise=0; ise<NSE; ise++) {
@@ -4618,6 +4721,25 @@
                 mWakelockStats.put(wakelockName, wakelock);
             }
 
+            int numSyncs = in.readInt();
+            mSyncStats.clear();
+            for (int j = 0; j < numSyncs; j++) {
+                String syncName = in.readString();
+                if (in.readInt() != 0) {
+                    mSyncStats.put(syncName,
+                            new StopwatchTimer(Uid.this, SYNC, null, timeBase, in));
+                }
+            }
+
+            int numJobs = in.readInt();
+            mJobStats.clear();
+            for (int j = 0; j < numJobs; j++) {
+                String jobName = in.readString();
+                if (in.readInt() != 0) {
+                    mJobStats.put(jobName, new StopwatchTimer(Uid.this, JOB, null, timeBase, in));
+                }
+            }
+
             int numSensors = in.readInt();
             mSensorStats.clear();
             for (int k = 0; k < numSensors; k++) {
@@ -5670,6 +5792,38 @@
             return ss;
         }
 
+        public StopwatchTimer getSyncTimerLocked(String name) {
+            StopwatchTimer t = mSyncStats.get(name);
+            if (t == null) {
+                final int N = mSyncStats.size();
+                if (N > MAX_WAKELOCKS_PER_UID) {
+                    name = BATCHED_WAKELOCK_NAME;
+                    t = mSyncStats.get(name);
+                }
+                if (t == null) {
+                    t = new StopwatchTimer(Uid.this, SYNC, null, mOnBatteryTimeBase);
+                    mSyncStats.put(name, t);
+                }
+            }
+            return t;
+        }
+
+        public StopwatchTimer getJobTimerLocked(String name) {
+            StopwatchTimer t = mJobStats.get(name);
+            if (t == null) {
+                final int N = mJobStats.size();
+                if (N > MAX_WAKELOCKS_PER_UID) {
+                    name = BATCHED_WAKELOCK_NAME;
+                    t = mJobStats.get(name);
+                }
+                if (t == null) {
+                    t = new StopwatchTimer(Uid.this, JOB, null, mOnBatteryTimeBase);
+                    mJobStats.put(name, t);
+                }
+            }
+            return t;
+        }
+
         public StopwatchTimer getWakeTimerLocked(String name, int type) {
             Wakelock wl = mWakelockStats.get(name);
             if (wl == null) {
@@ -5737,6 +5891,34 @@
             return t;
         }
 
+        public void noteStartSyncLocked(String name, long elapsedRealtimeMs) {
+            StopwatchTimer t = getSyncTimerLocked(name);
+            if (t != null) {
+                t.startRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
+        public void noteStopSyncLocked(String name, long elapsedRealtimeMs) {
+            StopwatchTimer t = getSyncTimerLocked(name);
+            if (t != null) {
+                t.stopRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
+        public void noteStartJobLocked(String name, long elapsedRealtimeMs) {
+            StopwatchTimer t = getJobTimerLocked(name);
+            if (t != null) {
+                t.startRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
+        public void noteStopJobLocked(String name, long elapsedRealtimeMs) {
+            StopwatchTimer t = getJobTimerLocked(name);
+            if (t != null) {
+                t.stopRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
         public void noteStartWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) {
             StopwatchTimer t = getWakeTimerLocked(name, type);
             if (t != null) {
@@ -7170,7 +7352,7 @@
         // the last run until samples in this run.
         if (mHistoryBaseTime > 0) {
             long oldnow = SystemClock.elapsedRealtime();
-            mHistoryBaseTime = (mHistoryBaseTime - oldnow) + 60*1000;
+            mHistoryBaseTime = mHistoryBaseTime - oldnow + 1;
             if (DEBUG_HISTORY) {
                 StringBuilder sb = new StringBuilder(128);
                 sb.append("****************** ADJUSTED mHistoryBaseTime: ");
@@ -7433,6 +7615,26 @@
                 }
             }
 
+            int NS = in.readInt();
+            if (NS > 100) {
+                Slog.w(TAG, "File corrupt: too many syncs " + NS);
+                return;
+            }
+            for (int is = 0; is < NS; is++) {
+                String name = in.readString();
+                u.getSyncTimerLocked(name).readSummaryFromParcelLocked(in);
+            }
+
+            int NJ = in.readInt();
+            if (NJ > 100) {
+                Slog.w(TAG, "File corrupt: too many job timers " + NJ);
+                return;
+            }
+            for (int ij = 0; ij < NJ; ij++) {
+                String name = in.readString();
+                u.getJobTimerLocked(name).readSummaryFromParcelLocked(in);
+            }
+
             int NP = in.readInt();
             if (NP > 1000) {
                 Slog.w(TAG, "File corrupt: too many sensors " + NP);
@@ -7484,7 +7686,7 @@
                 String pkgName = in.readString();
                 Uid.Pkg p = u.getPackageStatsLocked(pkgName);
                 p.mWakeups = p.mLoadedWakeups = in.readInt();
-                final int NS = in.readInt();
+                NS = in.readInt();
                 if (NS > 1000) {
                     Slog.w(TAG, "File corrupt: too many services " + NS);
                     return;
@@ -7717,6 +7919,20 @@
                 }
             }
 
+            int NS = u.mSyncStats.size();
+            out.writeInt(NS);
+            for (int is=0; is<NS; is++) {
+                out.writeString(u.mSyncStats.keyAt(is));
+                u.mSyncStats.valueAt(is).writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            }
+
+            int NJ = u.mJobStats.size();
+            out.writeInt(NJ);
+            for (int ij=0; ij<NJ; ij++) {
+                out.writeString(u.mJobStats.keyAt(ij));
+                u.mJobStats.valueAt(ij).writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            }
+
             int NSE = u.mSensorStats.size();
             out.writeInt(NSE);
             for (int ise=0; ise<NSE; ise++) {
@@ -7760,7 +7976,7 @@
                     out.writeString(ent.getKey());
                     Uid.Pkg ps = ent.getValue();
                     out.writeInt(ps.mWakeups);
-                    final int NS = ps.mServiceStats.size();
+                    NS = ps.mServiceStats.size();
                     out.writeInt(NS);
                     if (NS > 0) {
                         for (Map.Entry<String, BatteryStatsImpl.Uid.Pkg.Serv> sent
@@ -7786,7 +8002,7 @@
     void readFromParcelLocked(Parcel in) {
         int magic = in.readInt();
         if (magic != MAGIC) {
-            throw new ParcelFormatException("Bad magic number");
+            throw new ParcelFormatException("Bad magic number: #" + Integer.toHexString(magic));
         }
 
         readHistory(in, false);
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 7f6159d..3ed4d51 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -16,10 +16,14 @@
 
 package com.android.internal.util;
 
+import android.util.ArraySet;
+
 import dalvik.system.VMRuntime;
+
 import libcore.util.EmptyArray;
 
 import java.lang.reflect.Array;
+import java.util.ArrayList;
 
 /**
  * ArrayUtils contains some methods that you can call to find out
@@ -332,4 +336,52 @@
     public static long[] cloneOrNull(long[] array) {
         return (array != null) ? array.clone() : null;
     }
+
+    public static <T> ArraySet<T> add(ArraySet<T> cur, T val) {
+        if (cur == null) {
+            cur = new ArraySet<>();
+        }
+        cur.add(val);
+        return cur;
+    }
+
+    public static <T> ArraySet<T> remove(ArraySet<T> cur, T val) {
+        if (cur == null) {
+            return null;
+        }
+        cur.remove(val);
+        if (cur.isEmpty()) {
+            return null;
+        } else {
+            return cur;
+        }
+    }
+
+    public static <T> boolean contains(ArraySet<T> cur, T val) {
+        return (cur != null) ? cur.contains(val) : false;
+    }
+
+    public static <T> ArrayList<T> add(ArrayList<T> cur, T val) {
+        if (cur == null) {
+            cur = new ArrayList<>();
+        }
+        cur.add(val);
+        return cur;
+    }
+
+    public static <T> ArrayList<T> remove(ArrayList<T> cur, T val) {
+        if (cur == null) {
+            return null;
+        }
+        cur.remove(val);
+        if (cur.isEmpty()) {
+            return null;
+        } else {
+            return cur;
+        }
+    }
+
+    public static <T> boolean contains(ArrayList<T> cur, T val) {
+        return (cur != null) ? cur.contains(val) : false;
+    }
 }
diff --git a/core/java/com/android/internal/widget/AccessibleDateAnimator.java b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
new file mode 100644
index 0000000..e91a55c
--- /dev/null
+++ b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.content.Context;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ViewAnimator;
+
+/**
+ * @hide
+ */
+public class AccessibleDateAnimator extends ViewAnimator {
+    private long mDateMillis;
+
+    public AccessibleDateAnimator(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setDateMillis(long dateMillis) {
+        mDateMillis = dateMillis;
+    }
+
+    /**
+     * Announce the currently-selected date when launched.
+     */
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            // Clear the event's current text so that only the current date will be spoken.
+            event.getText().clear();
+            int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
+                    DateUtils.FORMAT_SHOW_WEEKDAY;
+
+            String dateString = DateUtils.formatDateTime(getContext(), mDateMillis, flags);
+            event.getText().add(dateString);
+            return true;
+        }
+        return super.dispatchPopulateAccessibilityEvent(event);
+    }
+}
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
new file mode 100644
index 0000000..11c4ca1
--- /dev/null
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.accessibility.*;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * ExploreByTouchHelper is a utility class for implementing accessibility
+ * support in custom {@link android.view.View}s that represent a collection of View-like
+ * logical items. It extends {@link android.view.accessibility.AccessibilityNodeProvider} and
+ * simplifies many aspects of providing information to accessibility services
+ * and managing accessibility focus. This class does not currently support
+ * hierarchies of logical items.
+ * <p>
+ * This should be applied to the parent view using
+ * {@link android.view.View#setAccessibilityDelegate}:
+ *
+ * <pre>
+ * mAccessHelper = ExploreByTouchHelper.create(someView, mAccessHelperCallback);
+ * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
+ * </pre>
+ */
+public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
+    /** Virtual node identifier value for invalid nodes. */
+    public static final int INVALID_ID = Integer.MIN_VALUE;
+
+    /** Default class name used for virtual views. */
+    private static final String DEFAULT_CLASS_NAME = View.class.getName();
+
+    // Temporary, reusable data structures.
+    private final Rect mTempScreenRect = new Rect();
+    private final Rect mTempParentRect = new Rect();
+    private final Rect mTempVisibleRect = new Rect();
+    private final int[] mTempGlobalRect = new int[2];
+
+    /** View's context **/
+    private Context mContext;
+
+    /** System accessibility manager, used to check state and send events. */
+    private final AccessibilityManager mManager;
+
+    /** View whose internal structure is exposed through this helper. */
+    private final View mView;
+
+    /** Node provider that handles creating nodes and performing actions. */
+    private ExploreByTouchNodeProvider mNodeProvider;
+
+    /** Virtual view id for the currently focused logical item. */
+    private int mFocusedVirtualViewId = INVALID_ID;
+
+    /** Virtual view id for the currently hovered logical item. */
+    private int mHoveredVirtualViewId = INVALID_ID;
+
+    /**
+     * Factory method to create a new {@link ExploreByTouchHelper}.
+     *
+     * @param forView View whose logical children are exposed by this helper.
+     */
+    public ExploreByTouchHelper(View forView) {
+        if (forView == null) {
+            throw new IllegalArgumentException("View may not be null");
+        }
+
+        mView = forView;
+        mContext = forView.getContext();
+        mManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+    }
+
+    /**
+     * Returns the {@link android.view.accessibility.AccessibilityNodeProvider} for this helper.
+     *
+     * @param host View whose logical children are exposed by this helper.
+     * @return The accessibility node provider for this helper.
+     */
+    @Override
+    public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
+        if (mNodeProvider == null) {
+            mNodeProvider = new ExploreByTouchNodeProvider();
+        }
+        return mNodeProvider;
+    }
+
+    /**
+     * Dispatches hover {@link android.view.MotionEvent}s to the virtual view hierarchy when
+     * the Explore by Touch feature is enabled.
+     * <p>
+     * This method should be called by overriding
+     * {@link View#dispatchHoverEvent}:
+     *
+     * <pre>&#64;Override
+     * public boolean dispatchHoverEvent(MotionEvent event) {
+     *   if (mHelper.dispatchHoverEvent(this, event) {
+     *     return true;
+     *   }
+     *   return super.dispatchHoverEvent(event);
+     * }
+     * </pre>
+     *
+     * @param event The hover event to dispatch to the virtual view hierarchy.
+     * @return Whether the hover event was handled.
+     */
+    public boolean dispatchHoverEvent(MotionEvent event) {
+        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
+            return false;
+        }
+
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_HOVER_MOVE:
+            case MotionEvent.ACTION_HOVER_ENTER:
+                final int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
+                updateHoveredVirtualView(virtualViewId);
+                return (virtualViewId != INVALID_ID);
+            case MotionEvent.ACTION_HOVER_EXIT:
+                if (mFocusedVirtualViewId != INVALID_ID) {
+                    updateHoveredVirtualView(INVALID_ID);
+                    return true;
+                }
+                return false;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Populates an event of the specified type with information about an item
+     * and attempts to send it up through the view hierarchy.
+     * <p>
+     * You should call this method after performing a user action that normally
+     * fires an accessibility event, such as clicking on an item.
+     *
+     * <pre>public void performItemClick(T item) {
+     *   ...
+     *   sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED);
+     * }
+     * </pre>
+     *
+     * @param virtualViewId The virtual view id for which to send an event.
+     * @param eventType The type of event to send.
+     * @return true if the event was sent successfully.
+     */
+    public boolean sendEventForVirtualView(int virtualViewId, int eventType) {
+        if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) {
+            return false;
+        }
+
+        final ViewParent parent = mView.getParent();
+        if (parent == null) {
+            return false;
+        }
+
+        final AccessibilityEvent event = createEvent(virtualViewId, eventType);
+        return parent.requestSendAccessibilityEvent(mView, event);
+    }
+
+    /**
+     * Notifies the accessibility framework that the properties of the parent
+     * view have changed.
+     * <p>
+     * You <b>must</b> call this method after adding or removing items from the
+     * parent view.
+     */
+    public void invalidateRoot() {
+        invalidateVirtualView(View.NO_ID);
+    }
+
+    /**
+     * Notifies the accessibility framework that the properties of a particular
+     * item have changed.
+     * <p>
+     * You <b>must</b> call this method after changing any of the properties set
+     * in {@link #onPopulateNodeForVirtualView}.
+     *
+     * @param virtualViewId The virtual view id to invalidate.
+     */
+    public void invalidateVirtualView(int virtualViewId) {
+        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+    }
+
+    /**
+     * Returns the virtual view id for the currently focused item,
+     *
+     * @return A virtual view id, or {@link #INVALID_ID} if no item is
+     *         currently focused.
+     */
+    public int getFocusedVirtualView() {
+        return mFocusedVirtualViewId;
+    }
+
+    /**
+     * Sets the currently hovered item, sending hover accessibility events as
+     * necessary to maintain the correct state.
+     *
+     * @param virtualViewId The virtual view id for the item currently being
+     *            hovered, or {@link #INVALID_ID} if no item is hovered within
+     *            the parent view.
+     */
+    private void updateHoveredVirtualView(int virtualViewId) {
+        if (mHoveredVirtualViewId == virtualViewId) {
+            return;
+        }
+
+        final int previousVirtualViewId = mHoveredVirtualViewId;
+        mHoveredVirtualViewId = virtualViewId;
+
+        // Stay consistent with framework behavior by sending ENTER/EXIT pairs
+        // in reverse order. This is accurate as of API 18.
+        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+        sendEventForVirtualView(previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityEvent} for the specified
+     * virtual view id, which includes the host view ({@link View#NO_ID}).
+     *
+     * @param virtualViewId The virtual view id for the item for which to
+     *            construct an event.
+     * @param eventType The type of event to construct.
+     * @return An {@link AccessibilityEvent} populated with information about
+     *         the specified item.
+     */
+    private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
+        switch (virtualViewId) {
+            case View.NO_ID:
+                return createEventForHost(eventType);
+            default:
+                return createEventForChild(virtualViewId, eventType);
+        }
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityEvent} for the host node.
+     *
+     * @param eventType The type of event to construct.
+     * @return An {@link AccessibilityEvent} populated with information about
+     *         the specified item.
+     */
+    private AccessibilityEvent createEventForHost(int eventType) {
+        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+        onInitializeAccessibilityEvent(mView, event);
+        return event;
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityEvent} populated with
+     * information about the specified item.
+     *
+     * @param virtualViewId The virtual view id for the item for which to
+     *            construct an event.
+     * @param eventType The type of event to construct.
+     * @return An {@link AccessibilityEvent} populated with information about
+     *         the specified item.
+     */
+    private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
+        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+        event.setEnabled(true);
+        event.setClassName(DEFAULT_CLASS_NAME);
+
+        // Allow the client to populate the event.
+        onPopulateEventForVirtualView(virtualViewId, event);
+
+        // Make sure the developer is following the rules.
+        if (event.getText().isEmpty() && (event.getContentDescription() == null)) {
+            throw new RuntimeException("Callbacks must add text or a content description in "
+                    + "populateEventForVirtualViewId()");
+        }
+
+        // Don't allow the client to override these properties.
+        event.setPackageName(mView.getContext().getPackageName());
+        event.setSource(mView, virtualViewId);
+
+        return event;
+    }
+
+    /**
+     * Constructs and returns an {@link android.view.accessibility.AccessibilityNodeInfo} for the
+     * specified virtual view id, which includes the host view
+     * ({@link View#NO_ID}).
+     *
+     * @param virtualViewId The virtual view id for the item for which to
+     *            construct a node.
+     * @return An {@link android.view.accessibility.AccessibilityNodeInfo} populated with information
+     *         about the specified item.
+     */
+    private AccessibilityNodeInfo createNode(int virtualViewId) {
+        switch (virtualViewId) {
+            case View.NO_ID:
+                return createNodeForHost();
+            default:
+                return createNodeForChild(virtualViewId);
+        }
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityNodeInfo} for the
+     * host view populated with its virtual descendants.
+     *
+     * @return An {@link AccessibilityNodeInfo} for the parent node.
+     */
+    private AccessibilityNodeInfo createNodeForHost() {
+        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView);
+        onInitializeAccessibilityNodeInfo(mView, node);
+
+        // Add the virtual descendants.
+        final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
+        getVisibleVirtualViews(virtualViewIds);
+
+        for (Integer childVirtualViewId : virtualViewIds) {
+            node.addChild(mView, childVirtualViewId);
+        }
+
+        return node;
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityNodeInfo} for the
+     * specified item. Automatically manages accessibility focus actions.
+     * <p>
+     * Allows the implementing class to specify most node properties, but
+     * overrides the following:
+     * <ul>
+     * <li>{@link AccessibilityNodeInfo#setPackageName}
+     * <li>{@link AccessibilityNodeInfo#setClassName}
+     * <li>{@link AccessibilityNodeInfo#setParent(View)}
+     * <li>{@link AccessibilityNodeInfo#setSource(View, int)}
+     * <li>{@link AccessibilityNodeInfo#setVisibleToUser}
+     * <li>{@link AccessibilityNodeInfo#setBoundsInScreen(Rect)}
+     * </ul>
+     * <p>
+     * Uses the bounds of the parent view and the parent-relative bounding
+     * rectangle specified by
+     * {@link AccessibilityNodeInfo#getBoundsInParent} to automatically
+     * update the following properties:
+     * <ul>
+     * <li>{@link AccessibilityNodeInfo#setVisibleToUser}
+     * <li>{@link AccessibilityNodeInfo#setBoundsInParent}
+     * </ul>
+     *
+     * @param virtualViewId The virtual view id for item for which to construct
+     *            a node.
+     * @return An {@link AccessibilityNodeInfo} for the specified item.
+     */
+    private AccessibilityNodeInfo createNodeForChild(int virtualViewId) {
+        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
+
+        // Ensure the client has good defaults.
+        node.setEnabled(true);
+        node.setClassName(DEFAULT_CLASS_NAME);
+
+        // Allow the client to populate the node.
+        onPopulateNodeForVirtualView(virtualViewId, node);
+
+        // Make sure the developer is following the rules.
+        if ((node.getText() == null) && (node.getContentDescription() == null)) {
+            throw new RuntimeException("Callbacks must add text or a content description in "
+                    + "populateNodeForVirtualViewId()");
+        }
+
+        node.getBoundsInParent(mTempParentRect);
+        if (mTempParentRect.isEmpty()) {
+            throw new RuntimeException("Callbacks must set parent bounds in "
+                    + "populateNodeForVirtualViewId()");
+        }
+
+        final int actions = node.getActions();
+        if ((actions & AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) != 0) {
+            throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
+                    + "populateNodeForVirtualViewId()");
+        }
+        if ((actions & AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
+            throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
+                    + "populateNodeForVirtualViewId()");
+        }
+
+        // Don't allow the client to override these properties.
+        node.setPackageName(mView.getContext().getPackageName());
+        node.setSource(mView, virtualViewId);
+        node.setParent(mView);
+
+        // Manage internal accessibility focus state.
+        if (mFocusedVirtualViewId == virtualViewId) {
+            node.setAccessibilityFocused(true);
+            node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+        } else {
+            node.setAccessibilityFocused(false);
+            node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+        }
+
+        // Set the visibility based on the parent bound.
+        if (intersectVisibleToUser(mTempParentRect)) {
+            node.setVisibleToUser(true);
+            node.setBoundsInParent(mTempParentRect);
+        }
+
+        // Calculate screen-relative bound.
+        mView.getLocationOnScreen(mTempGlobalRect);
+        final int offsetX = mTempGlobalRect[0];
+        final int offsetY = mTempGlobalRect[1];
+        mTempScreenRect.set(mTempParentRect);
+        mTempScreenRect.offset(offsetX, offsetY);
+        node.setBoundsInScreen(mTempScreenRect);
+
+        return node;
+    }
+
+    private boolean performAction(int virtualViewId, int action, Bundle arguments) {
+        switch (virtualViewId) {
+            case View.NO_ID:
+                return performActionForHost(action, arguments);
+            default:
+                return performActionForChild(virtualViewId, action, arguments);
+        }
+    }
+
+    private boolean performActionForHost(int action, Bundle arguments) {
+        return performAccessibilityAction(mView, action, arguments);
+    }
+
+    private boolean performActionForChild(int virtualViewId, int action, Bundle arguments) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+                return manageFocusForChild(virtualViewId, action, arguments);
+            default:
+                return onPerformActionForVirtualView(virtualViewId, action, arguments);
+        }
+    }
+
+    private boolean manageFocusForChild(int virtualViewId, int action, Bundle arguments) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+                return requestAccessibilityFocus(virtualViewId);
+            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+                return clearAccessibilityFocus(virtualViewId);
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Computes whether the specified {@link Rect} intersects with the visible
+     * portion of its parent {@link View}. Modifies {@code localRect} to contain
+     * only the visible portion.
+     *
+     * @param localRect A rectangle in local (parent) coordinates.
+     * @return Whether the specified {@link Rect} is visible on the screen.
+     */
+    private boolean intersectVisibleToUser(Rect localRect) {
+        // Missing or empty bounds mean this view is not visible.
+        if ((localRect == null) || localRect.isEmpty()) {
+            return false;
+        }
+
+        // Attached to invisible window means this view is not visible.
+        if (mView.getWindowVisibility() != View.VISIBLE) {
+            return false;
+        }
+
+        // An invisible predecessor means that this view is not visible.
+        ViewParent viewParent = mView.getParent();
+        while (viewParent instanceof View) {
+            final View view = (View) viewParent;
+            if ((view.getAlpha() <= 0) || (view.getVisibility() != View.VISIBLE)) {
+                return false;
+            }
+            viewParent = view.getParent();
+        }
+
+        // A null parent implies the view is not visible.
+        if (viewParent == null) {
+            return false;
+        }
+
+        // If no portion of the parent is visible, this view is not visible.
+        if (!mView.getLocalVisibleRect(mTempVisibleRect)) {
+            return false;
+        }
+
+        // Check if the view intersects the visible portion of the parent.
+        return localRect.intersect(mTempVisibleRect);
+    }
+
+    /**
+     * Returns whether this virtual view is accessibility focused.
+     *
+     * @return True if the view is accessibility focused.
+     */
+    private boolean isAccessibilityFocused(int virtualViewId) {
+        return (mFocusedVirtualViewId == virtualViewId);
+    }
+
+    /**
+     * Attempts to give accessibility focus to a virtual view.
+     * <p>
+     * A virtual view will not actually take focus if
+     * {@link AccessibilityManager#isEnabled()} returns false,
+     * {@link AccessibilityManager#isTouchExplorationEnabled()} returns false,
+     * or the view already has accessibility focus.
+     *
+     * @param virtualViewId The id of the virtual view on which to place
+     *            accessibility focus.
+     * @return Whether this virtual view actually took accessibility focus.
+     */
+    private boolean requestAccessibilityFocus(int virtualViewId) {
+        final AccessibilityManager accessibilityManager =
+                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+        if (!mManager.isEnabled()
+                || !accessibilityManager.isTouchExplorationEnabled()) {
+            return false;
+        }
+        // TODO: Check virtual view visibility.
+        if (!isAccessibilityFocused(virtualViewId)) {
+            mFocusedVirtualViewId = virtualViewId;
+            // TODO: Only invalidate virtual view bounds.
+            mView.invalidate();
+            sendEventForVirtualView(virtualViewId,
+                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Attempts to clear accessibility focus from a virtual view.
+     *
+     * @param virtualViewId The id of the virtual view from which to clear
+     *            accessibility focus.
+     * @return Whether this virtual view actually cleared accessibility focus.
+     */
+    private boolean clearAccessibilityFocus(int virtualViewId) {
+        if (isAccessibilityFocused(virtualViewId)) {
+            mFocusedVirtualViewId = INVALID_ID;
+            mView.invalidate();
+            sendEventForVirtualView(virtualViewId,
+                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Provides a mapping between view-relative coordinates and logical
+     * items.
+     *
+     * @param x The view-relative x coordinate
+     * @param y The view-relative y coordinate
+     * @return virtual view identifier for the logical item under
+     *         coordinates (x,y)
+     */
+    protected abstract int getVirtualViewAt(float x, float y);
+
+    /**
+     * Populates a list with the view's visible items. The ordering of items
+     * within {@code virtualViewIds} specifies order of accessibility focus
+     * traversal.
+     *
+     * @param virtualViewIds The list to populate with visible items
+     */
+    protected abstract void getVisibleVirtualViews(List<Integer> virtualViewIds);
+
+    /**
+     * Populates an {@link AccessibilityEvent} with information about the
+     * specified item.
+     * <p>
+     * Implementations <b>must</b> populate the following required fields:
+     * <ul>
+     * <li>event text, see {@link AccessibilityEvent#getText} or
+     * {@link AccessibilityEvent#setContentDescription}
+     * </ul>
+     * <p>
+     * The helper class automatically populates the following fields with
+     * default values, but implementations may optionally override them:
+     * <ul>
+     * <li>item class name, set to android.view.View, see
+     * {@link AccessibilityEvent#setClassName}
+     * </ul>
+     * <p>
+     * The following required fields are automatically populated by the
+     * helper class and may not be overridden:
+     * <ul>
+     * <li>package name, set to the package of the host view's
+     * {@link Context}, see {@link AccessibilityEvent#setPackageName}
+     * <li>event source, set to the host view and virtual view identifier,
+     * see {@link AccessibilityRecord#setSource(View, int)}
+     * </ul>
+     *
+     * @param virtualViewId The virtual view id for the item for which to
+     *            populate the event
+     * @param event The event to populate
+     */
+    protected abstract void onPopulateEventForVirtualView(
+            int virtualViewId, AccessibilityEvent event);
+
+    /**
+     * Populates an {@link AccessibilityNodeInfo} with information
+     * about the specified item.
+     * <p>
+     * Implementations <b>must</b> populate the following required fields:
+     * <ul>
+     * <li>event text, see {@link AccessibilityNodeInfo#setText} or
+     * {@link AccessibilityNodeInfo#setContentDescription}
+     * <li>bounds in parent coordinates, see
+     * {@link AccessibilityNodeInfo#setBoundsInParent}
+     * </ul>
+     * <p>
+     * The helper class automatically populates the following fields with
+     * default values, but implementations may optionally override them:
+     * <ul>
+     * <li>enabled state, set to true, see
+     * {@link AccessibilityNodeInfo#setEnabled}
+     * <li>item class name, identical to the class name set by
+     * {@link #onPopulateEventForVirtualView}, see
+     * {@link AccessibilityNodeInfo#setClassName}
+     * </ul>
+     * <p>
+     * The following required fields are automatically populated by the
+     * helper class and may not be overridden:
+     * <ul>
+     * <li>package name, identical to the package name set by
+     * {@link #onPopulateEventForVirtualView}, see
+     * {@link AccessibilityNodeInfo#setPackageName}
+     * <li>node source, identical to the event source set in
+     * {@link #onPopulateEventForVirtualView}, see
+     * {@link AccessibilityNodeInfo#setSource(View, int)}
+     * <li>parent view, set to the host view, see
+     * {@link AccessibilityNodeInfo#setParent(View)}
+     * <li>visibility, computed based on parent-relative bounds, see
+     * {@link AccessibilityNodeInfo#setVisibleToUser}
+     * <li>accessibility focus, computed based on internal helper state, see
+     * {@link AccessibilityNodeInfo#setAccessibilityFocused}
+     * <li>bounds in screen coordinates, computed based on host view bounds,
+     * see {@link AccessibilityNodeInfo#setBoundsInScreen}
+     * </ul>
+     * <p>
+     * Additionally, the helper class automatically handles accessibility
+     * focus management by adding the appropriate
+     * {@link AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS} or
+     * {@link AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
+     * action. Implementations must <b>never</b> manually add these actions.
+     * <p>
+     * The helper class also automatically modifies parent- and
+     * screen-relative bounds to reflect the portion of the item visible
+     * within its parent.
+     *
+     * @param virtualViewId The virtual view identifier of the item for
+     *            which to populate the node
+     * @param node The node to populate
+     */
+    protected abstract void onPopulateNodeForVirtualView(
+            int virtualViewId, AccessibilityNodeInfo node);
+
+    /**
+     * Performs the specified accessibility action on the item associated
+     * with the virtual view identifier. See
+     * {@link AccessibilityNodeInfo#performAction(int, Bundle)} for
+     * more information.
+     * <p>
+     * Implementations <b>must</b> handle any actions added manually in
+     * {@link #onPopulateNodeForVirtualView}.
+     * <p>
+     * The helper class automatically handles focus management resulting
+     * from {@link AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS}
+     * and
+     * {@link AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
+     * actions.
+     *
+     * @param virtualViewId The virtual view identifier of the item on which
+     *            to perform the action
+     * @param action The accessibility action to perform
+     * @param arguments (Optional) A bundle with additional arguments, or
+     *            null
+     * @return true if the action was performed
+     */
+    protected abstract boolean onPerformActionForVirtualView(
+            int virtualViewId, int action, Bundle arguments);
+
+    /**
+     * Exposes a virtual view hierarchy to the accessibility framework. Only
+     * used in API 16+.
+     */
+    private class ExploreByTouchNodeProvider extends AccessibilityNodeProvider {
+        @Override
+        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+            return ExploreByTouchHelper.this.createNode(virtualViewId);
+        }
+
+        @Override
+        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+            return ExploreByTouchHelper.this.performAction(virtualViewId, action, arguments);
+        }
+    }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 0e22174..f65aab5 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -90,12 +90,12 @@
 	android_util_Process.cpp \
 	android_util_StringBlock.cpp \
 	android_util_XmlBlock.cpp \
+	android_graphics_Canvas.cpp \
 	android_graphics_Picture.cpp \
 	android/graphics/AutoDecodeCancel.cpp \
 	android/graphics/Bitmap.cpp \
 	android/graphics/BitmapFactory.cpp \
 	android/graphics/Camera.cpp \
-	android/graphics/Canvas.cpp \
 	android/graphics/CanvasProperty.cpp \
 	android/graphics/ColorFilter.cpp \
 	android/graphics/DrawFilter.cpp \
@@ -122,6 +122,7 @@
 	android/graphics/Rasterizer.cpp \
 	android/graphics/Region.cpp \
 	android/graphics/Shader.cpp \
+	android/graphics/SkiaCanvas.cpp \
 	android/graphics/SurfaceTexture.cpp \
 	android/graphics/Typeface.cpp \
 	android/graphics/TypefaceImpl.cpp \
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index d17f46c..9f832b0 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,6 +3,7 @@
 
 #include "SkCamera.h"
 
+#include "Canvas.h"
 #include "GraphicsJNI.h"
 
 static jfieldID gNativeInstanceFieldID;
@@ -95,10 +96,10 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
-    v->applyToCanvas((SkCanvas*)native_canvas);
+    v->applyToCanvas(canvas);
 }
 
 static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj,
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
deleted file mode 100644
index 6254f3d..0000000
--- a/core/jni/android/graphics/Canvas.cpp
+++ /dev/null
@@ -1,1330 +0,0 @@
-/*
- * Copyright (C) 2006-2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include "SkCanvas.h"
-#include "SkClipStack.h"
-#include "SkDevice.h"
-#include "SkDeque.h"
-#include "SkDrawFilter.h"
-#include "SkGraphics.h"
-#include <SkImageInfo.h>
-#include "SkPorterDuff.h"
-#include "SkShader.h"
-#include "SkTArray.h"
-#include "SkTemplates.h"
-
-#include <minikin/Layout.h>
-#include "MinikinSkia.h"
-#include "MinikinUtils.h"
-
-#include "TypefaceImpl.h"
-
-#include "unicode/ubidi.h"
-#include "unicode/ushape.h"
-
-#include <utils/Log.h>
-
-namespace android {
-
-class ClipCopier : public SkCanvas::ClipVisitor {
-public:
-    ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
-
-    virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipRect(rect, op, antialias);
-    }
-    virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipRRect(rrect, op, antialias);
-    }
-    virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipPath(path, op, antialias);
-    }
-
-private:
-    SkCanvas* m_dstCanvas;
-};
-
-// Holds an SkCanvas reference plus additional native data.
-class NativeCanvasWrapper {
-private:
-    struct SaveRec {
-        int                 saveCount;
-        SkCanvas::SaveFlags saveFlags;
-    };
-
-public:
-    NativeCanvasWrapper(SkCanvas* canvas)
-        : mCanvas(canvas)
-        , mSaveStack(NULL) {
-        SkASSERT(canvas);
-    }
-
-    ~NativeCanvasWrapper() {
-        delete mSaveStack;
-    }
-
-    SkCanvas* getCanvas() const {
-        return mCanvas.get();
-    }
-
-    void setCanvas(SkCanvas* canvas) {
-        SkASSERT(canvas);
-        mCanvas.reset(canvas);
-
-        delete mSaveStack;
-        mSaveStack = NULL;
-    }
-
-    int save(SkCanvas::SaveFlags flags) {
-        int count = mCanvas->save();
-        recordPartialSave(flags);
-        return count;
-    }
-
-    int saveLayer(const SkRect* bounds, const SkPaint* paint,
-                            SkCanvas::SaveFlags flags) {
-        int count = mCanvas->saveLayer(bounds, paint,
-                static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
-        recordPartialSave(flags);
-        return count;
-    }
-
-    int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
-                       SkCanvas::SaveFlags flags) {
-        int count = mCanvas->saveLayerAlpha(bounds, alpha,
-                static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
-        recordPartialSave(flags);
-        return count;
-    }
-
-    void restore() {
-        const SaveRec* rec = (NULL == mSaveStack)
-                ? NULL
-                : static_cast<SaveRec*>(mSaveStack->back());
-        int currentSaveCount = mCanvas->getSaveCount() - 1;
-        SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
-
-        if (NULL == rec || rec->saveCount != currentSaveCount) {
-            // Fast path - no record for this frame.
-            mCanvas->restore();
-            return;
-        }
-
-        bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
-        bool preserveClip   = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
-
-        SkMatrix savedMatrix;
-        if (preserveMatrix) {
-            savedMatrix = mCanvas->getTotalMatrix();
-        }
-
-        SkTArray<SkClipStack::Element> savedClips;
-        if (preserveClip) {
-            saveClipsForFrame(savedClips, currentSaveCount);
-        }
-
-        mCanvas->restore();
-
-        if (preserveMatrix) {
-            mCanvas->setMatrix(savedMatrix);
-        }
-
-        if (preserveClip && !savedClips.empty()) {
-            applyClips(savedClips);
-        }
-
-        mSaveStack->pop_back();
-    }
-
-private:
-    void recordPartialSave(SkCanvas::SaveFlags flags) {
-        // A partial save is a save operation which doesn't capture the full canvas state.
-        // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
-
-        // Mask-out non canvas state bits.
-        flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
-
-        if (SkCanvas::kMatrixClip_SaveFlag == flags) {
-            // not a partial save.
-            return;
-        }
-
-        if (NULL == mSaveStack) {
-            mSaveStack = new SkDeque(sizeof(struct SaveRec), 8);
-        }
-
-        SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
-        // Store the save counter in the SkClipStack domain.
-        // (0-based, equal to the number of save ops on the stack).
-        rec->saveCount = mCanvas->getSaveCount() - 1;
-        rec->saveFlags = flags;
-    }
-
-    void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
-                           int frameSaveCount) {
-        SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
-                                       SkClipStack::Iter::kTop_IterStart);
-        while (const SkClipStack::Element* elem = clipIterator.next()) {
-            if (elem->getSaveCount() < frameSaveCount) {
-                // done with the current frame.
-                break;
-            }
-            SkASSERT(elem->getSaveCount() == frameSaveCount);
-            clips.push_back(*elem);
-        }
-    }
-
-    void applyClips(const SkTArray<SkClipStack::Element>& clips) {
-        ClipCopier clipCopier(mCanvas);
-
-        // The clip stack stores clips in device space.
-        SkMatrix origMatrix = mCanvas->getTotalMatrix();
-        mCanvas->resetMatrix();
-
-        // We pushed the clips in reverse order.
-        for (int i = clips.count() - 1; i >= 0; --i) {
-            clips[i].replay(&clipCopier);
-        }
-
-        mCanvas->setMatrix(origMatrix);
-    }
-
-    SkAutoTUnref<SkCanvas> mCanvas;
-    SkDeque* mSaveStack; // lazily allocated, tracks partial saves.
-};
-
-// Returns true if the SkCanvas's clip is non-empty.
-static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
-    bool emptyClip = canvas.isClipEmpty();
-    return emptyClip ? JNI_FALSE : JNI_TRUE;
-}
-
-class SkCanvasGlue {
-public:
-    // Get the native wrapper for a given handle.
-    static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) {
-        SkASSERT(nativeHandle);
-        return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
-    }
-
-    // Get the SkCanvas for a given native handle.
-    static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle);
-        SkCanvas* canvas = wrapper->getCanvas();
-        SkASSERT(canvas);
-
-        return canvas;
-    }
-
-    // Construct an SkCanvas from the bitmap.
-    static SkCanvas* createCanvas(SkBitmap* bitmap) {
-        if (bitmap) {
-            return SkNEW_ARGS(SkCanvas, (*bitmap));
-        }
-
-        // Create an empty bitmap device to prevent callers from crashing
-        // if they attempt to draw into this canvas.
-        SkBitmap emptyBitmap;
-        return new SkCanvas(emptyBitmap);
-    }
-
-    // Copy the canvas matrix & clip state.
-    static void copyCanvasState(SkCanvas* srcCanvas, SkCanvas* dstCanvas) {
-        if (srcCanvas && dstCanvas) {
-            dstCanvas->setMatrix(srcCanvas->getTotalMatrix());
-            if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) {
-                ClipCopier copier(dstCanvas);
-                srcCanvas->replayClips(&copier);
-            }
-        }
-    }
-
-    // Native JNI handlers
-    static void finalizer(JNIEnv* env, jobject clazz, jlong nativeHandle) {
-        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
-        delete wrapper;
-    }
-
-    // Native wrapper constructor used by Canvas(Bitmap)
-    static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
-        // No check - 0 is a valid bitmapHandle.
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        SkCanvas* canvas = createCanvas(bitmap);
-
-        return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
-    }
-
-    // Native wrapper constructor used by Canvas(native_canvas)
-    static jlong initCanvas(JNIEnv* env, jobject, jlong canvasHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
-        return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
-    }
-
-    // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
-    // optionally copying canvas matrix & clip state.
-    static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
-                          jboolean copyState) {
-        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(canvasHandle);
-        SkCanvas* newCanvas = createCanvas(reinterpret_cast<SkBitmap*>(bitmapHandle));
-        NPE_CHECK_RETURN_VOID(env, newCanvas);
-
-        if (copyState == JNI_TRUE) {
-            copyCanvasState(wrapper->getCanvas(), newCanvas);
-        }
-
-        // setCanvas() unrefs the old canvas.
-        wrapper->setCanvas(newCanvas);
-    }
-
-    static void freeCaches(JNIEnv* env, jobject) {
-        SkGraphics::PurgeFontCache();
-    }
-
-    static void freeTextLayoutCaches(JNIEnv* env, jobject) {
-        Layout::purgeCaches();
-    }
-
-    static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        bool result = canvas->getDevice()->accessBitmap(false).isOpaque();
-        return result ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        int width = canvas->getDevice()->accessBitmap(false).width();
-        return static_cast<jint>(width);
-    }
-
-    static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        int height = canvas->getDevice()->accessBitmap(false).height();
-        return static_cast<jint>(height);
-    }
-
-    static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-        return static_cast<jint>(wrapper->save(flags));
-    }
-
-    static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle,
-                          jfloat l, jfloat t, jfloat r, jfloat b,
-                          jlong paintHandle, jint flagsHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        SkPaint* paint  = reinterpret_cast<SkPaint*>(paintHandle);
-        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-        SkRect bounds;
-        bounds.set(l, t, r, b);
-        return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags));
-    }
-
-    static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
-                               jfloat l, jfloat t, jfloat r, jfloat b,
-                               jint alpha, jint flagsHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-        SkRect  bounds;
-        bounds.set(l, t, r, b);
-        return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags));
-    }
-
-    static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        if (wrapper->getCanvas()->getSaveCount() <= 1) {  // cannot restore anymore
-            doThrowISE(env, "Underflow in restore");
-            return;
-        }
-        wrapper->restore();
-    }
-
-    static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
-        return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount());
-    }
-
-    static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle,
-                               jint restoreCount) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        if (restoreCount < 1) {
-            doThrowIAE(env, "Underflow in restoreToCount");
-            return;
-        }
-
-        while (wrapper->getCanvas()->getSaveCount() > restoreCount) {
-            wrapper->restore();
-        }
-    }
-
-    static void translate(JNIEnv*, jobject, jlong canvasHandle,
-                          jfloat dx, jfloat dy) {
-        getNativeCanvas(canvasHandle)->translate(dx, dy);
-    }
-
-    static void scale__FF(JNIEnv*, jobject, jlong canvasHandle,
-                          jfloat sx, jfloat sy) {
-        getNativeCanvas(canvasHandle)->scale(sx, sy);
-    }
-
-    static void rotate__F(JNIEnv*, jobject, jlong canvasHandle,
-                          jfloat degrees) {
-        getNativeCanvas(canvasHandle)->rotate(degrees);
-    }
-
-    static void skew__FF(JNIEnv*, jobject, jlong canvasHandle,
-                         jfloat sx, jfloat sy) {
-        getNativeCanvas(canvasHandle)->skew(sx, sy);
-    }
-
-    static void concat(JNIEnv* env, jobject, jlong canvasHandle,
-                       jlong matrixHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        canvas->concat(*matrix);
-    }
-
-    static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle,
-                          jlong matrixHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        if (NULL == matrix) {
-            canvas->resetMatrix();
-        } else {
-            canvas->setMatrix(*matrix);
-        }
-    }
-
-    static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle,
-                                  jfloat left, jfloat top, jfloat right,
-                                  jfloat bottom, jint op) {
-        SkRect  r;
-        r.set(left, top, right, bottom);
-        SkCanvas* c = getNativeCanvas(canvasHandle);
-        c->clipRect(r, static_cast<SkRegion::Op>(op));
-        return hasNonEmptyClip(*c);
-    }
-
-    static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle,
-                             jlong pathHandle, jint op) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle),
-                static_cast<SkRegion::Op>(op));
-        return hasNonEmptyClip(*canvas);
-    }
-
-    static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle,
-                               jlong deviceRgnHandle, jint op) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
-        SkPath rgnPath;
-        if (deviceRgn->getBoundaryPath(&rgnPath)) {
-            // The region is specified in device space.
-            SkMatrix savedMatrix = canvas->getTotalMatrix();
-            canvas->resetMatrix();
-            canvas->clipPath(rgnPath, static_cast<SkRegion::Op>(op));
-            canvas->setMatrix(savedMatrix);
-        } else {
-            canvas->clipRect(SkRect::MakeEmpty(), static_cast<SkRegion::Op>(op));
-        }
-        return hasNonEmptyClip(*canvas);
-    }
-
-    static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle,
-                              jlong filterHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
-    }
-
-    static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle,
-                                       jlong pathHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        bool result = canvas->quickReject(*reinterpret_cast<SkPath*>(pathHandle));
-        return result ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static jboolean quickReject__FFFF(JNIEnv* env, jobject, jlong canvasHandle,
-                                       jfloat left, jfloat top, jfloat right,
-                                       jfloat bottom) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkRect r;
-        r.set(left, top, right, bottom);
-        bool result = canvas->quickReject(r);
-        return result ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static void drawRGB(JNIEnv* env, jobject, jlong canvasHandle,
-                        jint r, jint g, jint b) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->drawARGB(0xFF, r, g, b);
-    }
-
-    static void drawARGB(JNIEnv* env, jobject, jlong canvasHandle,
-                         jint a, jint r, jint g, jint b) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->drawARGB(a, r, g, b);
-    }
-
-    static void drawColor__I(JNIEnv* env, jobject, jlong canvasHandle,
-                             jint color) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->drawColor(color);
-    }
-
-    static void drawColor__II(JNIEnv* env, jobject, jlong canvasHandle,
-                              jint color, jint modeHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
-        canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
-    }
-
-    static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle,
-                          jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawPaint(*paint);
-    }
-
-    static void doPoints(JNIEnv* env, jlong canvasHandle,
-                         jfloatArray jptsArray, jint offset, jint count,
-                         jlong paintHandle, jint modeHandle) {
-        NPE_CHECK_RETURN_VOID(env, jptsArray);
-        SkCanvas::PointMode mode = static_cast<SkCanvas::PointMode>(modeHandle);
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-        AutoJavaFloatArray autoPts(env, jptsArray);
-        float* floats = autoPts.ptr();
-        const int length = autoPts.length();
-
-        if ((offset | count) < 0 || offset + count > length) {
-            doThrowAIOOBE(env);
-            return;
-        }
-
-        // now convert the floats into SkPoints
-        count >>= 1;    // now it is the number of points
-        SkAutoSTMalloc<32, SkPoint> storage(count);
-        SkPoint* pts = storage.get();
-        const float* src = floats + offset;
-        for (int i = 0; i < count; i++) {
-            pts[i].set(src[0], src[1]);
-            src += 2;
-        }
-        canvas->drawPoints(mode, count, pts, *paint);
-    }
-
-    static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle,
-                           jfloatArray jptsArray, jint offset,
-                           jint count, jlong paintHandle) {
-        doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
-                 SkCanvas::kPoints_PointMode);
-    }
-
-    static void drawLines(JNIEnv* env, jobject, jlong canvasHandle,
-                          jfloatArray jptsArray, jint offset, jint count,
-                          jlong paintHandle) {
-        doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
-                 SkCanvas::kLines_PointMode);
-    }
-
-    static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
-                          jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawPoint(x, y, *paint);
-    }
-
-    static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
-                                    jfloat startX, jfloat startY, jfloat stopX,
-                                    jfloat stopY, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawLine(startX, startY, stopX, stopY, *paint);
-    }
-
-    static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
-                                    jfloat left, jfloat top, jfloat right,
-                                    jfloat bottom, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawRectCoords(left, top, right, bottom, *paint);
-    }
-
-    static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
-            jfloat right, jfloat bottom, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-        canvas->drawOval(oval, *paint);
-    }
-
-    static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx,
-                           jfloat cy, jfloat radius, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawCircle(cx, cy, radius, *paint);
-    }
-
-    static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
-            jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, jboolean useCenter,
-            jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-        canvas->drawArc(oval, startAngle, sweepAngle, useCenter, *paint);
-    }
-
-    static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle,
-            jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry,
-            jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-        canvas->drawRoundRect(rect, rx, ry, *paint);
-    }
-
-    static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
-                         jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawPath(*path, *paint);
-    }
-
-    static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
-                                          jlong canvasHandle, jlong bitmapHandle,
-                                          jfloat left, jfloat top,
-                                          jlong paintHandle, jint canvasDensity,
-                                          jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-        if (canvasDensity == bitmapDensity || canvasDensity == 0
-                || bitmapDensity == 0) {
-            if (screenDensity != 0 && screenDensity != bitmapDensity) {
-                SkPaint filteredPaint;
-                if (paint) {
-                    filteredPaint = *paint;
-                }
-                filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-                canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
-            } else {
-                canvas->drawBitmap(*bitmap, left, top, paint);
-            }
-        } else {
-            canvas->save();
-            SkScalar scale = canvasDensity / (float)bitmapDensity;
-            canvas->translate(left, top);
-            canvas->scale(scale, scale);
-
-            SkPaint filteredPaint;
-            if (paint) {
-                filteredPaint = *paint;
-            }
-            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-
-            canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
-
-            canvas->restore();
-        }
-    }
-
-    static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap,
-                        jobject srcIRect, const SkRect& dst, SkPaint* paint,
-                        jint screenDensity, jint bitmapDensity) {
-        SkIRect    src, *srcPtr = NULL;
-
-        if (NULL != srcIRect) {
-            GraphicsJNI::jrect_to_irect(env, srcIRect, &src);
-            srcPtr = &src;
-        }
-
-        if (screenDensity != 0 && screenDensity != bitmapDensity) {
-            SkPaint filteredPaint;
-            if (paint) {
-                filteredPaint = *paint;
-            }
-            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-            canvas->drawBitmapRect(*bitmap, srcPtr, dst, &filteredPaint);
-        } else {
-            canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint);
-        }
-    }
-
-    static void drawBitmapRF(JNIEnv* env, jobject, jlong canvasHandle,
-                             jlong bitmapHandle, jobject srcIRect,
-                             jobject dstRectF, jlong paintHandle,
-                             jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect      dst;
-        GraphicsJNI::jrectf_to_rect(env, dstRectF, &dst);
-        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
-                screenDensity, bitmapDensity);
-    }
-
-    static void drawBitmapRR(JNIEnv* env, jobject, jlong canvasHandle,
-                             jlong bitmapHandle, jobject srcIRect,
-                             jobject dstRect, jlong paintHandle,
-                             jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect      dst;
-        GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
-        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
-                screenDensity, bitmapDensity);
-    }
-
-    static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
-                                jintArray jcolors, jint offset, jint stride,
-                                jfloat x, jfloat y, jint width, jint height,
-                                jboolean hasAlpha, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
-        // correct the alphaType to kOpaque_SkAlphaType.
-        SkImageInfo info = SkImageInfo::Make(width, height,
-                               hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
-                               kPremul_SkAlphaType);
-        SkBitmap    bitmap;
-        if (!bitmap.allocPixels(info)) {
-            return;
-        }
-
-        if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
-                0, 0, width, height, bitmap)) {
-            return;
-        }
-
-        canvas->drawBitmap(bitmap, x, y, paint);
-    }
-
-    static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle,
-                                 jlong bitmapHandle, jlong matrixHandle,
-                                 jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawBitmapMatrix(*bitmap, *matrix, paint);
-    }
-
-    static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle,
-                          jlong bitmapHandle, jint meshWidth, jint meshHeight,
-                          jfloatArray jverts, jint vertIndex, jintArray jcolors,
-                          jint colorIndex, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-        const int ptCount = (meshWidth + 1) * (meshHeight + 1);
-        const int indexCount = meshWidth * meshHeight * 6;
-
-        AutoJavaFloatArray  vertA(env, jverts, vertIndex + (ptCount << 1));
-        AutoJavaIntArray    colorA(env, jcolors, colorIndex + ptCount);
-
-        /*  Our temp storage holds 2 or 3 arrays.
-            texture points [ptCount * sizeof(SkPoint)]
-            optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
-                copy to convert from float to fixed
-            indices [ptCount * sizeof(uint16_t)]
-        */
-        ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
-        storageSize += indexCount * sizeof(uint16_t);  // indices[]
-
-        SkAutoMalloc storage(storageSize);
-        SkPoint* texs = (SkPoint*)storage.get();
-        SkPoint* verts;
-        uint16_t* indices;
-#ifdef SK_SCALAR_IS_FLOAT
-        verts = (SkPoint*)(vertA.ptr() + vertIndex);
-        indices = (uint16_t*)(texs + ptCount);
-#else
-        SkASSERT(false);
-#endif
-
-        // cons up texture coordinates and indices
-        {
-            const SkScalar w = SkIntToScalar(bitmap->width());
-            const SkScalar h = SkIntToScalar(bitmap->height());
-            const SkScalar dx = w / meshWidth;
-            const SkScalar dy = h / meshHeight;
-
-            SkPoint* texsPtr = texs;
-            SkScalar y = 0;
-            for (int i = 0; i <= meshHeight; i++) {
-                if (i == meshHeight) {
-                    y = h;  // to ensure numerically we hit h exactly
-                }
-                SkScalar x = 0;
-                for (int j = 0; j < meshWidth; j++) {
-                    texsPtr->set(x, y);
-                    texsPtr += 1;
-                    x += dx;
-                }
-                texsPtr->set(w, y);
-                texsPtr += 1;
-                y += dy;
-            }
-            SkASSERT(texsPtr - texs == ptCount);
-        }
-
-        // cons up indices
-        {
-            uint16_t* indexPtr = indices;
-            int index = 0;
-            for (int i = 0; i < meshHeight; i++) {
-                for (int j = 0; j < meshWidth; j++) {
-                    // lower-left triangle
-                    *indexPtr++ = index;
-                    *indexPtr++ = index + meshWidth + 1;
-                    *indexPtr++ = index + meshWidth + 2;
-                    // upper-right triangle
-                    *indexPtr++ = index;
-                    *indexPtr++ = index + meshWidth + 2;
-                    *indexPtr++ = index + 1;
-                    // bump to the next cell
-                    index += 1;
-                }
-                // bump to the next row
-                index += 1;
-            }
-            SkASSERT(indexPtr - indices == indexCount);
-            SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
-        }
-
-        // double-check that we have legal indices
-#ifdef SK_DEBUG
-        {
-            for (int i = 0; i < indexCount; i++) {
-                SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
-            }
-        }
-#endif
-
-        // cons-up a shader for the bitmap
-        SkPaint tmpPaint;
-        if (paint) {
-            tmpPaint = *paint;
-        }
-        SkShader* shader = SkShader::CreateBitmapShader(*bitmap,
-                        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-        SkSafeUnref(tmpPaint.setShader(shader));
-
-        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, verts,
-                             texs, (const SkColor*)colorA.ptr(), NULL, indices,
-                             indexCount, tmpPaint);
-    }
-
-    static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
-                             jint modeHandle, jint vertexCount,
-                             jfloatArray jverts, jint vertIndex,
-                             jfloatArray jtexs, jint texIndex,
-                             jintArray jcolors, jint colorIndex,
-                             jshortArray jindices, jint indexIndex,
-                             jint indexCount, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-        AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
-        AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
-        AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
-        AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
-
-        const int ptCount = vertexCount >> 1;
-
-        SkPoint* verts;
-        SkPoint* texs = NULL;
-#ifdef SK_SCALAR_IS_FLOAT
-        verts = (SkPoint*)(vertA.ptr() + vertIndex);
-        if (jtexs != NULL) {
-            texs = (SkPoint*)(texA.ptr() + texIndex);
-        }
-#else
-        SkASSERT(false);
-#endif
-
-        const SkColor* colors = NULL;
-        const uint16_t* indices = NULL;
-        if (jcolors != NULL) {
-            colors = (const SkColor*)(colorA.ptr() + colorIndex);
-        }
-        if (jindices != NULL) {
-            indices = (const uint16_t*)(indexA.ptr() + indexIndex);
-        }
-
-        canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
-                             indices, indexCount, *paint);
-    }
-
-
-    static void drawText___CIIFFIPaintTypeface(JNIEnv* env, jobject, jlong canvasHandle,
-                                               jcharArray text, jint index, jint count,
-                                               jfloat x, jfloat y, jint bidiFlags,
-                                               jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-        jchar* textArray = env->GetCharArrayElements(text, NULL);
-        drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, bidiFlags, paint, typeface);
-        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
-    }
-
-    static void drawText__StringIIFFIPaintTypeface(JNIEnv* env, jobject,
-                                                   jlong canvasHandle, jstring text,
-                                                   jint start, jint end,
-                                                   jfloat x, jfloat y, jint bidiFlags,
-                                                   jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-        const jchar* textArray = env->GetStringChars(text, NULL);
-        drawTextWithGlyphs(canvas, textArray, start, end, x, y, bidiFlags, paint, typeface);
-        env->ReleaseStringChars(text, textArray);
-    }
-
-    class DrawTextFunctor {
-    public:
-        DrawTextFunctor(const Layout& layout, SkCanvas* canvas, jfloat x, jfloat y, SkPaint* paint,
-                    uint16_t* glyphs, SkPoint* pos)
-                : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
-                    pos(pos) { }
-
-        void operator()(size_t start, size_t end) {
-            for (size_t i = start; i < end; i++) {
-                glyphs[i] = layout.getGlyphId(i);
-                pos[i].fX = x + layout.getX(i);
-                pos[i].fY = y + layout.getY(i);
-            }
-            canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
-        }
-    private:
-        const Layout& layout;
-        SkCanvas* canvas;
-        jfloat x;
-        jfloat y;
-        SkPaint* paint;
-        uint16_t* glyphs;
-        SkPoint* pos;
-    };
-
-    static void drawGlyphsToSkia(SkCanvas* canvas, SkPaint* paint, const Layout& layout, float x, float y) {
-        size_t nGlyphs = layout.nGlyphs();
-        uint16_t* glyphs = new uint16_t[nGlyphs];
-        SkPoint* pos = new SkPoint[nGlyphs];
-
-        x += MinikinUtils::xOffsetForTextAlign(paint, layout);
-        SkPaint::Align align = paint->getTextAlign();
-        paint->setTextAlign(SkPaint::kLeft_Align);
-        paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        DrawTextFunctor f(layout, canvas, x, y, paint, glyphs, pos);
-        MinikinUtils::forFontRun(layout, paint, f);
-        doDrawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
-        paint->setTextAlign(align);
-        delete[] glyphs;
-        delete[] pos;
-    }
-
-    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
-            int start, int end,
-            jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
-
-        jint count = end - start;
-        drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, bidiFlags, paint,
-                typeface);
-    }
-
-    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
-            int start, int count, int contextCount,
-            jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
-
-        Layout layout;
-        std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
-        layout.doLayout(textArray, start, count, contextCount, css);
-        drawGlyphsToSkia(canvas, paint, layout, x, y);
-    }
-
-// Same values used by Skia
-#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
-#define kStdUnderline_Offset    (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
-    static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length,
-            SkPaint* paint) {
-        uint32_t flags;
-        SkDrawFilter* drawFilter = canvas->getDrawFilter();
-        if (drawFilter) {
-            SkPaint paintCopy(*paint);
-            drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
-            flags = paintCopy.getFlags();
-        } else {
-            flags = paint->getFlags();
-        }
-        if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
-            SkScalar left = x;
-            SkScalar right = x + length;
-            float textSize = paint->getTextSize();
-            float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
-            if (flags & SkPaint::kUnderlineText_Flag) {
-                SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
-                SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
-                canvas->drawRectCoords(left, top, right, bottom, *paint);
-            }
-            if (flags & SkPaint::kStrikeThruText_Flag) {
-                SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
-                SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
-                canvas->drawRectCoords(left, top, right, bottom, *paint);
-            }
-        }
-    }
-
-    static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
-            int index, int count, jfloat x, jfloat y, SkPaint* paint) {
-        SkPoint* posPtr = new SkPoint[count];
-        for (int indx = 0; indx < count; indx++) {
-            posPtr[indx].fX = x + posArray[indx * 2];
-            posPtr[indx].fY = y + posArray[indx * 2 + 1];
-        }
-        canvas->drawPosText(glyphArray, count << 1, posPtr, *paint);
-        delete[] posPtr;
-    }
-
-    static void drawTextRun___CIIIIFFZPaintTypeface(
-            JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
-            jint count, jint contextIndex, jint contextCount,
-            jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
-        jchar* chars = env->GetCharArrayElements(text, NULL);
-        drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
-                count, contextCount, x, y, bidiFlags, paint, typeface);
-        env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
-    }
-
-    static void drawTextRun__StringIIIIFFZPaintTypeface(
-            JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
-            jint end, jint contextStart, jint contextEnd,
-            jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
-        jint count = end - start;
-        jint contextCount = contextEnd - contextStart;
-        const jchar* chars = env->GetStringChars(text, NULL);
-        drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
-                count, contextCount, x, y, bidiFlags, paint, typeface);
-        env->ReleaseStringChars(text, chars);
-    }
-
-    static void drawPosText___CII_FPaint(JNIEnv* env, jobject, jlong canvasHandle,
-                                         jcharArray text, jint index, jint count,
-                                         jfloatArray pos, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
-        jsize textCount = text ? env->GetArrayLength(text) : NULL;
-        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
-        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
-        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
-        int indx;
-        for (indx = 0; indx < posCount; indx++) {
-            posPtr[indx].fX = posArray[indx << 1];
-            posPtr[indx].fY = posArray[(indx << 1) + 1];
-        }
-
-        SkPaint::TextEncoding encoding = paint->getTextEncoding();
-        paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-        canvas->drawPosText(textArray + index, count << 1, posPtr, *paint);
-        paint->setTextEncoding(encoding);
-
-        if (text) {
-            env->ReleaseCharArrayElements(text, textArray, 0);
-        }
-        if (pos) {
-            env->ReleaseFloatArrayElements(pos, posArray, 0);
-        }
-        delete[] posPtr;
-    }
-
-    static void drawPosText__String_FPaint(JNIEnv* env, jobject,
-                                           jlong canvasHandle, jstring text,
-                                           jfloatArray pos,
-                                           jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
-        int byteLength = text ? env->GetStringLength(text) : 0;
-        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
-        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
-        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
-
-        for (int indx = 0; indx < posCount; indx++) {
-            posPtr[indx].fX = posArray[indx << 1];
-            posPtr[indx].fY = posArray[(indx << 1) + 1];
-        }
-
-        SkPaint::TextEncoding encoding = paint->getTextEncoding();
-        paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-        canvas->drawPosText(text_, byteLength << 1, posPtr, *paint);
-        paint->setTextEncoding(encoding);
-
-        if (text) {
-            env->ReleaseStringChars(text, (const jchar*) text_);
-        }
-        if (pos) {
-            env->ReleaseFloatArrayElements(pos, posArray, 0);
-        }
-        delete[] posPtr;
-    }
-
-    class DrawTextOnPathFunctor {
-    public:
-        DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset,
-                    float vOffset, SkPaint* paint, SkPath* path)
-                : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
-                    paint(paint), path(path) {
-        }
-        void operator()(size_t start, size_t end) {
-            uint16_t glyphs[1];
-            for (size_t i = start; i < end; i++) {
-                glyphs[0] = layout.getGlyphId(i);
-                float x = hOffset + layout.getX(i);
-                float y = vOffset + layout.getY(i);
-                canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint);
-            }
-        }
-    private:
-        const Layout& layout;
-        SkCanvas* canvas;
-        float hOffset;
-        float vOffset;
-        SkPaint* paint;
-        SkPath* path;
-    };
-
-    static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags,
-            float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) {
-        Layout layout;
-        std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
-        layout.doLayout(text, 0, count, count, css);
-        hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
-        // Set align to left for drawing, as we don't want individual
-        // glyphs centered or right-aligned; the offset above takes
-        // care of all alignment.
-        SkPaint::Align align = paint->getTextAlign();
-        paint->setTextAlign(SkPaint::kLeft_Align);
-
-        DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path);
-        MinikinUtils::forFontRun(layout, paint, f);
-        paint->setTextAlign(align);
-    }
-
-    static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
-            jlong canvasHandle, jcharArray text, jint index, jint count,
-            jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
-            jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-        jchar* textArray = env->GetCharArrayElements(text, NULL);
-        doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
-                                   path, canvas, typeface);
-        env->ReleaseCharArrayElements(text, textArray, 0);
-    }
-
-    static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
-            jlong canvasHandle, jstring text, jlong pathHandle,
-            jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
-            jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-        const jchar* text_ = env->GetStringChars(text, NULL);
-        int count = env->GetStringLength(text);
-        doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
-                                   path, canvas, typeface);
-        env->ReleaseStringChars(text, text_);
-    }
-
-
-    // This function is a mirror of SkCanvas::getClipBounds except that it does
-    // not outset the edge of the clip to account for anti-aliasing. There is
-    // a skia bug to investigate pushing this logic into back into skia.
-    // (see https://code.google.com/p/skia/issues/detail?id=1303)
-    static bool getHardClipBounds(SkCanvas* canvas, SkRect* bounds) {
-        SkIRect ibounds;
-        if (!canvas->getClipDeviceBounds(&ibounds)) {
-            return false;
-        }
-
-        SkMatrix inverse;
-        // if we can't invert the CTM, we can't return local clip bounds
-        if (!canvas->getTotalMatrix().invert(&inverse)) {
-            if (bounds) {
-                bounds->setEmpty();
-            }
-            return false;
-        }
-
-        if (NULL != bounds) {
-            SkRect r = SkRect::Make(ibounds);
-            inverse.mapRect(bounds, r);
-        }
-        return true;
-    }
-
-    static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle,
-                                  jobject bounds) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkRect   r;
-        SkIRect ir;
-        bool result = getHardClipBounds(canvas, &r);
-
-        if (!result) {
-            r.setEmpty();
-        }
-        r.round(&ir);
-
-        (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
-        return result ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static void getCTM(JNIEnv* env, jobject, jlong canvasHandle,
-                       jlong matrixHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        *matrix = canvas->getTotalMatrix();
-    }
-};
-
-static JNINativeMethod gCanvasMethods[] = {
-    {"finalizer", "(J)V", (void*) SkCanvasGlue::finalizer},
-    {"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster},
-    {"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas},
-    {"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap},
-    {"native_isOpaque","(J)Z", (void*) SkCanvasGlue::isOpaque},
-    {"native_getWidth","(J)I", (void*) SkCanvasGlue::getWidth},
-    {"native_getHeight","(J)I", (void*) SkCanvasGlue::getHeight},
-    {"native_save","(JI)I", (void*) SkCanvasGlue::save},
-    {"native_saveLayer","(JFFFFJI)I", (void*) SkCanvasGlue::saveLayer},
-    {"native_saveLayerAlpha","(JFFFFII)I", (void*) SkCanvasGlue::saveLayerAlpha},
-    {"native_restore","(J)V", (void*) SkCanvasGlue::restore},
-    {"native_getSaveCount","(J)I", (void*) SkCanvasGlue::getSaveCount},
-    {"native_restoreToCount","(JI)V", (void*) SkCanvasGlue::restoreToCount},
-    {"native_translate","(JFF)V", (void*) SkCanvasGlue::translate},
-    {"native_scale","(JFF)V", (void*) SkCanvasGlue::scale__FF},
-    {"native_rotate","(JF)V", (void*) SkCanvasGlue::rotate__F},
-    {"native_skew","(JFF)V", (void*) SkCanvasGlue::skew__FF},
-    {"native_concat","(JJ)V", (void*) SkCanvasGlue::concat},
-    {"native_setMatrix","(JJ)V", (void*) SkCanvasGlue::setMatrix},
-    {"native_clipRect","(JFFFFI)Z", (void*) SkCanvasGlue::clipRect},
-    {"native_clipPath","(JJI)Z", (void*) SkCanvasGlue::clipPath},
-    {"native_clipRegion","(JJI)Z", (void*) SkCanvasGlue::clipRegion},
-    {"nativeSetDrawFilter", "(JJ)V", (void*) SkCanvasGlue::setDrawFilter},
-    {"native_getClipBounds","(JLandroid/graphics/Rect;)Z",
-        (void*) SkCanvasGlue::getClipBounds},
-    {"native_getCTM", "(JJ)V", (void*)SkCanvasGlue::getCTM},
-    {"native_quickReject","(JJ)Z", (void*) SkCanvasGlue::quickReject__Path},
-    {"native_quickReject","(JFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
-    {"native_drawRGB","(JIII)V", (void*) SkCanvasGlue::drawRGB},
-    {"native_drawARGB","(JIIII)V", (void*) SkCanvasGlue::drawARGB},
-    {"native_drawColor","(JI)V", (void*) SkCanvasGlue::drawColor__I},
-    {"native_drawColor","(JII)V", (void*) SkCanvasGlue::drawColor__II},
-    {"native_drawPaint","(JJ)V", (void*) SkCanvasGlue::drawPaint},
-    {"native_drawPoint", "(JFFJ)V", (void*) SkCanvasGlue::drawPoint},
-    {"native_drawPoints", "(J[FIIJ)V", (void*) SkCanvasGlue::drawPoints},
-    {"native_drawLines", "(J[FIIJ)V", (void*) SkCanvasGlue::drawLines},
-    {"native_drawLine","(JFFFFJ)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
-    {"native_drawRect","(JFFFFJ)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
-    {"native_drawOval","(JFFFFJ)V", (void*) SkCanvasGlue::drawOval},
-    {"native_drawCircle","(JFFFJ)V", (void*) SkCanvasGlue::drawCircle},
-    {"native_drawArc","(JFFFFFFZJ)V", (void*) SkCanvasGlue::drawArc},
-    {"native_drawRoundRect","(JFFFFFFJ)V",
-        (void*) SkCanvasGlue::drawRoundRect},
-    {"native_drawPath","(JJJ)V", (void*) SkCanvasGlue::drawPath},
-    {"native_drawBitmap","(JJFFJIII)V",
-        (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
-    {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/RectF;JII)V",
-        (void*) SkCanvasGlue::drawBitmapRF},
-    {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/Rect;JII)V",
-        (void*) SkCanvasGlue::drawBitmapRR},
-    {"native_drawBitmap", "(J[IIIFFIIZJ)V",
-    (void*)SkCanvasGlue::drawBitmapArray},
-    {"nativeDrawBitmapMatrix", "(JJJJ)V",
-        (void*)SkCanvasGlue::drawBitmapMatrix},
-    {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V",
-        (void*)SkCanvasGlue::drawBitmapMesh},
-    {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V",
-        (void*)SkCanvasGlue::drawVertices},
-    {"native_drawText","(J[CIIFFIJJ)V",
-        (void*) SkCanvasGlue::drawText___CIIFFIPaintTypeface},
-    {"native_drawText","(JLjava/lang/String;IIFFIJJ)V",
-        (void*) SkCanvasGlue::drawText__StringIIFFIPaintTypeface},
-    {"native_drawTextRun","(J[CIIIIFFZJJ)V",
-        (void*) SkCanvasGlue::drawTextRun___CIIIIFFZPaintTypeface},
-    {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V",
-        (void*) SkCanvasGlue::drawTextRun__StringIIIIFFZPaintTypeface},
-    {"native_drawTextOnPath","(J[CIIJFFIJJ)V",
-        (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
-    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V",
-        (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
-
-    {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
-
-    {"freeTextLayoutCaches", "()V", (void*) SkCanvasGlue::freeTextLayoutCaches}
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include <android_runtime/AndroidRuntime.h>
-
-#define REG(env, name, array) \
-    result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
-                                                    SK_ARRAY_COUNT(array));  \
-    if (result < 0) return result
-
-int register_android_graphics_Canvas(JNIEnv* env) {
-    int result;
-
-    REG(env, "android/graphics/Canvas", gCanvasMethods);
-
-    return result;
-}
-
-} // namespace android
-
-// GraphicsJNI helper for external clients.
-// We keep the implementation here to avoid exposing NativeCanvasWrapper
-// externally.
-SkCanvas* GraphicsJNI::getNativeCanvas(jlong nativeHandle) {
-    return android::SkCanvasGlue::getNativeCanvas(nativeHandle);
-}
diff --git a/core/jni/android/graphics/Canvas.h b/core/jni/android/graphics/Canvas.h
new file mode 100644
index 0000000..710845d
--- /dev/null
+++ b/core/jni/android/graphics/Canvas.h
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_GRAPHICS_CANVAS_H
+#define ANDROID_GRAPHICS_CANVAS_H
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+
+namespace android {
+
+// TODO: move this further up the stack so that all interaction with minikin
+//       happens prior to calling into this interface
+class TypefaceImpl;
+
+class Canvas {
+public:
+    virtual ~Canvas() {};
+
+    static Canvas* create_canvas(SkBitmap* bitmap);
+    static Canvas* create_canvas(SkCanvas* skiaCanvas);
+
+    // TODO: enable HWUI to either create similar canvas wrapper or subclass
+    //       directly from Canvas
+    //static Canvas* create_canvas(uirenderer::Renderer* renderer);
+
+    // TODO: this is a temporary affordance until all necessary logic can be
+    //       moved within this interface! Further, the return value should
+    //       NOT be unref'd and is valid until this canvas is destroyed or a
+    //       new bitmap is set.
+    virtual SkCanvas* getSkCanvas() = 0;
+
+    virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
+
+    virtual bool isOpaque() = 0;
+    virtual int width() = 0;
+    virtual int height() = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+    // Save (layer)
+    virtual int getSaveCount() const = 0;
+    virtual int save(SkCanvas::SaveFlags flags) = 0;
+    virtual void restore() = 0;
+    virtual void restoreToCount(int saveCount) = 0;
+
+    virtual int saveLayer(float left, float top, float right, float bottom,
+                const SkPaint* paint, SkCanvas::SaveFlags flags) = 0;
+    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+            int alpha, SkCanvas::SaveFlags flags) = 0;
+
+    // Matrix
+    virtual void getMatrix(SkMatrix* outMatrix) const = 0;
+    virtual void setMatrix(const SkMatrix& matrix) = 0;
+
+    virtual void concat(const SkMatrix& matrix) = 0;
+    virtual void rotate(float degrees) = 0;
+    virtual void scale(float sx, float sy) = 0;
+    virtual void skew(float sx, float sy) = 0;
+    virtual void translate(float dx, float dy) = 0;
+
+    // clip
+    virtual bool getClipBounds(SkRect* outRect) const = 0;
+    virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0;
+    virtual bool quickRejectPath(const SkPath& path) const = 0;
+
+    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;
+    virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
+    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
+
+    // filters
+    virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+    virtual void drawColor(int color, SkXfermode::Mode mode) = 0;
+    virtual void drawPaint(const SkPaint& paint) = 0;
+
+    // Geometry
+    virtual void drawPoint(float x, float y, const SkPaint& paint) = 0;
+    virtual void drawPoints(const float* points, int count, const SkPaint& paint) = 0;
+    virtual void drawLine(float startX, float startY, float stopX, float stopY,
+                const SkPaint& paint) = 0;
+    virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0;
+    virtual void drawRect(float left, float top, float right, float bottom,
+            const SkPaint& paint) = 0;
+    virtual void drawRoundRect(float left, float top, float right, float bottom,
+            float rx, float ry, const SkPaint& paint) = 0;
+    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
+    virtual void drawOval(float left, float top, float right, float bottom,
+            const SkPaint& paint) = 0;
+    virtual void drawArc(float left, float top, float right, float bottom,
+            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) = 0;
+    virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0;
+    virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+                              const float* verts, const float* tex, const int* colors,
+                              const uint16_t* indices, int indexCount, const SkPaint& paint) = 0;
+
+    // Bitmap-based
+    virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+            const SkPaint* paint) = 0;
+    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+            const SkPaint* paint) = 0;
+    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint) = 0;
+    virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+            const float* vertices, const int* colors, const SkPaint* paint) = 0;
+
+    // Text
+    virtual void drawText(const char* text, int start, int count, int contextCount,
+            float x, float y, int bidiFlags, const SkPaint& paint,
+            TypefaceImpl* typeface) = 0;
+    virtual void drawPosText(const char* text, const float* positions, int count, int posCount,
+            const SkPaint& paint) = 0;
+    virtual void drawTextOnPath(const char* text, int count, const SkPath& path,
+            float hOffset, float vOffset, const SkPaint& paint) = 0;
+};
+
+}; // namespace android
+#endif // ANDROID_GRAPHICS_CANVAS_H
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5cc2b95..74be577 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -4,6 +4,7 @@
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
 
+#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkMath.h"
@@ -364,7 +365,7 @@
     SkASSERT(canvas);
     SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
     jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
-    SkCanvas* c = getNativeCanvas(canvasHandle);
+    SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
     SkASSERT(c);
     return c;
 }
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8150edf..28a6edb 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -47,7 +47,6 @@
     static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
     static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
 
-    static SkCanvas* getNativeCanvas(jlong nativeHandle);
     static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
     static SkPaint*  getNativePaint(JNIEnv*, jobject paint);
     static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index ab5bdb0..e82e8a6 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -23,6 +23,7 @@
 
 #include <Caches.h>
 
+#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkRegion.h"
 #include "GraphicsJNI.h"
@@ -119,7 +120,7 @@
     static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
+        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
@@ -138,7 +139,7 @@
     static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
+        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index bc0c25f..d214575 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
+#include "Canvas.h"
 #include "Picture.h"
 
-#include "SkCanvas.h"
 #include "SkStream.h"
 
 namespace android {
@@ -36,12 +36,13 @@
     }
 }
 
-SkCanvas* Picture::beginRecording(int width, int height) {
+Canvas* Picture::beginRecording(int width, int height) {
     mPicture.reset(NULL);
     mRecorder.reset(new SkPictureRecorder);
     mWidth = width;
     mHeight = height;
-    return mRecorder->beginRecording(width, height, NULL, 0);
+    SkCanvas* canvas = mRecorder->beginRecording(width, height, NULL, 0);
+    return Canvas::create_canvas(canvas);
 }
 
 void Picture::endRecording() {
@@ -93,14 +94,14 @@
     }
 }
 
-void Picture::draw(SkCanvas* canvas) {
+void Picture::draw(Canvas* canvas) {
     if (NULL != mRecorder.get()) {
         this->endRecording();
         SkASSERT(NULL != mPicture.get());
     }
     if (NULL != mPicture.get()) {
         // TODO: remove this const_cast once pictures are immutable
-        const_cast<SkPicture*>(mPicture.get())->draw(canvas);
+        const_cast<SkPicture*>(mPicture.get())->draw(canvas->getSkCanvas());
     }
 }
 
diff --git a/core/jni/android/graphics/Picture.h b/core/jni/android/graphics/Picture.h
index abb0403..a2e5d4a 100644
--- a/core/jni/android/graphics/Picture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -22,14 +22,13 @@
 #include "SkRefCnt.h"
 #include "SkTemplates.h"
 
-class SkCanvas;
-class SkPicture;
-class SkPictureRecorder;
 class SkStream;
 class SkWStream;
 
 namespace android {
 
+class Canvas;
+
 // Skia's SkPicture class has been split into an SkPictureRecorder
 // and an SkPicture. AndroidPicture recreates the functionality
 // of the old SkPicture interface by flip-flopping between the two
@@ -38,7 +37,7 @@
 public:
     explicit Picture(const Picture* src = NULL);
 
-    SkCanvas* beginRecording(int width, int height);
+    Canvas* beginRecording(int width, int height);
 
     void endRecording();
 
@@ -50,7 +49,7 @@
 
     void serialize(SkWStream* stream) const;
 
-    void draw(SkCanvas* canvas);
+    void draw(Canvas* canvas);
 
 private:
     int mWidth;
diff --git a/core/jni/android/graphics/SkiaCanvas.cpp b/core/jni/android/graphics/SkiaCanvas.cpp
new file mode 100644
index 0000000..5e93313
--- /dev/null
+++ b/core/jni/android/graphics/SkiaCanvas.cpp
@@ -0,0 +1,773 @@
+/*
+ * 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.
+ */
+
+#include "jni.h"
+#include "Canvas.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkCanvas.h"
+#include "SkClipStack.h"
+#include "SkDevice.h"
+#include "SkDeque.h"
+#include "SkDrawFilter.h"
+#include "SkGraphics.h"
+#include "SkPorterDuff.h"
+#include "SkShader.h"
+#include "SkTArray.h"
+#include "SkTemplates.h"
+
+#include <minikin/Layout.h>
+#include "MinikinSkia.h"
+#include "MinikinUtils.h"
+
+#include "TypefaceImpl.h"
+
+#include "unicode/ubidi.h"
+#include "unicode/ushape.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+// Holds an SkCanvas reference plus additional native data.
+class SkiaCanvas : public Canvas {
+public:
+    SkiaCanvas(SkBitmap* bitmap);
+
+    SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {
+        SkASSERT(canvas);
+    }
+
+    virtual SkCanvas* getSkCanvas() {
+        return mCanvas.get();
+    }
+
+    virtual void setBitmap(SkBitmap* bitmap, bool copyState);
+
+    virtual bool isOpaque();
+    virtual int width();
+    virtual int height();
+
+    virtual int getSaveCount() const;
+    virtual int save(SkCanvas::SaveFlags flags);
+    virtual void restore();
+    virtual void restoreToCount(int saveCount);
+
+    virtual int saveLayer(float left, float top, float right, float bottom,
+                const SkPaint* paint, SkCanvas::SaveFlags flags);
+    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+            int alpha, SkCanvas::SaveFlags flags);
+
+    virtual void getMatrix(SkMatrix* outMatrix) const;
+    virtual void setMatrix(const SkMatrix& matrix);
+    virtual void concat(const SkMatrix& matrix);
+    virtual void rotate(float degrees);
+    virtual void scale(float sx, float sy);
+    virtual void skew(float sx, float sy);
+    virtual void translate(float dx, float dy);
+
+    virtual bool getClipBounds(SkRect* outRect) const;
+    virtual bool quickRejectRect(float left, float top, float right, float bottom) const;
+    virtual bool quickRejectPath(const SkPath& path) const;
+    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+    virtual bool clipPath(const SkPath* path, SkRegion::Op op);
+    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
+
+    virtual void setDrawFilter(SkDrawFilter* drawFilter);
+
+    virtual void drawColor(int color, SkXfermode::Mode mode);
+    virtual void drawPaint(const SkPaint& paint);
+
+    virtual void drawPoint(float x, float y, const SkPaint& paint);
+    virtual void drawPoints(const float* points, int count, const SkPaint& paint);
+    virtual void drawLine(float startX, float startY, float stopX, float stopY,
+            const SkPaint& paint);
+    virtual void drawLines(const float* points, int count, const SkPaint& paint);
+    virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint);
+    virtual void drawRoundRect(float left, float top, float right, float bottom,
+            float rx, float ry, const SkPaint& paint);
+    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint);
+    virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint);
+    virtual void drawArc(float left, float top, float right, float bottom,
+            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint);
+    virtual void drawPath(const SkPath& path, const SkPaint& paint);
+    virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+            const float* verts, const float* tex, const int* colors,
+            const uint16_t* indices, int indexCount, const SkPaint& paint);
+
+    virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint);
+    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);
+    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint);
+    virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+            const float* vertices, const int* colors, const SkPaint* paint);
+
+    virtual void drawText(const char* text, int start, int count, int contextCount,
+            float x, float y, int bidiFlags, const SkPaint& paint, TypefaceImpl* typeface);
+    virtual void drawPosText(const char* text, const float* positions, int count, int posCount,
+            const SkPaint& paint);
+    virtual void drawTextOnPath(const char* text, int count, const SkPath& path,
+            float hOffset, float vOffset, const SkPaint& paint);
+
+private:
+    struct SaveRec {
+        int                 saveCount;
+        SkCanvas::SaveFlags saveFlags;
+    };
+
+    void recordPartialSave(SkCanvas::SaveFlags flags);
+    void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
+    void applyClips(const SkTArray<SkClipStack::Element>& clips);
+
+    void drawPoints(const float* points, int count, const SkPaint& paint,
+                    SkCanvas::PointMode mode);
+    void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+
+    SkAutoTUnref<SkCanvas> mCanvas;
+    SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
+};
+
+// Construct an SkCanvas from the bitmap.
+static SkCanvas* createCanvas(SkBitmap* bitmap) {
+    if (bitmap) {
+        return SkNEW_ARGS(SkCanvas, (*bitmap));
+    }
+
+    // Create an empty bitmap device to prevent callers from crashing
+    // if they attempt to draw into this canvas.
+    SkBitmap emptyBitmap;
+    return new SkCanvas(emptyBitmap);
+}
+
+Canvas* Canvas::create_canvas(SkBitmap* bitmap) {
+    return new SkiaCanvas(bitmap);
+}
+
+Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
+    return new SkiaCanvas(skiaCanvas);
+}
+
+SkiaCanvas::SkiaCanvas(SkBitmap* bitmap) {
+    mCanvas.reset(createCanvas(bitmap));
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Replace Bitmap
+// ----------------------------------------------------------------------------
+
+class ClipCopier : public SkCanvas::ClipVisitor {
+public:
+    ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
+
+    virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipRect(rect, op, antialias);
+    }
+    virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipRRect(rrect, op, antialias);
+    }
+    virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipPath(path, op, antialias);
+    }
+
+private:
+    SkCanvas* m_dstCanvas;
+};
+
+void SkiaCanvas::setBitmap(SkBitmap* bitmap, bool copyState) {
+    SkCanvas* newCanvas = createCanvas(bitmap);
+    SkASSERT(newCanvas);
+
+    if (copyState) {
+        // Copy the canvas matrix & clip state.
+        newCanvas->setMatrix(mCanvas->getTotalMatrix());
+        if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
+            ClipCopier copier(newCanvas);
+            mCanvas->replayClips(&copier);
+        }
+    }
+
+    // unrefs the existing canvas
+    mCanvas.reset(newCanvas);
+
+    // clean up the old save stack
+    mSaveStack.reset(NULL);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+
+bool SkiaCanvas::isOpaque() {
+    return mCanvas->getDevice()->accessBitmap(false).isOpaque();
+}
+
+int SkiaCanvas::width() {
+    return mCanvas->getBaseLayerSize().width();
+}
+
+int SkiaCanvas::height() {
+    return mCanvas->getBaseLayerSize().height();
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Save (layer)
+// ----------------------------------------------------------------------------
+
+int SkiaCanvas::getSaveCount() const {
+    return mCanvas->getSaveCount();
+}
+
+int SkiaCanvas::save(SkCanvas::SaveFlags flags) {
+    int count = mCanvas->save();
+    recordPartialSave(flags);
+    return count;
+}
+
+void SkiaCanvas::restore() {
+    const SaveRec* rec = (NULL == mSaveStack.get())
+            ? NULL
+            : static_cast<SaveRec*>(mSaveStack->back());
+    int currentSaveCount = mCanvas->getSaveCount() - 1;
+    SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
+
+    if (NULL == rec || rec->saveCount != currentSaveCount) {
+        // Fast path - no record for this frame.
+        mCanvas->restore();
+        return;
+    }
+
+    bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
+    bool preserveClip   = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+
+    SkMatrix savedMatrix;
+    if (preserveMatrix) {
+        savedMatrix = mCanvas->getTotalMatrix();
+    }
+
+    SkTArray<SkClipStack::Element> savedClips;
+    if (preserveClip) {
+        saveClipsForFrame(savedClips, currentSaveCount);
+    }
+
+    mCanvas->restore();
+
+    if (preserveMatrix) {
+        mCanvas->setMatrix(savedMatrix);
+    }
+
+    if (preserveClip && !savedClips.empty()) {
+        applyClips(savedClips);
+    }
+
+    mSaveStack->pop_back();
+}
+
+void SkiaCanvas::restoreToCount(int restoreCount) {
+    while (mCanvas->getSaveCount() > restoreCount) {
+        this->restore();
+    }
+}
+
+int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
+            const SkPaint* paint, SkCanvas::SaveFlags flags) {
+    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag);
+    recordPartialSave(flags);
+    return count;
+}
+
+int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
+        int alpha, SkCanvas::SaveFlags flags) {
+    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag);
+    recordPartialSave(flags);
+    return count;
+}
+
+// ----------------------------------------------------------------------------
+// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) {
+    // A partial save is a save operation which doesn't capture the full canvas state.
+    // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+
+    // Mask-out non canvas state bits.
+    flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+
+    if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+        // not a partial save.
+        return;
+    }
+
+    if (NULL == mSaveStack.get()) {
+        mSaveStack.reset(SkNEW_ARGS(SkDeque, (sizeof(struct SaveRec), 8)));
+    }
+
+    SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
+    // Store the save counter in the SkClipStack domain.
+    // (0-based, equal to the number of save ops on the stack).
+    rec->saveCount = mCanvas->getSaveCount() - 1;
+    rec->saveFlags = flags;
+}
+
+void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount) {
+    SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
+                                   SkClipStack::Iter::kTop_IterStart);
+    while (const SkClipStack::Element* elem = clipIterator.next()) {
+        if (elem->getSaveCount() < frameSaveCount) {
+            // done with the current frame.
+            break;
+        }
+        SkASSERT(elem->getSaveCount() == frameSaveCount);
+        clips.push_back(*elem);
+    }
+}
+
+void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) {
+    ClipCopier clipCopier(mCanvas);
+
+    // The clip stack stores clips in device space.
+    SkMatrix origMatrix = mCanvas->getTotalMatrix();
+    mCanvas->resetMatrix();
+
+    // We pushed the clips in reverse order.
+    for (int i = clips.count() - 1; i >= 0; --i) {
+        clips[i].replay(&clipCopier);
+    }
+
+    mCanvas->setMatrix(origMatrix);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Matrix
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::getMatrix(SkMatrix* outMatrix) const {
+    *outMatrix = mCanvas->getTotalMatrix();
+}
+
+void SkiaCanvas::setMatrix(const SkMatrix& matrix) {
+    mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvas::concat(const SkMatrix& matrix) {
+    mCanvas->concat(matrix);
+}
+
+void SkiaCanvas::rotate(float degrees) {
+    mCanvas->rotate(degrees);
+}
+
+void SkiaCanvas::scale(float sx, float sy) {
+    mCanvas->scale(sx, sy);
+}
+
+void SkiaCanvas::skew(float sx, float sy) {
+    mCanvas->skew(sx, sy);
+}
+
+void SkiaCanvas::translate(float dx, float dy) {
+    mCanvas->translate(dx, dy);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Clips
+// ----------------------------------------------------------------------------
+
+// This function is a mirror of SkCanvas::getClipBounds except that it does
+// not outset the edge of the clip to account for anti-aliasing. There is
+// a skia bug to investigate pushing this logic into back into skia.
+// (see https://code.google.com/p/skia/issues/detail?id=1303)
+bool SkiaCanvas::getClipBounds(SkRect* outRect) const {
+    SkIRect ibounds;
+    if (!mCanvas->getClipDeviceBounds(&ibounds)) {
+        return false;
+    }
+
+    SkMatrix inverse;
+    // if we can't invert the CTM, we can't return local clip bounds
+    if (!mCanvas->getTotalMatrix().invert(&inverse)) {
+        if (outRect) {
+            outRect->setEmpty();
+        }
+        return false;
+    }
+
+    if (NULL != outRect) {
+        SkRect r = SkRect::Make(ibounds);
+        inverse.mapRect(outRect, r);
+    }
+    return true;
+}
+
+bool SkiaCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
+    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    return mCanvas->quickReject(bounds);
+}
+
+bool SkiaCanvas::quickRejectPath(const SkPath& path) const {
+    return mCanvas->quickReject(path);
+}
+
+bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+    mCanvas->clipRect(rect, op);
+    return mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+    mCanvas->clipPath(*path, op);
+    return mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+    SkPath rgnPath;
+    if (region->getBoundaryPath(&rgnPath)) {
+        // The region is specified in device space.
+        SkMatrix savedMatrix = mCanvas->getTotalMatrix();
+        mCanvas->resetMatrix();
+        mCanvas->clipPath(rgnPath, op);
+        mCanvas->setMatrix(savedMatrix);
+    } else {
+        mCanvas->clipRect(SkRect::MakeEmpty(), op);
+    }
+    return mCanvas->isClipEmpty();
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Filters
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
+    mCanvas->setDrawFilter(drawFilter);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) {
+    mCanvas->drawColor(color, mode);
+}
+
+void SkiaCanvas::drawPaint(const SkPaint& paint) {
+    mCanvas->drawPaint(paint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Geometry
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
+                            SkCanvas::PointMode mode) {
+    // convert the floats into SkPoints
+    count >>= 1;    // now it is the number of points
+    SkAutoSTMalloc<32, SkPoint> storage(count);
+    SkPoint* pts = storage.get();
+    for (int i = 0; i < count; i++) {
+        pts[i].set(points[0], points[1]);
+        points += 2;
+    }
+    mCanvas->drawPoints(mode, count, pts, paint);
+}
+
+
+void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
+    mCanvas->drawPoint(x, y, paint);
+}
+
+void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
+    this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
+}
+
+void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
+                          const SkPaint& paint) {
+    mCanvas->drawLine(startX, startY, stopX, stopY, paint);
+}
+
+void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+    this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
+}
+
+void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
+        const SkPaint& paint) {
+    mCanvas->drawRectCoords(left, top, right, bottom, paint);
+
+}
+
+void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
+        float rx, float ry, const SkPaint& paint) {
+    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+    mCanvas->drawRoundRect(rect, rx, ry, paint);
+}
+
+void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+    mCanvas->drawCircle(x, y, radius, paint);
+}
+
+void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+    SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+    mCanvas->drawOval(oval, paint);
+}
+
+void SkiaCanvas::drawArc(float left, float top, float right, float bottom,
+        float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+    SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
+    mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
+}
+
+void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+                              const float* verts, const float* texs, const int* colors,
+                              const uint16_t* indices, int indexCount, const SkPaint& paint) {
+#ifndef SK_SCALAR_IS_FLOAT
+    SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
+#endif
+    const int ptCount = vertexCount >> 1;
+    mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs,
+                          (SkColor*)colors, NULL, indices, indexCount, paint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Bitmaps
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
+    mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+    mCanvas->drawBitmapMatrix(bitmap, matrix, paint);
+}
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+                            float srcRight, float srcBottom, float dstLeft, float dstTop,
+                            float dstRight, float dstBottom, const SkPaint* paint) {
+    SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
+    SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    mCanvas->drawBitmapRectToRect(bitmap, &srcRect, dstRect, paint);
+}
+
+void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+        const float* vertices, const int* colors, const SkPaint* paint) {
+
+    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+    const int indexCount = meshWidth * meshHeight * 6;
+
+    /*  Our temp storage holds 2 or 3 arrays.
+        texture points [ptCount * sizeof(SkPoint)]
+        optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
+            copy to convert from float to fixed
+        indices [ptCount * sizeof(uint16_t)]
+    */
+    ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
+    storageSize += indexCount * sizeof(uint16_t);  // indices[]
+
+
+#ifndef SK_SCALAR_IS_FLOAT
+    SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
+#endif
+    SkAutoMalloc storage(storageSize);
+    SkPoint* texs = (SkPoint*)storage.get();
+    uint16_t* indices = (uint16_t*)(texs + ptCount);
+
+    // cons up texture coordinates and indices
+    {
+        const SkScalar w = SkIntToScalar(bitmap.width());
+        const SkScalar h = SkIntToScalar(bitmap.height());
+        const SkScalar dx = w / meshWidth;
+        const SkScalar dy = h / meshHeight;
+
+        SkPoint* texsPtr = texs;
+        SkScalar y = 0;
+        for (int i = 0; i <= meshHeight; i++) {
+            if (i == meshHeight) {
+                y = h;  // to ensure numerically we hit h exactly
+            }
+            SkScalar x = 0;
+            for (int j = 0; j < meshWidth; j++) {
+                texsPtr->set(x, y);
+                texsPtr += 1;
+                x += dx;
+            }
+            texsPtr->set(w, y);
+            texsPtr += 1;
+            y += dy;
+        }
+        SkASSERT(texsPtr - texs == ptCount);
+    }
+
+    // cons up indices
+    {
+        uint16_t* indexPtr = indices;
+        int index = 0;
+        for (int i = 0; i < meshHeight; i++) {
+            for (int j = 0; j < meshWidth; j++) {
+                // lower-left triangle
+                *indexPtr++ = index;
+                *indexPtr++ = index + meshWidth + 1;
+                *indexPtr++ = index + meshWidth + 2;
+                // upper-right triangle
+                *indexPtr++ = index;
+                *indexPtr++ = index + meshWidth + 2;
+                *indexPtr++ = index + 1;
+                // bump to the next cell
+                index += 1;
+            }
+            // bump to the next row
+            index += 1;
+        }
+        SkASSERT(indexPtr - indices == indexCount);
+        SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
+    }
+
+    // double-check that we have legal indices
+#ifdef SK_DEBUG
+    {
+        for (int i = 0; i < indexCount; i++) {
+            SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
+        }
+    }
+#endif
+
+    // cons-up a shader for the bitmap
+    SkPaint tmpPaint;
+    if (paint) {
+        tmpPaint = *paint;
+    }
+    SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+                                                    SkShader::kClamp_TileMode,
+                                                    SkShader::kClamp_TileMode);
+    SkSafeUnref(tmpPaint.setShader(shader));
+
+    mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices,
+                         texs, (const SkColor*)colors, NULL, indices,
+                         indexCount, tmpPaint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Text
+// ----------------------------------------------------------------------------
+
+class DrawTextFunctor {
+public:
+    DrawTextFunctor(const Layout& layout, SkCanvas* canvas, float x, float y, SkPaint* paint,
+                uint16_t* glyphs, SkPoint* pos)
+            : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
+                pos(pos) { }
+
+    void operator()(size_t start, size_t end) {
+        for (size_t i = start; i < end; i++) {
+            glyphs[i] = layout.getGlyphId(i);
+            pos[i].fX = x + layout.getX(i);
+            pos[i].fY = y + layout.getY(i);
+        }
+        canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
+    }
+private:
+    const Layout& layout;
+    SkCanvas* canvas;
+    float x;
+    float y;
+    SkPaint* paint;
+    uint16_t* glyphs;
+    SkPoint* pos;
+};
+
+void SkiaCanvas::drawText(const char* text, int start, int count, int contextCount,
+        float x, float y, int bidiFlags, const SkPaint& paint, TypefaceImpl* typeface) {
+    Layout layout;
+    std::string css = MinikinUtils::setLayoutProperties(&layout, &paint, bidiFlags, typeface);
+    layout.doLayout((uint16_t*)text, start, count, contextCount, css);
+
+    size_t nGlyphs = layout.nGlyphs();
+    uint16_t* glyphs = new uint16_t[nGlyphs];
+    SkPoint* pos = new SkPoint[nGlyphs];
+
+    SkPaint paintCopy(paint);
+    x += MinikinUtils::xOffsetForTextAlign(&paintCopy, layout);
+    paintCopy.setTextAlign(SkPaint::kLeft_Align);
+    paintCopy.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    DrawTextFunctor f(layout, mCanvas, x, y, &paintCopy, glyphs, pos);
+    MinikinUtils::forFontRun(layout, &paintCopy, f);
+    drawTextDecorations(x, y, layout.getAdvance(), paintCopy);
+
+    delete[] glyphs;
+    delete[] pos;
+}
+
+// Same values used by Skia
+#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
+#define kStdUnderline_Offset    (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+void SkiaCanvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
+    uint32_t flags;
+    SkDrawFilter* drawFilter = mCanvas->getDrawFilter();
+    if (drawFilter) {
+        SkPaint paintCopy(paint);
+        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+        flags = paintCopy.getFlags();
+    } else {
+        flags = paint.getFlags();
+    }
+    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+        SkScalar left = x;
+        SkScalar right = x + length;
+        float textSize = paint.getTextSize();
+        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+        if (flags & SkPaint::kUnderlineText_Flag) {
+            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+            mCanvas->drawRectCoords(left, top, right, bottom, paint);
+        }
+        if (flags & SkPaint::kStrikeThruText_Flag) {
+            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+            mCanvas->drawRectCoords(left, top, right, bottom, paint);
+        }
+    }
+}
+
+void SkiaCanvas::drawPosText(const char* text, const float* positions, int count, int posCount,
+        const SkPaint& paint) {
+    SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
+    int indx;
+    for (indx = 0; indx < posCount; indx++) {
+        posPtr[indx].fX = positions[indx << 1];
+        posPtr[indx].fY = positions[(indx << 1) + 1];
+    }
+
+    SkPaint paintCopy(paint);
+    paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    mCanvas->drawPosText(text, count, posPtr, paintCopy);
+
+    delete[] posPtr;
+}
+
+void SkiaCanvas::drawTextOnPath(const char* text, int count, const SkPath& path,
+        float hOffset, float vOffset, const SkPaint& paint) {
+    mCanvas->drawTextOnPathHV(text, count, path, hOffset, vOffset, paint);
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index 3812c27..9436a47 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -19,9 +19,9 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <vector>
 
+#include "Canvas.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 
-#include "SkCanvas.h"
 #include "SkDocument.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -132,8 +132,9 @@
         jint pageWidth, jint pageHeight,
         jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
-    return reinterpret_cast<jlong>(document->startPage(pageWidth, pageHeight,
-            contentLeft, contentTop, contentRight, contentBottom));
+    SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
+            contentLeft, contentTop, contentRight, contentBottom);
+    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
 }
 
 static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
new file mode 100644
index 0000000..fd96a90
--- /dev/null
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -0,0 +1,653 @@
+/*
+ * 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.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "Canvas.h"
+#include "SkGraphics.h"
+#include "SkPorterDuff.h"
+#include "TypefaceImpl.h"
+
+#include <minikin/Layout.h>
+#include "MinikinSkia.h"
+#include "MinikinUtils.h"
+
+namespace android {
+
+namespace CanvasJNI {
+
+static Canvas* get_canvas(jlong canvasHandle) {
+    return reinterpret_cast<Canvas*>(canvasHandle);
+}
+
+static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
+    delete get_canvas(canvasHandle);
+}
+
+// Native wrapper constructor used by Canvas(Bitmap)
+static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
+}
+
+// Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
+// optionally copying canvas matrix & clip state.
+static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                      jboolean copyState) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    get_canvas(canvasHandle)->setBitmap(bitmap, copyState);
+}
+
+static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
+    return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->width());
+}
+
+static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->height());
+}
+
+static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
+}
+
+static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
+    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
+}
+
+static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
+                      jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
+    SkPaint* paint  = reinterpret_cast<SkPaint*>(paintHandle);
+    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
+}
+
+static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
+                           jfloat r, jfloat b, jint alpha, jint flagsHandle) {
+    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
+}
+
+static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
+        doThrowISE(env, "Underflow in restore");
+        return;
+    }
+    canvas->restore();
+}
+
+static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
+        doThrowIAE(env, "Underflow in restoreToCount");
+        return;
+    }
+    canvas->restoreToCount(restoreCount);
+}
+
+static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->getMatrix(matrix);
+}
+
+static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
+}
+
+static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->concat(*matrix);
+}
+
+static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
+    get_canvas(canvasHandle)->rotate(degrees);
+}
+
+static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
+    get_canvas(canvasHandle)->scale(sx, sy);
+}
+
+static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
+    get_canvas(canvasHandle)->skew(sx, sy);
+}
+
+static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
+    get_canvas(canvasHandle)->translate(dx, dy);
+}
+
+static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
+    SkRect   r;
+    SkIRect ir;
+    bool result = get_canvas(canvasHandle)->getClipBounds(&r);
+
+    if (!result) {
+        r.setEmpty();
+    }
+    r.round(&ir);
+
+    (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
+                                jfloat left, jfloat top, jfloat right, jfloat bottom) {
+    bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
+                         jfloat r, jfloat b, jint opHandle) {
+    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+    bool emptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
+    return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+                         jint opHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+    bool emptyClip = get_canvas(canvasHandle)->clipPath(path, op);
+    return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
+                           jint opHandle) {
+    SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
+    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+    bool emptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
+    return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
+     SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
+     get_canvas(canvasHandle)->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
+}
+
+static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPaint(*paint);
+}
+
+static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
+                      jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPoint(x, y, *paint);
+}
+
+static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
+                       jint offset, jint count, jlong paintHandle) {
+    NPE_CHECK_RETURN_VOID(env, jptsArray);
+    AutoJavaFloatArray autoPts(env, jptsArray);
+    float* floats = autoPts.ptr();
+    const int length = autoPts.length();
+
+    if ((offset | count) < 0 || offset + count > length) {
+        doThrowAIOOBE(env);
+        return;
+    }
+
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
+}
+
+static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
+                     jfloat stopX, jfloat stopY, jlong paintHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
+}
+
+static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
+                      jint offset, jint count, jlong paintHandle) {
+    NPE_CHECK_RETURN_VOID(env, jptsArray);
+    AutoJavaFloatArray autoPts(env, jptsArray);
+    float* floats = autoPts.ptr();
+    const int length = autoPts.length();
+
+    if ((offset | count) < 0 || offset + count > length) {
+        doThrowAIOOBE(env);
+        return;
+    }
+
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
+}
+
+static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                     jfloat right, jfloat bottom, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
+}
+
+static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                          jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
+}
+
+static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
+                       jfloat radius, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
+}
+
+static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                     jfloat right, jfloat bottom, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
+}
+
+static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                    jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
+                    jboolean useCenter, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
+                                       useCenter, *paint);
+}
+
+static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+                     jlong paintHandle) {
+    const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPath(*path, *paint);
+}
+
+static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
+                         jint modeHandle, jint vertexCount,
+                         jfloatArray jverts, jint vertIndex,
+                         jfloatArray jtexs, jint texIndex,
+                         jintArray jcolors, jint colorIndex,
+                         jshortArray jindices, jint indexIndex,
+                         jint indexCount, jlong paintHandle) {
+    AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
+    AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
+    AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
+    AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
+
+    const float* verts = vertA.ptr() + vertIndex;
+    const float* texs = texA.ptr() + vertIndex;
+    const int* colors = NULL;
+    const uint16_t* indices = NULL;
+
+    if (jcolors != NULL) {
+        colors = colorA.ptr() + colorIndex;
+    }
+    if (jindices != NULL) {
+        indices = (const uint16_t*)(indexA.ptr() + indexIndex);
+    }
+
+    SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
+                                           indices, indexCount, *paint);
+}
+
+static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jlong bitmapHandle,
+                       jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
+                       jint screenDensity, jint bitmapDensity) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+    if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
+        if (screenDensity != 0 && screenDensity != bitmapDensity) {
+            SkPaint filteredPaint;
+            if (paint) {
+                filteredPaint = *paint;
+            }
+            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+            canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
+        } else {
+            canvas->drawBitmap(*bitmap, left, top, paint);
+        }
+    } else {
+        canvas->save(SkCanvas::kMatrixClip_SaveFlag);
+        SkScalar scale = canvasDensity / (float)bitmapDensity;
+        canvas->translate(left, top);
+        canvas->scale(scale, scale);
+
+        SkPaint filteredPaint;
+        if (paint) {
+            filteredPaint = *paint;
+        }
+        filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+
+        canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
+        canvas->restore();
+    }
+}
+
+static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                             jlong matrixHandle, jlong paintHandle) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawBitmap(*bitmap, *matrix, paint);
+}
+
+static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                           float srcLeft, float srcTop, float srcRight, float srcBottom,
+                           float dstLeft, float dstTop, float dstRight, float dstBottom,
+                           jlong paintHandle, jint screenDensity, jint bitmapDensity) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+    if (screenDensity != 0 && screenDensity != bitmapDensity) {
+        SkPaint filteredPaint;
+        if (paint) {
+            filteredPaint = *paint;
+        }
+        filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
+                           dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
+    } else {
+        canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
+                           dstLeft, dstTop, dstRight, dstBottom, paint);
+    }
+}
+
+static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
+                            jintArray jcolors, jint offset, jint stride,
+                            jfloat x, jfloat y, jint width, jint height,
+                            jboolean hasAlpha, jlong paintHandle) {
+    // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+    // correct the alphaType to kOpaque_SkAlphaType.
+    SkImageInfo info = SkImageInfo::Make(width, height,
+                           hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+                           kPremul_SkAlphaType);
+    SkBitmap bitmap;
+    if (!bitmap.allocPixels(info)) {
+        return;
+    }
+
+    if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
+        return;
+    }
+
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
+}
+
+static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                           jint meshWidth, jint meshHeight, jfloatArray jverts,
+                           jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
+    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+    AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
+    AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
+
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawBitmapMesh(*bitmap, meshWidth, meshHeight,
+                                             vertA.ptr(), colorA.ptr(), paint);
+}
+
+static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+                          jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
+                          jlong paintHandle, jlong typefaceHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    jchar* jchars = env->GetCharArrayElements(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars) + index;
+    get_canvas(canvasHandle)->drawText(textArray, 0, count, count, x, y, bidiFlags, *paint, typeface);
+    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
+}
+
+static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+                           jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
+                           jlong paintHandle, jlong typefaceHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    const int count = end - start;
+    const jchar* jchars = env->GetStringChars(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars) + start;
+    get_canvas(canvasHandle)->drawText(textArray, 0, count, count, x, y, bidiFlags, *paint, typeface);
+    env->ReleaseStringChars(text, jchars);
+}
+
+static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
+                             jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
+                             jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+    const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+    jchar* jchars = env->GetCharArrayElements(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars) + contextIndex;
+    get_canvas(canvasHandle)->drawText(textArray, index - contextIndex, count, contextCount,
+                                       x, y, bidiFlags, *paint, typeface);
+    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
+}
+
+static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
+                              jint start, jint end, jint contextStart, jint contextEnd,
+                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
+                              jlong typefaceHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+    int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+    jint count = end - start;
+    jint contextCount = contextEnd - contextStart;
+    const jchar* jchars = env->GetStringChars(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars) + contextStart;
+    get_canvas(canvasHandle)->drawText(textArray, start - contextStart, count, contextCount,
+                                       x, y, bidiFlags, *paint, typeface);
+    env->ReleaseStringChars(text, jchars);
+}
+
+static void drawPosTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+                             jint index, jint count, jfloatArray pos, jlong paintHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    jchar* jchars = text ? env->GetCharArrayElements(text, NULL) : NULL;
+    float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+    int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+
+    const char* textArray = reinterpret_cast<const char*>(jchars) + index;
+    get_canvas(canvasHandle)->drawPosText(textArray, posArray, count << 1, posCount, *paint);
+
+    if (text) {
+        env->ReleaseCharArrayElements(text, jchars, 0);
+    }
+    if (pos) {
+        env->ReleaseFloatArrayElements(pos, posArray, 0);
+    }
+}
+
+
+static void drawPosTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+                              jfloatArray pos, jlong paintHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    const jchar* jchars = text ? env->GetStringChars(text, NULL) : NULL;
+    int byteLength = text ? env->GetStringLength(text) : 0;
+    float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+    int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+
+    const char* textArray = reinterpret_cast<const char*>(jchars);
+    get_canvas(canvasHandle)->drawPosText(textArray , posArray, byteLength << 1, posCount, *paint);
+
+    if (text) {
+        env->ReleaseStringChars(text, jchars);
+    }
+    if (pos) {
+        env->ReleaseFloatArrayElements(pos, posArray, 0);
+    }
+}
+
+class DrawTextOnPathFunctor {
+public:
+    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
+                float vOffset, const SkPaint& paint, const SkPath& path)
+            : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
+                paint(paint), path(path) {
+    }
+    void operator()(size_t start, size_t end) {
+        uint16_t glyphs[1];
+        for (size_t i = start; i < end; i++) {
+            glyphs[0] = layout.getGlyphId(i);
+            float x = hOffset + layout.getX(i);
+            float y = vOffset + layout.getY(i);
+            canvas->drawTextOnPath((const char*) glyphs, 1, path, x, y, paint);
+        }
+    }
+private:
+    const Layout& layout;
+    Canvas* canvas;
+    float hOffset;
+    float vOffset;
+    const SkPaint& paint;
+    const SkPath& path;
+};
+
+static void drawTextOnPath(Canvas* canvas, const char* text, int count, int bidiFlags,
+                           const SkPath& path, float hOffset, float vOffset,
+                           const SkPaint& paint, TypefaceImpl* typeface) {
+    SkPaint paintCopy(paint);
+    Layout layout;
+    std::string css = MinikinUtils::setLayoutProperties(&layout, &paintCopy, bidiFlags, typeface);
+    layout.doLayout((uint16_t*)text, 0, count, count, css);
+    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
+
+    // Set align to left for drawing, as we don't want individual
+    // glyphs centered or right-aligned; the offset above takes
+    // care of all alignment.
+    paintCopy.setTextAlign(SkPaint::kLeft_Align);
+
+    DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
+    MinikinUtils::forFontRun(layout, &paintCopy, f);
+}
+
+static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+                                jint index, jint count, jlong pathHandle, jfloat hOffset,
+                                jfloat vOffset, jint bidiFlags, jlong paintHandle,
+                                jlong typefaceHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+    jchar* jchars = env->GetCharArrayElements(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars);
+
+    drawTextOnPath(get_canvas(canvasHandle), textArray + index, count, bidiFlags, *path,
+                   hOffset, vOffset, *paint, typeface);
+
+    env->ReleaseCharArrayElements(text, jchars, 0);
+}
+
+static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+                                 jlong pathHandle, jfloat hOffset, jfloat vOffset,
+                                 jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+    const jchar* jchars = env->GetStringChars(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars);
+    int count = env->GetStringLength(text);
+
+    drawTextOnPath(get_canvas(canvasHandle), textArray, count, bidiFlags, *path,
+                   hOffset, vOffset, *paint, typeface);
+
+    env->ReleaseStringChars(text, jchars);
+}
+
+static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
+    get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
+}
+
+static void freeCaches(JNIEnv* env, jobject) {
+    SkGraphics::PurgeFontCache();
+}
+
+static void freeTextLayoutCaches(JNIEnv* env, jobject) {
+    Layout::purgeCaches();
+}
+
+}; // namespace CanvasJNI
+
+static JNINativeMethod gMethods[] = {
+    {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
+    {"initRaster", "(J)J", (void*) CanvasJNI::initRaster},
+    {"native_setBitmap", "(JJZ)V", (void*) CanvasJNI::setBitmap},
+    {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
+    {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
+    {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
+    {"native_save","(JI)I", (void*) CanvasJNI::save},
+    {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
+    {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+    {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
+    {"native_restore","(J)V", (void*) CanvasJNI::restore},
+    {"native_restoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
+    {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
+    {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
+    {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
+    {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
+    {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
+    {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
+    {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
+    {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
+    {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
+    {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
+    {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
+    {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
+    {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
+    {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
+    {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
+    {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
+    {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
+    {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
+    {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
+    {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
+    {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+    {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
+    {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
+    {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
+    {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
+    {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
+    {"native_drawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
+    {"nativeDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
+    {"native_drawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
+    {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+    {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
+    {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
+    {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
+    {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
+    {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
+    {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
+    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
+    {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
+    {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
+    {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
+};
+
+int register_android_graphics_Canvas(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp
index f827907..eb8f6dd 100644
--- a/core/jni/android_graphics_Picture.cpp
+++ b/core/jni/android_graphics_Picture.cpp
@@ -51,7 +51,7 @@
 
 static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle,
                                           jlong pictureHandle) {
-    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasHandle);
     Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
     SkASSERT(canvas);
     SkASSERT(picture);
@@ -84,12 +84,7 @@
 static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
                                                      jint w, jint h) {
     Picture* pict = reinterpret_cast<Picture*>(pictHandle);
-    // beginRecording does not ref its return value, it just returns it.
-    SkCanvas* canvas = pict->beginRecording(w, h);
-    // the java side will wrap this guy in a Canvas.java, which will call
-    // unref in its finalizer, so we have to ref it here, so that both that
-    // Canvas.java and our picture can both be owners
-    canvas->ref();
+    Canvas* canvas = pict->beginRecording(w, h);
     return reinterpret_cast<jlong>(canvas);
 }
 
diff --git a/core/res/res/anim/launch_task_behind_background.xml b/core/res/res/anim/launch_task_behind_background.xml
new file mode 100644
index 0000000..358511f
--- /dev/null
+++ b/core/res/res/anim/launch_task_behind_background.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+
+    <translate android:fromYDelta="110%" android:toYDelta="66%"
+               android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+               android:interpolator="@interpolator/decelerate_quint"
+               android:startOffset="50"
+               android:duration="300" />
+
+    <translate android:fromYDelta="0%" android:toYDelta="167%"
+               android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+               android:interpolator="@interpolator/accelerate_quint"
+               android:startOffset="433"
+               android:duration="300" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/launch_task_behind_source.xml b/core/res/res/anim/launch_task_behind_source.xml
new file mode 100644
index 0000000..426ee5d
--- /dev/null
+++ b/core/res/res/anim/launch_task_behind_source.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.6"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/accelerate_cubic"
+        android:duration="133"/>
+
+    <translate android:fromYDelta="0" android:toYDelta="10%"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/accelerate_cubic"
+        android:duration="350"/>
+
+    <scale android:fromXScale="1.0" android:toXScale="0.9"
+        android:fromYScale="1.0" android:toYScale="0.9"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:pivotX="50%p" android:pivotY="50%p"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="350" />
+
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.6"
+        android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+        android:interpolator="@interpolator/decelerate_cubic"
+        android:startOffset="433"
+        android:duration="133"/>
+
+    <translate android:fromYDelta="0%" android:toYDelta="-10%"
+        android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+        android:interpolator="@interpolator/decelerate_cubic"
+        android:startOffset="433"
+        android:duration="350"/>
+
+    <scale android:fromXScale="1.0" android:toXScale="1.1"
+        android:fromYScale="1.0" android:toYScale="1.1"
+        android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+        android:pivotX="50%p" android:pivotY="50%p"
+        android:interpolator="@interpolator/decelerate_cubic"
+        android:startOffset="433"
+        android:duration="350" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_calendar_holo_dark.xml b/core/res/res/color/date_picker_calendar_holo_dark.xml
new file mode 100644
index 0000000..d29486f
--- /dev/null
+++ b/core/res/res/color/date_picker_calendar_holo_dark.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_enabled="true" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_dark"/>
+    <item android:state_enabled="true" android:state_selected="true"
+          android:color="@color/holo_blue_light"/>
+    <item android:state_enabled="false"
+          android:color="@color/datepicker_default_disabled_text_color_holo_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_calendar_holo_light.xml b/core/res/res/color/date_picker_calendar_holo_light.xml
new file mode 100644
index 0000000..776f39b
--- /dev/null
+++ b/core/res/res/color/date_picker_calendar_holo_light.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_enabled="true" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_light"/>
+    <item android:state_enabled="true" android:state_selected="true"
+          android:color="@color/holo_blue_light"/>
+    <item android:state_enabled="false"
+          android:color="@color/datepicker_default_disabled_text_color_holo_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_calendar_material_dark.xml b/core/res/res/color/date_picker_calendar_material_dark.xml
new file mode 100644
index 0000000..86a0673
--- /dev/null
+++ b/core/res/res/color/date_picker_calendar_material_dark.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_enabled="true" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_dark"/>
+    <item android:state_enabled="true" android:state_selected="true"
+          android:color="@color/holo_blue_light"/>
+    <item android:state_enabled="false"
+          android:color="@color/datepicker_default_disabled_text_color_material_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_calendar_material_light.xml b/core/res/res/color/date_picker_calendar_material_light.xml
new file mode 100644
index 0000000..015eed7
--- /dev/null
+++ b/core/res/res/color/date_picker_calendar_material_light.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_enabled="true" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_light"/>
+    <item android:state_enabled="true" android:state_selected="true"
+          android:color="@color/holo_blue_light"/>
+    <item android:state_enabled="false"
+          android:color="@color/datepicker_default_disabled_text_color_material_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_selector_holo_dark.xml b/core/res/res/color/date_picker_selector_holo_dark.xml
new file mode 100644
index 0000000..9e5a5bd
--- /dev/null
+++ b/core/res/res/color/date_picker_selector_holo_dark.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_holo_dark"/>
+    <item android:state_pressed="false" android:state_selected="true"
+          android:color="@color/datepicker_default_selected_text_color_holo_dark"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_selector_holo_light.xml b/core/res/res/color/date_picker_selector_holo_light.xml
new file mode 100644
index 0000000..bf8667c
--- /dev/null
+++ b/core/res/res/color/date_picker_selector_holo_light.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_holo_light"/>
+    <item android:state_pressed="false" android:state_selected="true"
+          android:color="@color/datepicker_default_selected_text_color_holo_light"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_selector_material_dark.xml b/core/res/res/color/date_picker_selector_material_dark.xml
new file mode 100644
index 0000000..e407387
--- /dev/null
+++ b/core/res/res/color/date_picker_selector_material_dark.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_material_dark"/>
+    <item android:state_pressed="false" android:state_selected="true"
+          android:color="@color/datepicker_default_selected_text_color_material_dark"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_selector_material_light.xml b/core/res/res/color/date_picker_selector_material_light.xml
new file mode 100644
index 0000000..b4c6a47
--- /dev/null
+++ b/core/res/res/color/date_picker_selector_material_light.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_material_light"/>
+    <item android:state_pressed="false" android:state_selected="true"
+          android:color="@color/datepicker_default_selected_text_color_material_light"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_year_selector_holo_dark.xml b/core/res/res/color/date_picker_year_selector_holo_dark.xml
new file mode 100644
index 0000000..ce519b2
--- /dev/null
+++ b/core/res/res/color/date_picker_year_selector_holo_dark.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_holo_dark"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_year_selector_holo_light.xml b/core/res/res/color/date_picker_year_selector_holo_light.xml
new file mode 100644
index 0000000..c228711
--- /dev/null
+++ b/core/res/res/color/date_picker_year_selector_holo_light.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_holo_light"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_year_selector_material_dark.xml b/core/res/res/color/date_picker_year_selector_material_dark.xml
new file mode 100644
index 0000000..b5ff09a
--- /dev/null
+++ b/core/res/res/color/date_picker_year_selector_material_dark.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_material_dark"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_year_selector_material_light.xml b/core/res/res/color/date_picker_year_selector_material_light.xml
new file mode 100644
index 0000000..5e329b3
--- /dev/null
+++ b/core/res/res/color/date_picker_year_selector_material_light.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_material_light"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable/background_leanback_setup.xml b/core/res/res/drawable/background_leanback_setup.xml
new file mode 100644
index 0000000..ca3392c
--- /dev/null
+++ b/core/res/res/drawable/background_leanback_setup.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:drawable="@color/black"/>
+</layer-list>
diff --git a/core/res/res/layout-land/date_picker_holo.xml b/core/res/res/layout-land/date_picker_holo.xml
new file mode 100644
index 0000000..98e26ca
--- /dev/null
+++ b/core/res/res/layout-land/date_picker_holo.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="@dimen/datepicker_view_animator_height"
+              android:gravity="center"
+              android:orientation="horizontal" >
+
+    <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:background="?android:attr/datePickerHeaderSelectorBackgroundColor"
+            android:orientation="vertical" >
+
+        <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="0dip"
+                android:layout_weight="1"
+                android:orientation="vertical" >
+
+            <include layout="@layout/date_picker_header_view" />
+
+            <include layout="@layout/date_picker_selected_date" />
+        </LinearLayout>
+
+        <include layout="@layout/date_picker_done_button" />
+    </LinearLayout>
+
+    <include layout="@layout/date_picker_view_animator" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout-sw600dp/date_picker_holo.xml b/core/res/res/layout-sw600dp/date_picker_holo.xml
new file mode 100644
index 0000000..847aa32
--- /dev/null
+++ b/core/res/res/layout-sw600dp/date_picker_holo.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="wrap_content"
+              android:layout_height="match_parent"
+              android:background="@color/datepicker_default_view_animator_color_holo_light"
+              android:gravity="center"
+              android:orientation="vertical" >
+
+    <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/datepicker_selected_calendar_layout_height"
+            android:orientation="vertical" >
+
+        <include layout="@layout/date_picker_header_view" />
+
+        <include layout="@layout/date_picker_selected_date" />
+    </LinearLayout>
+
+    <include layout="@layout/date_picker_view_animator" />
+
+    <include layout="@layout/date_picker_done_button" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/date_picker_done_button.xml b/core/res/res/layout/date_picker_done_button.xml
new file mode 100644
index 0000000..b8e8c03
--- /dev/null
+++ b/core/res/res/layout/date_picker_done_button.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/layout_buttons"
+              style="?android:attr/buttonBarStyle"
+              android:layout_width="@dimen/datepicker_component_width"
+              android:layout_height="wrap_content"
+              android:orientation="vertical"
+              android:divider="?android:attr/dividerHorizontal"
+              android:showDividers="beginning" >
+
+    <Button
+            android:id="@+id/done"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="48dp"
+            android:text="@string/done_label"
+            android:textSize="@dimen/datepicker_done_label_size" />
+</LinearLayout>
diff --git a/core/res/res/layout/date_picker_header_view.xml b/core/res/res/layout/date_picker_header_view.xml
new file mode 100644
index 0000000..eccdd3e
--- /dev/null
+++ b/core/res/res/layout/date_picker_header_view.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="@+id/date_picker_header"
+          android:layout_width="@dimen/datepicker_component_width"
+          android:layout_height="@dimen/datepicker_header_height"
+          android:background="?android:attr/datePickerHeaderDayOfWeekLabelBackgroundColor"
+          android:gravity="center"
+          android:textAppearance="?android:attr/datePickerHeaderDayOfWeekLabelTextAppearance"
+          android:importantForAccessibility="no"
+          android:textAllCaps="true"
+          />
diff --git a/core/res/res/layout/date_picker_holo.xml b/core/res/res/layout/date_picker_holo.xml
index b465d97..389c2b5 100644
--- a/core/res/res/layout/date_picker_holo.xml
+++ b/core/res/res/layout/date_picker_holo.xml
@@ -1,92 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
+     Copyright (C) 2011 The Android Open Source Project
 
-<!-- Layout of date picker-->
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
 
-<!-- Warning: everything within the "pickers" layout is removed and re-ordered
-     depending on the date format selected by the user.
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal"
-    android:orientation="horizontal"
-    android:gravity="center">
+              android:layout_width="@dimen/datepicker_component_width"
+              android:layout_height="match_parent"
+              android:gravity="center"
+              android:orientation="vertical" >
 
-    <LinearLayout android:id="@+id/pickers"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="horizontal"
-        android:gravity="center">
-
-        <!-- Month -->
-        <NumberPicker
-            android:id="@+id/month"
+    <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginTop="16dip"
-            android:layout_marginBottom="16dip"
-            android:layout_marginStart="8dip"
-            android:layout_marginEnd="8dip"
-            android:focusable="true"
-            android:focusableInTouchMode="true"
-            />
+            android:orientation="vertical" >
 
-        <!-- Day -->
-        <NumberPicker
-            android:id="@+id/day"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dip"
-            android:layout_marginBottom="16dip"
-            android:layout_marginStart="8dip"
-            android:layout_marginEnd="8dip"
-            android:focusable="true"
-            android:focusableInTouchMode="true"
-            />
+        <include layout="@layout/date_picker_header_view" />
 
-        <!-- Year -->
-        <NumberPicker
-            android:id="@+id/year"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dip"
-            android:layout_marginBottom="16dip"
-            android:layout_marginStart="8dip"
-            android:layout_marginEnd="16dip"
-            android:focusable="true"
-            android:focusableInTouchMode="true"
-            />
-
+        <include layout="@layout/date_picker_selected_date" />
     </LinearLayout>
 
-    <!-- calendar view -->
-    <CalendarView
-        android:id="@+id/calendar_view"
-        android:layout_width="245dip"
-        android:layout_height="280dip"
-        android:layout_marginStart="16dip"
-        android:layout_marginEnd="16dip"
-        android:layout_weight="1"
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        />
+    <include layout="@layout/date_picker_view_animator" />
 
-</LinearLayout>
+    <include layout="@layout/date_picker_done_button" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/date_picker.xml b/core/res/res/layout/date_picker_legacy.xml
similarity index 100%
rename from core/res/res/layout/date_picker.xml
rename to core/res/res/layout/date_picker_legacy.xml
diff --git a/core/res/res/layout/date_picker_legacy_holo.xml b/core/res/res/layout/date_picker_legacy_holo.xml
new file mode 100644
index 0000000..b465d97
--- /dev/null
+++ b/core/res/res/layout/date_picker_legacy_holo.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Layout of date picker-->
+
+<!-- Warning: everything within the "pickers" layout is removed and re-ordered
+     depending on the date format selected by the user.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:orientation="horizontal"
+    android:gravity="center">
+
+    <LinearLayout android:id="@+id/pickers"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center">
+
+        <!-- Month -->
+        <NumberPicker
+            android:id="@+id/month"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dip"
+            android:layout_marginBottom="16dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginEnd="8dip"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            />
+
+        <!-- Day -->
+        <NumberPicker
+            android:id="@+id/day"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dip"
+            android:layout_marginBottom="16dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginEnd="8dip"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            />
+
+        <!-- Year -->
+        <NumberPicker
+            android:id="@+id/year"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dip"
+            android:layout_marginBottom="16dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginEnd="16dip"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            />
+
+    </LinearLayout>
+
+    <!-- calendar view -->
+    <CalendarView
+        android:id="@+id/calendar_view"
+        android:layout_width="245dip"
+        android:layout_height="280dip"
+        android:layout_marginStart="16dip"
+        android:layout_marginEnd="16dip"
+        android:layout_weight="1"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        />
+
+</LinearLayout>
diff --git a/core/res/res/layout/date_picker_selected_date.xml b/core/res/res/layout/date_picker_selected_date.xml
new file mode 100644
index 0000000..426deed
--- /dev/null
+++ b/core/res/res/layout/date_picker_selected_date.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/day_picker_selector_layout"
+              android:layout_width="@dimen/datepicker_component_width"
+              android:layout_height="0dip"
+              android:layout_weight="1"
+              android:paddingTop="8dip"
+              android:paddingBottom="8dip"
+              android:background="?android:attr/datePickerHeaderSelectorBackgroundColor"
+              android:gravity="center"
+              android:orientation="vertical" >
+
+    <LinearLayout
+            android:id="@+id/date_picker_month_and_day_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:clickable="true"
+            android:orientation="vertical" >
+
+        <TextView
+                android:id="@+id/date_picker_month"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:duplicateParentState="true"
+                android:gravity="center_horizontal|bottom"
+                android:textAppearance="?android:attr/datePickerHeaderSelectorMonthLabelTextAppearance" />
+
+        <TextView
+                android:id="@+id/date_picker_day"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:layout_marginBottom="-10dip"
+                android:layout_marginTop="-10dip"
+                android:duplicateParentState="true"
+                android:gravity="center"
+                android:textAppearance="?android:attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance" />
+    </LinearLayout>
+
+    <TextView
+            android:id="@+id/date_picker_year"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center_horizontal|top"
+            android:textAppearance="?android:attr/datePickerHeaderSelectorYearLabelTextAppearance" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/date_picker_view_animator.xml b/core/res/res/layout/date_picker_view_animator.xml
new file mode 100644
index 0000000..9085ed5
--- /dev/null
+++ b/core/res/res/layout/date_picker_view_animator.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.internal.widget.AccessibleDateAnimator
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/animator"
+        android:layout_width="@dimen/datepicker_component_width"
+        android:layout_height="@dimen/datepicker_view_animator_height"
+        android:gravity="center" />
\ No newline at end of file
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index b3a3478..5acb588 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -35,7 +35,6 @@
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
         android:transitionName="android:action_bar"
-        android:touchscreenBlocksFocus="true"
         android:gravity="top">
         <com.android.internal.widget.ActionBarView
             android:id="@+id/action_bar"
@@ -54,6 +53,5 @@
                   android:layout_height="wrap_content"
                   style="?attr/actionBarSplitStyle"
                   android:visibility="gone"
-                  android:touchscreenBlocksFocus="true"
                   android:gravity="center"/>
 </com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml
index 039e89f..56815f8 100644
--- a/core/res/res/layout/screen_toolbar.xml
+++ b/core/res/res/layout/screen_toolbar.xml
@@ -35,7 +35,6 @@
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
         android:transitionName="android:action_bar"
-        android:touchscreenBlocksFocus="true"
         android:gravity="top">
         <Toolbar
             android:id="@+id/action_bar"
diff --git a/core/res/res/layout/year_label_text_view.xml b/core/res/res/layout/year_label_text_view.xml
new file mode 100644
index 0000000..4e39831
--- /dev/null
+++ b/core/res/res/layout/year_label_text_view.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<android.widget.TextViewWithCircularIndicator
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/month_text_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/datepicker_year_label_height"
+        android:layout_gravity="center"
+        android:gravity="center"
+        android:textAppearance="?android:attr/datePickerHeaderListYearLabelTextAppearance" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ed228e4..93d59b4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -691,6 +691,33 @@
         <!-- The DatePicker style. -->
         <attr name="datePickerStyle" format="reference" />
 
+        <!-- The DatePicker Header day of week label background color . -->
+        <attr name="datePickerHeaderDayOfWeekLabelBackgroundColor" format="reference" />
+
+        <!-- The DatePicker Header day of week label text appearance -->
+        <attr name="datePickerHeaderDayOfWeekLabelTextAppearance" format="reference" />
+
+        <!-- The DatePicker Header selector background color . -->
+        <attr name="datePickerHeaderSelectorBackgroundColor" format="reference" />
+
+        <!-- The DatePicker Header selector month label text appearance -->
+        <attr name="datePickerHeaderSelectorMonthLabelTextAppearance" format="reference" />
+
+        <!-- The DatePicker Header selector day of month label text appearance -->
+        <attr name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance" format="reference" />
+
+        <!-- The DatePicker Header selector year label text appearance -->
+        <attr name="datePickerHeaderSelectorYearLabelTextAppearance" format="reference" />
+
+        <!-- The DatePicker Header list year label text appearance -->
+        <attr name="datePickerHeaderListYearLabelTextAppearance" format="reference" />
+
+        <!-- DatePicker list year label circle background color -->
+        <attr name="datePickerHeaderListYearLabelCircleBackgroundColor" format="reference" />
+
+        <!-- The DatePicker dialog theme. -->
+        <attr name="datePickerDialogTheme" format="reference" />
+
         <!-- Default ActivityChooserView style. -->
         <attr name="activityChooserViewStyle" format="reference" />
 
@@ -1892,6 +1919,14 @@
         <!--  When opening an activity in a new task, this is the animation that is
               run on the activity of the old task (which is exiting the screen). -->
         <attr name="taskOpenExitAnimation" format="reference" />
+        <!--  When opening an activity in a new task using Intent/FLAG_ACTIVITY_LAUNCH_BEHIND,
+              this is the animation that is run on the activity of the new task (which is
+              entering the screen and then leaving). -->
+        <attr name="launchTaskBehindBackgroundAnimation" format="reference" />
+        <!--  When opening an activity in a new task using Intent.FLAG_ACTIVITY_LAUNCH_BEHIND,
+              this is the animation that is run on the activity of the old task (which is
+              already on the screen and then stays on). -->
+        <attr name="launchTaskBehindSourceAnimation" format="reference" />
         <!--  When closing the last activity of a task, this is the animation that is
               run on the activity of the next task (which is entering the screen). -->
         <attr name="taskCloseEnterAnimation" format="reference" />
@@ -4175,6 +4210,28 @@
         <attr name="maxDate" format="string" />
         <!-- @hide The layout of the date picker. -->
         <attr name="internalLayout" format="reference"  />
+        <!-- @hide The layout of the legacy DatePicker. -->
+        <attr name="legacyLayout" />
+        <!-- @hide Enables or disable the use of the legacy layout for the DatePicker. -->
+        <attr name="legacyMode" />
+        <!-- The background color for the date selector 's day of week of the non legacy DatePicker. -->
+        <attr name="dateSelectorDayOfWeekBackgroundColor" format="color|reference" />
+        <!-- The text color for the date selector's day of week of the non legacy DatePicker. -->
+        <attr name="dateSelectorDayOfWeekTextAppearance" format="reference" />
+        <!-- The background color for the date selector of the non legacy DatePicker. -->
+        <attr name="dateSelectorBackgroundColor" format="color|reference" />
+        <!-- The month's text appearance in the date selector of the non legacy DatePicker. -->
+        <attr name="dateSelectorMonthTextAppearance" format="reference" />
+        <!-- The day of month's text appearance in the date selector of the non legacy DatePicker. -->
+        <attr name="dateSelectorDayOfMonthTextAppearance" format="reference" />
+        <!-- The year's text appearance in the date selector of the non legacy DatePicker. -->
+        <attr name="dateSelectorYearTextAppearance" format="reference" />
+        <!-- The list year's text appearance in the list of the non legacy DatePicker. -->
+        <attr name="dateSelectorYearListItemTextAppearance" format="reference" />
+        <!-- The list year's selected circle color in the list of the non legacy DatePicker. -->
+        <attr name="dateSelectorYearListSelectedCircleColor" format="color|reference" />
+        <!-- The text color list of the calendar of the non legacy DatePicker. -->
+        <attr name="calendarTextColor" format="color|reference" />
     </declare-styleable>
 
     <declare-styleable name="TwoLineListItem">
@@ -7161,4 +7218,21 @@
         <attr name="ambientShadowAlpha" format="float" />
         <attr name="spotShadowAlpha" format="float" />
     </declare-styleable>
+
+    <declare-styleable name="RestrictionEntry">
+        <attr name="key" />
+        <attr name="restrictionType">
+            <enum name="hidden" value="0" />
+            <enum name="bool" value="1" />
+            <enum name="choice" value="2" />
+            <enum name="multi-select" value="4" />
+            <enum name="integer" value="5" />
+            <enum name="string" value="6" />
+        </attr>
+        <attr name="title" />
+        <attr name="description" />
+        <attr name="defaultValue" />
+        <attr name="entries" />
+        <attr name="entryValues" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index 97b4803..8785a567 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -120,4 +120,32 @@
 
     <color name="timepicker_default_ampm_unselected_background_color_holo_light">@color/white</color>
     <color name="timepicker_default_ampm_unselected_background_color_holo_dark">@color/transparent</color>
+
+    <!-- DatePicker colors -->
+    <eat-comment />
+
+    <color name="datepicker_default_header_selector_background_holo_light">@android:color/white</color>
+    <color name="datepicker_default_header_selector_background_holo_dark">#ff303030</color>
+
+    <color name="datepicker_default_header_dayofweek_background_color_holo_light">#999999</color>
+    <color name="datepicker_default_header_dayofweek_background_color_holo_dark">@android:color/white</color>
+
+    <color name="datepicker_default_normal_text_color_holo_light">#ff999999</color>
+    <color name="datepicker_default_normal_text_color_holo_dark">@android:color/white</color>
+
+    <color name="datepicker_default_disabled_text_color_holo_light">#80999999</color>
+    <color name="datepicker_default_disabled_text_color_holo_dark">#80999999</color>
+
+    <color name="datepicker_default_selected_text_color_holo_light">#33b5e5</color>
+    <color name="datepicker_default_selected_text_color_holo_dark">#33b5e5</color>
+
+    <color name="datepicker_default_pressed_text_color_holo_light">#0099cc</color>
+    <color name="datepicker_default_pressed_text_color_holo_dark">#0099cc</color>
+
+    <color name="datepicker_default_circle_background_color_holo_light">@android:color/holo_blue_light</color>
+    <color name="datepicker_default_circle_background_color_holo_dark">@android:color/holo_blue_light</color>
+
+    <color name="datepicker_default_view_animator_color_holo_light">#f2f2f2</color>
+    <color name="datepicker_default_view_animator_color_holo_dark">#ff303030</color>
+
 </resources>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 7371d4e..c4f4891 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -166,4 +166,32 @@
     <color name="timepicker_default_ampm_unselected_background_color_material">@color/transparent</color>
     <color name="timepicker_default_selector_color_material">@color/material_light_blue_A200</color>
     <color name="timepicker_default_numbers_background_color_material">@color/transparent</color>
+
+    <!-- DatePicker colors -->
+    <eat-comment />
+
+    <color name="datepicker_default_header_selector_background_material_light">@android:color/white</color>
+    <color name="datepicker_default_header_selector_background_material_dark">#ff303030</color>
+
+    <color name="datepicker_default_header_dayofweek_background_color_material_light">#999999</color>
+    <color name="datepicker_default_header_dayofweek_background_color_material_dark">@android:color/white</color>
+
+    <color name="datepicker_default_normal_text_color_material_light">#ff999999</color>
+    <color name="datepicker_default_normal_text_color_material_dark">@android:color/white</color>
+
+    <color name="datepicker_default_disabled_text_color_material_light">#80999999</color>
+    <color name="datepicker_default_disabled_text_color_material_dark">#80999999</color>
+
+    <color name="datepicker_default_selected_text_color_material_light">#33b5e5</color>
+    <color name="datepicker_default_selected_text_color_material_dark">#33b5e5</color>
+
+    <color name="datepicker_default_pressed_text_color_material_light">#0099cc</color>
+    <color name="datepicker_default_pressed_text_color_material_dark">#0099cc</color>
+
+    <color name="datepicker_default_circle_background_color_material_light">@android:color/holo_blue_light</color>
+    <color name="datepicker_default_circle_background_color_material_dark">@android:color/holo_blue_light</color>
+
+    <color name="datepicker_default_view_animator_color_material_light">#f2f2f2</color>
+    <color name="datepicker_default_view_animator_color_material_dark">#ff303030</color>
+
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c9c29ca..0954ddf 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -564,6 +564,12 @@
         <item>5</item>
     </integer-array>
 
+    <!-- Vibrator pattern for feedback when selecting a day/month/year date of a Calendar -->
+    <integer-array name="config_calendarDateVibePattern">
+        <item>125</item>
+        <item>5</item>
+    </integer-array>
+
     <!-- Vibrator pattern for feedback about booting with safe mode disabled -->
     <integer-array name="config_safeModeDisabledVibePattern">
         <item>0</item>
@@ -1513,6 +1519,7 @@
      See {@link com.android.server.notification.NotificationSignalExtractor} -->
     <string-array name="config_notificationSignalExtractors">
         <item>com.android.server.notification.ValidateNotificationPeople</item>
+        <item>com.android.server.notification.PackagePriorityExtractor</item>
         <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
     </string-array>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index ad6c6cd..77b115b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -362,6 +362,24 @@
     <dimen name="timepicker_minimum_margin_top_bottom">24dip</dimen>
     <dimen name="timepicker_radial_picker_dimen">270dip</dimen>
 
+    <dimen name="datepicker_done_label_size">14sp</dimen>
+    <dimen name="datepicker_day_number_size">16sp</dimen>
+    <dimen name="datepicker_month_label_size">16sp</dimen>
+    <dimen name="datepicker_month_day_label_text_size">10sp</dimen>
+    <dimen name="datepicker_day_number_select_circle_radius">16dp</dimen>
+    <dimen name="datepicker_month_list_item_header_height">50dp</dimen>
+    <dimen name="datepicker_view_animator_height">270dp</dimen>
+    <dimen name="datepicker_year_picker_padding_top">8dp</dimen>
+    <dimen name="datepicker_year_label_height">64dp</dimen>
+    <dimen name="datepicker_year_label_text_size">22dp</dimen>
+    <dimen name="datepicker_component_width">270dp</dimen>
+    <dimen name="datepicker_selected_calendar_layout_height">155dp</dimen>
+    <dimen name="datepicker_selected_date_day_size">75dp</dimen>
+    <dimen name="datepicker_selected_date_month_size">30dp</dimen>
+    <dimen name="datepicker_selected_date_year_size">30dp</dimen>
+    <dimen name="datepicker_header_height">30dp</dimen>
+    <dimen name="datepicker_header_text_size">14dp</dimen>
+
     <!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
     <dimen name="immersive_mode_cling_width">-1px</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fee5c43..7b9a38f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2236,6 +2236,20 @@
   <public type="attr" name="multiArch" />
   <public type="attr" name="touchscreenBlocksFocus" />
   <public type="attr" name="windowElevation" />
+  <public type="attr" name="launchTaskBehindBackgroundAnimation" />
+  <public type="attr" name="launchTaskBehindSourceAnimation" />
+  <!-- Attribute specified in a restriction entry to denote the type of restriction. -->
+  <public type="attr" name="restrictionType" />
+
+  <public type="attr" name="dateSelectorDayOfWeekBackgroundColor" />
+  <public type="attr" name="dateSelectorDayOfWeekTextAppearance" />
+  <public type="attr" name="dateSelectorBackgroundColor" />
+  <public type="attr" name="dateSelectorMonthTextAppearance" />
+  <public type="attr" name="dateSelectorDayOfMonthTextAppearance" />
+  <public type="attr" name="dateSelectorYearTextAppearance" />
+  <public type="attr" name="dateSelectorYearListItemTextAppearance" />
+  <public type="attr" name="dateSelectorYearListSelectedCircleColor" />
+  <public type="attr" name="calendarTextColor" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
@@ -2483,6 +2497,8 @@
 
   <public-padding type="interpolator" name="l_resource_pad" end="0x010c0010" />
 
+  <public type="style" name="Theme.Leanback.FormWizard"/>
+
   <!-- An interpolator which accelerates fast but decelerates slowly. -->
   <public type="interpolator" name="fast_out_slow_in" />
   <!-- An interpolator which starts with a peak non-zero velocity and decelerates slowly. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 32065a18..94ac19c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -82,6 +82,8 @@
         <item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
         <item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
         <item name="taskOpenExitAnimation">@anim/task_open_exit</item>
+        <item name="launchTaskBehindBackgroundAnimation">@anim/launch_task_behind_background</item>
+        <item name="launchTaskBehindSourceAnimation">@anim/launch_task_behind_source</item>
         <item name="taskCloseEnterAnimation">@anim/task_close_enter</item>
         <item name="taskCloseExitAnimation">@anim/task_close_exit</item>
         <item name="taskToFrontEnterAnimation">@anim/task_open_enter</item>
@@ -570,7 +572,7 @@
     </style>
 
     <style name="Widget.DatePicker">
-        <item name="internalLayout">@layout/date_picker</item>
+        <item name="legacyLayout">@android:layout/date_picker_legacy</item>
         <item name="calendarViewShown">false</item>
     </style>
 
@@ -1200,7 +1202,6 @@
         <item name="navigationButtonStyle">@style/Widget.Toolbar.Button.Navigation</item>
         <item name="collapseIcon">?attr/homeAsUpIndicator</item>
         <item name="contentInsetStart">16dp</item>
-        <item name="touchscreenBlocksFocus">true</item>
     </style>
 
     <style name="Widget.Toolbar.Button.Navigation" parent="Widget">
@@ -1336,11 +1337,6 @@
     <style name="TextAppearance.TimePicker.AmPmLabel" parent="TextAppearance">
     </style>
 
-    <style name="TextAppearance.Holo.TimePicker.TimeLabel" parent="TextAppearance.Holo">
-        <item name="textSize">@dimen/timepicker_time_label_size</item>
-        <item name="textColor">@color/timepicker_default_text_color_holo_dark</item>
-    </style>
-
     <style name="Widget.FastScroll">
         <item name="thumbDrawable">?attr/fastScrollThumbDrawable</item>
         <item name="trackDrawable">?attr/fastScrollTrackDrawable</item>
@@ -1364,4 +1360,22 @@
         <item name="spotShadowAlpha">0.1765</item>
     </style>
 
+    <style name="TextAppearance.DatePicker.DayOfWeekLabel" parent="TextAppearance">
+    </style>
+
+    <style name="TextAppearance.DatePicker.Selector" parent="TextAppearance">
+    </style>
+
+    <style name="TextAppearance.DatePicker.Selector.MonthLabel" parent="TextAppearance.DatePicker.Selector">
+    </style>
+
+    <style name="TextAppearance.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.DatePicker.Selector">
+    </style>
+
+    <style name="TextAppearance.DatePicker.Selector.YearLabel" parent="TextAppearance.DatePicker.Selector">
+    </style>
+
+    <style name="TextAppearance.DatePicker.List.YearLabel" parent="TextAppearance">
+    </style>
+
 </resources>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 84d38ce..9b5629b 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -280,4 +280,29 @@
     <style name="DeviceDefault.Light.ButtonBar" parent="Widget.Material.Light.ButtonBar"/>
     <style name="DeviceDefault.Light.ButtonBar.AlertDialog" parent="Widget.Material.Light.ButtonBar.AlertDialog"/>
     <style name="DeviceDefault.Light.SegmentedButton" parent="Widget.Material.Light.SegmentedButton"/>
+
+    <style name="TextAppearance.DeviceDefault.Light.TimePicker.TimeLabel" parent="TextAppearance.Material.TimePicker.TimeLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.TimePicker.AmPmLabel" parent="TextAppearance.Material.TimePicker.AmPmLabel"/>
+
+    <style name="Theme.DeviceDefault.Dialog.TimePicker" parent="Theme.Material.Dialog.TimePicker"/>
+    <style name="Theme.DeviceDefault.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.TimePicker"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material.DatePicker.DayOfWeekLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material.Light.DatePicker.DayOfWeekLabel"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.Selector" parent="TextAppearance.Material.DatePicker.Selector"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.Selector" parent="TextAppearance.Material.Light.DatePicker.Selector"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.Selector.MonthLabel" parent="TextAppearance.Material.DatePicker.Selector.MonthLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.Selector.MonthLabel" parent="TextAppearance.Material.Light.DatePicker.Selector.MonthLabel"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Material.DatePicker.Selector.DayOfMonthLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Material.Light.DatePicker.Selector.DayOfMonthLabel"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.Selector.YearLabel" parent="TextAppearance.Material.DatePicker.Selector.YearLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.Selector.YearLabel" parent="TextAppearance.Material.Light.DatePicker.Selector.YearLabel"/>
+
+    <style name="Theme.DeviceDefault.Dialog.DatePicker" parent="Theme.Material.Dialog.DatePicker"/>
+    <style name="Theme.DeviceDefault.Light.Dialog.DatePicker" parent="Theme.Material.Light.Dialog.DatePicker"/>
+
 </resources>
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index 327d6b5..5dfbaed 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -477,8 +477,18 @@
     </style>
 
     <style name="Widget.Holo.DatePicker" parent="Widget.DatePicker">
+        <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <item name="internalLayout">@layout/date_picker_holo</item>
         <item name="calendarViewShown">true</item>
+        <item name="dateSelectorDayOfWeekBackgroundColor">?attr/datePickerHeaderDayOfWeekLabelBackgroundColor</item>
+        <item name="dateSelectorDayOfWeekTextAppearance">?attr/datePickerHeaderDayOfWeekLabelTextAppearance</item>
+        <item name="dateSelectorBackgroundColor">?attr/datePickerHeaderSelectorBackgroundColor</item>
+        <item name="dateSelectorMonthTextAppearance">?attr/datePickerHeaderSelectorMonthLabelTextAppearance</item>
+        <item name="dateSelectorDayOfMonthTextAppearance">?attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance</item>
+        <item name="dateSelectorYearTextAppearance">?attr/datePickerHeaderSelectorYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListItemTextAppearance">?attr/datePickerHeaderListYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListSelectedCircleColor">?attr/datePickerHeaderListYearLabelCircleBackgroundColor</item>
+        <item name="calendarTextColor">@color/date_picker_calendar_holo_dark</item>
     </style>
 
     <style name="Widget.Holo.ActivityChooserView" parent="Widget.ActivityChooserView" />
@@ -884,7 +894,20 @@
         <item name="numbersSelectorColor">@color/holo_blue_light</item>
     </style>
 
-    <style name="Widget.Holo.Light.DatePicker" parent="Widget.Holo.DatePicker" />
+    <style name="Widget.Holo.Light.DatePicker" parent="Widget.DatePicker">
+        <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
+        <item name="internalLayout">@layout/date_picker_holo</item>
+        <item name="calendarViewShown">true</item>
+        <item name="dateSelectorDayOfWeekBackgroundColor">?attr/datePickerHeaderDayOfWeekLabelBackgroundColor</item>
+        <item name="dateSelectorDayOfWeekTextAppearance">?attr/datePickerHeaderDayOfWeekLabelTextAppearance</item>
+        <item name="dateSelectorBackgroundColor">?attr/datePickerHeaderSelectorBackgroundColor</item>
+        <item name="dateSelectorMonthTextAppearance">?attr/datePickerHeaderSelectorMonthLabelTextAppearance</item>
+        <item name="dateSelectorDayOfMonthTextAppearance">?attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance</item>
+        <item name="dateSelectorYearTextAppearance">?attr/datePickerHeaderSelectorYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListItemTextAppearance">?attr/datePickerHeaderListYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListSelectedCircleColor">?attr/datePickerHeaderListYearLabelCircleBackgroundColor</item>
+        <item name="calendarTextColor">@color/date_picker_calendar_holo_light</item>
+    </style>
 
     <style name="Widget.Holo.Light.ActivityChooserView" parent="Widget.Holo.ActivityChooserView">
         <item name="background">@drawable/ab_share_pack_holo_light</item>
@@ -1183,6 +1206,11 @@
         <item name="externalRouteEnabledDrawable">@drawable/ic_media_route_holo_light</item>
     </style>
 
+    <style name="TextAppearance.Holo.TimePicker.TimeLabel" parent="TextAppearance.Holo">
+        <item name="textSize">@dimen/timepicker_time_label_size</item>
+        <item name="textColor">@color/timepicker_default_text_color_holo_dark</item>
+    </style>
+
     <style name="TextAppearance.Holo.TimePicker.AmPmLabel" parent="TextAppearance.Holo">
         <item name="textSize">@dimen/timepicker_ampm_label_size</item>
         <item name="textAllCaps">true</item>
@@ -1202,6 +1230,62 @@
         <item name="textStyle">bold</item>
     </style>
 
+    <style name="TextAppearance.Holo.DatePicker.DayOfWeekLabel" parent="TextAppearance.Holo">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/black</item>
+        <item name="textSize">@dimen/datepicker_header_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.Selector" parent="TextAppearance.Holo">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/date_picker_selector_holo_dark</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.Selector.MonthLabel" parent="TextAppearance.Holo.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Holo.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.Selector.YearLabel" parent="TextAppearance.Holo.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.List.YearLabel" parent="TextAppearance.Holo">
+        <item name="textColor">@color/date_picker_year_selector_holo_dark</item>
+        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.DayOfWeekLabel" parent="TextAppearance.Holo">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/white</item>
+        <item name="textSize">@dimen/datepicker_header_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.Selector" parent="TextAppearance.Holo">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/date_picker_selector_holo_light</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.Selector.MonthLabel" parent="TextAppearance.Holo.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Holo.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.Selector.YearLabel" parent="TextAppearance.Holo.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.List.YearLabel" parent="TextAppearance.Holo">
+        <item name="textColor">@color/date_picker_year_selector_holo_light</item>
+        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+    </style>
+
     <style name="Widget.Holo.FastScroll" parent="Widget.FastScroll">
         <item name="thumbMinWidth">0dp</item>
         <item name="thumbMinHeight">0dp</item>
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index 6f9a88e..5531433 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -30,4 +30,30 @@
         <item name="hideWheelUntilFocused">true</item>
     </style>
 
+    <!-- Setup and form wizard themes -->
+    <style name="TextAppearance.Leanback.FormWizard" parent="@style/TextAppearance.Material">
+        <item name="textSize">18sp</item>
+        <item name="fontFamily">sans-serif-light</item>
+    </style>
+
+    <style name="TextAppearance.Leanback.FormWizard.Small" parent="@style/TextAppearance.Material.Small">
+        <item name="textSize">18sp</item>
+        <item name="fontFamily">sans-serif-light</item>
+    </style>
+
+    <style name="TextAppearance.Leanback.FormWizard.Medium" parent="@style/TextAppearance.Material.Medium">
+        <item name="textSize">36sp</item>
+        <item name="fontFamily">sans-serif-thin</item>
+    </style>
+
+    <style name="TextAppearance.Leanback.FormWizard.Large" parent="@style/TextAppearance.Material.Large">
+        <item name="textSize">56sp</item>
+        <item name="fontFamily">sans-serif-thin</item>
+    </style>
+
+    <style name="TextAppearance.Leanback.FormWizard.ListItem" parent="@style/TextAppearance.Material.Subhead">
+        <item name="textSize">18sp</item>
+        <item name="fontFamily">sans-serif-condensed</item>
+    </style>
+
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 4623258..1d07c8d 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -353,6 +353,62 @@
         <item name="textStyle">bold</item>
     </style>
 
+    <style name="TextAppearance.Material.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/black</item>
+        <item name="textSize">@dimen/datepicker_header_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.Selector" parent="TextAppearance.Material">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/date_picker_selector_material_dark</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.Selector.MonthLabel" parent="TextAppearance.Material.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Material.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.Selector.YearLabel" parent="TextAppearance.Material.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.List.YearLabel" parent="TextAppearance.Material">
+        <item name="textColor">@color/date_picker_year_selector_material_dark</item>
+        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/white</item>
+        <item name="textSize">@dimen/datepicker_header_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.Selector" parent="TextAppearance.Material">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/date_picker_selector_material_light</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.Selector.MonthLabel" parent="TextAppearance.Material.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Material.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.Selector.YearLabel" parent="TextAppearance.Material.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.List.YearLabel" parent="TextAppearance.Material">
+        <item name="textColor">@color/date_picker_year_selector_material_light</item>
+        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+    </style>
+
     <style name="TextAppearance.StatusBar.Material" />
 
     <style name="TextAppearance.StatusBar.Material.EventContent">
@@ -575,8 +631,18 @@
     </style>
 
     <style name="Widget.Material.DatePicker" parent="Widget.DatePicker">
+        <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <item name="internalLayout">@layout/date_picker_holo</item>
         <item name="calendarViewShown">true</item>
+        <item name="dateSelectorDayOfWeekBackgroundColor">?attr/datePickerHeaderDayOfWeekLabelBackgroundColor</item>
+        <item name="dateSelectorDayOfWeekTextAppearance">?attr/datePickerHeaderDayOfWeekLabelTextAppearance</item>
+        <item name="dateSelectorBackgroundColor">?attr/datePickerHeaderSelectorBackgroundColor</item>
+        <item name="dateSelectorMonthTextAppearance">?attr/datePickerHeaderSelectorMonthLabelTextAppearance</item>
+        <item name="dateSelectorDayOfMonthTextAppearance">?attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance</item>
+        <item name="dateSelectorYearTextAppearance">?attr/datePickerHeaderSelectorYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListItemTextAppearance">?attr/datePickerHeaderListYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListSelectedCircleColor">?attr/datePickerHeaderListYearLabelCircleBackgroundColor</item>
+        <item name="calendarTextColor">@color/date_picker_calendar_holo_dark</item>
     </style>
 
     <style name="Widget.Material.ActivityChooserView" parent="Widget.ActivityChooserView">
@@ -897,7 +963,21 @@
         <item name="disabledColor">@color/bright_foreground_disabled_material_light</item>
     </style>
 
-    <style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker"/>
+    <style name="Widget.Material.Light.DatePicker" parent="Widget.DatePicker">
+        <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
+        <item name="internalLayout">@layout/date_picker_holo</item>
+        <item name="calendarViewShown">true</item>
+        <item name="dateSelectorDayOfWeekBackgroundColor">?attr/datePickerHeaderDayOfWeekLabelBackgroundColor</item>
+        <item name="dateSelectorDayOfWeekTextAppearance">?attr/datePickerHeaderDayOfWeekLabelTextAppearance</item>
+        <item name="dateSelectorBackgroundColor">?attr/datePickerHeaderSelectorBackgroundColor</item>
+        <item name="dateSelectorMonthTextAppearance">?attr/datePickerHeaderSelectorMonthLabelTextAppearance</item>
+        <item name="dateSelectorDayOfMonthTextAppearance">?attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance</item>
+        <item name="dateSelectorYearTextAppearance">?attr/datePickerHeaderSelectorYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListItemTextAppearance">?attr/datePickerHeaderListYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListSelectedCircleColor">?attr/datePickerHeaderListYearLabelCircleBackgroundColor</item>
+        <item name="calendarTextColor">@color/date_picker_calendar_holo_light</item>
+    </style>
+
     <style name="Widget.Material.Light.ActivityChooserView" parent="Widget.Material.ActivityChooserView" />
     <style name="Widget.Material.Light.ImageWell" parent="Widget.Material.ImageWell"/>
     <style name="Widget.Material.Light.ListView" parent="Widget.Material.ListView"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 29829e8..4b4e633 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1182,7 +1182,7 @@
   <java-symbol type="layout" name="calendar_view" />
   <java-symbol type="layout" name="character_picker" />
   <java-symbol type="layout" name="character_picker_button" />
-  <java-symbol type="layout" name="date_picker" />
+  <java-symbol type="layout" name="date_picker_legacy" />
   <java-symbol type="layout" name="date_picker_dialog" />
   <java-symbol type="layout" name="expanded_menu_layout" />
   <java-symbol type="layout" name="fragment_bread_crumb_item" />
@@ -1840,9 +1840,10 @@
   <java-symbol type="dimen" name="subtitle_shadow_offset" />
   <java-symbol type="dimen" name="subtitle_outline_width" />
 
-  <!-- From the new TimePicker -->
-  <java-symbol type="attr" name="timePickerHeaderBackgroundColor" />
+  <!-- From the new TimePicker and DatePicker -->
   <java-symbol type="attr" name="timePickerDialogTheme" />
+  <java-symbol type="attr" name="timePickerHeaderTimeLabelTextAppearance" />
+  <java-symbol type="attr" name="timePickerHeaderBackgroundColor" />
   <java-symbol type="attr" name="headerSelectedTextColor" />
   <java-symbol type="attr" name="headerUnselectedTextColor" />
   <java-symbol type="attr" name="numbersTextColor" />
@@ -1853,9 +1854,18 @@
   <java-symbol type="attr" name="numbersSelectorColor" />
   <java-symbol type="attr" name="timePickerHeaderTimeLabelTextAppearance" />
   <java-symbol type="attr" name="nestedScrollingEnabled" />
+  <java-symbol type="attr" name="datePickerDialogTheme" />
+  <java-symbol type="attr" name="datePickerHeaderSelectorBackgroundColor" />
+  <java-symbol type="attr" name="datePickerHeaderListYearLabelCircleBackgroundColor" />
+  <java-symbol type="attr" name="calendarTextColor" />
+
   <java-symbol type="style" name="TextAppearance.Holo.TimePicker.TimeLabel" />
+
   <java-symbol type="layout" name="time_picker_holo" />
   <java-symbol type="layout" name="time_header_label" />
+  <java-symbol type="layout" name="year_label_text_view" />
+  <java-symbol type="layout" name="date_picker_holo" />
+
   <java-symbol type="id" name="time_header" />
   <java-symbol type="id" name="hours" />
   <java-symbol type="id" name="minutes" />
@@ -1864,6 +1874,15 @@
   <java-symbol type="id" name="separator" />
   <java-symbol type="id" name="layout_buttons" />
   <java-symbol type="id" name="done_button" />
+  <java-symbol type="id" name="date_picker_header" />
+  <java-symbol type="id" name="date_picker_month_and_day_layout" />
+  <java-symbol type="id" name="day_picker_selector_layout" />
+  <java-symbol type="id" name="date_picker_month" />
+  <java-symbol type="id" name="date_picker_day" />
+  <java-symbol type="id" name="date_picker_year" />
+  <java-symbol type="id" name="animator" />
+  <java-symbol type="id" name="done" />
+
   <java-symbol type="string" name="done_label" />
   <java-symbol type="string" name="hour_picker_description" />
   <java-symbol type="string" name="minute_picker_description" />
@@ -1885,7 +1904,42 @@
   <java-symbol type="string" name="timepicker_numbers_radius_multiplier_normal" />
   <java-symbol type="string" name="timepicker_transition_mid_radius_multiplier" />
   <java-symbol type="string" name="timepicker_transition_end_radius_multiplier" />
+
+  <java-symbol type="string" name="item_is_selected" />
+  <java-symbol type="string" name="day_of_week_label_typeface" />
+  <java-symbol type="string" name="select_day" />
+  <java-symbol type="string" name="day_picker_description" />
+  <java-symbol type="string" name="select_year" />
+  <java-symbol type="string" name="year_picker_description" />
+
+  <java-symbol type="dimen" name="datepicker_day_number_size" />
+  <java-symbol type="dimen" name="datepicker_month_label_size" />
+  <java-symbol type="dimen" name="datepicker_month_day_label_text_size" />
+  <java-symbol type="dimen" name="datepicker_month_list_item_header_height" />
+  <java-symbol type="dimen" name="datepicker_day_number_select_circle_radius" />
+  <java-symbol type="dimen" name="datepicker_view_animator_height" />
+  <java-symbol type="dimen" name="datepicker_year_label_height" />
+  <java-symbol type="dimen" name="datepicker_year_picker_padding_top" />
+
+  <java-symbol type="color" name="timepicker_default_text_color_holo_light" />
+  <java-symbol type="color" name="timepicker_default_disabled_color_holo_light" />
+  <java-symbol type="color" name="timepicker_default_ampm_unselected_background_color_holo_light" />
+  <java-symbol type="color" name="timepicker_default_ampm_selected_background_color_holo_light" />
+
+  <java-symbol type="color" name="datepicker_default_normal_text_color_holo_light" />
+  <java-symbol type="color" name="datepicker_default_disabled_text_color_holo_light" />
+  <java-symbol type="color" name="datepicker_default_circle_background_color_holo_light" />
+  <java-symbol type="color" name="datepicker_default_header_dayofweek_background_color_holo_light" />
+  <java-symbol type="color" name="datepicker_default_header_selector_background_holo_light" />
+
+  <java-symbol type="color" name="datepicker_default_normal_text_color_material_light" />
+  <java-symbol type="color" name="datepicker_default_disabled_text_color_material_light" />
+  <java-symbol type="color" name="datepicker_default_circle_background_color_material_light" />
+  <java-symbol type="color" name="datepicker_default_header_dayofweek_background_color_material_light" />
+  <java-symbol type="color" name="datepicker_default_header_selector_background_material_light" />
+
   <java-symbol type="array" name="config_clockTickVibePattern" />
+  <java-symbol type="array" name="config_calendarDateVibePattern" />
 
   <!-- From various Material changes -->
   <java-symbol type="attr" name="toolbarStyle" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index a519c37..0438ed1 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -409,7 +409,34 @@
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.DatePicker</item>
 
-        <item name="fastScrollThumbDrawable">@drawable/scrollbar_handle_accelerated_anim2</item>
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/darker_gray</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/darker_gray</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/darker_gray</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Dialog.DatePicker</item>
+
+        <item name="fastScrollThumbDrawable">@android:drawable/scrollbar_handle_accelerated_anim2</item>
         <item name="fastScrollTrackDrawable">@null</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/menu_submenu_background</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/menu_submenu_background</item>
@@ -731,6 +758,14 @@
         <item name="windowContentOverlay">@null</item>
     </style>
 
+    <!-- Default heme for the DatePicker dialog windows, which is used by the
+         {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Dialog.DatePicker">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="windowTitleStyle">@android:style/DialogWindowTitle</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
     <!-- Default dark theme for panel windows (on API level 10 and lower).  This removes all
          extraneous window decorations, so you basically have an empty rectangle in which
          to place your content.  It makes the window floating, with a transparent
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index ee2c7df..2febbef 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -207,7 +207,23 @@
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.DeviceDefault.DatePicker</item>
 
+        <!-- The DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.DeviceDefault.DatePicker.DayOfWeekLabel</item>
+
+        <!-- The DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.DeviceDefault.DatePicker.Selector.MonthLabel</item>
+
+        <!-- The DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.DeviceDefault.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- The DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.DeviceDefault.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.DeviceDefault.Dialog.DatePicker</item>
+
         <item name="mediaRouteButtonStyle">@style/Widget.DeviceDefault.MediaRouteButton</item>
+
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
@@ -466,7 +482,23 @@
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.DeviceDefault.Light.DatePicker</item>
 
+        <!-- The DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.DeviceDefault.Light.DatePicker.DayOfWeekLabel</item>
+
+        <!-- The DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.DeviceDefault.Light.DatePicker.Selector.MonthLabel</item>
+
+        <!-- The DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.DeviceDefault.Light.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- The DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.DeviceDefault.Light.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.DeviceDefault.Light.Dialog.DatePicker</item>
+
         <item name="mediaRouteButtonStyle">@style/Widget.DeviceDefault.Light.MediaRouteButton</item>
+
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
diff --git a/core/res/res/values/themes_holo.xml b/core/res/res/values/themes_holo.xml
index 76bfc4b..dda42c1 100644
--- a/core/res/res/values/themes_holo.xml
+++ b/core/res/res/values/themes_holo.xml
@@ -389,6 +389,33 @@
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.Holo.DatePicker</item>
 
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/datepicker_default_header_dayofweek_background_color_holo_dark</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/datepicker_default_header_selector_background_holo_dark</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/datepicker_default_circle_background_color_holo_dark</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Holo.Dialog.DatePicker</item>
+
         <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_holo</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
@@ -728,6 +755,33 @@
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.Holo.Light.DatePicker</item>
 
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/datepicker_default_header_dayofweek_background_color_holo_light</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/datepicker_default_header_selector_background_holo_light</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/datepicker_default_circle_background_color_holo_light</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Holo.Light.Dialog.DatePicker</item>
+
         <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_holo</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
@@ -944,6 +998,14 @@
          {@link android.app.TimePickerDialog} class. -->
     <style name="Theme.Holo.Dialog.TimePicker" parent="Theme.Holo.Dialog.Alert" />
 
+    <!-- Holo theme for the DatePicker dialog windows, which is used by the
+            {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Holo.Dialog.DatePicker">
+        <item name="windowBackground">@color/transparent</item>
+        <item name="windowTitleStyle">@style/DialogWindowTitle.Holo</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
     <!-- Theme for a window that will be displayed either full-screen on
          smaller screens (small, normal) or as a dialog on larger screens
          (large, xlarge). -->
@@ -1059,6 +1121,14 @@
          {@link android.app.TimePickerDialog} class. -->
     <style name="Theme.Holo.Light.Dialog.TimePicker" parent="Theme.Holo.Light.Dialog.Alert" />
 
+    <!-- Holo Light theme for the DatePicker dialog windows, which is used by the
+           {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Holo.Light.Dialog.DatePicker">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="windowTitleStyle">@android:style/DialogWindowTitle.Holo.Light</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
     <!-- Theme for a presentation window on a secondary display. -->
     <style name="Theme.Holo.Light.Dialog.Presentation" parent="Theme.Holo.Light.NoActionBar.Fullscreen" />
 
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index 6f6385b..534e323 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -62,4 +62,16 @@
         <item name="windowCloseOnTouchOutside">false</item>
         <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
     </style>
+
+    <!-- Setup and form wizard themes @hide @SystemApi-->
+    <style name="Theme.Leanback.FormWizard" parent="Theme.Material.NoActionBar">
+        <item name="windowBackground">@drawable/background_leanback_setup</item>
+        <item name="colorBackgroundCacheHint">@null</item>
+        <item name="windowShowWallpaper">false</item>
+        <item name="textAppearanceSmall">@style/TextAppearance.Leanback.FormWizard.Small</item>
+        <item name="textAppearanceMedium">@style/TextAppearance.Leanback.FormWizard.Medium</item>
+        <item name="textAppearanceLarge">@style/TextAppearance.Leanback.FormWizard.Large</item>
+        <item name="textAppearanceListItem">@style/TextAppearance.Leanback.FormWizard.ListItem</item>
+        <item name="textAppearance">@style/TextAppearance.Leanback.FormWizard</item>
+    </style>
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index efc92d9..bdaeb45 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -367,6 +367,33 @@
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.Material.DatePicker</item>
 
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/datepicker_default_header_dayofweek_background_color_material_dark</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.Material.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/datepicker_default_header_selector_background_material_dark</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.Material.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.Material.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.Material.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/datepicker_default_circle_background_color_material_dark</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Material.Dialog.DatePicker</item>
+
         <!-- TODO: This belongs in a FastScroll style -->
         <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_material</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
@@ -715,6 +742,33 @@
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.Material.Light.DatePicker</item>
 
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/datepicker_default_header_dayofweek_background_color_material_light</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/datepicker_default_header_selector_background_material_light</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/datepicker_default_circle_background_color_material_light</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Material.Light.Dialog.DatePicker</item>
+
         <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_material</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
@@ -1110,6 +1164,16 @@
          {@link android.app.TimePickerDialog} class. -->
     <style name="Theme.Material.Dialog.TimePicker" parent="Theme.Material.Dialog.BaseTimePicker"/>
 
+    <style name="Theme.Material.Dialog.BaseDatePicker">
+        <item name="windowBackground">@color/transparent</item>
+        <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Material theme for the DatePicker dialog windows, which is used by the
+         {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Material.Dialog.DatePicker" parent="Theme.Material.Dialog.BaseDatePicker"/>
+
     <!-- Theme for a window that will be displayed either full-screen on
          smaller screens (small, normal) or as a dialog on larger screens
          (large, xlarge). -->
@@ -1222,12 +1286,23 @@
         <item name="windowBackground">@color/transparent</item>
         <item name="windowElevation">0dp</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
+        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- Material Light theme for the TimePicker dialog windows, which is used by the
          {@link android.app.TimePickerDialog} class. -->
     <style name="Theme.Material.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker"/>
 
+    <style name="Theme.Material.Light.Dialog.BaseDatePicker">
+        <item name="windowBackground">@color/transparent</item>
+        <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Material Light theme for the DatePicker dialog windows, which is used by the
+         {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Material.Light.Dialog.DatePicker" parent="Theme.Material.Light.Dialog.BaseDatePicker"/>
+
     <!-- Theme for a presentation window on a secondary display. -->
     <style name="Theme.Material.Light.Dialog.Presentation" parent="@style/Theme.Material.Light.NoActionBar.Fullscreen" />
 
diff --git a/docs/html/distribute/googleplay/edu/about.jd b/docs/html/distribute/googleplay/edu/about.jd
index 60d9402..469b899 100644
--- a/docs/html/distribute/googleplay/edu/about.jd
+++ b/docs/html/distribute/googleplay/edu/about.jd
@@ -21,16 +21,6 @@
   directly to classrooms and schools.
 </p>
 
-
-<div class="resource-widget resource-flow-layout col-13" style="height:323px"
-  data-query="collection:distribute/googleplay/gpfe/highlight"
-  data-sortOrder="-timestamp"
-  data-cardSizes="18x6,"
-  data-maxResults="1"></div>
-
-
-<!-- <div class="center-img"><img src="{@docRoot}images/gp-edu-hero14.jpg" class="" /></div> -->
-
 <p>
   If you have an educational app, include it in Google Play for Education.
   Google Play for Education can help your innovative educational apps gain
diff --git a/docs/html/distribute/googleplay/edu/start.jd b/docs/html/distribute/googleplay/edu/start.jd
index 136611c..067a15f 100644
--- a/docs/html/distribute/googleplay/edu/start.jd
+++ b/docs/html/distribute/googleplay/edu/start.jd
@@ -1,4 +1,4 @@
-page.title=Publish Android Apps for Education
+page.title=Publish Apps
 page.image=/distribute/images/play-education.jpg
 meta.tags="education", "guidelines", "quality"
 page.tags="education", "addendum"
diff --git a/docs/html/distribute/googleplay/edu/videos.jd b/docs/html/distribute/googleplay/edu/videos.jd
new file mode 100644
index 0000000..ca4da7a
--- /dev/null
+++ b/docs/html/distribute/googleplay/edu/videos.jd
@@ -0,0 +1,30 @@
+page.title=Video Resources
+page.image=http://i1.ytimg.com/vi/vzvpcEffvaE/maxresdefault.jpg
+meta.tags="education"
+page.tags="education"
+page.metaDescription=Watch video resources from Google and successful educational developers.
+
+@jd:body
+
+<p>
+With Google Play for Education, bringing your app to the classroom has never been easier. However,
+you may want to familiarize yourself with some best practices before diving in.
+To help you visualize what a great education app might look like, what Google Play for Education is all
+about, and how to achieve success with the platform, here is a selection of recent videos from
+Google and successful educational developers.
+</p>
+
+<h2>Developer Stories</h2>
+<div style="margin-top:20px" class="resource-widget resource-flow-layout wrap col-13
+   no-section" data-query="collection:distribute/edu/videos/stories" data-resourcestyle="card"
+   data-maxresults="6" data-cardsizes="18x12x2"></div>
+
+<h2>Best Practices</h2>
+<div style="margin-top:20px" class="resource-widget resource-flow-layout wrap col-13
+   no-section" data-query="collection:distribute/edu/videos/bestpractices" data-resourcestyle="card"
+   data-maxresults="6" data-cardsizes="18x6, 6x6, 6x6, 6x6"></div>
+
+<h2>Teacher/Classroom Experience</h2>
+<div style="margin-top:20px" class="resource-widget resource-flow-layout wrap col-13
+   no-section" data-query="collection:distribute/edu/videos/experience" data-resourcestyle="card"
+   data-maxresults="6" data-cardsizes="18x12x2"></div>
\ No newline at end of file
diff --git a/docs/html/distribute/googleplay/googleplay_toc.cs b/docs/html/distribute/googleplay/googleplay_toc.cs
index 36e424a..45464c7 100644
--- a/docs/html/distribute/googleplay/googleplay_toc.cs
+++ b/docs/html/distribute/googleplay/googleplay_toc.cs
@@ -18,24 +18,24 @@
     </div>
   </li>
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/edu/about.html">
+    <div class="nav-section-header" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/edu/about.html">
           <span class="en">Google Play for Education</span>
         </a>
     </div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/edu/start.html">
-          <span class="en">Publish Android Apps for Education</span>
-        </a>
-    </div>
+    <ul>
+      <li><a href="<?cs var:toroot?>distribute/googleplay/edu/start.html">
+          <span class="en">Publish Apps</span>
+        </a></li>
+      <li><a href="<?cs var:toroot?>distribute/googleplay/edu/videos.html">
+          <span class="en">Video Resources</span>
+        </a></li>
+    </ul>
   </li>
 </ul>
 
-
 <script type="text/javascript">
 <!--
     buildToggleLists();
     changeNavLang(getLangPref());
 //-->
-</script>
-
+</script>
\ No newline at end of file
diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd
index d842cb9..4b5a121 100644
--- a/docs/html/guide/topics/graphics/2d-graphics.jd
+++ b/docs/html/guide/topics/graphics/2d-graphics.jd
@@ -32,7 +32,7 @@
   </div>
 </div>
 
-<p>The Android framework APIs provides a set 2D drawing APIs that allow you to render your own
+<p>The Android framework APIs provides a set of 2D-drawing APIs that allow you to render your own
 custom graphics onto a canvas or to modify existing Views to customize their look and feel.
 When drawing 2D graphics, you'll typically do so in one of two ways:</p>
 
@@ -515,4 +515,4 @@
 stretches to accommodate it.
 </p>
 
-<img src="{@docRoot}images/ninepatch_examples.png" alt=""/>
\ No newline at end of file
+<img src="{@docRoot}images/ninepatch_examples.png" alt=""/>
diff --git a/docs/html/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd
index 616949b..0c72657 100644
--- a/docs/html/guide/topics/ui/declaring-layout.jd
+++ b/docs/html/guide/topics/ui/declaring-layout.jd
@@ -88,8 +88,8 @@
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="fill_parent" 
-              android:layout_height="fill_parent" 
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
               android:orientation="vertical" >
     &lt;TextView android:id="@+id/text"
               android:layout_width="wrap_content"
@@ -215,14 +215,14 @@
 <ul>
   <li><var>wrap_content</var> tells your view to size itself to the dimensions
 required by its content</li>
-  <li><var>fill_parent</var> (renamed <var>match_parent</var> in API Level 8)
+  <li><var>match_parent</var> (named <var>fill_parent</var> before API Level 8)
 tells your view to become as big as its parent view group will allow.</li>
 </ul>
 
 <p>In general, specifying a layout width and height using absolute units such as
 pixels is not recommended. Instead, using relative measurements such as
 density-independent pixel units (<var>dp</var>), <var>wrap_content</var>, or
-<var>fill_parent</var>, is a better approach, because it helps ensure that
+<var>match_parent</var>, is a better approach, because it helps ensure that
 your application will display properly across a variety of device screen sizes.
 The accepted measurement types are defined in the
 <a href="{@docRoot}guide/topics/resources/available-resources.html#dimension">
diff --git a/docs/html/guide/topics/ui/layout/grid.jd b/docs/html/guide/topics/ui/layout/grid.jd
index 52f453b..c2f1321 100644
--- a/docs/html/guide/topics/ui/layout/grid.jd
+++ b/docs/html/guide/topics/ui/layout/grid.jd
@@ -41,8 +41,8 @@
         <pre>
 &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
 &lt;TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:stretchColumns="1">
     &lt;TableRow>
         &lt;TextView
@@ -82,8 +82,8 @@
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:stretchColumns="1">
 
     &lt;TableRow>
diff --git a/docs/html/guide/topics/ui/layout/gridview.jd b/docs/html/guide/topics/ui/layout/gridview.jd
index b8d24b6..a4bf224 100644
--- a/docs/html/guide/topics/ui/layout/gridview.jd
+++ b/docs/html/guide/topics/ui/layout/gridview.jd
@@ -43,10 +43,10 @@
   <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
-&lt;GridView xmlns:android="http://schemas.android.com/apk/res/android" 
+&lt;GridView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/gridview"
-    android:layout_width="fill_parent" 
-    android:layout_height="fill_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:columnWidth="90dp"
     android:numColumns="auto_fit"
     android:verticalSpacing="10dp"
diff --git a/docs/html/guide/topics/ui/layout/linear.jd b/docs/html/guide/topics/ui/layout/linear.jd
index 902f22f..f4babfe 100644
--- a/docs/html/guide/topics/ui/layout/linear.jd
+++ b/docs/html/guide/topics/ui/layout/linear.jd
@@ -82,21 +82,21 @@
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:paddingLeft="16dp"
     android:paddingRight="16dp"
     android:orientation="vertical" >
     &lt;EditText
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:hint="@string/to" />
     &lt;EditText
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:hint="@string/subject" />
     &lt;EditText
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
         android:gravity="top"
diff --git a/docs/html/guide/topics/ui/layout/relative.jd b/docs/html/guide/topics/ui/layout/relative.jd
index 4354d7f..ca5cb48 100644
--- a/docs/html/guide/topics/ui/layout/relative.jd
+++ b/docs/html/guide/topics/ui/layout/relative.jd
@@ -82,13 +82,13 @@
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:paddingLeft="16dp"
     android:paddingRight="16dp" >
     &lt;EditText
         android:id="@+id/name"
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:hint="@string/reminder" />
     &lt;Spinner
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index cd49ace..852db1f 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -7,12 +7,26 @@
       "sdk/installing/studio.html" 
     ]
   },
-  "index/devices": {
+  "distribute/edu/videos/stories": {
     "title": "",
     "resources": [
-      "wear/index.html",
-      "tv/index.html",
-      "auto/index.html"
+      "https://www.youtube.com/watch?v=Idu7VcTTXfk",
+      "https://www.youtube.com/watch?v=iokH4SAIfRw"
+    ]
+  },
+  "distribute/edu/videos/bestpractices": {
+    "title": "",
+    "resources": [
+      "https://www.youtube.com/watch?v=iulXz8QTD1g",
+      "https://www.youtube.com/watch?v=IKhU180eJMo",
+      "https://www.youtube.com/watch?v=_AZ6UcPz-_g",
+      "https://www.youtube.com/watch?v=Eh2adsAyTKc"
+    ]
+  },
+  "distribute/edu/videos/experience": {
+    "title": "",
+    "resources": [
+      "http://youtu.be/vzvpcEffvaE"
     ]
   },
   "launch/static": {
@@ -45,7 +59,7 @@
     "resources": [
       "distribute/googleplay/edu/about.html",
       "distribute/googleplay/edu/start.html",
-      "https://developers.google.com/edu/faq"
+      "distribute/googleplay/edu/videos.html"
     ]
   },
   "distribute/essentials": {
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index dccdf7e..a4302ef 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -15,6 +15,90 @@
 
 DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
   {
+    "title":"Quizlet Developer Story",
+    "titleFriendly":"",
+    "summary":"Quizlet is an extremely popular online learning tool for students. See how they optimized for the classroom with Android and the power of Google Play for Education.",
+    "url":"https://www.youtube.com/watch?v=Idu7VcTTXfk",
+    "group":"",
+    "keywords": [],
+    "tags": [
+      "#gpfe",
+      "#googleplay"
+    ],
+    "image":"http://i1.ytimg.com/vi/Idu7VcTTXfk/maxresdefault.jpg",
+    "type":"video"
+  },
+  {
+    "title":"Whats New in Google Play",
+    "titleFriendly":"",
+    "summary":"Learn about the vision and philosophy behind Google Play for Education",
+    "url":"https://www.youtube.com/watch?v=IKhU180eJMo",
+    "group":"",
+    "keywords": [],
+    "tags": [
+      "#gpfe",
+      "#googleplay"
+    ],
+    "image":"http://i1.ytimg.com/vi/IKhU180eJMo/maxresdefault.jpg",
+    "type":"video"
+  },
+  {
+    "title":"ClassDojo Developer Story",
+    "titleFriendly":"",
+    "summary":"ClassDojo is a classroom tool that helps teachers improve behavior in their classrooms quickly and easily. See how they optimized for the classroom with Android and the power of Google Play for Education.",
+    "url":"https://www.youtube.com/watch?v=iokH4SAIfRw",
+    "group":"",
+    "keywords": [],
+    "tags": [
+      "#gpfe",
+      "#googleplay"
+    ],
+    "image":"http://i1.ytimg.com/vi/iokH4SAIfRw/maxresdefault.jpg",
+    "type":"video"
+  },
+  {
+    "title":"5 Tips for Succeeding",
+    "titleFriendly":"",
+    "summary":"See inspirational ways of using technology in the classroom.",
+    "url":"https://www.youtube.com/watch?v=Eh2adsAyTKc",
+    "group":"",
+    "keywords": [],
+    "tags": [
+      "#gpfe",
+      "#googleplay"
+    ],
+    "image":"http://i1.ytimg.com/vi/Eh2adsAyTKc/maxresdefault.jpg",
+    "type":"video"
+  },
+  {
+    "title":"Optimizing Apps for Education",
+    "titleFriendly":"",
+    "summary":"Learn how to optimize your app for teachers and students.",
+    "url":"https://www.youtube.com/watch?v=_AZ6UcPz-_g",
+    "group":"",
+    "keywords": [],
+    "tags": [
+      "#gpfe",
+      "#googleplay"
+    ],
+    "image":"http://i1.ytimg.com/vi/_AZ6UcPz-_g/maxresdefault.jpg",
+    "type":"video"
+  },
+  {
+    "title":"Ideas and Tools for Building Innovative Education Apps",
+    "titleFriendly":"",
+    "summary":"Are you hungry to build an awesome app for education but don't quite know where to start? Come hear about apps that teachers want, and the APIs you're going to need to build them! In particular, we'll talk about app ideas that combine APIs for Google Drive, Google Login, Android Single Task Mode and more to build transformative Educational apps that will delight educators and kids in and out of the classroom.",
+    "url":"https://www.youtube.com/watch?v=iulXz8QTD1g",
+    "group":"",
+    "keywords": [],
+    "tags": [
+      "#gpfe",
+      "#googleplay"
+    ],
+    "image":"http://i1.ytimg.com/vi/iulXz8QTD1g/maxresdefault.jpg",
+    "type":"video"
+  },
+  {
     "title":"Developer Registration",
     "titleFriendly":"",
     "summary":"Additional information about the registration process.",
@@ -699,10 +783,10 @@
     "url": "http://youtu.be/vzvpcEffvaE",
     "timestamp": 1383243492000,
     "image": "http://i1.ytimg.com/vi/vzvpcEffvaE/maxresdefault.jpg",
-    "title": "Introducing Google Play for Education",
-    "summary": "Google Play for Education is a destination where schools can find great, teacher-approved, educational content&mdash;from videos and books, to educational apps&mdash;all in one place. Teachers can filter content by subject matter, grade and other criteria. Bulk purchase and instant distribution let educators bring your apps directly to classrooms and schools.",
+    "title": "Introducing Tablets with Google Play for Education",
+    "summary": "Schools in Hillsborough, New Jersey were among the first to try out Nexus 7 tablets with Google Play for Education. See the difference it made for students, teachers, and administrators.",
     "keywords": [],
-    "type": "youtube",
+    "type": "video",
     "titleFriendly": ""
   },
   {
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 16bc444..cf594dc 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -571,7 +571,7 @@
 manifest in order for your app to use the new advertising and scanning features.</a>
 
 <p>To begin Bluetooth LE advertising so that other devices can discover
-your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()}
+your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvertising()}
 and pass in an implementation of the
 {@code android.bluetooth.le.AdvertiseCallback} class. The callback object
 receives a report of the success or failure of the advertising operation.</p>
diff --git a/docs/html/preview/material/animations.jd b/docs/html/preview/material/animations.jd
index b8d063b..353f0f2 100644
--- a/docs/html/preview/material/animations.jd
+++ b/docs/html/preview/material/animations.jd
@@ -130,7 +130,7 @@
 <ul>
 <li><em>explode</em> - Moves views in or out from the center of the scene.</li>
 <li><em>slide</em> - Moves views in or out from one of the edges of the scene.</li>
-<li><em>fade</em> - Mades views in or out of the scene.</li>
+<li><em>fade</em> - Moves views in or out of the scene.</li>
 </ul>
 
 <p>Any transition that extends the <code>android.transition.Visibility</code> class is supported
@@ -236,7 +236,7 @@
 
 <h3>Shared elements transitions</h3>
 
-<p>To make a screne transition animation between two activities that have a shared element:</p>
+<p>To make a screen transition animation between two activities that have a shared element:</p>
 
 <ol>
 <li>Enable window content transitions in your style.</li>
diff --git a/docs/html/preview/material/compatibility.jd b/docs/html/preview/material/compatibility.jd
index fb97112..b4d26a7 100644
--- a/docs/html/preview/material/compatibility.jd
+++ b/docs/html/preview/material/compatibility.jd
@@ -78,5 +78,5 @@
 <li>Path-based animations</li>
 </ul>
 
-<p>To preserve compatibility with earlier verisons of Android, check the system version at
+<p>To preserve compatibility with earlier versions of Android, check the system version at
 runtime before you invoke these APIs.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/theme.jd b/docs/html/preview/material/theme.jd
index 740bf56..dceeb47 100644
--- a/docs/html/preview/material/theme.jd
+++ b/docs/html/preview/material/theme.jd
@@ -6,7 +6,7 @@
 <div id="qv">
 <h2>In this document</h2>
 <ol>
-  <li><a href="#colorpalette">Customize the Colot Palette</a></li>
+  <li><a href="#colorpalette">Customize the Color Palette</a></li>
   <li><a href="#statusbar">Customize the Status Bar</a></li>
   <li><a href="#inheritance">Theme Individual Views</a></li>
 </ol>
diff --git a/docs/html/preview/material/ui-widgets.jd b/docs/html/preview/material/ui-widgets.jd
index 31604d6..2266815 100644
--- a/docs/html/preview/material/ui-widgets.jd
+++ b/docs/html/preview/material/ui-widgets.jd
@@ -171,10 +171,10 @@
 <p>Here's how to specify properties of <code>CardView</code>:</p>
 
 <ul>
-  <li>To set the corner radius in your layouts, use the <code>android:cardCornerRadius</code>
+  <li>To set the corner radius in your layouts, use the <code>card_view:cardCornerRadius</code>
   attribute.</li>
   <li>To set the corner radius in your code, use the <code>CardView.setRadius</code> method.</li>
-  <li>To set the background color of a card, use the <code>android:cardBackgroundColor</code>
+  <li>To set the background color of a card, use the <code>card_view:cardBackgroundColor</code>
 attribute.</li>
 </ul>
 
diff --git a/docs/html/preview/material/views-shadows.jd b/docs/html/preview/material/views-shadows.jd
index f7682f5..78c0062 100644
--- a/docs/html/preview/material/views-shadows.jd
+++ b/docs/html/preview/material/views-shadows.jd
@@ -73,7 +73,7 @@
 &lt;/shape>
 </pre>
 
-<p>Then this view and drawable cast the appropiate shadow.</p>
+<p>Then this view and drawable cast the appropriate shadow.</p>
 
 <p>You can also create outlines in your code using the methods in the <code>Outline</code> class,
 and you can assign them to views with the <code>View.setOutline</code> method.</p>
diff --git a/docs/html/preview/notifications.jd b/docs/html/preview/notifications.jd
index 375fad9..44f46ea5 100644
--- a/docs/html/preview/notifications.jd
+++ b/docs/html/preview/notifications.jd
@@ -183,7 +183,7 @@
 <img src="{@docRoot}preview/images/notifications/ReplyAction.png" width="156px" height="156px"
   alt="" />
 
-  
+
 </div>
 
 
@@ -315,7 +315,7 @@
 priority</strong></h4>
 
 <p>Default, High, and Max priority are interruptive priority levels and risk interrupting the user
-from what they are doing. This should not not be taken lightly, so these levels should be  reserved
+from what they are doing. This should not be taken lightly, so these levels should be  reserved
 for notifications that:</p>
 
 <ul>
@@ -498,7 +498,7 @@
 
 <h3 style="clear:both" id="make_notifications_optional">Make notifications optional</h3>
 
-<p>Users should always be in control of notifications. Allow the user to diszable your app's
+<p>Users should always be in control of notifications. Allow the user to disable your app's
 notifications or change their alert properties, such as alert sound and whether to use vibration,
 by adding a notification settings item to your application settings.</p>
 
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
index d83dd11..e09dcbf 100644
--- a/docs/html/preview/tv/adt-1/index.jd
+++ b/docs/html/preview/tv/adt-1/index.jd
@@ -160,7 +160,7 @@
 <p>
 <p>You cast to an ADT-1 device the same way you do with a Chromecast device. Open the supported
   Cast apps or webpages, press the <strong>Cast</strong> button and you should see the ADT-1 as a
-  Cast target. For more infomation about on how to cast, see
+  Cast target. For more information about on how to cast, see
   <a href="http://www.google.com/intl/en/chrome/devices/chromecast/learn.html">Learn How to
   Cast</a>.
   </p>
diff --git a/docs/html/preview/tv/design/patterns.jd b/docs/html/preview/tv/design/patterns.jd
index cdba74c..48faee9 100644
--- a/docs/html/preview/tv/design/patterns.jd
+++ b/docs/html/preview/tv/design/patterns.jd
@@ -45,7 +45,7 @@
 <h3>Recommendation Icons</h3>
 
 <p>Recommendation cards include a small icon that is imposed over a colored background.
-  An example and specifications for the this icon are shown below:</p>
+  An example and specifications for this icon are shown below:</p>
 
 <img src="{@docRoot}preview/tv/design/images/icon.png" alt="Recommendation icon examples" />
 
diff --git a/docs/html/preview/tv/games/index.jd b/docs/html/preview/tv/games/index.jd
index 763eada..61a26d2c 100644
--- a/docs/html/preview/tv/games/index.jd
+++ b/docs/html/preview/tv/games/index.jd
@@ -146,7 +146,7 @@
 site.</p>
 
 <h2 id="web">Web</h2>
-<p>We discourage including web browsing in games for Android TV. The television set is not well-suited for browsing,, either in terms of display or control scheme.</p>
+<p>We discourage including web browsing in games for Android TV. The television set is not well-suited for browsing, either in terms of display or control scheme.</p>
 <p class="note"><strong>Note:</strong> You can use the {@link android.webkit.WebView} class for logins to services like Google+ and
 Facebook. </p>
 
diff --git a/docs/html/preview/tv/ui/navigation.jd b/docs/html/preview/tv/ui/navigation.jd
index 92b34cf..684b743 100644
--- a/docs/html/preview/tv/ui/navigation.jd
+++ b/docs/html/preview/tv/ui/navigation.jd
@@ -104,7 +104,7 @@
 
 <p>Android provides <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">
 Drawable State List Resources</a> to implement highlights for selected and focused controls. The
-following code example demonstates how to indicate selection of a button object:
+following code example demonstrates how to indicate selection of a button object:
 </p>
 
 <pre>
diff --git a/docs/html/tools/publishing/preparing.jd b/docs/html/tools/publishing/preparing.jd
index 7192aa8..5265fce 100644
--- a/docs/html/tools/publishing/preparing.jd
+++ b/docs/html/tools/publishing/preparing.jd
@@ -191,6 +191,13 @@
 added to your code, such as {@link android.os.Debug#startMethodTracing()} and
 {@link android.os.Debug#stopMethodTracing()} method calls.</p>
 
+<p class="caution"><strong>Important:</strong> Ensure that you disable debugging for
+your app if using {@link android.webkit.WebView} to display paid for content or if using JavaScript
+interfaces, since debugging allows users to inject scripts and extract content using Chrome
+DevTools. To disable debugging, use the
+{@link android.webkit.WebView#setWebContentsDebuggingEnabled(boolean) WebView.setWebContentsDebuggingEnabled()}
+method.</p>
+
 <h4>Clean up your project directories</h4>
 
 <p>Clean up your project and make sure it conforms to the directory structure described in <a
diff --git a/docs/html/tools/sdk/ndk/index.jd b/docs/html/tools/sdk/ndk/index.jd
index 0ac1881..b77a72a 100644
--- a/docs/html/tools/sdk/ndk/index.jd
+++ b/docs/html/tools/sdk/ndk/index.jd
@@ -2,33 +2,59 @@
 page.template=sdk
 
 
-ndk.mac64_download=android-ndk-r9d-darwin-x86_64.tar.bz2
-ndk.mac64_bytes=400339614
-ndk.mac64_checksum=c914164b1231c574dbe40debef7048be
+ndk.mac64_download=android-ndk32-r10-darwin-x86_64.tar.bz2
+ndk.mac64_bytes=411610468
+ndk.mac64_checksum=3ce1fa3dbe7a188f5d2640fd2f7ca944
 
-ndk.mac32_download=android-ndk-r9d-darwin-x86.tar.bz2
-ndk.mac32_bytes=393866116
-ndk.mac32_checksum=ee6544bd8093c79ea08c2e3a6ffe3573
+ndk.mac32_download=android-ndk32-r10-darwin-x86.tar.bz2
+ndk.mac32_bytes=404768263
+ndk.mac32_checksum=1824eec1f6749b6cb7bb306a3b924c33
 
-ndk.linux64_download=android-ndk-r9d-linux-x86_64.tar.bz2
-ndk.linux64_bytes=412879983
-ndk.linux64_checksum=c7c775ab3342965408d20fd18e71aa45
+ndk.linux64_download=android-ndk32-r10-linux-x86_64.tar.bz2
+ndk.linux64_bytes=420671390
+ndk.linux64_checksum=e3ff629d212a8106a43415862fa39baf
 
-ndk.linux32_download=android-ndk-r9d-linux-x86.tar.bz2
-ndk.linux32_bytes=405218267
-ndk.linux32_checksum=6c1d7d99f55f0c17ecbcf81ba0eb201f
+ndk.linux32_download=android-ndk32-r10-linux-x86.tar.bz2
+ndk.linux32_bytes=420078216
+ndk.linux32_checksum=8d9a5faa6e77b43bfae0f169079b21c4
 
-ndk.win64_download=android-ndk-r9d-windows-x86_64.zip
-ndk.win64_bytes=520997454
-ndk.win64_checksum=8cd244fc799d0e6e59d65a59a8692588
+ndk.win64_download=android-ndk32-r10-windows-x86_64.zip
+ndk.win64_bytes=529850429
+ndk.win64_checksum=b11f9239344f7c377ed5b627f0fb236e
 
-ndk.win32_download=android-ndk-r9d-windows-x86.zip
-ndk.win32_bytes=491440074
-ndk.win32_checksum=b16516b611841a075685a10c59d6d7a2
+ndk.win32_download=android-ndk32-r10-windows-x86.zip
+ndk.win32_bytes=500135685
+ndk.win32_checksum=0a3c01147abba945cc4ef5837519ec97
 
-ndk.debug_info_download=android-ndk-r9d-cxx-stl-libs-with-debug-info.zip
-ndk.debug_info_bytes=104947363
-ndk.debug_info_checksum=906c8d88e0f02295c3bfe6b8e98a1a35
+
+
+ndk.mac64_64_download=android-ndk64-r10-darwin-x86_64.tar.bz2
+ndk.mac64_64_bytes=327740247
+ndk.mac64_64_checksum=72561b27acc6192a2e81b345ea128a20
+
+ndk.mac32_64_download=android-ndk64-r10-darwin-x86.tar.bz2
+ndk.mac32_64_bytes=323736411
+ndk.mac32_64_checksum=5bbaf9d8051ba5d2c0fff74cfd87c374
+
+ndk.linux64_64_download=android-ndk64-r10-linux-x86_64.tar.bz2
+ndk.linux64_64_bytes=339708042
+ndk.linux64_64_checksum=737290195583268b7fbff4aa56465ab6
+
+ndk.linux32_64_download=android-ndk64-r10-linux-x86.tar.bz2
+ndk.linux32_64_bytes=338544906
+ndk.linux32_64_checksum=bea5d027baeb948cbff6af840d26b80d
+
+ndk.win64_64_download=android-ndk64-r10-windows-x86_64.zip
+ndk.win64_64_bytes=417411195
+ndk.win64_64_checksum=91879ec85539b45313a21b9526b911a8
+
+ndk.win32_64_download=android-ndk64-r10-windows-x86.zip
+ndk.win32_64_bytes=396751892
+ndk.win32_64_checksum=f79070ace2cde9ebf6a2e2be4a61ac7a
+
+ndk.debug_info_download=android-ndk-r10-cxx-stl-libs-with-debug-info.zip
+ndk.debug_info_bytes=253198908
+ndk.debug_info_checksum=c2a90c43d17dbb5f0609cc8237491788
 
 
 page.title=Android NDK
@@ -56,129 +82,261 @@
 This is the Android Software Development Kit License Agreement
 
 <h3>1. Introduction</h3>
-1.1 The Android Software Development Kit (referred to in this License Agreement as the "SDK" and specifically including the Android system files, packaged APIs, and Google APIs add-ons) is licensed to you subject to the terms of this License Agreement. This License Agreement forms a legally binding contract between you and Google in relation to your use of the SDK.
+1.1 The Android Software Development Kit (referred to in this License Agreement as the "SDK" and
+specifically including the Android system files, packaged APIs, and Google APIs add-ons) is
+licensed to you subject to the terms of this License Agreement. This License Agreement forms a
+legally binding contract between you and Google in relation to your use of the SDK.
 
-1.2 “Android” means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL: http://source.android.com/, as updated from time to time.
+1.2 “Android” means the Android software stack for devices, as made available under the Android
+Open Source Project, which is located at the following URL: http://source.android.com/, as updated
+from time to time.
 
-1.3 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.
+1.3 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600
+Amphitheatre Parkway, Mountain View, CA 94043, United States.
 
 
 <h3>2. Accepting this License Agreement</h3>
-2.1 In order to use the SDK, you must first agree to this License Agreement. You may not use the SDK if you do not accept this License Agreement.
+2.1 In order to use the SDK, you must first agree to this License Agreement. You may not use the
+SDK if you do not accept this License Agreement.
 
 2.2 By clicking to accept, you hereby agree to the terms of this License Agreement.
 
-2.3 You may not use the SDK and may not accept the License Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries including the country in which you are resident or from which you use the SDK.
+2.3 You may not use the SDK and may not accept the License Agreement if you are a person barred
+from receiving the SDK under the laws of the United States or other countries including the country
+in which you are resident or from which you use the SDK.
 
-2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to this License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the SDK on behalf of your employer or other entity.
+2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or other
+entity, you represent and warrant that you have full legal authority to bind your employer or such
+entity to this License Agreement. If you do not have the requisite authority, you may not accept
+the License Agreement or use the SDK on behalf of your employer or other entity.
 
 
 <h3>3. SDK License from Google</h3>
-3.1 Subject to the terms of this License Agreement, Google grants you a limited, worldwide, royalty-free, non-assignable and non-exclusive license to use the SDK solely to develop applications to run on the Android platform.
+3.1 Subject to the terms of this License Agreement, Google grants you a limited, worldwide,
+royalty-free, non-assignable and non-exclusive license to use the SDK solely to develop
+applications to run on the Android platform.
 
-3.2 You agree that Google or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you.
+3.2 You agree that Google or third parties own all legal right, title and interest in and to the
+SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property
+Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law,
+and any and all other proprietary rights. Google reserves all rights not expressly granted to you.
 
-3.3 You may not use the SDK for any purpose not expressly permitted by this License Agreement.  Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the SDK or any part of the SDK; or (b) load any part of the SDK onto a mobile handset or any other hardware device except a personal computer, combine any part of the SDK with other software, or distribute any software or device incorporating a part of the SDK.
+3.3 You may not use the SDK for any purpose not expressly permitted by this License Agreement.
+Except to the extent required by applicable third party licenses, you may not: (a) copy (except for
+backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create
+derivative works of the SDK or any part of the SDK; or (b) load any part of the SDK onto a mobile
+handset or any other hardware device except a personal computer, combine any part of the SDK with
+other software, or distribute any software or device incorporating a part of the SDK.
 
-3.4 You agree that you will not take any actions that may cause or result in the fragmentation of Android, including but not limited to distributing, participating in the creation of, or promoting in any way a software development kit derived from the SDK.
+3.4 You agree that you will not take any actions that may cause or result in the fragmentation of
+Android, including but not limited to distributing, participating in the creation of, or promoting
+in any way a software development kit derived from the SDK.
 
-3.5 Use, reproduction and distribution of components of the SDK licensed under an open source software license are governed solely by the terms of that open source software license and not this License Agreement.
+3.5 Use, reproduction and distribution of components of the SDK licensed under an open source
+software license are governed solely by the terms of that open source software license and not this
+License Agreement.
 
-3.6 You agree that the form and nature of the SDK that Google provides may change without prior notice to you and that future versions of the SDK may be incompatible with applications developed on previous versions of the SDK. You agree that Google may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you or to users generally at Google's sole discretion, without prior notice to you.
+3.6 You agree that the form and nature of the SDK that Google provides may change without prior
+notice to you and that future versions of the SDK may be incompatible with applications developed
+on previous versions of the SDK. You agree that Google may stop (permanently or temporarily)
+providing the SDK (or any features within the SDK) to you or to users generally at Google's sole
+discretion, without prior notice to you.
 
-3.7 Nothing in this License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features.
+3.7 Nothing in this License Agreement gives you a right to use any of Google's trade names,
+trademarks, service marks, logos, domain names, or other distinctive brand features.
 
-3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK.
+3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including
+copyright and trademark notices) that may be affixed to or contained within the SDK.
 
 
 <h3>4. Use of the SDK by You</h3>
-4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under this License Agreement in or to any software applications that you develop using the SDK, including any intellectual property rights that subsist in those applications.
+4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under
+this License Agreement in or to any software applications that you develop using the SDK, including
+any intellectual property rights that subsist in those applications.
 
-4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) this License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).
+4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a)
+this License Agreement and (b) any applicable law, regulation or generally accepted practices or
+guidelines in the relevant jurisdictions (including any laws regarding the export of data or
+software to and from the United States or other relevant countries).
 
-4.3 You agree that if you use the SDK to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so.
+4.3 You agree that if you use the SDK to develop applications for general public users, you will
+protect the privacy and legal rights of those users. If the users provide you with user names,
+passwords, or other login information or personal information, you must make the users aware that
+the information will be available to your application, and you must provide legally adequate
+privacy notice and protection for those users. If your application stores personal or sensitive
+information provided by users, it must do so securely. If the user provides your application with
+Google Account information, your application may only use that information to access the user's
+Google Account when, and for the limited purposes for which, the user has given you permission to
+do so.
 
-4.4 You agree that you will not engage in any activity with the SDK, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of any third party including, but not limited to, Google or any mobile communications carrier.
+4.4 You agree that you will not engage in any activity with the SDK, including the development or
+distribution of an application, that interferes with, disrupts, damages, or accesses in an
+unauthorized manner the servers, networks, or other properties or services of any third party
+including, but not limited to, Google or any mobile communications carrier.
 
-4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android and/or applications for Android, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so.
+4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or
+to any third party for) any data, content, or resources that you create, transmit or display
+through Android and/or applications for Android, and for the consequences of your actions
+(including any loss or damage which Google may suffer) by doing so.
 
-4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under this License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach.
+4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or
+to any third party for) any breach of your obligations under this License Agreement, any applicable
+third party contract or Terms of Service, or any applicable law or regulation, and for the
+consequences (including any loss or damage which Google or any third party may suffer) of any such
+breach.
 
 
 <h3>5. Your Developer Credentials</h3>
-5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials.
+5.1 You agree that you are responsible for maintaining the confidentiality of any developer
+credentials that may be issued to you by Google or which you may choose yourself and that you will
+be solely responsible for all applications that are developed under your developer credentials.
 
 
 <h3>6. Privacy and Information</h3>
-6.1 In order to continually innovate and improve the SDK, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the SDK are being used and how they are being used. Before any of this information is collected, the SDK will notify you and seek your consent. If you withhold consent, the information will not be collected.
+6.1 In order to continually innovate and improve the SDK, Google may collect certain usage
+statistics from the software including but not limited to a unique identifier, associated IP
+address, version number of the software, and information on which tools and/or services in the SDK
+are being used and how they are being used. Before any of this information is collected, the SDK
+will notify you and seek your consent. If you withhold consent, the information will not be
+collected.
 
-6.2 The data collected is examined in the aggregate to improve the SDK and is maintained in accordance with Google's Privacy Policy.
+6.2 The data collected is examined in the aggregate to improve the SDK and is maintained in
+accordance with Google's Privacy Policy.
 
 
 <h3>7. Third Party Applications</h3>
-7.1 If you use the SDK to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources.
+7.1 If you use the SDK to run applications developed by a third party or that access data, content
+or resources provided by a third party, you agree that Google is not responsible for those
+applications, data, content, or resources. You understand that all data, content or resources which
+you may access through such third party applications are the sole responsibility of the person from
+which they originated and that Google is not liable for any loss or damage that you may experience
+as a result of the use or access of any of those third party applications, data, content, or
+resources.
 
-7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners.
+7.2 You should be aware the data, content, and resources presented to you through such a third
+party application may be protected by intellectual property rights which are owned by the providers
+(or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell,
+distribute or create derivative works based on these data, content, or resources (either in whole
+or in part) unless you have been specifically given permission to do so by the relevant owners.
 
-7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, this License Agreement does not affect your legal relationship with these third parties.
+7.3 You acknowledge that your use of such third party applications, data, content, or resources may
+be subject to separate terms between you and the relevant third party. In that case, this License
+Agreement does not affect your legal relationship with these third parties.
 
 
 <h3>8. Using Android APIs</h3>
 8.1 Google Data APIs
 
-8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service.
+8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be
+protected by intellectual property rights which are owned by Google or those parties that provide
+the data (or by other persons or companies on their behalf). Your use of any such API may be
+subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or
+create derivative works based on this data (either in whole or in part) unless allowed by the
+relevant Terms of Service.
 
-8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so.
+8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you
+shall retrieve data only with the user's explicit consent and only when, and for the limited
+purposes for which, the user has given you permission to do so.
 
 
 <h3>9. Terminating this License Agreement</h3>
-9.1 This License Agreement will continue to apply until terminated by either you or Google as set out below.
+9.1 This License Agreement will continue to apply until terminated by either you or Google as set
+out below.
 
-9.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials.
+9.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the SDK
+and any relevant developer credentials.
 
 9.3 Google may at any time, terminate this License Agreement with you if:
 (A) you have breached any provision of this License Agreement; or
 (B) Google is required to do so by law; or
-(C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the SDK to you; or
-(D) Google decides to no longer provide the SDK or certain parts of the SDK to users in the country in which you are resident or from which you use the service, or the provision of the SDK or certain SDK services to you by Google is, in Google's sole discretion, no longer commercially viable.
+(C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has terminated
+its relationship with Google or ceased to offer certain parts of the SDK to you; or
+(D) Google decides to no longer provide the SDK or certain parts of the SDK to users in the country
+in which you are resident or from which you use the service, or the provision of the SDK or certain
+SDK services to you by Google is, in Google's sole discretion, no longer commercially viable.
 
-9.4 When this License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst this License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely.
+9.4 When this License Agreement comes to an end, all of the legal rights, obligations and
+liabilities that you and Google have benefited from, been subject to (or which have accrued over
+time whilst this License Agreement has been in force) or which are expressed to continue
+indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall
+continue to apply to such rights, obligations and liabilities indefinitely.
 
 
 <h3>10. DISCLAIMER OF WARRANTIES</h3>
-10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND THAT THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE.
+10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND THAT THE
+SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE.
 
-10.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE.
+10.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE
+SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR
+COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE.
 
-10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 
 
 <h3>11. LIMITATION OF LIABILITY</h3>
-11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.
+11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS
+LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY
+LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN
+AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.
 
 
 <h3>12. Indemnification</h3>
-12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this License Agreement.
+12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless
+Google, its affiliates and their respective directors, officers, employees and agents from and
+against any and all claims, actions, suits or proceedings, as well as any and all losses,
+liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or
+accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes
+any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of
+any person or defames any person or violates their rights of publicity or privacy, and (c) any
+non-compliance by you with this License Agreement.
 
 
 <h3>13. Changes to the License Agreement</h3>
-13.1 Google may make changes to the License Agreement as it distributes new versions of the SDK. When these changes are made, Google will make a new version of the License Agreement available on the website where the SDK is made available.
+13.1 Google may make changes to the License Agreement as it distributes new versions of the SDK.
+When these changes are made, Google will make a new version of the License Agreement available on
+the website where the SDK is made available.
 
 
 <h3>14. General Legal Terms</h3>
-14.1 This License Agreement constitutes the whole legal agreement between you and Google and governs your use of the SDK (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the SDK.
+14.1 This License Agreement constitutes the whole legal agreement between you and Google and
+governs your use of the SDK (excluding any services which Google may provide to you under a
+separate written agreement), and completely replaces any prior agreements between you and Google in
+relation to the SDK.
 
-14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in this License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google.
+14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is
+contained in this License Agreement (or which Google has the benefit of under any applicable law),
+this will not be taken to be a formal waiver of Google's rights and that those rights or remedies
+will still be available to Google.
 
-14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of this License Agreement is invalid, then that provision will be removed from this License Agreement without affecting the rest of this License Agreement. The remaining provisions of this License Agreement will continue to be valid and enforceable.
+14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any
+provision of this License Agreement is invalid, then that provision will be removed from this
+License Agreement without affecting the rest of this License Agreement. The remaining provisions of
+this License Agreement will continue to be valid and enforceable.
 
-14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to this License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of this License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to this License Agreement.
+14.4 You acknowledge and agree that each member of the group of companies of which Google is the
+parent shall be third party beneficiaries to this License Agreement and that such other companies
+shall be entitled to directly enforce, and rely upon, any provision of this License Agreement that
+confers a benefit on (or rights in favor of) them. Other than this, no other person or company
+shall be third party beneficiaries to this License Agreement.
 
-14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.
+14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST
+COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE
+LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.
 
-14.6 The rights granted in this License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under this License Agreement without the prior written approval of the other party.
+14.6 The rights granted in this License Agreement may not be assigned or transferred by either you
+or Google without the prior written approval of the other party. Neither you nor Google shall be
+permitted to delegate their responsibilities or obligations under this License Agreement without
+the prior written approval of the other party.
 
-14.7 This License Agreement, and your relationship with Google under this License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from this License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
+14.7 This License Agreement, and your relationship with Google under this License Agreement, shall
+be governed by the laws of the State of California without regard to its conflict of laws
+provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located
+within the county of Santa Clara, California to resolve any legal matter arising from this License
+Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for
+injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
 
 
 <em>November 13, 2012</em>
@@ -191,7 +349,8 @@
 <input id="agree" type="checkbox" name="agree" value="1" onclick="onAgreeChecked()" />
 <label id="agreeLabel" for="agree">I have read and agree with the above terms and conditions</label>
 </p>
-<p><a href="" class="button disabled ndk" id="downloadForRealz" onclick="return onDownloadNdkForRealz(this);"></a></p>
+<p><a href="" class="button disabled ndk" id="downloadForRealz" onclick="return
+onDownloadNdkForRealz(this);"></a></p>
 </div>
 
 
@@ -272,6 +431,182 @@
  <p>
    <a href="#" onclick="return toggleContent(this)"> <img
      src="/assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+   >Android NDK, Revision 10</a> <em>(July 2014)</em>
+ </p>
+ <div class="toggle-content-toggleme">
+    <dl>
+      <dt>Important changes:</dt>
+      <dd>
+      <ul>
+        <li>Added 3 new ABIs, all 64-bit: arm64-v8a, x86_64, mips64.</li> Note that:
+        <ul>
+           <li>GCC 4.9 is the default compiler for 64-bit ABIs. Clang is currently version 3.4.
+<code>NDK_TOOLCHAIN_VERSION=clang</code>
+      may not work for arm64-v8a and mips64.</li>
+           <li>Android API level L is the first level with 64-bit support.  Note that this API
+level is a temporary one, and only for L-preview. An actual API level number will replace it at
+L-release.</li>
+           <li>This release includes now includes <code>all32</code> and <code>all64</code>
+settings for <code>APP_ABI</code>.
+              <ul>
+              <li><code>APP_ABI=all32</code> is equivalent to
+<code>APP_ABI=armeabi,armeabi-v7a,x86,mips</code>.</li>
+              <li><code>APP_ABI=all64</code> is equivalent to
+<code>APP_ABI=arm64-v8a,x86_64,mips64</code>.</li>
+              <li><code>APP_ABI=all</code> selects all ABIs.</li>
+              </ul>
+           <li>The new GNU libstdc++ in Android-L contains all <code>&lt;tr1/cmath&gt;</code>
+Before defining your own math function, check <code>_GLIBCXX_USE_C99_MATH_TR1</code> to see a
+function with that name already exists, in order to avoid "multiple definition" errors from the
+linker.</li>
+           <li>The cpu-features library has been updated for the ARMv8 kernel.  The existing
+cpu-features library may fail to detect the presence of NEON on the ARMv8 platform. Recompile your
+code with the new version.</li>
+        </ul>
+        <li>Added a new <code>platforms/android-L/</code> API directory. It includes:</li>
+        <ul>
+           <li>Updated Bionic headers, which had not changed from Android API levels 3
+(Cupcake) to 19 (KitKat). This new version, for level L, is to be synchronized with AOSP.</li>
+           <li>New media APIs and a native-codec sample.</li>
+           <li>An updated <code>Android.h</code> header for SLES/OpenSLES, enabling support for
+single-precision, floating-point audio format in AudioPlayer.</li>
+           <li>GLES 3.1 and AEP extensions to <code>libGLESv3.so.</code></li>
+           <li>GLES2 and GLES3 headers updated to the latest official Khronos versions.</li>
+        </ul>
+        <li>Added GCC 4.9 compilers to the 32-/64-bit ABIs.  GCC 4.9 is the default (only) compiler
+for 64-bit ABIs, as previously mentioned.  For 32-bit ABIs, you must explcitly enable GCC 4.9, as
+GCC 4.6 is still the default.</li>
+        <ul>
+           <li>For ndk-build, enable 32-bit, GCC 4.9 building either by adding
+<code>NDK_TOOLCHAIN_VERSION=4.9</code> to <code>Application.mk</code>, or exporting it as an
+environment variable from the command line.</li>
+           <li>For a standalone toolchain, use the <code> --toolchain=</code> option in the
+<code>make-standalone-toolchain.sh</code> script. For example: <code>--toolchain=arm-linux-androideabi-4.9.</code></li>
+        </ul>
+        <li>Upgraded GDB to version 7.6 in GCC 4.8/4.9 and x86*. Since GDB is still at version GDB-7.3.x in
+GCC 4.6 (the default for ARM and MIPS), you must set
+<code>NDK_TOOLCHAIN_VERSION=4.8</code> or <code>4.9</code> to enable ndk-gdb to select GDB 7.6.</li>
+        <li>Added the <code>-mssse3</code> build option to provide SSSE3 support, and made it the default for ABI x86
+(upgrading from SSE3). The image released by Google does not contain SSSE3 instructions.</li>
+        <li>Updated GCC 4.8 to 4.8.3.</li>
+        <li>Improved ARM libc++ EH support by switching from gabi++ to libc++abi. For details, see the "C++ Support" section of the documentation.
+  Note that:</li>
+        <ul>
+           <li>All tests except for locale now pass for Clang 3.4 and GCC 4.8. For more
+information, see the "C++ Support" section of the documentation.</li>
+           <li>The libc++ libraries for X86 and MIPS libc++ still use gabi++.</li>
+           <li>GCC 4.7 and later can now use &lt;atomic&gt;.</li>
+           <li>You must add <code>-fno-strict-aliasing</code> if you use <code> &lt;list&gt;</code>, because <code>__list_imp::_end</code>_ breaks
+      TBAA rules.  (Issue <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61571">61571</a>.)</li>
+           <li>As of GCC 4.6, LIBCXX_FORCE_REBUILD:=true no longer rebuilds libc++. Rebuilding it
+requires the use of a different compiler. Note that Clang 3.3 is untested.</li>
+        </ul>
+        <li>mclinker is now version 2.7, and has aarch64 Linux support.</li>
+        <li>Added precompiled header support for headers specified by <code>LOCAL_PCH</code>.  (Issue <a href="http://b.android.com/25412">25412</a>).</li>
+      </dd>
+   <dl>
+
+
+     <dt>Important bug fixes:</dt>
+     <dd>
+     <ul>
+       <li>Fixed libc++ so that it now compiles <code>std::feof</code>, etc. (Issue <a
+href="http://b.android.com/66668">66668</a>).</li>
+       <li>Fixed a Clang 3.3/3.4 atomic library call that caused crashes in some of the libc++
+tests for ABI armeabi.</li>
+       <li>Fixed Clang 3.4 crashes that were occurring on reading precompiled headers. (Issue <a
+href="http://b.android.com/66657">66657</a>).</li>
+       <li>Fixed the Clang 3.3/3.4 <code>-O3</code> assert on:</li>
+       <code>llvm-3.2/llvm/include/llvm/MDBuilder.h:64: llvm::MDNode*
+llvm::MDBuilder::createBranchWeights(llvm::ArrayRef<unsigned int>): Assertion Weights.size() >= 2
+&& "Need at least two branch weights!"</code> (Issue <a href="http://b.android.com/57381">57381</a>).
+       <li>Fixed the following Clang 3.3/3.4 crash:</li>
+       <code>Assertion failed: (!Fn && "cast failed but able to resolve overload expression!!"), function CheckCXXCStyleCast, file
+Volumes/data/ndk-toolchain/src/llvm-3.3/llvm/tools/clang/lib/Sema/SemaCast.cpp, line 2018</code>.
+(Issue <a href="http://b.android.com/66950">66950</a>).
+     </ul>
+     </dd>
+
+     <dt>Other bug fixes:</dt>
+     <dd>
+     <ul>
+       <li>Fixed headers:</li>
+       <ul>
+          <li>Fixed 32-bit <code>ssize_t</code> to be <code>int</code> instead of <code>long
+int</code>.</li>
+          <li>Fixed <code>WCHAR_MIN</code> and <code>WCHAR_MAX</code> so that they they take
+appropriate signs according to the architecture they're running on:</li>
+          <ul>
+             <li>X86/MIPS: signed.
+             <li>ARM: unsigned.
+             <li>To force X86/MIPS to default to unsigned, use
+<code>-D__WCHAR_UNSIGNED__</code>.</li>
+             <li>To force <code>wchar_t</code> to be 16 bits, use <code>-fshort-wchar</code>.</li>
+          </ul>
+          <li>Removed non-existent symbols from 32-bit <code>libc.so</code>, and added <code>pread64</code>,
+<code>pwrite64</code>, <code>ftruncate64</code> for
+Android API level 12 and higher. (Issue <a href="http://b.android.com/69319">69319</a>). For more
+information, see the commit message accompanying AOSP change list
+     <a href="https://android-review.googlesource.com/#/c/94137">94137</a>.</li>
+       </ul>
+       <li>Fixed GCC warning about redefinition of <code>putchar</code>. Warning message reads:</li>
+       <code>include/stdio.h:236:5: warning: conflicts with previous declaration here
+[-Wattributes] int  putchar(int);</code> (Change list <a
+href="https://android-review.googlesource.com/#/c/91185">91185</a>).
+       <li>Fixed <code>make-standalone-toolchain.sh --stl=libc++</code> so that it:</li>
+       <ul>
+          <li>Copies <code>cxxabi.h</code>. (Issue <a
+href="http://b.android.com/68001">68001</a>).</li>
+          <li>Runs in directories other than the NDK install directory. (Issues <a
+href="http://b.android.com/67690">67690</a> and <a href="http://b.android.com/68647">68647</a>).</li>
+       </ul>
+       <li>Fixed GCC/Windows to quote arguments only when necessary for spawning processes in
+external programs. This change decreases the likelihood of exceeding the 32K length limit.</li>
+       <li>Fixed an issue that made it impossible to adjust the <code>APP_PLATFORM</code>
+environment variable.</li>
+       <li>Fixed the implementation of <code>IsSystemLibrary()</code> in crazy_linker so that it
+uses <code>strrchr()</code>
+  instead of <code>strchr()</code> to find the library path's true basename.</li>
+       <li>Fixed native-audio's inability to build in debug mode.</li>
+       <li>Fixed gdb's inability to print extreme floating-point numbers. (Issue <a
+href="http://b.android.com/69203">69203</a>).</li>
+       <li>Fixed Clang 3.4 inability to compile with <code>-Wl,-shared</code> (as opposed to
+<code>-shared</code>, which
+  had no compilation issues).  The problem was that Clang added <code>-pie</code> for Android
+targets if neither <code>-shared</code> nor <code>-static</code> existed. This behavior, which was
+incorrect, caused the linker to complain that <code>-shared</code> and <code>-pie</code> could not
+co-exist.</li>
+
+     </ul>
+     </dd>
+
+
+     <dt>Other changes:</dt>
+     <dd>
+     <ul>
+        <li>Added <code>arm_neon.h</code> to the x86 toolchain so that it now emulates ~47% of
+Neon. There is currently no support for 64-bit types. For more information, see the section on ARM
+Neon intrinsics support in the x86 documentation.</li>
+        <li>Ported ARM/GOT_PREL optimization (present in GCC 4.6 built from the GCC google branch) to
+ARM GCC 4.8/4.9.  This optimization sometimes reduces instruction count when accessing global
+variables.  As an example, see the build.sh script in
+<code>$NDK/tests/build/b14811006-GOT_PREL-optimization/</code>.</li>
+        <li>Added ARM version for STL gabi++, stlport, and libc++. They now have both it and Thumb
+mode.</li>
+        <li>It is now possible to call the make-standalone-toolchain.sh script with
+<code>--toolchain=x86_64-linux-android-4.9</code>, which is equivalent to
+<code>--toolchain=x86_64-4.9</code>.</li>
+     </dd>
+     </ul>
+   </dl>
+ </div>
+</div>
+
+
+<div class="toggle-content closed">
+ <p>
+   <a href="#" onclick="return toggleContent(this)"> <img
+     src="/assets/images/triangle-closed.png" class="toggle-content-img" alt=""
    >Android NDK, Revision 9d</a> <em>(March 2014)</em>
  </p>
  <div class="toggle-content-toggleme">
@@ -284,7 +619,7 @@
 still the default compiler.</li>
         <li>Added <code>APP_ABI=armeabi-v7a-hard</code>, with
 additional multilib option <code>-mfloat-abi=hard</code>. These options are for
-use with ARM GCC 4.6/4.8 and clang 3.3/3.4 (which use 4.8's assembler, linker,
+use with ARM GCC 4.6/4.8 and Clang 3.3/3.4 (which use 4.8's assembler, linker,
 and libs). When using these options, note the following changes:</li>
         <ul>
            <li> When executing the <code>ndk-build</code> script, add the
@@ -322,7 +657,7 @@
         </ul>
         For more information, see
 <code>CPLUSPLUS-SUPPORT.html</code>.
-(Issue <a href="b.android.com/36496">36496</a>)</li>
+(Issue <a href="http://b.android.com/36496">36496</a>)</li>
       </ul>
       </dd>
    <dl>
@@ -337,7 +672,7 @@
   a dependent, non-type template argument. (GCC Issue <a
 href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59052">59052</a>)</li>
        <li>Added more modules to prebuilt python (Issue <a
-href="b.android.com/59902">59902</a>):
+href="http://b.android.com/59902">59902</a>):
                <ul>
                  <li>Mac OS X: <code>zlib</code>, <code>bz2</code>,
 <code>_curses</code>, <code>_curses_panel</code>, <code>_hashlib</code>,
@@ -637,7 +972,8 @@
           (<a href="http://b.android.com/55826">Issue 55826</a>)</li>
         <li>Fixed Clang 3.3 MIPS compiler problem where HI and LO registers are incorrectly
           reused.</li>
-        <li>Fixed issue with MIPS 4.7 ICE in {@code dbx_reg_number}. The error message is as follows:
+        <li>Fixed issue with MIPS 4.7 ICE in {@code dbx_reg_number}. The error message is as
+follows:
 <pre>
 external/icu4c/i18n/decimfmt.cpp:1322:1:
 internal compiler error: in dbx_reg_number, at dwarf2out.c:10185
@@ -721,11 +1057,13 @@
         <li>Modified GCC builds so that all {@code libgcc.a} files are built with
           <code>-funwind-tables</code> to allow the stack to be unwound past previously blocked
           points, such as <code>__aeabi_idiv0</code>.</li>
-        <li>Added Ingenic MXU support in MIPS GCC4.6/4.7/4.8 with new <code>-mmxu</code> option.</li>
+        <li>Added Ingenic MXU support in MIPS GCC4.6/4.7/4.8 with new <code>-mmxu</code>
+option.</li>
         <li>Extended MIPS GCC4.6/4.7/4.8 <code>-mldc1-sdc1</code> to control ldxc1/sdxc1 too</li>
         <li>Added crazy linker. For more information, see
           {@code sources/android/crazy_linker/README.TXT}.</li>
-        <li>Fixed {@code bitmap-plasma} to draw to full screen rather than a 200x200 pixel area.</li>
+        <li>Fixed {@code bitmap-plasma} to draw to full screen rather than a 200x200 pixel
+area.</li>
         <li>Reduced linux and darwin toolchain sizes by 25% by creating symlinks to identical files.
           </li>
       </ul>
@@ -935,7 +1273,8 @@
           <li>Added two flags to re-enable two optimizations in upstream Clang but disabled in
               NDK for better compatibility with code compiled by GCC:
             <ul>
-              <li>Added a {@code -fcxx-missing-return-semantics} flag to re-enable <em>missing return
+              <li>Added a {@code -fcxx-missing-return-semantics} flag to re-enable <em>missing
+return
                 semantics</em> in Clang 3.2+. Normally, all paths should terminate with a return
                 statement for a value-returning function. If this is not the case, clang inserts
                 an undefined instruction (or trap in debug mode) at the path without a return
@@ -1079,7 +1418,8 @@
             (<a href="https://android-review.googlesource.com/#/c/52134">Change 52134</a>)</li>
           <li>Fixed Clang 3.1 internal compiler error when using Eigen library.
             (<a href="http://b.android.com/41246">Issue 41246</a>)</li>
-          <li>Fixed Clang 3.1 internal compiler error including {@code &lt;chrono&gt;} in C++11 mode.
+          <li>Fixed Clang 3.1 internal compiler error including {@code &lt;chrono&gt;} in C++11
+mode.
             (<a href="http://b.android.com/39600">Issue 39600</a>)</li>
           <li>Fixed Clang 3.1 internal compiler error when generating object code for a method
             call to a uniform initialized {@code rvalue}.
@@ -1088,7 +1428,8 @@
             (<a href="https://android-review.googlesource.com/#/c/52154">Change 52154</a>)</li>
           <li>Fixed problem with GNU Debugger (GDB) SIGILL when debugging on Android 4.1.2.
             (<a href="http://b.android.com/40941">Issue 40941</a>)</li>
-          <li>Fixed problem where GDB cannot set {@code source:line} breakpoints when symbols contain
+          <li>Fixed problem where GDB cannot set {@code source:line} breakpoints when symbols
+contain
             long, indirect file paths.
             (<a href="http://b.android.com/42448">Issue 42448</a>)</li>
           <li>Fixed GDB {@code read_program_header} for MIPS PIE executables.
@@ -1182,7 +1523,8 @@
           <li>Enabled MIPS floating-point {@code madd/msub/nmadd/nmsub/recip/rsqrt}
             instructions with 32-bit FPU.</li>
           <li>Enabled graphite loop optimizer in GCC 4.6 and 4.7 to allow more optimizations:
-            {@code -fgraphite}, {@code -fgraphite-identity}, {@code -floop-block}, {@code -floop-flatten},
+            {@code -fgraphite}, {@code -fgraphite-identity}, {@code -floop-block}, {@code
+-floop-flatten},
             {@code -floop-interchange}, {@code -floop-strip-mine}, {@code -floop-parallelize-all},
             and {@code -ftree-loop-linear}.
             (<a href="http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html">info</a>)</li>
@@ -1379,7 +1721,9 @@
           <li>Updated {@code make-standalone-toolchain.sh} to accept the suffix {@code -clang3.1}
             which is equivalent to adding {@code --llvm-version=3.1} to the GCC 4.6 toolchain.</li>
           <li>Updated GCC and Clang bug report URL to:
-            <a href="http://source.android.com/source/report-bugs.html">http://source.android.com/source/report-bugs.html</a></li>
+            <a
+href="http://source.android.com/source/report-bugs.html">http://source.android.com/source/report-bug
+s.html</a></li>
           <li>Added ARM ELF support to {@code llvm-objdump}.</li>
           <li>Suppressed <em>treating c input as c++</em> warning for Clang builds.</li>
           <li>Updated build so that only the 32-bit version of {@code libiberty.a} is built and
@@ -1418,7 +1762,8 @@
             <a href="http://code.google.com/p/android/issues/list">report any issues</a>.</p></li>
           <li>Added Gold linker {@code ld.gold} for the Windows toolchain. Gold linker is also the
             default for ARM and X86 on all hosts. You may override it to use the {@code ld.bfd}
-            linker by adding {@code LOCAL_LDFLAGS += -fuse-ld=bfd} to {@code Android.mk}, or by passing
+            linker by adding {@code LOCAL_LDFLAGS += -fuse-ld=bfd} to {@code Android.mk}, or by
+passing
             {@code -fuse-ld=bfd} to the g++/clang++ command line that does the linking.</li>
           <li>Added checks for spaces in the NDK path to the {@code ndk-build[.cmd]} and
             {@code ndk-gdb} scripts, to prevent build errors that are difficult to diagnose.</li>
@@ -1605,7 +1950,8 @@
               <li>Replaced {@code link.h} for MIPS with new version supporting all platforms.</li>
               <li>Removed {@code linux-unistd.h}</li>
               <li>Move GLibc-specific macros {@code LONG_LONG_MIN}, {@code LONG_LONG_MAX} and
-              {@code ULONG_LONG_MAX} from {@code &lt;pthread.h&gt;} to {@code &lt;limits.h&gt;}.</li>
+              {@code ULONG_LONG_MAX} from {@code &lt;pthread.h&gt;} to {@code
+&lt;limits.h&gt;}.</li>
             </ul>
           </li>
           <li>Fixed a buffer overflow in {@code ndk-stack-parser}.</li>
@@ -1767,7 +2113,8 @@
                   <li>Fixed ARM {@code strip} command to preserve the original {@code p_align} and
 {@code p_flags} in {@code GNU_RELRO} section if they are valid. Without this fix, programs
 built with {@code -fPIE} could not be debugged. (<a
-href="http://sourceware.org/cgi-bin/cvsweb.cgi/src/bfd/elf.c.diff?cvsroot=src&r1=1.552&r2=1.553">more info</a>)</li>
+href="http://sourceware.org/cgi-bin/cvsweb.cgi/src/bfd/elf.c.diff?cvsroot=src&r1=1.552&r2=1.553">mor
+e info</a>)</li>
                 </ul>
               </li>
               <li>Disabled {@code sincos()} optimization for compatibility with older
@@ -2505,7 +2852,8 @@
 
             </li>
 
-            <li>You can build a standalone x86 toolchain using the <code>--toolchain=x86-4.4.3</code>
+            <li>You can build a standalone x86 toolchain using the
+<code>--toolchain=x86-4.4.3</code>
             option when calling <code>make-standalone-toolchain.sh</code>. See
             <code>docs/STANDALONE-TOOLCHAIN.html</code> for more details.
             </li>
@@ -2548,7 +2896,8 @@
           <li>Fixed a bug where code linked against <code>gnustl_static</code> crashed when run on
           platform releases older than API level 8 (Android 2.2).</li>
 
-          <li><code>ndk-gdb</code>: Fixed a bug that caused a segmentation fault when debugging Android 3.0
+          <li><code>ndk-gdb</code>: Fixed a bug that caused a segmentation fault when debugging
+Android 3.0
           or newer devices.</li>
 
           <li><code>&lt;android/input.h&gt;</code>: Two functions that were introduced in API level
@@ -2617,12 +2966,14 @@
   </p>
 
   <div class="toggle-content-toggleme">
-      <p>This release of the NDK does not include any new features compared to r5. The r5b release addresses the
+      <p>This release of the NDK does not include any new features compared to r5. The r5b release
+addresses the
       following problems in the r5 release:
       </p>
       <ul>
     <li>The r5 binaries required glibc 2.11, but the r5b binaries are generated with a special
-    toolchain that targets glibc 2.7 or higher instead. The Linux toolchain binaries now run on Ubuntu 8.04 or higher. </li>
+    toolchain that targets glibc 2.7 or higher instead. The Linux toolchain binaries now run on
+Ubuntu 8.04 or higher. </li>
     <li>Fixes a compiler bug in the arm-linux-androideabi-4.4.3 toolchain.
     The previous binary generated invalid thumb instruction sequences when
     dealing with signed chars.</li>
@@ -2642,21 +2993,29 @@
         with the new NDK toolchain.</li>
         <li>Builds in Cygwin are faster by avoiding calls to <code>cygpath -m</code>
         from GNU Make for every source or object file, which caused problems
-        with very large source trees. In case this doesn't work properly, define <code>NDK_USE_CYGPATH=1</code> in your
+        with very large source trees. In case this doesn't work properly, define
+<code>NDK_USE_CYGPATH=1</code> in your
         environment to use <code>cygpath -m</code> again.</li>
-        <li>The Cygwin installation now notifies the user of invalid installation paths that contain spaces. Previously, an invalid path
-        would output an error that complained about an incorrect version of GNU Make, even if the right one was installed.
+        <li>The Cygwin installation now notifies the user of invalid installation paths that
+contain spaces. Previously, an invalid path
+        would output an error that complained about an incorrect version of GNU Make, even if the
+right one was installed.
       </ul>
     </li>
-  <li>Fixed a typo that prevented the <code>NDK_MODULE_PATH</code> environment variable from working properly when
+  <li>Fixed a typo that prevented the <code>NDK_MODULE_PATH</code> environment variable from
+working properly when
   it contained multiple directories separated with a colon. </li>
   <li>The <code>prebuilt-common.sh</code> script contains fixes to check the compiler for 64-bit
   generated machine code, instead of relying on the host tag, which
-  allows the 32-bit toolchain to rebuild properly on Snow Leopard. The toolchain rebuild scripts now also support
+  allows the 32-bit toolchain to rebuild properly on Snow Leopard. The toolchain rebuild scripts
+now also support
   using a 32-bit host toolchain.</li>
-  <li>A missing declaration for <code>INET_ADDRSTRLEN</code> was added to <code>&lt;netinet/in.h&gt;</code>.</li>
-  <li>Missing declarations for <code>IN6_IS_ADDR_MC_NODELOCAL</code> and <code>IN6_IS_ADDR_MC_GLOBAL</code> were added to <code>&lt;netinet/in6.h&gt;</code>.</li>
-  <li>'asm' was replaced with '__asm__' in <code>&lt;asm/byteorder.h&gt;</code> to allow compilation with <code>-std=c99</code>.</li>
+  <li>A missing declaration for <code>INET_ADDRSTRLEN</code> was added to
+<code>&lt;netinet/in.h&gt;</code>.</li>
+  <li>Missing declarations for <code>IN6_IS_ADDR_MC_NODELOCAL</code> and
+<code>IN6_IS_ADDR_MC_GLOBAL</code> were added to <code>&lt;netinet/in6.h&gt;</code>.</li>
+  <li>'asm' was replaced with '__asm__' in <code>&lt;asm/byteorder.h&gt;</code> to allow
+compilation with <code>-std=c99</code>.</li>
   </ul>
   </div>
   </div>
@@ -2673,8 +3032,10 @@
          of native code. Using the APIs, developers have direct native access to events, audio,
          graphics and window management, assets, and storage. Developers can also implement the
          Android application lifecycle in native code with help from the new
-         {@link android.app.NativeActivity} class. For detailed information describing the changes in this
-         release, read the <code>CHANGES.HTML</code> document included in the downloaded NDK package.
+         {@link android.app.NativeActivity} class. For detailed information describing the changes
+in this
+         release, read the <code>CHANGES.HTML</code> document included in the downloaded NDK
+package.
       </p>
       <dl>
         <dt>General notes:</dt>
@@ -2703,35 +3064,46 @@
               </ul>
             </li>
 
-            <li>Includes a new toolchain (based on GCC 4.4.3), which generates better code, and can also now
+            <li>Includes a new toolchain (based on GCC 4.4.3), which generates better code, and can
+also now
             be used as a standalone cross-compiler, for people who want to build their stuff with
             <code>./configure &amp;&amp; make</code>. See
-            docs/STANDALONE-TOOLCHAIN.html for the details. The binaries for GCC 4.4.0 are still provided,
+            docs/STANDALONE-TOOLCHAIN.html for the details. The binaries for GCC 4.4.0 are still
+provided,
             but the 4.2.1 binaries were removed.</li>
 
-            <li>Adds support for prebuilt static and shared libraries (docs/PREBUILTS.html) and module
+            <li>Adds support for prebuilt static and shared libraries (docs/PREBUILTS.html) and
+module
             exports and imports to make sharing and reuse of third-party modules much easier
             (docs/IMPORT-MODULE.html explains why).</li>
 
-            <li>Provides a default C++ STL implementation (based on STLport) as a helper module. It can be used either
-            as a static or shared library (details and usage examples are in sources/android/stlport/README). Prebuilt
-            binaries for STLport (static or shared) and GNU libstdc++ (static only) are also provided if you choose to
+            <li>Provides a default C++ STL implementation (based on STLport) as a helper module. It
+can be used either
+            as a static or shared library (details and usage examples are in
+sources/android/stlport/README). Prebuilt
+            binaries for STLport (static or shared) and GNU libstdc++ (static only) are also
+provided if you choose to
             compile against those libraries instead of the default C++ STL implementation.
-            C++ Exceptions and RTTI are not supported in the default STL implementation. For more information, see
+            C++ Exceptions and RTTI are not supported in the default STL implementation. For more
+information, see
             docs/CPLUSPLUS-SUPPORT.HTML.</li>
 
-            <li>Includes improvements to the <code>cpufeatures</code> helper library that improves reporting
-            of the CPU type (some devices previously reported ARMv7 CPU when the device really was an ARMv6). We
+            <li>Includes improvements to the <code>cpufeatures</code> helper library that improves
+reporting
+            of the CPU type (some devices previously reported ARMv7 CPU when the device really was
+an ARMv6). We
             recommend developers that use this library to rebuild their applications then
             upload to Google Play to benefit from the improvements.</li>
 
             <li>Adds an EGL library that lets you create and manage OpenGL ES textures and
               services.</li>
 
-            <li>Adds new sample applications, <code>native-plasma</code> and <code>native-activity</code>,
+            <li>Adds new sample applications, <code>native-plasma</code> and
+<code>native-activity</code>,
             to demonstrate how to write a native activity.</li>
 
-            <li>Includes many bugfixes and other small improvements; see docs/CHANGES.html for a more
+            <li>Includes many bugfixes and other small improvements; see docs/CHANGES.html for a
+more
               detailed list of changes.</li>
           </ul>
         </dd>
@@ -3043,7 +3415,8 @@
       </table>
 
       <p>For more information about API Level and its relationship to Android platform versions,
-      see <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">Android API Levels</a>.</p>
+      see <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">Android API
+Levels</a>.</p>
     </li>
 
     <li>Additionally, an application using the OpenGL ES APIs should declare a
@@ -3061,7 +3434,8 @@
 </pre>
 
       <p>For more information, see the <a href=
-      "{@docRoot}guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature&gt;</code></a>
+
+"{@docRoot}guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature&gt;</code></a>
       documentation.</p>
     </li>
 
@@ -3104,7 +3478,8 @@
 
   <p>Before you get started make sure that you have downloaded the latest <a href=
   "{@docRoot}sdk/index.html">Android SDK</a> and upgraded your applications and environment as
-  needed. The NDK is compatible with older platform versions but not older versions of the SDK tools.
+  needed. The NDK is compatible with older platform versions but not older versions of the SDK
+tools.
   Also, take a moment to review the <a href="#Reqs">System and
 Software Requirements</a>
   for the NDK, if you haven't already.</p>
@@ -3178,7 +3553,8 @@
     <p>Write a native activity, which allows you to implement the lifecycle callbacks in native
     code. The Android SDK provides the {@link android.app.NativeActivity} class, which is a
     convenience class that notifies your
-    native code of any activity lifecycle callbacks (<code>onCreate()</code>, <code>onPause()</code>,
+    native code of any activity lifecycle callbacks (<code>onCreate()</code>,
+<code>onPause()</code>,
     <code>onResume()</code>, etc). You can implement the callbacks in your native code to handle
     these events when they occur. Applications that use native activities must be run on Android
     2.3 (API Level 9) or later.</p>
@@ -3230,7 +3606,8 @@
   difference between the two instruction sets is that ARMv7-A supports hardware FPU, Thumb-2, and
   NEON instructions. You can target either or both of the instruction sets &mdash; ARMv5TE is the
   default, but switching to ARMv7-A is as easy as adding a single line to the application's
-  <code>Application.mk</code> file, without needing to change anything else in the file. You can also build for
+  <code>Application.mk</code> file, without needing to change anything else in the file. You can
+also build for
   both architectures at the same time and have everything stored in the final <code>.apk</code>.
   Complete information is provided in the CPU-ARCH-ABIS.HTML in the NDK package.</p>
 
@@ -3311,13 +3688,15 @@
 
     <li>CHANGES.HTML &mdash; a complete list of changes to the NDK across all releases.</li>
 
-    <li>DEVELOPMENT.HTML &mdash; describes how to modify the NDK and generate release packages for it</li>
+    <li>DEVELOPMENT.HTML &mdash; describes how to modify the NDK and generate release packages for
+it</li>
 
     <li>HOWTO.HTML &mdash; information about common tasks associated with NDK development</li>
 
     <li>IMPORT-MODULE.HTML &mdash; describes how to share and reuse modules</li>
 
-    <li>LICENSES.HTML  &mdash; information about the various open source licenses that govern the Android NDK</li>
+    <li>LICENSES.HTML  &mdash; information about the various open source licenses that govern the
+Android NDK</li>
 
     <li>NATIVE-ACTIVITY.HTML &mdash; describes how to implement native activities</li>
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 99596ef..5211762 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -158,7 +158,7 @@
         if (nativeCanvas == 0) {
             throw new IllegalStateException();
         }
-        mNativeCanvasWrapper = initCanvas(nativeCanvas);
+        mNativeCanvasWrapper = nativeCanvas;
         mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         mDensity = Bitmap.getDefaultDensity();
     }
@@ -921,7 +921,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawRGB(int r, int g, int b) {
-        native_drawRGB(mNativeCanvasWrapper, r, g, b);
+        drawColor(Color.rgb(r, g, b));
     }
 
     /**
@@ -934,7 +934,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawARGB(int a, int r, int g, int b) {
-        native_drawARGB(mNativeCanvasWrapper, a, r, g, b);
+        drawColor(Color.argb(a, r, g, b));
     }
 
     /**
@@ -944,7 +944,7 @@
      * @param color the color to draw onto the canvas
      */
     public void drawColor(int color) {
-        native_drawColor(mNativeCanvasWrapper, color);
+        native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
     }
 
     /**
@@ -1298,13 +1298,28 @@
      */
     public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
             @Nullable Paint paint) {
-        if (dst == null) {
-            throw new NullPointerException();
-        }
-        throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
-                          paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
-    }
+      if (dst == null) {
+          throw new NullPointerException();
+      }
+      throwIfCannotDraw(bitmap);
+      final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+      float left, top, right, bottom;
+      if (src == null) {
+          left = top = 0;
+          right = bitmap.getWidth();
+          bottom = bitmap.getHeight();
+      } else {
+          left = src.left;
+          right = src.right;
+          top = src.top;
+          bottom = src.bottom;
+      }
+
+      native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, right, bottom,
+              dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
+              bitmap.mDensity);
+  }
 
     /**
      * Draw the specified bitmap, scaling/translating automatically to fill
@@ -1334,8 +1349,23 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
-                paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+        int left, top, right, bottom;
+        if (src == null) {
+            left = top = 0;
+            right = bitmap.getWidth();
+            bottom = bitmap.getHeight();
+        } else {
+            left = src.left;
+            right = src.right;
+            top = src.top;
+            bottom = src.bottom;
+        }
+
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, right, bottom,
+            dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
+            bitmap.mDensity);
     }
 
     /**
@@ -1863,7 +1893,6 @@
     public static native void freeTextLayoutCaches();
 
     private static native long initRaster(long nativeBitmapOrZero);
-    private static native long initCanvas(long canvasHandle);
     private static native void native_setBitmap(long canvasHandle,
                                                 long bitmapHandle,
                                                 boolean copyState);
@@ -1916,11 +1945,6 @@
     private static native boolean native_quickReject(long nativeCanvas,
                                                      float left, float top,
                                                      float right, float bottom);
-    private static native void native_drawRGB(long nativeCanvas, int r, int g,
-                                              int b);
-    private static native void native_drawARGB(long nativeCanvas, int a, int r,
-                                               int g, int b);
-    private static native void native_drawColor(long nativeCanvas, int color);
     private static native void native_drawColor(long nativeCanvas, int color,
                                                 int mode);
     private static native void native_drawPaint(long nativeCanvas,
@@ -1962,16 +1986,9 @@
                                                  int screenDensity,
                                                  int bitmapDensity);
     private native void native_drawBitmap(long nativeCanvas, long nativeBitmap,
-                                                 Rect src, RectF dst,
-                                                 long nativePaintOrZero,
-                                                 int screenDensity,
-                                                 int bitmapDensity);
-    private static native void native_drawBitmap(long nativeCanvas,
-                                                 long nativeBitmap,
-                                                 Rect src, Rect dst,
-                                                 long nativePaintOrZero,
-                                                 int screenDensity,
-                                                 int bitmapDensity);
+            float srcLeft, float srcTop, float srcRight, float srcBottom,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            long nativePaintOrZero, int screenDensity, int bitmapDensity);
     private static native void native_drawBitmap(long nativeCanvas, int[] colors,
                                                 int offset, int stride, float x,
                                                  float y, int width, int height,
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index e7a073c..89236ad 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -516,6 +516,11 @@
      */
     public void setHotspotBounds(int left, int top, int right, int bottom) {}
 
+    /** @hide For internal use only. Individual results may vary. */
+    public void getHotspotBounds(Rect outRect) {
+        outRect.set(getBounds());
+    }
+
     /**
      * Whether this drawable requests projection.
      *
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index ed44cde..771322d 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -52,6 +52,7 @@
      */
     private static final boolean DEFAULT_DITHER = true;
     private DrawableContainerState mDrawableContainerState;
+    private Rect mHotspotBounds;
     private Drawable mCurrDrawable;
     private int mAlpha = 0xFF;
 
@@ -273,11 +274,27 @@
 
     @Override
     public void setHotspotBounds(int left, int top, int right, int bottom) {
+        if (mHotspotBounds == null) {
+            mHotspotBounds = new Rect(left, top, bottom, right);
+        } else {
+            mHotspotBounds.set(left, top, bottom, right);
+        }
+
         if (mCurrDrawable != null) {
             mCurrDrawable.setHotspotBounds(left, top, right, bottom);
         }
     }
 
+    /** @hide */
+    @Override
+    public void getHotspotBounds(Rect outRect) {
+        if (mHotspotBounds != null) {
+            outRect.set(mHotspotBounds);
+        } else {
+            super.getHotspotBounds(outRect);
+        }
+    }
+
     @Override
     protected boolean onStateChange(int[] state) {
         if (mLastDrawable != null) {
@@ -430,6 +447,12 @@
                 d.setBounds(getBounds());
                 d.setLayoutDirection(getLayoutDirection());
                 d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+
+                final Rect hotspotBounds = mHotspotBounds;
+                if (hotspotBounds != null) {
+                    d.setHotspotBounds(hotspotBounds.left, hotspotBounds.top,
+                            hotspotBounds.right, hotspotBounds.bottom);
+                }
             }
         } else {
             mCurrDrawable = null;
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index d214a47..6db96b6 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -232,6 +232,12 @@
         mInsetState.mDrawable.setHotspotBounds(left, top, right, bottom);
     }
 
+    /** @hide */
+    @Override
+    public void getHotspotBounds(Rect outRect) {
+        mInsetState.mDrawable.getHotspotBounds(outRect);
+    }
+
     @Override
     public boolean setVisible(boolean visible, boolean restart) {
         mInsetState.mDrawable.setVisible(visible, restart);
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index fa68bc5..8d83c74 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -80,6 +80,7 @@
     private int[] mPaddingB;
 
     private final Rect mTmpRect = new Rect();
+    private Rect mHotspotBounds;
     private boolean mMutated;
 
     /**
@@ -630,6 +631,22 @@
         for (int i = 0; i < N; i++) {
             array[i].mDrawable.setHotspotBounds(left, top, right, bottom);
         }
+
+        if (mHotspotBounds == null) {
+            mHotspotBounds = new Rect(left, top, right, bottom);
+        } else {
+            mHotspotBounds.set(left, top, right, bottom);
+        }
+    }
+
+    /** @hide */
+    @Override
+    public void getHotspotBounds(Rect outRect) {
+        if (mHotspotBounds != null) {
+            outRect.set(mHotspotBounds);
+        } else {
+            super.getHotspotBounds(outRect);
+        }
     }
 
     @Override
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 4f05313..f955f7c 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -471,6 +471,12 @@
         onHotspotBoundsChanged();
     }
 
+    /** @hide */
+    @Override
+    public void getHotspotBounds(Rect outRect) {
+        outRect.set(mHotspotBounds);
+    }
+
     /**
      * Notifies all the animating ripples that the hotspot bounds have changed.
      */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index c9f541b..7fa1975 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2351,7 +2351,8 @@
         return DrawGlInfo::kStatusDone;
     }
 
-    const Rect& bounds = vertexBuffer.getBounds();
+    Rect bounds(vertexBuffer.getBounds());
+    bounds.translate(translateX, translateY);
     dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
 
     int color = paint->getColor();
diff --git a/media/java/android/media/AudioDevice.java b/media/java/android/media/AudioDevice.java
index 1fd27fe..96d6196 100644
--- a/media/java/android/media/AudioDevice.java
+++ b/media/java/android/media/AudioDevice.java
@@ -66,8 +66,20 @@
         return mConfig.port().address();
     }
 
+    /** @hide */
+    public static int convertDeviceTypeToInternalDevice(int deviceType) {
+        return EXT_TO_INT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+    }
+
+    /** @hide */
+    public static int convertInternalDeviceToDeviceType(int intDevice) {
+        return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, DEVICE_TYPE_UNKNOWN);
+    }
+
     private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
 
+    private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING;
+
     static {
         INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, DEVICE_TYPE_BUILTIN_EARPIECE);
@@ -110,6 +122,27 @@
         // not covered here, legacy
         //AudioSystem.DEVICE_OUT_REMOTE_SUBMIX
         //AudioSystem.DEVICE_IN_REMOTE_SUBMIX
+
+        // privileges mapping to output device
+        EXT_TO_INT_DEVICE_MAPPING = new SparseIntArray();
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_EARPIECE, AudioSystem.DEVICE_OUT_EARPIECE);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_SPEAKER, AudioSystem.DEVICE_OUT_SPEAKER);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_WIRED_HEADSET, AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_WIRED_HEADPHONES, AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_LINE_ANALOG, AudioSystem.DEVICE_OUT_LINE);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_LINE_DIGITAL, AudioSystem.DEVICE_OUT_SPDIF);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_OUT_BLUETOOTH_SCO);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_HDMI, AudioSystem.DEVICE_OUT_HDMI);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_HDMI_ARC, AudioSystem.DEVICE_OUT_HDMI_ARC);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_USB_DEVICE, AudioSystem.DEVICE_OUT_USB_DEVICE);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_USB_ACCESSORY, AudioSystem.DEVICE_OUT_USB_ACCESSORY);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_DOCK, AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_FM, AudioSystem.DEVICE_OUT_FM);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_TELEPHONY, AudioSystem.DEVICE_OUT_TELEPHONY_TX);
     }
 }
 
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 025d354..79be108 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -78,9 +78,9 @@
     public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100;
     public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200;
     public static final int CHANNEL_OUT_BACK_CENTER = 0x400;
-    /** @hide */
+    /** @hide  CANDIDATE FOR PUBLIC API */
     public static final int CHANNEL_OUT_SIDE_LEFT =         0x800;
-    /** @hide */
+    /** @hide  CANDIDATE FOR PUBLIC API */
     public static final int CHANNEL_OUT_SIDE_RIGHT =       0x1000;
     /** @hide */
     public static final int CHANNEL_OUT_TOP_CENTER =       0x2000;
@@ -128,6 +128,35 @@
             CHANNEL_OUT_LOW_FREQUENCY);
     // CHANNEL_OUT_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_OUT_ALL
 
+    /**
+     * @hide
+     * Return the number of channels from an output channel mask
+     * @param mask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT
+     * @return number of channels for the mask
+     */
+    public static int channelCountFromOutChannelMask(int mask) {
+        return Integer.bitCount(mask);
+    }
+    /**
+     * @hide
+     * Return a channel mask ready to be used by native code
+     * @param mask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT
+     * @return a native channel mask
+     */
+    public static int convertChannelOutMaskToNativeMask(int javaMask) {
+        return (javaMask >> 2);
+    }
+
+    /**
+     * @hide
+     * Return a java output channel mask
+     * @param mask a native channel mask
+     * @return a combination of the CHANNEL_OUT_* definitions
+     */
+    public static int convertNativeChannelMaskToOutMask(int nativeMask) {
+        return (nativeMask << 2);
+    }
+
     public static final int CHANNEL_IN_DEFAULT = 1;
     // These directly match native
     public static final int CHANNEL_IN_LEFT = 0x4;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 4abcb81..f84c383 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -136,14 +136,17 @@
  * cycle is necessary.
  *
  * <p> During its life, a codec conceptually exists in one of the following states:
- * Initialized, Configured, Executing, Uninitialized, (omitting transitory states
+ * Initialized, Configured, Executing, Error, Uninitialized, (omitting transitory states
  * between them). When created by one of the factory methods,
  * the codec is in the Initialized state; {@link #configure} brings it to the
  * Configured state; {@link #start} brings it to the Executing state.
  * In the Executing state, decoding or encoding occurs through the buffer queue
  * manipulation described above. The method {@link #stop}
  * returns the codec to the Initialized state, whereupon it may be configured again,
- * and {@link #release} brings the codec to the terminal Uninitialized state.
+ * and {@link #release} brings the codec to the terminal Uninitialized state.  When
+ * a codec error occurs, the codec moves to the Error state.  Use {@link #reset} to
+ * bring the codec back to the Initialized state, or {@link #release} to move it
+ * to the Uninitialized state.
  *
  * <p> The factory methods
  * {@link #createByCodecName},
@@ -170,7 +173,8 @@
  * then resources are temporarily unavailable and the method may be retried at a later time.
  * If both {@link MediaCodec.CodecException#isRecoverable}
  * and {@link MediaCodec.CodecException#isTransient} return false,
- * then the {@link MediaCodec.CodecException} is fatal and the codec must be released.
+ * then the {@link MediaCodec.CodecException} is fatal and the codec must be
+ * {@link #reset reset} or {@link #release released}.
  * Both {@link MediaCodec.CodecException#isRecoverable} and
  * {@link MediaCodec.CodecException#isTransient} do not return true at the same time.
  */
@@ -429,6 +433,23 @@
     }
 
     /**
+     * Returns the codec to its initial (Initialized) state.
+     *
+     * Call this if an {@link MediaCodec.CodecException#isRecoverable unrecoverable}
+     * error has occured to reset the codec to its initial state after creation.
+     *
+     * @throws CodecException if an unrecoverable error has occured and the codec
+     * could not be reset.
+     * @throws IllegalStateException if in the Uninitialized state.
+     */
+    public final void reset() {
+        freeAllTrackedBuffers(); // free buffers first
+        native_reset();
+    }
+
+    private native final void native_reset();
+
+    /**
      * Make sure you call this when you're done to free up any opened
      * component instance instead of relying on the garbage collector
      * to do this for you at some point in the future.
@@ -646,9 +667,10 @@
     /**
      * After filling a range of the input buffer at the specified index
      * submit it to the component. Once an input buffer is queued to
-     * the codec, it MUST not be used until it is later retrieved by
-     * {#getInputBuffer} in response to a {#dequeueInputBuffer}
-     * response.
+     * the codec, it MUST NOT be used until it is later retrieved by
+     * {@link #getInputBuffer} in response to a {@link #dequeueInputBuffer}
+     * return value or a {@link Callback#onInputBufferAvailable}
+     * callback.
      * <p>
      * Many decoders require the actual compressed data stream to be
      * preceded by "codec specific data", i.e. setup data used to initialize
@@ -905,9 +927,10 @@
      * the codec. If you previously specified a surface when configuring this
      * video decoder you can optionally render the buffer.
      *
-     * Once an output buffer is released to the codec, it MUST not
-     * be used until it is later retrieved by {#getOutputBuffer} in
-     * response to a {#dequeueOutputBuffer} response
+     * Once an output buffer is released to the codec, it MUST NOT
+     * be used until it is later retrieved by {@link #getOutputBuffer} in response
+     * to a {@link #dequeueOutputBuffer} return value or a
+     * {@link Callback#onOutputBufferAvailable} callback.
      *
      * @param index The index of a client-owned output buffer previously returned
      *              from a call to {@link #dequeueOutputBuffer}.
@@ -961,9 +984,10 @@
      * </td></tr>
      * </table>
      *
-     * Once an output buffer is released to the codec, it MUST not
-     * be used until it is later retrieved by {#getOutputBuffer} in
-     * response to a {#dequeueOutputBuffer} response
+     * Once an output buffer is released to the codec, it MUST NOT
+     * be used until it is later retrieved by {@link #getOutputBuffer} in response
+     * to a {@link #dequeueOutputBuffer} return value or a
+     * {@link Callback#onOutputBufferAvailable} callback.
      *
      * @param index The index of a client-owned output buffer previously returned
      *              from a call to {@link #dequeueOutputBuffer}.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ab65ba0..5e398c6 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -378,6 +378,13 @@
  *     <td>Successful invoke of this method in a valid state does not change
  *         the state. Calling this method in an invalid state transfers the
  *         object to the <em>Error</em> state. </p></td></tr>
+ * <tr><td>setAudioAttributes </p></td>
+ *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
+ *          PlaybackCompleted}</p></td>
+ *     <td>{Error}</p></td>
+ *     <td>Successful invoke of this method does not change the state. In order for the
+ *         target audio attributes type to become effective, this method must be called before
+ *         prepare() or prepareAsync().</p></td></tr>
  * <tr><td>setAudioSessionId </p></td>
  *     <td>{Idle} </p></td>
  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
@@ -787,6 +794,10 @@
      * <p>When done with the MediaPlayer, you should call  {@link #release()},
      * to free the resources. If not released, too many MediaPlayer instances will
      * result in an exception.</p>
+     * <p>Note that since {@link #prepare()} is called automatically in this method,
+     * you cannot change the audio stream type (see {@link #setAudioStreamType(int)}), audio
+     * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
+     * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
      *
      * @param context the Context to use
      * @param uri the Uri from which to get the datasource
@@ -802,6 +813,10 @@
      * <p>When done with the MediaPlayer, you should call  {@link #release()},
      * to free the resources. If not released, too many MediaPlayer instances will
      * result in an exception.</p>
+     * <p>Note that since {@link #prepare()} is called automatically in this method,
+     * you cannot change the audio stream type (see {@link #setAudioStreamType(int)}), audio
+     * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
+     * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
      *
      * @param context the Context to use
      * @param uri the Uri from which to get the datasource
@@ -840,6 +855,10 @@
      * <p>When done with the MediaPlayer, you should call  {@link #release()},
      * to free the resources. If not released, too many MediaPlayer instances will
      * result in an exception.</p>
+     * <p>Note that since {@link #prepare()} is called automatically in this method,
+     * you cannot change the audio stream type (see {@link #setAudioStreamType(int)}), audio
+     * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
+     * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
      *
      * @param context the Context to use
      * @param resid the raw resource id (<var>R.raw.&lt;something></var>) for
@@ -1454,16 +1473,15 @@
     private native boolean setParameter(int key, Parcel value);
 
     /**
-     * @hide
-     * CANDIDATE FOR PUBLIC API
-     * Must call this method before prepare() or
-     * prepareAsync() in order for the audio attributes to become effective
-     * thereafter.
+     * Sets the audio attributes for this MediaPlayer.
+     * See {@link AudioAttributes} for how to build and configure an instance of this class.
+     * You must call this method before {@link #prepare()} or {@link #prepareAsync()} in order
+     * for the audio attributes to become effective thereafter.
      * @param attributes a non-null set of audio attributes
      */
     public void setAudioAttributes(AudioAttributes attributes) throws IllegalArgumentException {
         if (attributes == null) {
-            final String msg = "Cannot set audio attributes to null";
+            final String msg = "Cannot set AudioAttributes to null";
             throw new IllegalArgumentException(msg);
         }
         Parcel pattributes = Parcel.obtain();
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 9b381cc..9fa3f50 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -1282,7 +1282,7 @@
     /**
      * @hide
      */
-    public int byteArrayToInt(byte[] valueBuf) {
+    public static int byteArrayToInt(byte[] valueBuf) {
         return byteArrayToInt(valueBuf, 0);
 
     }
@@ -1290,7 +1290,7 @@
     /**
      * @hide
      */
-    public int byteArrayToInt(byte[] valueBuf, int offset) {
+    public static int byteArrayToInt(byte[] valueBuf, int offset) {
         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
         converter.order(ByteOrder.nativeOrder());
         return converter.getInt(offset);
@@ -1300,7 +1300,7 @@
     /**
      * @hide
      */
-    public byte[] intToByteArray(int value) {
+    public static byte[] intToByteArray(int value) {
         ByteBuffer converter = ByteBuffer.allocate(4);
         converter.order(ByteOrder.nativeOrder());
         converter.putInt(value);
@@ -1310,14 +1310,14 @@
     /**
      * @hide
      */
-    public short byteArrayToShort(byte[] valueBuf) {
+    public static short byteArrayToShort(byte[] valueBuf) {
         return byteArrayToShort(valueBuf, 0);
     }
 
     /**
      * @hide
      */
-    public short byteArrayToShort(byte[] valueBuf, int offset) {
+    public static short byteArrayToShort(byte[] valueBuf, int offset) {
         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
         converter.order(ByteOrder.nativeOrder());
         return converter.getShort(offset);
@@ -1327,7 +1327,7 @@
     /**
      * @hide
      */
-    public byte[] shortToByteArray(short value) {
+    public static byte[] shortToByteArray(short value) {
         ByteBuffer converter = ByteBuffer.allocate(2);
         converter.order(ByteOrder.nativeOrder());
         short sValue = (short) value;
@@ -1338,7 +1338,7 @@
     /**
      * @hide
      */
-    public byte[] concatArrays(byte[]... arrays) {
+    public static byte[] concatArrays(byte[]... arrays) {
         int len = 0;
         for (byte[] a : arrays) {
             len += a.length;
diff --git a/media/java/android/media/audiofx/Virtualizer.java b/media/java/android/media/audiofx/Virtualizer.java
index 6b20006..136761b 100644
--- a/media/java/android/media/audiofx/Virtualizer.java
+++ b/media/java/android/media/audiofx/Virtualizer.java
@@ -16,9 +16,13 @@
 
 package android.media.audiofx;
 
+import android.media.AudioDevice;
+import android.media.AudioFormat;
 import android.media.audiofx.AudioEffect;
 import android.util.Log;
 
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.StringTokenizer;
 
 
@@ -44,8 +48,10 @@
 public class Virtualizer extends AudioEffect {
 
     private final static String TAG = "Virtualizer";
+    private final static boolean DEBUG = false;
 
-    // These constants must be synchronized with those in frameworks/base/include/media/EffectVirtualizerApi.h
+    // These constants must be synchronized with those in
+    //        system/media/audio_effects/include/audio_effects/effect_virtualizer.h
     /**
      * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter().
      */
@@ -55,6 +61,21 @@
      * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
      */
     public static final int PARAM_STRENGTH = 1;
+    /**
+     * @hide
+     * Parameter ID to query the virtual speaker angles for a channel mask / device configuration.
+     */
+    public static final int PARAM_VIRTUAL_SPEAKER_ANGLES = 2;
+    /**
+     * @hide
+     * Parameter ID to force the virtualization mode to be that of a specific device
+     */
+    public static final int PARAM_FORCE_VIRTUALIZATION_MODE = 3;
+    /**
+     * @hide
+     * Parameter ID to query the current virtualization mode.
+     */
+    public static final int PARAM_VIRTUALIZATION_MODE = 4;
 
     /**
      * Indicates if strength parameter is supported by the virtualizer engine
@@ -145,6 +166,223 @@
     }
 
     /**
+     * Checks if a configuration is supported, and query the virtual speaker angles.
+     * @param inputChannelMask
+     * @param deviceType
+     * @param angles if non-null: array in which the angles will be written. If null, no angles
+     *    are returned
+     * @return true if the combination of channel mask and output device type is supported, false
+     *    otherwise
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @throws UnsupportedOperationException
+     */
+    private boolean getAnglesInt(int inputChannelMask, int deviceType, int[] angles)
+            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+        // parameter check
+        if (inputChannelMask == AudioFormat.CHANNEL_INVALID) {
+            throw (new IllegalArgumentException(
+                    "Virtualizer: illegal CHANNEL_INVALID channel mask"));
+        }
+        int channelMask = inputChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT ?
+                AudioFormat.CHANNEL_OUT_STEREO : inputChannelMask;
+        int nbChannels = AudioFormat.channelCountFromOutChannelMask(channelMask);
+        if ((angles != null) && (angles.length < (nbChannels * 3))) {
+            Log.e(TAG, "Size of array for angles cannot accomodate number of channels in mask ("
+                    + nbChannels + ")");
+            throw (new IllegalArgumentException(
+                    "Virtualizer: array for channel / angle pairs is too small: is " + angles.length
+                    + ", should be " + (nbChannels * 3)));
+        }
+
+        ByteBuffer paramsConverter = ByteBuffer.allocate(3 /* param + mask + device*/ * 4);
+        paramsConverter.order(ByteOrder.nativeOrder());
+        paramsConverter.putInt(PARAM_VIRTUAL_SPEAKER_ANGLES);
+        // convert channel mask to internal native representation
+        paramsConverter.putInt(AudioFormat.convertChannelOutMaskToNativeMask(channelMask));
+        // convert Java device type to internal representation
+        paramsConverter.putInt(AudioDevice.convertDeviceTypeToInternalDevice(deviceType));
+        // allocate an array to store the results
+        byte[] result = new byte[nbChannels * 4/*int to byte*/ * 3/*for mask, azimuth, elevation*/];
+
+        // call into the effect framework
+        int status = getParameter(paramsConverter.array(), result);
+        if (DEBUG) {
+            Log.v(TAG, "getAngles(0x" + Integer.toHexString(inputChannelMask) + ", 0x"
+                    + Integer.toHexString(deviceType) + ") returns " + status);
+        }
+
+        if (status >= 0) {
+            if (angles != null) {
+                // convert and copy the results
+                ByteBuffer resultConverter = ByteBuffer.wrap(result);
+                resultConverter.order(ByteOrder.nativeOrder());
+                for (int i = 0 ; i < nbChannels ; i++) {
+                    // write the channel mask
+                    angles[3 * i] = AudioFormat.convertNativeChannelMaskToOutMask(
+                            resultConverter.getInt((i * 4 * 3)));
+                    // write the azimuth
+                    angles[3 * i + 1] = resultConverter.getInt(i * 4 * 3 + 4);
+                    // write the elevation
+                    angles[3 * i + 2] = resultConverter.getInt(i * 4 * 3 + 8);
+                    if (DEBUG) {
+                        Log.v(TAG, "channel 0x" + Integer.toHexString(angles[3*i]).toUpperCase()
+                                + " at az=" + angles[3*i+1] + "deg"
+                                + " elev="  + angles[3*i+2] + "deg");
+                    }
+                }
+            }
+            return true;
+        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+            // a BAD_VALUE return from getParameter indicates the configuration is not supported
+            // don't throw an exception, just return false
+            return false;
+        } else {
+            // something wrong may have happened
+            checkStatus(status);
+        }
+        // unexpected virtualizer behavior
+        Log.e(TAG, "unexpected status code " + status
+                + " after getParameter(PARAM_VIRTUAL_SPEAKER_ANGLES)");
+        return false;
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Checks if the combination of a channel mask and device type is supported by this virtualizer.
+     * Some virtualizer implementations may only support binaural processing (i.e. only support
+     * headphone output), some may support transaural processing (i.e. for speaker output) for the
+     * built-in speakers. Use this method to query the virtualizer implementation capabilities.
+     * @param inputChannelMask the channel mask of the content to virtualize.
+     * @param deviceType the device type for which virtualization processing is to be performed.
+     *    Valid values are the device types defined in {@link AudioDevice}.
+     * @return true if the combination of channel mask and output device type is supported, false
+     *    otherwise.
+     *    <br>An indication that a certain channel mask is not supported doesn't necessarily mean
+     *    you cannot play content with that channel mask, it more likely implies the content will
+     *    be downmixed before being virtualized. For instance a virtualizer that only supports a
+     *    mask such as {@link AudioFormat#CHANNEL_OUT_STEREO}
+     *    will still be able to process content with a mask of
+     *    {@link AudioFormat#CHANNEL_OUT_5POINT1}, but will downmix the content to stereo first, and
+     *    then will virtualize, as opposed to virtualizing each channel individually.
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @throws UnsupportedOperationException
+     */
+    public boolean canVirtualize(int inputChannelMask, int deviceType)
+            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+        return getAnglesInt(inputChannelMask, deviceType, null);
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel
+     * mask and device type.
+     * If the virtualization configuration (mask and device) is supported (see
+     * {@link #canVirtualize(int, int)}, the array angles will contain upon return the
+     * definition of each virtual speaker and its azimuth and elevation angles relative to the
+     * listener.
+     * <br>Note that in some virtualizer implementations, the angles may be strength-dependent.
+     * @param inputChannelMask the channel mask of the content to virtualize.
+     * @param deviceType the device type for which virtualization processing is to be performed.
+     *    Valid values are the device types defined in {@link AudioDevice}.
+     * @param angles a non-null array whose length is 3 times the number of channels in the channel
+     *    mask.
+     *    If the method indicates the configuration is supported, the array will contain upon return
+     *    triplets of values: for each channel <code>i</code> among the channels of the mask:
+     *    <ul>
+     *      <li>the element at index <code>3*i</code> in the array contains the speaker
+     *          identification (e.g. {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT}),</li>
+     *      <li>the element at index <code>3*i+1</code> contains its corresponding azimuth angle
+     *          expressed in degrees, where 0 is the direction the listener faces, 180 is behind
+     *          the listener, and -90 is to her/his left,</li>
+     *      <li>the element at index <code>3*i+2</code> contains its corresponding elevation angle
+     *          where +90 is directly above the listener, 0 is the horizontal plane, and -90 is
+     *          directly below the listener.</li>
+     * @return true if the combination of channel mask and output device type is supported, false
+     *    otherwise.
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @throws UnsupportedOperationException
+     */
+    public boolean getSpeakerAngles(int inputChannelMask, int deviceType, int[] angles)
+            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+        if (angles == null) {
+            throw (new IllegalArgumentException(
+                    "Virtualizer: illegal null channel / angle array"));
+        }
+
+        return getAnglesInt(inputChannelMask, deviceType, angles);
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Forces the virtualizer effect to use the processing mode used for the given device type.
+     * The effect must be enabled for the forced mode to be applied.
+     * @param deviceType one of the device types defined in {@link AudioDevice}.
+     *     Use {@link AudioDevice#DEVICE_TYPE_UNKNOWN} to return to the non-forced mode.
+     * @return true if the processing mode for the device type is supported, and it is successfully
+     *     set, or forcing was successfully disabled with {@link AudioDevice#DEVICE_TYPE_UNKNOWN},
+     *     false otherwise.
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @throws UnsupportedOperationException
+     */
+    public boolean forceVirtualizationMode(int deviceType)
+            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+        // convert Java device type to internal representation
+        int internalDevice = AudioDevice.convertDeviceTypeToInternalDevice(deviceType);
+
+        int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice);
+
+        if (status >= 0) {
+            return true;
+        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+            // a BAD_VALUE return from setParameter indicates the mode can't be forced to that
+            // of this device, don't throw an exception, just return false
+            return false;
+        } else {
+            // something wrong may have happened
+            checkStatus(status);
+        }
+        // unexpected virtualizer behavior
+        Log.e(TAG, "unexpected status code " + status
+                + " after setParameter(PARAM_FORCE_VIRTUALIZATION_MODE)");
+        return false;
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Return the device type which reflects the virtualization mode being used, if any.
+     * @return a device type (as defined in {@link AudioDevice}) which reflects the virtualization
+     *     mode being used.
+     *     If virtualization is not active, the device type will be
+     *     {@link AudioDevice#DEVICE_TYPE_UNKNOWN}. Virtualization may not be active either because
+     *     the effect is not enabled or because the current output device is not compatible with
+     *     this virtualization implementation.
+     */
+    public int getVirtualizationMode() {
+        int[] value = new int[1];
+        int status = getParameter(PARAM_VIRTUALIZATION_MODE, value);
+        if (status >= 0) {
+            return AudioDevice.convertInternalDeviceToDeviceType(value[0]);
+        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+            return AudioDevice.DEVICE_TYPE_UNKNOWN;
+        } else {
+            // something wrong may have happened
+            checkStatus(status);
+        }
+        // unexpected virtualizer behavior
+        Log.e(TAG, "unexpected status code " + status
+                + " after getParameter(PARAM_VIRTUALIZATION_MODE)");
+        return AudioDevice.DEVICE_TYPE_UNKNOWN;
+    }
+
+    /**
      * The OnParameterChangeListener interface defines a method called by the Virtualizer when a
      * parameter value has changed.
      */
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index b7294b8..04ff098 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -197,6 +197,10 @@
     return mCodec->flush();
 }
 
+status_t JMediaCodec::reset() {
+    return mCodec->reset();
+}
+
 status_t JMediaCodec::queueInputBuffer(
         size_t index,
         size_t offset, size_t size, int64_t timeUs, uint32_t flags,
@@ -854,6 +858,26 @@
     throwExceptionAsNecessary(env, err);
 }
 
+static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
+    ALOGV("android_media_MediaCodec_reset");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        // should never be here
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = codec->reset();
+    if (err != OK) {
+        // treat all errors as fatal for now, though resource not available
+        // errors could be treated as transient.
+        err = 0x80000000;
+    }
+    throwExceptionAsNecessary(env, err);
+}
+
 static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
     ALOGV("android_media_MediaCodec_flush");
 
@@ -1398,6 +1422,8 @@
 static JNINativeMethod gMethods[] = {
     { "native_release", "()V", (void *)android_media_MediaCodec_release },
 
+    { "native_reset", "()V", (void *)android_media_MediaCodec_reset },
+
     { "native_setCallback",
       "(Landroid/media/MediaCodec$Callback;)V",
       (void *)android_media_MediaCodec_native_setCallback },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 2e650e3..dbccb0f 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -56,6 +56,7 @@
 
     status_t start();
     status_t stop();
+    status_t reset();
 
     status_t flush();
 
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 260ee39..9a62864 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -69,7 +69,7 @@
         <activity
             android:name=".ui.SelectPrinterActivity"
             android:label="@string/all_printers_label"
-            android:theme="@style/SelectPrinterActivityTheme"
+            android:theme="@android:style/Theme.Material.Settings"
             android:exported="false">
         </activity>
 
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_check_circle.png b/packages/PrintSpooler/res/drawable-hdpi/ic_check_circle.png
new file mode 100644
index 0000000..4ad5417
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/ic_check_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png
new file mode 100644
index 0000000..b6a5eb5
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less_24dp.png
deleted file mode 100644
index d2e5408..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png
new file mode 100644
index 0000000..4e36bd2
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more_24dp.png
deleted file mode 100644
index f4c4b0c..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_remove_circle.png b/packages/PrintSpooler/res/drawable-hdpi/ic_remove_circle.png
new file mode 100644
index 0000000..ef053b6
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/ic_remove_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_check_circle.png b/packages/PrintSpooler/res/drawable-mdpi/ic_check_circle.png
new file mode 100644
index 0000000..f66065a
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/ic_check_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png
new file mode 100644
index 0000000..428a946
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png
new file mode 100644
index 0000000..fbbd094
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_remove_circle.png b/packages/PrintSpooler/res/drawable-mdpi/ic_remove_circle.png
new file mode 100644
index 0000000..7e044ac
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/ic_remove_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_check_circle.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_check_circle.png
new file mode 100644
index 0000000..d8ea4d2
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/ic_check_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png
new file mode 100644
index 0000000..6161c20
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less_24dp.png
deleted file mode 100644
index f007427..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png
new file mode 100644
index 0000000..3a89805
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more_24dp.png
deleted file mode 100644
index 43debb3..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_remove_circle.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_remove_circle.png
new file mode 100644
index 0000000..622989c
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/ic_remove_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_check_circle.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_check_circle.png
new file mode 100644
index 0000000..ac36eba
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxhdpi/ic_check_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png
new file mode 100644
index 0000000..52a52d9
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less_24dp.png
deleted file mode 100644
index 39bc2ba..0000000
--- a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png
new file mode 100644
index 0000000..15e6abd
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more_24dp.png
deleted file mode 100644
index 664f3f2..0000000
--- a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_remove_circle.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_remove_circle.png
new file mode 100644
index 0000000..303ccfb
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxhdpi/ic_remove_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_check_circle.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_check_circle.png
new file mode 100644
index 0000000..1737f0a
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_check_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png
new file mode 100644
index 0000000..46811a1
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less_24dp.png
deleted file mode 100644
index fe9c539..0000000
--- a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png
new file mode 100644
index 0000000..141f28b
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more_24dp.png
deleted file mode 100644
index 18d075c..0000000
--- a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_remove_circle.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_remove_circle.png
new file mode 100644
index 0000000..e9c6252
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_remove_circle.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable/ic_expand_less.xml b/packages/PrintSpooler/res/drawable/ic_expand_less.xml
index b0c7d51..6f1ece1 100644
--- a/packages/PrintSpooler/res/drawable/ic_expand_less.xml
+++ b/packages/PrintSpooler/res/drawable/ic_expand_less.xml
@@ -20,7 +20,7 @@
     <item
         android:state_checked="true">
         <bitmap
-            android:src="@drawable/ic_expand_less_24dp"
+            android:src="@drawable/ic_expand_less"
             android:tint="?android:attr/colorControlActivated">
         </bitmap>
     </item>
@@ -28,14 +28,14 @@
     <item
         android:state_pressed="true">
         <bitmap
-            android:src="@drawable/ic_expand_less_24dp"
+            android:src="@drawable/ic_expand_less"
             android:tint="?android:attr/colorControlActivated">
         </bitmap>
     </item>
 
     <item>
         <bitmap
-            android:src="@drawable/ic_expand_less_24dp"
+            android:src="@drawable/ic_expand_less"
             android:tint="?android:attr/colorControlNormal">
         </bitmap>
     </item>
diff --git a/packages/PrintSpooler/res/drawable/ic_expand_more.xml b/packages/PrintSpooler/res/drawable/ic_expand_more.xml
index b809c25..8d71452 100644
--- a/packages/PrintSpooler/res/drawable/ic_expand_more.xml
+++ b/packages/PrintSpooler/res/drawable/ic_expand_more.xml
@@ -20,7 +20,7 @@
     <item
         android:state_checked="true">
         <bitmap
-            android:src="@drawable/ic_expand_more_24dp"
+            android:src="@drawable/ic_expand_more"
             android:tint="?android:attr/colorControlActivated">
         </bitmap>
     </item>
@@ -28,14 +28,14 @@
     <item
         android:state_pressed="true">
         <bitmap
-            android:src="@drawable/ic_expand_more_24dp"
+            android:src="@drawable/ic_expand_more"
             android:tint="?android:attr/colorControlActivated">
         </bitmap>
     </item>
 
     <item>
         <bitmap
-            android:src="@drawable/ic_expand_more_24dp"
+            android:src="@drawable/ic_expand_more"
             android:tint="?android:attr/colorControlNormal">
         </bitmap>
     </item>
diff --git a/packages/PrintSpooler/res/drawable/ic_search.xml b/packages/PrintSpooler/res/drawable/ic_search.xml
new file mode 100644
index 0000000..991fa38
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/ic_search.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true">
+
+    <item
+        android:state_checked="true">
+        <bitmap
+            android:src="@*android:drawable/ic_menu_search"
+            android:tint="?android:attr/colorControlActivated">
+        </bitmap>
+    </item>
+
+    <item
+        android:state_pressed="true">
+        <bitmap
+            android:src="@*android:drawable/ic_menu_search"
+            android:tint="?android:attr/colorControlActivated">
+        </bitmap>
+    </item>
+
+    <item>
+        <bitmap
+            android:src="@*android:drawable/ic_menu_search"
+            android:tint="?android:attr/colorControlNormal">
+        </bitmap>
+    </item>
+
+</selector>
diff --git a/packages/PrintSpooler/res/drawable/page_selector_background.xml b/packages/PrintSpooler/res/drawable/page_selector_background.xml
new file mode 100644
index 0000000..7f1da31
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/page_selector_background.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:enterFadeDuration="@android:integer/config_shortAnimTime"
+    android:exitFadeDuration="@android:integer/config_shortAnimTime">
+
+    <item
+        android:state_selected="true">
+        <bitmap
+            android:src="@drawable/ic_check_circle"
+            android:tint="@color/promoted_action_background_color">
+        </bitmap>
+    </item>
+
+    <item>
+        <bitmap
+            android:src="@drawable/ic_remove_circle"
+            android:tint="@color/promoted_action_background_color">
+        </bitmap>
+    </item>
+
+</selector>
diff --git a/packages/PrintSpooler/res/drawable/print_button_background.xml b/packages/PrintSpooler/res/drawable/print_button_background.xml
index 7b9aea5..aec8474 100644
--- a/packages/PrintSpooler/res/drawable/print_button_background.xml
+++ b/packages/PrintSpooler/res/drawable/print_button_background.xml
@@ -18,7 +18,7 @@
     android:shape="oval">
 
     <solid
-        android:color="#FF00E5FF">
+        android:color="@color/promoted_action_background_color">
     </solid>
 
     <size
diff --git a/packages/PrintSpooler/res/layout/preview_page.xml b/packages/PrintSpooler/res/layout/preview_page.xml
index 0e314d1..509a1d2 100644
--- a/packages/PrintSpooler/res/layout/preview_page.xml
+++ b/packages/PrintSpooler/res/layout/preview_page.xml
@@ -31,7 +31,7 @@
     <RelativeLayout
         android:id="@+id/page_footer"
         android:layout_width="fill_parent"
-        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:layout_height="32dip"
         android:background="@*android:color/material_grey_500"
         android:orientation="horizontal">
 
@@ -40,18 +40,19 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_centerInParent="true"
-            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textAppearance="?android:attr/textAppearanceSmall"
             android:textColor="?android:attr/textColorPrimary">
         </TextView>
 
-        <CheckBox
+        <ImageView
             android:id="@+id/page_selector"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginRight="8dip"
             android:layout_alignParentEnd="true"
-            android:layout_centerVertical="true">
-        </CheckBox>
+            android:layout_centerVertical="true"
+            android:background="@drawable/page_selector_background">
+        </ImageView>
 
     </RelativeLayout>
 
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 8896a7b..3905646 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -29,7 +29,7 @@
         android:layout_height="wrap_content"
         android:padding="16dip"
         android:elevation="@dimen/preview_controls_elevation"
-        android:background="?android:attr/colorForegroundInverse">
+        android:background="?android:attr/colorPrimary">
 
         <Spinner
             android:id="@+id/destination_spinner"
@@ -51,7 +51,7 @@
         android:paddingEnd="16dip"
         android:orientation="horizontal"
         android:elevation="@dimen/preview_controls_elevation"
-        android:background="?android:attr/colorForegroundInverse">
+        android:background="?android:attr/colorPrimary">
 
         <TextView
             android:layout_width="wrap_content"
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 2da0714..ef6044a 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -22,7 +22,7 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:elevation="@dimen/preview_controls_elevation"
-    android:background="?android:attr/colorForegroundInverse">
+    android:background="?android:attr/colorPrimary">
 
         <LinearLayout
          android:id="@+id/draggable_content"
@@ -61,6 +61,8 @@
                      android:layout_width="fill_parent"
                      android:layout_height="wrap_content"
                      style="?android:attr/editTextStyle"
+                     android:singleLine="true"
+                     android:ellipsize="end"
                      android:inputType="numberDecimal">
                  </view>
 
@@ -88,8 +90,7 @@
                  <Spinner
                      android:id="@+id/paper_size_spinner"
                      android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="@style/PrintOptionSpinnerStyle">
+                     android:layout_height="wrap_content">
                  </Spinner>
 
              </LinearLayout>
@@ -116,8 +117,7 @@
                  <Spinner
                      android:id="@+id/color_spinner"
                      android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="@style/PrintOptionSpinnerStyle">
+                     android:layout_height="wrap_content">
                  </Spinner>
 
              </LinearLayout>
@@ -144,8 +144,7 @@
                  <Spinner
                      android:id="@+id/orientation_spinner"
                      android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="@style/PrintOptionSpinnerStyle">
+                     android:layout_height="wrap_content">
                  </Spinner>
 
              </LinearLayout>
@@ -160,21 +159,19 @@
                  <!-- Range options -->
 
                  <TextView
-                     android:id="@+id/range_options_title"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:layout_marginTop="8dip"
                      android:layout_marginStart="12dip"
                      android:textAppearance="?android:attr/textAppearanceSmall"
                      android:labelFor="@+id/range_options_spinner"
-                     android:text="@string/page_count_unknown">
+                     android:text="@string/label_pages">
                  </TextView>
 
                  <Spinner
                      android:id="@+id/range_options_spinner"
                      android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="@style/PrintOptionSpinnerStyle">
+                     android:layout_height="wrap_content">
                  </Spinner>
 
              </LinearLayout>
@@ -207,7 +204,8 @@
                      android:layout_width="fill_parent"
                      android:layout_height="wrap_content"
                      android:layout_gravity="bottom|fill_horizontal"
-                     style="@style/PrintOptionEditTextStyle"
+                     android:singleLine="true"
+                     android:ellipsize="end"
                      android:visibility="visible"
                      android:inputType="textNoSuggestions">
                  </view>
diff --git a/packages/PrintSpooler/res/layout/printer_list_item.xml b/packages/PrintSpooler/res/layout/printer_list_item.xml
index 1f5efbc..7bc144a 100644
--- a/packages/PrintSpooler/res/layout/printer_list_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_list_item.xml
@@ -49,7 +49,7 @@
             android:ellipsize="end"
             android:textIsSelectable="false"
             android:gravity="top|start"
-            android:textColor="?android:attr/textColorSecondary"
+            android:textColor="?android:attr/textColorPrimary"
             android:duplicateParentState="true">
         </TextView>
 
@@ -62,7 +62,7 @@
             android:ellipsize="end"
             android:textIsSelectable="false"
             android:visibility="gone"
-            android:textColor="?android:attr/textColorSecondary"
+            android:textColor="?android:attr/textColorPrimary"
             android:duplicateParentState="true">
         </TextView>
 
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
index 1fb221a..14403a1 100644
--- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
@@ -17,8 +17,6 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
-    android:paddingStart="8dip"
-    android:paddingEnd="8dip"
     android:minHeight="?android:attr/listPreferredItemHeightSmall"
     android:orientation="vertical"
     android:gravity="start|center_vertical">
@@ -27,12 +25,13 @@
         android:id="@+id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        style="?android:attr/spinnerDropDownItemStyle"
         android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorPrimary"
         android:singleLine="true"
         android:ellipsize="end"
         android:textIsSelectable="false"
         android:gravity="top|left"
-        android:textColor="?android:attr/textColorPrimary"
         android:duplicateParentState="true">
     </TextView>
 
@@ -40,12 +39,13 @@
         android:id="@+id/subtitle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        style="?android:attr/spinnerDropDownItemStyle"
         android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorPrimary"
         android:singleLine="true"
         android:ellipsize="end"
         android:textIsSelectable="false"
         android:visibility="gone"
-        android:textColor="?android:attr/textColorPrimary"
         android:duplicateParentState="true">
     </TextView>
 
diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml
index ee62f9f..8da5769 100644
--- a/packages/PrintSpooler/res/menu/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml
@@ -19,7 +19,7 @@
     <item
         android:id="@+id/action_search"
         android:title="@string/search"
-        android:icon="@*android:drawable/ic_menu_search_holo_light"
+        android:icon="@*android:drawable/ic_search"
         android:actionViewClass="android.widget.SearchView"
         android:showAsAction="ifRoom|collapseActionView"
         android:alphabeticShortcut="f"
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index 677fda7..de74a41 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -22,4 +22,6 @@
 
     <color name="print_preview_background_color">#F2F1F2</color>
 
+    <color name="promoted_action_background_color">#FF80CBC4</color>
+
 </resources>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index dd90bec..5b7fda3 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -44,7 +44,13 @@
     <string name="label_orientation">Orientation</string>
 
     <!-- Label of the page selection widget. [CHAR LIMIT=20] -->
-    <string name="label_pages">Pages (<xliff:g id="page_count" example="5">%1$s</xliff:g>)</string>
+    <string name="label_pages">Pages</string>
+
+    <!-- Template for the all pages option in the page selection widget. [CHAR LIMIT=20] -->
+    <string name="template_all_pages">All <xliff:g id="page_count" example="100">%1$s</xliff:g></string>
+
+    <!-- Template for the page range option in the page selection widget. [CHAR LIMIT=20] -->
+    <string name="template_page_range">Range of <xliff:g id="page_count" example="100">%1$s</xliff:g></string>
 
     <!-- Page range exmple used as a hint of how to specify such. [CHAR LIMIT=20] -->
     <string name="pages_range_example">e.g. 1&#8212;5,8,11&#8212;13</string>
@@ -58,9 +64,6 @@
     <!-- Title of the message that the printing application crashed. [CHAR LIMIT=50] -->
     <string name="printing_app_crashed">Printing app crashed</string>
 
-    <!-- Title if the number of pages in a printed document is unknown. [CHAR LIMIT=20] -->
-    <string name="page_count_unknown">Pages</string>
-
     <!-- Title for the temporary dialog show while an app is generating a print job. [CHAR LIMIT=30] -->
     <string name="generating_print_job">Generating print job</string>
 
@@ -174,14 +177,6 @@
         <item>Landscape</item>
     </string-array>
 
-    <!-- Page options labels. -->
-    <string-array name="page_options_labels">
-        <!-- Page range option label: Print all pages [CHAR LIMIT=30] -->
-        <item>All</item>
-        <!-- Page range option label: Print a page range [CHAR LIMIT=30] -->
-        <item>Range</item>
-    </string-array>
-
     <!-- Permissions -->
 
     <!-- Title of an application permission, listed so the user can choose whether they want
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
deleted file mode 100644
index 9637847..0000000
--- a/packages/PrintSpooler/res/values/styles.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-
-    <style name="PrintOptionSpinnerStyle">
-        <item name="android:paddingTop">0dip</item>
-        <item name="android:paddingBottom">0dip</item>
-        <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
-    </style>
-
-    <style name="PrintOptionEditTextStyle">
-
-         <item name="android:singleLine">true</item>
-         <item name="android:ellipsize">end</item>
-    </style>
-
-</resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index e1e6c44..7d0da14 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -16,19 +16,16 @@
 
 <resources>
 
-    <style name="PrintActivity" parent="@android:style/Theme.DeviceDefault.NoActionBar">
+    <style name="PrintActivity" parent="@android:style/Theme.Material">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
         <item name="android:backgroundDimEnabled">false</item>
-    </style>
-
-    <style name="SelectPrinterActivityTheme" parent="@android:style/Theme.DeviceDefault.Light">
-        <item name="android:actionBarStyle">@style/SelectPrinterActivityActionBarStyle</item>
-    </style>
-
-    <style name="SelectPrinterActivityActionBarStyle" parent="@android:style/Widget.DeviceDefault.Light.ActionBar">
-        <item name="android:displayOptions">showTitle</item>
+        <item name="android:colorPrimary">@*android:color/material_blue_grey_900</item>
+        <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item>
+        <item name="android:colorAccent">@*android:color/material_dark_teal_A400</item>
     </style>
 
 </resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 094edf8..30808ba 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -334,22 +334,22 @@
         content.init(provider, mMediaSize, mMinMargins);
 
 
-        CheckBox checkbox = (CheckBox) page.findViewById(R.id.page_selector);
-        checkbox.setTag(myHolder);
+        View pageSelector = page.findViewById(R.id.page_selector);
+        pageSelector.setTag(myHolder);
         if (pageCount > 1) {
-            checkbox.setOnClickListener(mPageClickListener);
-            checkbox.setVisibility(View.VISIBLE);
+            pageSelector.setOnClickListener(mPageClickListener);
+            pageSelector.setVisibility(View.VISIBLE);
         } else {
-            checkbox.setOnClickListener(null);
-            checkbox.setVisibility(View.GONE);
+            pageSelector.setOnClickListener(null);
+            pageSelector.setVisibility(View.GONE);
         }
 
         if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) >= 0) {
-            checkbox.setChecked(true);
+            pageSelector.setSelected(true);
             page.setTranslationZ(mSelectedPageElevation);
             page.setAlpha(mSelectedPageAlpha);
         } else {
-            checkbox.setChecked(false);
+            pageSelector.setSelected(false);
             page.setTranslationZ(mUnselectedPageElevation);
             page.setAlpha(mUnselectedPageAlpha);
         }
@@ -767,15 +767,15 @@
             MyViewHolder holder = (MyViewHolder) page.getTag();
             final int pageInAdapter = holder.mPageInAdapter;
             final int pageInDocument = computePageIndexInDocument(pageInAdapter);
-            CheckBox pageSelector = (CheckBox) page.findViewById(R.id.page_selector);
+            View pageSelector = page.findViewById(R.id.page_selector);
             if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) < 0) {
                 mConfirmedPagesInDocument.put(pageInDocument, null);
-                pageSelector.setChecked(true);
+                pageSelector.setSelected(true);
                 page.animate().translationZ(mSelectedPageElevation)
                         .alpha(mSelectedPageAlpha);
             } else {
                 mConfirmedPagesInDocument.remove(pageInDocument);
-                pageSelector.setChecked(false);
+                pageSelector.setSelected(false);
                 page.animate().translationZ(mUnselectedPageElevation)
                         .alpha(mUnselectedPageAlpha);
             }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 5e646ff..a01e45c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -157,7 +157,6 @@
 
     private EditText mCopiesEditText;
 
-    private TextView mPageRangeOptionsTitle;
     private TextView mPageRangeTitle;
     private EditText mPageRangeEditText;
 
@@ -196,6 +195,8 @@
 
     private String mCallingPackageName;
 
+    private int mCurrentPageCount;
+
     private int mState;
 
     private int mUiState = UI_STATE_PREVIEW;
@@ -996,19 +997,10 @@
         // Range options
         ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter =
                 new ArrayAdapter<>(this, R.layout.spinner_dropdown_item, R.id.title);
-        final int[] rangeOptionsValues = getResources().getIntArray(
-                R.array.page_options_values);
-        String[] rangeOptionsLabels = getResources().getStringArray(
-                R.array.page_options_labels);
-        final int rangeOptionsCount = rangeOptionsLabels.length;
-        for (int i = 0; i < rangeOptionsCount; i++) {
-            rangeOptionsSpinnerAdapter.add(new SpinnerItem<>(
-                    rangeOptionsValues[i], rangeOptionsLabels[i]));
-        }
-        mPageRangeOptionsTitle = (TextView) findViewById(R.id.range_options_title);
         mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
         mRangeOptionsSpinner.setAdapter(rangeOptionsSpinnerAdapter);
         mRangeOptionsSpinner.setOnItemSelectedListener(itemSelectedListener);
+        updatePageRangeOptions(PrintDocumentInfo.PAGE_COUNT_UNKNOWN);
 
         // Page range
         mPageRangeTitle = (TextView) findViewById(R.id.page_range_title);
@@ -1265,22 +1257,23 @@
                     mPageRangeTitle.setVisibility(View.INVISIBLE);
                 }
             }
-            String title = (pageCount != PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
-                    ? getString(R.string.label_pages, String.valueOf(pageCount))
-                    : getString(R.string.page_count_unknown);
-            mPageRangeOptionsTitle.setText(title);
         } else {
             if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
                 mRangeOptionsSpinner.setSelection(0);
                 mPageRangeEditText.setText("");
             }
             mRangeOptionsSpinner.setEnabled(false);
-            mPageRangeOptionsTitle.setText(getString(R.string.page_count_unknown));
             mPageRangeEditText.setEnabled(false);
             mPageRangeEditText.setVisibility(View.INVISIBLE);
             mPageRangeTitle.setVisibility(View.INVISIBLE);
         }
 
+        final int newPageCount = getAdjustedPageCount(info);
+        if (newPageCount != mCurrentPageCount) {
+            mCurrentPageCount = newPageCount;
+            updatePageRangeOptions(newPageCount);
+        }
+
         // Advanced print options
         ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
         if (!TextUtils.isEmpty(PrintOptionUtils.getAdvancedOptionsActivityName(
@@ -1320,6 +1313,27 @@
         }
     }
 
+    private void updatePageRangeOptions(int pageCount) {
+        ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter =
+                (ArrayAdapter) mRangeOptionsSpinner.getAdapter();
+        rangeOptionsSpinnerAdapter.clear();
+
+        final int[] rangeOptionsValues = getResources().getIntArray(
+                R.array.page_options_values);
+
+        String pageCountLabel = (pageCount > 0) ? String.valueOf(pageCount) : "";
+        String[] rangeOptionsLabels = new String[] {
+            getString(R.string.template_all_pages, pageCountLabel),
+            getString(R.string.template_page_range, pageCountLabel)
+        };
+
+        final int rangeOptionsCount = rangeOptionsLabels.length;
+        for (int i = 0; i < rangeOptionsCount; i++) {
+            rangeOptionsSpinnerAdapter.add(new SpinnerItem<>(
+                    rangeOptionsValues[i], rangeOptionsLabels[i]));
+        }
+    }
+
     private PageRange[] computeSelectedPages() {
         if (hasErrors()) {
             return null;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index 555aa97..efb030e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -22,6 +22,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
 import com.android.printspooler.R;
 
 /**
@@ -301,6 +302,7 @@
             mSummaryContent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             mDraggableContent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             mMoreOptionsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            ensureImeClosedAndInputFocusCleared();
         }
         if ((mDragProgress > 0 && progress == 0)
                 || (mDragProgress < 1.0f && progress == 1.0f)) {
@@ -351,6 +353,18 @@
         }
     }
 
+    private void ensureImeClosedAndInputFocusCleared() {
+        View focus = findFocus();
+        if (focus != null) {
+            InputMethodManager imm = (InputMethodManager) mContext.getSystemService(
+                    Context.INPUT_METHOD_SERVICE);
+            if (imm.isActive(focus)) {
+                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+            }
+            focus.clearFocus();
+        }
+    }
+
     private final class DragCallbacks extends ViewDragHelper.Callback {
         @Override
         public boolean tryCaptureView(View child, int pointerId) {
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 7e8bfd3..3e4c1f6 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -28,7 +28,7 @@
         android:layout_height="@dimen/recents_task_bar_height"
         android:layout_gravity="top|center_horizontal"
         android:background="@color/recents_task_bar_default_background_color">
-        <ImageView
+        <com.android.systemui.recents.views.FixedSizeImageView
             android:id="@+id/application_icon"
             android:layout_width="@dimen/recents_task_view_application_icon_size"
             android:layout_height="@dimen/recents_task_view_application_icon_size"
@@ -51,7 +51,7 @@
             android:maxLines="2"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
-        <ImageView
+        <com.android.systemui.recents.views.FixedSizeImageView
             android:id="@+id/dismiss_task"
             android:layout_width="48dp"
             android:layout_height="48dp"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 3d53f9c..adab243 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -45,7 +45,7 @@
     <color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
     <color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white -->
     <color name="data_usage_graph_warning">#FFFFFFFF</color>
-    <color name="status_bar_clock_color">#33FFFFFF</color>
+    <color name="status_bar_clock_color">#FFFFFFFF</color>
 
     <!-- Tint color for the content on the notification overflow card. -->
     <color name="keyguard_overflow_content_color">#ff686868</color>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index 2f1c1c4..31011ae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -25,7 +25,6 @@
 
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * The package monitor listens for changes from PackageManager to update the contents of the Recents
@@ -33,7 +32,7 @@
  */
 public class RecentsPackageMonitor extends PackageMonitor {
     public interface PackageCallbacks {
-        public void onComponentRemoved(Set<ComponentName> cns);
+        public void onComponentRemoved(HashSet<ComponentName> cns);
     }
 
     PackageCallbacks mCb;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index e3bcff0..13fbe64 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -204,8 +204,8 @@
                 removeGroup(group);
             }
             // Update the lock-to-app state
-            Task newFrontMostTask = getFrontMostTask();
             t.canLockToTask = false;
+            Task newFrontMostTask = getFrontMostTask();
             if (newFrontMostTask != null) {
                 newFrontMostTask.canLockToTask = true;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
new file mode 100644
index 0000000..3adee0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
@@ -0,0 +1,82 @@
+/*
+ * 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.systemui.recents.views;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * This is an optimized ImageView that does not trigger a requestLayout() or invalidate() when
+ * setting the image to Null.
+ */
+public class FixedSizeImageView extends ImageView {
+
+    int mFixedWidth;
+    int mFixedHeight;
+    boolean mAllowRelayout = true;
+    boolean mAllowInvalidate = true;
+
+    public FixedSizeImageView(Context context) {
+        this(context, null);
+    }
+
+    public FixedSizeImageView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FixedSizeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public FixedSizeImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        mFixedWidth = getMeasuredWidth();
+        mFixedHeight = getMeasuredHeight();
+    }
+
+    @Override
+    public void requestLayout() {
+        if (mAllowRelayout) {
+            super.requestLayout();
+        }
+    }
+
+    @Override
+    public void invalidate() {
+        if (mAllowInvalidate) {
+            super.invalidate();
+        }
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        if (drawable == null || (mFixedWidth > 0 && mFixedHeight > 0)) {
+            mAllowRelayout = false;
+            mAllowInvalidate = false;
+        }
+        super.setImageDrawable(drawable);
+        mAllowRelayout = true;
+        mAllowInvalidate = true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 7bb6144..b32d3dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -45,7 +45,7 @@
 import com.android.systemui.recents.model.TaskStack;
 
 import java.util.ArrayList;
-import java.util.Set;
+import java.util.HashSet;
 
 /**
  * This view is the the top level layout that contains TaskStacks (which are laid out according
@@ -551,7 +551,7 @@
     /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
 
     @Override
-    public void onComponentRemoved(Set<ComponentName> cns) {
+    public void onComponentRemoved(HashSet<ComponentName> cns) {
         // Propagate this event down to each task stack view
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
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 7b52163..0b35f59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,9 +23,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.SystemClock;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -45,7 +44,7 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Set;
+import java.util.HashSet;
 
 
 /* The visual representation of a task stack view */
@@ -83,6 +82,7 @@
     int mFocusedTaskIndex = -1;
     OverScroller mScroller;
     ObjectAnimator mScrollAnimator;
+    boolean mEnableStackClipping = true;
 
     // Optimizations
     ReferenceCountedTrigger mHwLayersTrigger;
@@ -169,6 +169,7 @@
     void requestSynchronizeStackViewsWithModel(int duration) {
         if (!mStackViewsDirty) {
             invalidate(mStackAlgorithm.mStackRect);
+            mStackViewsDirty = true;
         }
         if (mAwaitingFirstLayout) {
             // Skip the animation if we are awaiting first layout
@@ -176,7 +177,6 @@
         } else {
             mStackViewsAnimationDuration = Math.max(mStackViewsAnimationDuration, duration);
         }
-        mStackViewsDirty = true;
     }
 
     /** Returns a mapping of child view to Task. */
@@ -338,7 +338,7 @@
     /** Updates the clip for each of the task views. */
     void clipTaskViews() {
         // Update the clip on each task child
-        if (Constants.DebugFlags.App.EnableTaskStackClipping) {
+        if (Constants.DebugFlags.App.EnableTaskStackClipping && mEnableStackClipping) {
             int childCount = getChildCount();
             for (int i = 0; i < childCount - 1; i++) {
                 TaskView tv = (TaskView) getChildAt(i);
@@ -360,10 +360,12 @@
                     // stacked and we can make assumptions about the visibility of the this
                     // task relative to the ones in front of it.
                     if (nextTv != null) {
-                        // XXX: Can hash the visible rects for this run
+                        // We calculate the bottom clip independent of the footer (since we animate
+                        // that)
+                        int scaledMaxFooterHeight = (int) (tv.getMaxFooterHeight() * tv.getScaleX());
                         tv.getHitRect(mTmpRect);
                         nextTv.getHitRect(mTmpRect2);
-                        clipBottom = (mTmpRect.bottom - mTmpRect2.top);
+                        clipBottom = (mTmpRect.bottom - scaledMaxFooterHeight - mTmpRect2.top);
                     }
                 }
                 tv.setClipFromBottom(clipBottom);
@@ -376,6 +378,18 @@
         }
     }
 
+    /** Enables/Disables clipping of the tasks in the stack. */
+    void setStackClippingEnabled(boolean stackClippingEnabled) {
+        if (!stackClippingEnabled) {
+            int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                TaskView tv = (TaskView) getChildAt(i);
+                tv.setClipFromBottom(0);
+            }
+        }
+        mEnableStackClipping = stackClippingEnabled;
+    }
+
     /** Sets the current stack scroll */
     public void setStackScroll(int value) {
         mStackScroll = value;
@@ -614,7 +628,6 @@
     public void computeScroll() {
         if (mScroller.computeScrollOffset()) {
             setStackScroll(mScroller.getCurrY());
-            invalidate(mStackAlgorithm.mStackRect);
 
             // If we just finished scrolling, then disable the hw layers
             if (mScroller.isFinished()) {
@@ -668,7 +681,7 @@
             TaskView t = (TaskView) getChildAt(i);
             t.measure(MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(), MeasureSpec.EXACTLY),
                     MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
-                            mConfig.taskViewLockToAppButtonHeight, MeasureSpec.EXACTLY));
+                            t.getMaxFooterHeight(), MeasureSpec.EXACTLY));
         }
 
         setMeasuredDimension(width, height);
@@ -687,7 +700,7 @@
             TaskView t = (TaskView) getChildAt(i);
             t.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mStackRectSansPeek.top,
                     mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mStackRectSansPeek.top +
-                    mStackAlgorithm.mTaskRect.height() + mConfig.taskViewLockToAppButtonHeight);
+                    mStackAlgorithm.mTaskRect.height() + t.getMaxFooterHeight());
         }
 
         if (mAwaitingFirstLayout) {
@@ -1056,7 +1069,7 @@
     /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
 
     @Override
-    public void onComponentRemoved(Set<ComponentName> cns) {
+    public void onComponentRemoved(HashSet<ComponentName> cns) {
         // For other tasks, just remove them directly if they no longer exist
         ArrayList<Task> tasks = mStack.getTasks();
         for (int i = tasks.size() - 1; i >= 0; i--) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 9c48896..65407a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -146,7 +146,8 @@
         transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
 
         // Set the alphas
-        transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
+        // transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
+        transformOut.dismissAlpha = 1f;
 
         // Update the rect and visibility
         transformOut.rect.set(mTaskRect);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
index 636746d..08a25f1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
@@ -21,12 +21,11 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.ImageView;
 import com.android.systemui.recents.model.Task;
 
 
 /** The task thumbnail view */
-public class TaskThumbnailView extends ImageView {
+public class TaskThumbnailView extends FixedSizeImageView {
 
     Task mTask;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 7e30047..199d3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -495,7 +495,7 @@
         mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
     }
 
-    /** Sets the stubbed state of this task view.
+    /** Sets the stubbed state of this task view. */
     void setStubState(boolean isStub) {
         if (!mIsStub && isStub) {
             // This is now a stub task view, so clip to the bar height, hide the thumbnail
@@ -508,7 +508,7 @@
             mThumbnailView.setVisibility(View.VISIBLE);
         }
         mIsStub = isStub;
-    } */
+    }
 
     /**
      * Returns whether this view should be clipped, or any views below should clip against this
@@ -549,9 +549,19 @@
         return mFooterHeight;
     }
 
+    /** Gets the max footer height. */
+    public int getMaxFooterHeight() {
+        return mMaxFooterHeight;
+    }
+
     /** Animates the footer into and out of view. */
     public void animateFooterVisibility(boolean visible, int duration, int delay) {
-        if (!mTask.canLockToTask) return;
+        if (!mTask.canLockToTask) {
+            if (mLockToAppButtonView.getVisibility() == View.VISIBLE) {
+                mLockToAppButtonView.setVisibility(View.INVISIBLE);
+            }
+            return;
+        }
         if (mMaxFooterHeight <= 0) return;
 
         if (mFooterAnimator != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 9c166ac..cf04219 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -32,7 +32,6 @@
 import android.service.notification.ZenModeConfig;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.animation.AnimationUtils;
@@ -54,8 +53,11 @@
     private static final String TAG = "ZenModePanel";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final int SECONDS_MS = 1000;
+    private static final int MINUTES_MS = 60 * SECONDS_MS;
+
     private static final int[] MINUTE_BUCKETS = DEBUG
-            ? new int[] { 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 }
+            ? new int[] { 0, 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 }
             : new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
     private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
     private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
@@ -64,9 +66,7 @@
     private static final int TIME_CONDITION_INDEX = 1;
     private static final int FIRST_CONDITION_INDEX = 2;
     private static final float SILENT_HINT_PULSE_SCALE = 1.1f;
-
-    private static final int SECONDS_MS = 1000;
-    private static final int MINUTES_MS = 60 * SECONDS_MS;
+    private static final int ZERO_VALUE_MS = 20 * SECONDS_MS;
 
     public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
 
@@ -152,6 +152,7 @@
         if (DEBUG) Log.d(mTag, "onAttachedToWindow");
         mAttachedZen = getSelectedZen(-1);
         refreshExitConditionText();
+        updateWidgets();
     }
 
     @Override
@@ -292,7 +293,8 @@
 
     private Condition newTimeCondition(int minutesFromNow) {
         final long now = System.currentTimeMillis();
-        return timeCondition(now + minutesFromNow * MINUTES_MS, minutesFromNow);
+        final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
+        return timeCondition(now + millis, minutesFromNow);
     }
 
     private Condition timeCondition(long time, int minutes) {
@@ -369,6 +371,9 @@
         }
         tag.conditionId = condition != null ? condition.id : null;
         tag.rb.setEnabled(enabled);
+        if (Objects.equals(tag.conditionId, mExitConditionId)) {
+            tag.rb.setChecked(true);
+        }
         tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index ef15a80..608aa44 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -250,6 +250,9 @@
     // Vibrator pattern for a short vibration when tapping on an hour/minute tick of a Clock.
     long[] mClockTickVibePattern;
 
+    // Vibrator pattern for a short vibration when tapping on a day/month/year date of a Calendar.
+    long[] mCalendarDateVibePattern;
+
     // Vibrator pattern for haptic feedback during boot when safe mode is disabled.
     long[] mSafeModeDisabledVibePattern;
 
@@ -1071,6 +1074,8 @@
                 com.android.internal.R.array.config_keyboardTapVibePattern);
         mClockTickVibePattern = getLongIntArray(mContext.getResources(),
                 com.android.internal.R.array.config_clockTickVibePattern);
+        mCalendarDateVibePattern = getLongIntArray(mContext.getResources(),
+                com.android.internal.R.array.config_calendarDateVibePattern);
         mSafeModeDisabledVibePattern = getLongIntArray(mContext.getResources(),
                 com.android.internal.R.array.config_safeModeDisabledVibePattern);
         mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
@@ -5406,6 +5411,9 @@
             case HapticFeedbackConstants.CLOCK_TICK:
                 pattern = mClockTickVibePattern;
                 break;
+            case HapticFeedbackConstants.CALENDAR_DATE:
+                pattern = mCalendarDateVibePattern;
+                break;
             case HapticFeedbackConstants.SAFE_MODE_DISABLED:
                 pattern = mSafeModeDisabledVibePattern;
                 break;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6554ed3..86f8777 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3199,13 +3199,17 @@
                     break;
                 }
                 case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
-                        break;
+                    if (msg.arg1 == 0) {
+                        setProvNotificationVisibleIntent(false, msg.arg2, 0, null, null);
+                    } else {
+                        NetworkAgentInfo nai = mNetworkForNetId.get(msg.arg2);
+                        if (nai == null) {
+                            loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
+                            break;
+                        }
+                        setProvNotificationVisibleIntent(true, msg.arg2, nai.networkInfo.getType(),
+                                nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
                     }
-                    setProvNotificationVisibleIntent(msg.arg1 != 0, nai.networkInfo.getType(),
-                            nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
                     break;
                 }
                 case NetworkStateTracker.EVENT_STATE_CHANGED: {
@@ -4958,10 +4962,19 @@
                     break;
             }
         }
-        setProvNotificationVisibleIntent(visible, networkType, extraInfo, pendingIntent);
+        // Concatenate the range of types onto the range of NetIDs.
+        int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
+        setProvNotificationVisibleIntent(visible, id, networkType, extraInfo, pendingIntent);
     }
 
-    private void setProvNotificationVisibleIntent(boolean visible, int networkType,
+    /**
+     * Show or hide network provisioning notificaitons.
+     *
+     * @param id an identifier that uniquely identifies this notification.  This must match
+     *         between show and hide calls.  We use the NetID value but for legacy callers
+     *         we concatenate the range of types with the range of NetIDs.
+     */
+    private void setProvNotificationVisibleIntent(boolean visible, int id, int networkType,
             String extraInfo, PendingIntent intent) {
         if (DBG) {
             log("setProvNotificationVisibleIntent: E visible=" + visible + " networkType=" +
@@ -5008,14 +5021,14 @@
             notification.contentIntent = intent;
 
             try {
-                notificationManager.notify(NOTIFICATION_ID, networkType, notification);
+                notificationManager.notify(NOTIFICATION_ID, id, notification);
             } catch (NullPointerException npe) {
                 loge("setNotificaitionVisible: visible notificationManager npe=" + npe);
                 npe.printStackTrace();
             }
         } else {
             try {
-                notificationManager.cancel(NOTIFICATION_ID, networkType);
+                notificationManager.cancel(NOTIFICATION_ID, id);
             } catch (NullPointerException npe) {
                 loge("setNotificaitionVisible: cancel notificationManager npe=" + npe);
                 npe.printStackTrace();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 543384f..79cb60e 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1811,10 +1811,18 @@
                 break;
             }
             case SYSTEM_USER_START_MSG: {
+                mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
+                        Integer.toString(msg.arg1), msg.arg1);
                 mSystemServiceManager.startUser(msg.arg1);
                 break;
             }
             case SYSTEM_USER_CURRENT_MSG: {
+                mBatteryStatsService.noteEvent(
+                        BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
+                        Integer.toString(msg.arg2), msg.arg2);
+                mBatteryStatsService.noteEvent(
+                        BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
+                        Integer.toString(msg.arg1), msg.arg1);
                 mSystemServiceManager.switchUser(msg.arg1);
                 break;
             }
@@ -5655,6 +5663,11 @@
     }
 
     @Override
+    public final void notifyLaunchTaskBehindComplete(IBinder token) {
+        mStackSupervisor.scheduleLaunchTaskBehindComplete(token);
+    }
+
+    @Override
     public String getCallingPackage(IBinder token) {
         synchronized (this) {
             ActivityRecord r = getCallingRecordLocked(token);
@@ -10163,7 +10176,11 @@
         }
 
         if (goingCallback != null) goingCallback.run();
-        
+
+        mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
+                Integer.toString(mCurrentUserId), mCurrentUserId);
+        mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
+                Integer.toString(mCurrentUserId), mCurrentUserId);
         mSystemServiceManager.startUser(mCurrentUserId);
 
         synchronized (this) {
@@ -12436,12 +12453,11 @@
                 pw.print(" lastCachedPss="); pw.println(r.lastCachedPss);
                 pw.print(prefix);
                 pw.print("    ");
-                pw.print("keeping="); pw.print(r.keeping);
-                pw.print(" cached="); pw.print(r.cached);
+                pw.print("cached="); pw.print(r.cached);
                 pw.print(" empty="); pw.print(r.empty);
                 pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
 
-                if (!r.keeping) {
+                if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
                     if (r.lastWakeTime != 0) {
                         long wtime;
                         BatteryStatsImpl stats = service.mBatteryStatsService.getActiveStatistics();
@@ -15195,7 +15211,6 @@
             app.adjSeq = mAdjSeq;
             app.curRawAdj = app.maxAdj;
             app.foregroundActivities = false;
-            app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
             app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
             // System processes can do UI, and when they do we want to have
@@ -15219,7 +15234,6 @@
             return (app.curAdj=app.maxAdj);
         }
 
-        app.keeping = false;
         app.systemNoUi = false;
 
         // Determine the importance of the process, starting with most
@@ -15464,9 +15478,6 @@
                         app.adjType = "cch-started-services";
                     }
                 }
-                // Don't kill this process because it is doing work; it
-                // has said it is doing work.
-                app.keeping = true;
             }
             for (int conni = s.connections.size()-1;
                     conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
@@ -15556,9 +15567,6 @@
                                 if (!client.cached) {
                                     app.cached = false;
                                 }
-                                if (client.keeping) {
-                                    app.keeping = true;
-                                }
                                 adjType = "service";
                             }
                         }
@@ -15670,7 +15678,6 @@
                         app.adjType = "provider";
                     }
                     app.cached &= client.cached;
-                    app.keeping |= client.keeping;
                     app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                             .REASON_PROVIDER_IN_USE;
                     app.adjSource = client;
@@ -15714,7 +15721,6 @@
                     adj = ProcessList.FOREGROUND_APP_ADJ;
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.cached = false;
-                    app.keeping = true;
                     app.adjType = "provider";
                     app.adjTarget = cpr.name;
                 }
@@ -15797,9 +15803,6 @@
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             }
         }
-        if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
-            app.keeping = true;
-        }
 
         // Do final modification to adj.  Everything we do between here and applying
         // the final setAdj must be done in this function, because we will also use
@@ -16021,7 +16024,7 @@
         while (i > 0) {
             i--;
             ProcessRecord app = mLruProcesses.get(i);
-            if (!app.keeping) {
+            if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
                 long wtime;
                 synchronized (stats) {
                     wtime = stats.getProcessWakeTime(app.info.uid,
@@ -16066,7 +16069,7 @@
                             + " during " + realtimeSince);
                     app.baseProcessTracker.reportExcessiveWake(app.pkgList);
                 } else if (doCpuKills && uptimeSince > 0
-                        && ((cputimeUsed*100)/uptimeSince) >= 50) {
+                        && ((cputimeUsed*100)/uptimeSince) >= 25) {
                     synchronized (stats) {
                         stats.reportExcessiveCpuLocked(app.info.uid, app.processName,
                                 uptimeSince, cputimeUsed);
@@ -16082,23 +16085,11 @@
         }
     }
 
-    private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping,
+    private final boolean applyOomAdjLocked(ProcessRecord app,
             ProcessRecord TOP_APP, boolean doingAll, long now) {
         boolean success = true;
 
         if (app.curRawAdj != app.setRawAdj) {
-            if (wasKeeping && !app.keeping) {
-                // This app is no longer something we want to keep.  Note
-                // its current wake lock time to later know to kill it if
-                // it is not behaving well.
-                BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
-                synchronized (stats) {
-                    app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
-                            app.pid, SystemClock.elapsedRealtime());
-                }
-                app.lastCpuTime = app.curCpuTime;
-            }
-
             app.setRawAdj = app.curRawAdj;
         }
 
@@ -16187,6 +16178,21 @@
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
                     "Proc state change of " + app.processName
                     + " to " + app.curProcState);
+            boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
+            boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE;
+            if (setImportant && !curImportant) {
+                // This app is no longer something we consider important enough to allow to
+                // use arbitrary amounts of battery power.  Note
+                // its current wake lock time to later know to kill it if
+                // it is not behaving well.
+                BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+                synchronized (stats) {
+                    app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
+                            app.pid, SystemClock.elapsedRealtime());
+                }
+                app.lastCpuTime = app.curCpuTime;
+
+            }
             app.setProcState = app.curProcState;
             if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
                 app.notCachedSinceIdle = false;
@@ -16263,11 +16269,9 @@
             return false;
         }
 
-        final boolean wasKeeping = app.keeping;
-
         computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
 
-        return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll, now);
+        return applyOomAdjLocked(app, TOP_APP, doingAll, now);
     }
 
     final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
@@ -16423,7 +16427,6 @@
             ProcessRecord app = mLruProcesses.get(i);
             if (!app.killedByAm && app.thread != null) {
                 app.procStateChanged = false;
-                final boolean wasKeeping = app.keeping;
                 computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
 
                 // If we haven't yet assigned the final cached adj
@@ -16478,7 +16481,7 @@
                     }
                 }
 
-                applyOomAdjLocked(app, wasKeeping, TOP_APP, true, now);
+                applyOomAdjLocked(app, TOP_APP, true, now);
 
                 // Count the number of process types.
                 switch (app.curProcState) {
@@ -17122,7 +17125,8 @@
                 }
 
                 if (foreground) {
-                    mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId));
+                    mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
+                            oldUserId));
                     mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
                     mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
                     mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
@@ -17497,6 +17501,9 @@
                             }
                             uss.mState = UserStartedState.STATE_SHUTDOWN;
                         }
+                        mBatteryStatsService.noteEvent(
+                                BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
+                                Integer.toString(userId), userId);
                         mSystemServiceManager.stopUser(userId);
                         broadcastIntentLocked(null, null, shutdownIntent,
                                 null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 46521c5..6c47922 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -167,6 +167,8 @@
     ActivityContainer mInitialActivityContainer;
 
     TaskDescription taskDescription; // the recents information for this activity
+    boolean mLaunchTaskBehind; // this activity is actively being launched with
+        // ActivityOptions.setLaunchTaskBehind, will be cleared once launch is completed.
 
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
@@ -400,6 +402,7 @@
         mInitialActivityContainer = container;
         if (options != null) {
             pendingOptions = new ActivityOptions(options);
+            mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind();
         }
 
         // This starts out true, since the initial state of an activity
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9d675e1..32f2624 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1135,19 +1135,15 @@
         return true;
     }
 
-    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
-        ActivityRecord r = topRunningActivityLocked(null);
-        if (r != null) {
-            ensureActivitiesVisibleLocked(r, starting, null, configChanges);
-        }
-    }
-
     /**
      * Make sure that all activities that need to be visible (that is, they
      * currently can be seen by the user) actually are.
      */
-    final void ensureActivitiesVisibleLocked(ActivityRecord top, ActivityRecord starting,
-            String onlyThisProcess, int configChanges) {
+    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+        ActivityRecord top = topRunningActivityLocked(null);
+        if (top == null) {
+            return;
+        }
         if (DEBUG_VISBILITY) Slog.v(
                 TAG, "ensureActivitiesVisible behind " + top
                 + " configChanges=0x" + Integer.toHexString(configChanges));
@@ -1179,37 +1175,34 @@
                     continue;
                 }
                 aboveTop = false;
-                if (!behindFullscreen) {
+                // mLaunchingBehind: Activities launching behind are at the back of the task stack
+                // but must be drawn initially for the animation as though they were visible.
+                if (!behindFullscreen || r.mLaunchTaskBehind) {
                     if (DEBUG_VISBILITY) Slog.v(
                             TAG, "Make visible? " + r + " finishing=" + r.finishing
                             + " state=" + r.state);
 
-                    final boolean doThisProcess = onlyThisProcess == null
-                            || onlyThisProcess.equals(r.processName);
-
                     // First: if this is not the current activity being started, make
                     // sure it matches the current configuration.
-                    if (r != starting && doThisProcess) {
+                    if (r != starting) {
                         ensureActivityConfigurationLocked(r, 0);
                     }
 
                     if (r.app == null || r.app.thread == null) {
-                        if (onlyThisProcess == null || onlyThisProcess.equals(r.processName)) {
-                            // This activity needs to be visible, but isn't even
-                            // running...  get it started, but don't resume it
-                            // at this point.
-                            if (DEBUG_VISBILITY) Slog.v(TAG, "Start and freeze screen for " + r);
-                            if (r != starting) {
-                                r.startFreezingScreenLocked(r.app, configChanges);
-                            }
-                            if (!r.visible) {
-                                if (DEBUG_VISBILITY) Slog.v(
-                                        TAG, "Starting and making visible: " + r);
-                                setVisibile(r, true);
-                            }
-                            if (r != starting) {
-                                mStackSupervisor.startSpecificActivityLocked(r, false, false);
-                            }
+                        // This activity needs to be visible, but isn't even
+                        // running...  get it started, but don't resume it
+                        // at this point.
+                        if (DEBUG_VISBILITY) Slog.v(TAG, "Start and freeze screen for " + r);
+                        if (r != starting) {
+                            r.startFreezingScreenLocked(r.app, configChanges);
+                        }
+                        if (!r.visible || r.mLaunchTaskBehind) {
+                            if (DEBUG_VISBILITY) Slog.v(
+                                    TAG, "Starting and making visible: " + r);
+                            setVisibile(r, true);
+                        }
+                        if (r != starting) {
+                            mStackSupervisor.startSpecificActivityLocked(r, false, false);
                         }
 
                     } else if (r.visible) {
@@ -1225,7 +1218,7 @@
                             }
                         } catch(RemoteException e) {
                         }
-                    } else if (onlyThisProcess == null) {
+                    } else {
                         // This activity is not currently visible, but is running.
                         // Tell it to become visible.
                         r.visible = true;
@@ -1648,7 +1641,9 @@
                 } else {
                     mWindowManager.prepareAppTransition(prev.task == next.task
                             ? AppTransition.TRANSIT_ACTIVITY_OPEN
-                            : AppTransition.TRANSIT_TASK_OPEN, false);
+                            : next.mLaunchTaskBehind
+                                    ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
+                                    : AppTransition.TRANSIT_TASK_OPEN, false);
                 }
             }
             if (false) {
@@ -1840,8 +1835,11 @@
             ActivityStack lastStack = mStackSupervisor.getLastStack();
             final boolean fromHome = lastStack.isHomeStack();
             if (!isHomeStack() && (fromHome || topTask() != task)) {
-                task.setTaskToReturnTo(fromHome ?
-                        lastStack.topTask().taskType : APPLICATION_ACTIVITY_TYPE);
+                task.setTaskToReturnTo(fromHome
+                        ? lastStack.topTask() == null
+                                ? HOME_ACTIVITY_TYPE
+                                : lastStack.topTask().taskType
+                        : APPLICATION_ACTIVITY_TYPE);
             }
         } else {
             task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
@@ -1849,17 +1847,17 @@
 
         mTaskHistory.remove(task);
         // Now put task at top.
-        int stackNdx = mTaskHistory.size();
+        int taskNdx = mTaskHistory.size();
         if (!isCurrentProfileLocked(task.userId)) {
             // Put non-current user tasks below current user tasks.
-            while (--stackNdx >= 0) {
-                if (!isCurrentProfileLocked(mTaskHistory.get(stackNdx).userId)) {
+            while (--taskNdx >= 0) {
+                if (!isCurrentProfileLocked(mTaskHistory.get(taskNdx).userId)) {
                     break;
                 }
             }
-            ++stackNdx;
+            ++taskNdx;
         }
-        mTaskHistory.add(stackNdx, task);
+        mTaskHistory.add(taskNdx, task);
         updateTaskMovement(task, true);
     }
 
@@ -1867,7 +1865,8 @@
             boolean doResume, boolean keepCurTransition, Bundle options) {
         TaskRecord rTask = r.task;
         final int taskId = rTask.taskId;
-        if (taskForIdLocked(taskId) == null || newTask) {
+        // mLaunchTaskBehind tasks get placed at the back of the task stack.
+        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
             // Last activity in task had been removed or ActivityManagerService is reusing task.
             // Insert or replace.
             // Might not even be in.
@@ -1892,7 +1891,8 @@
                         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                                 (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
-                                r.userId, r.info.configChanges, task.voiceSession != null);
+                                r.userId, r.info.configChanges, task.voiceSession != null,
+                                r.mLaunchTaskBehind);
                         if (VALIDATE_TOKENS) {
                             validateAppTokensLocked();
                         }
@@ -1946,14 +1946,16 @@
                 mNoAnimActivities.add(r);
             } else {
                 mWindowManager.prepareAppTransition(newTask
-                        ? AppTransition.TRANSIT_TASK_OPEN
+                        ? r.mLaunchTaskBehind
+                                ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
+                                : AppTransition.TRANSIT_TASK_OPEN
                         : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                 mNoAnimActivities.remove(r);
             }
             mWindowManager.addAppToken(task.mActivities.indexOf(r),
                     r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
-                    r.info.configChanges, task.voiceSession != null);
+                    r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
             boolean doShow = true;
             if (newTask) {
                 // Even though this activity is starting fresh, we still need
@@ -1969,7 +1971,12 @@
                     == ActivityOptions.ANIM_SCENE_TRANSITION) {
                 doShow = false;
             }
-            if (SHOW_APP_STARTING_PREVIEW && doShow) {
+            if (r.mLaunchTaskBehind) {
+                // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
+                // tell WindowManager that r is visible even though it is at the back of the stack.
+                mWindowManager.setAppVisibility(r.appToken, true);
+                ensureActivitiesVisibleLocked(null, 0);
+            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                 // Figure out if we are transitioning from another activity that is
                 // "has the same starting icon" as the next one.  This allows the
                 // window manager to keep the previous window it had previously
@@ -2000,7 +2007,7 @@
             mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                     r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
-                    r.info.configChanges, task.voiceSession != null);
+                    r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
             ActivityOptions.abort(options);
             options = null;
         }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index bc184c6..7c8dd81 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -144,6 +144,7 @@
     static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
     static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
     static final int CONTAINER_TASK_LIST_EMPTY_TIMEOUT = FIRST_SUPERVISOR_STACK_MSG + 12;
+    static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 13;
 
     private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
 
@@ -1557,9 +1558,9 @@
                     break;
             }
         }
-        final int launchBehindFlags = Intent.FLAG_ACTIVITY_LAUNCH_BEHIND |
-                Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-        final boolean affiliateTask = (launchFlags & launchBehindFlags) == launchBehindFlags;
+
+        final boolean launchTaskBehind = r.mLaunchTaskBehind &&
+                (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
 
         if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
             // For whatever reason this activity is being launched into a new
@@ -1709,7 +1710,7 @@
                                 sourceStack.topActivity().task == sourceRecord.task)) {
                             // We really do want to push this one into the
                             // user's face, right now.
-                            if (affiliateTask && sourceRecord != null) {
+                            if (launchTaskBehind && sourceRecord != null) {
                                 intentActivity.setTaskToAffiliateWith(sourceRecord.task);
                             }
                             movedHome = true;
@@ -1886,7 +1887,7 @@
         boolean newTask = false;
         boolean keepCurTransition = false;
 
-        TaskRecord taskToAffiliate = affiliateTask && sourceRecord != null ?
+        TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
                 sourceRecord.task : null;
 
         // Should this be considered a new task?
@@ -1898,12 +1899,15 @@
             }
             newTask = true;
             targetStack = adjustStackFocus(r, newTask);
-            targetStack.moveToFront();
+            if (!launchTaskBehind) {
+                targetStack.moveToFront();
+            }
             if (reuseTask == null) {
                 r.setTask(targetStack.createTaskRecord(getNextTaskId(),
                         newTaskInfo != null ? newTaskInfo : r.info,
                         newTaskIntent != null ? newTaskIntent : intent,
-                        voiceSession, voiceInteractor, true), taskToAffiliate);
+                        voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
+                        taskToAffiliate);
                 if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
                         r.task);
             } else {
@@ -1997,7 +2001,10 @@
         ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
         targetStack.mLastPausedActivity = null;
         targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
-        mService.setFocusedActivityLocked(r);
+        if (!launchTaskBehind) {
+            // Don't set focus on an activity that's going to the back.
+            mService.setFocusedActivityLocked(r);
+        }
         return ActivityManager.START_SUCCESS;
     }
 
@@ -2394,7 +2401,8 @@
                 mWindowManager.addAppToken(0, r.appToken, taskId, stackId,
                         r.info.screenOrientation, r.fullscreen,
                         (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
-                        r.userId, r.info.configChanges, task.voiceSession != null);
+                        r.userId, r.info.configChanges, task.voiceSession != null,
+                        r.mLaunchTaskBehind);
             }
             mWindowManager.addTask(taskId, stackId, false);
         }
@@ -2642,6 +2650,19 @@
         return true;
     }
 
+    // Called when WindowManager has finished animating the launchingBehind activity to the back.
+    void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
+        r.mLaunchTaskBehind = false;
+        final TaskRecord task = r.task;
+        task.setLastThumbnail(task.stack.screenshotActivities(r));
+        mService.addRecentTaskLocked(task);
+        mWindowManager.setAppVisibility(r.appToken, false);
+    }
+
+    void scheduleLaunchTaskBehindComplete(IBinder token) {
+        mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
+    }
+
     void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
         // First the front stacks. In case any are not fullscreen and are in front of home.
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -3293,6 +3314,14 @@
                         ((ActivityContainer) msg.obj).onTaskListEmptyLocked();
                     }
                 } break;
+                case LAUNCH_TASK_BEHIND_COMPLETE: {
+                    synchronized (mService) {
+                        ActivityRecord r = ActivityRecord.forToken((IBinder) msg.obj);
+                        if (r != null) {
+                            handleLaunchTaskBehindCompleteLocked(r);
+                        }
+                    }
+                } break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index da444f9..ac19bde 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -186,6 +186,34 @@
         }
     }
 
+    public void noteSyncStart(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteSyncStartLocked(name, uid);
+        }
+    }
+
+    public void noteSyncFinish(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteSyncFinishLocked(name, uid);
+        }
+    }
+
+    public void noteJobStart(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteJobStartLocked(name, uid);
+        }
+    }
+
+    public void noteJobFinish(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteJobFinishLocked(name, uid);
+        }
+    }
+
     public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
             boolean unimportantForLogging) {
         enforceCallingPermission();
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 2f25bd4..a20be73 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -83,7 +83,6 @@
     int pssProcState = -1;      // The proc state we are currently requesting pss for
     boolean serviceb;           // Process currently is on the service B list
     boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
-    boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
     boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
     boolean hasClientActivities;  // Are there any client services with activities?
@@ -225,8 +224,7 @@
                 pw.print(" lruSeq="); pw.print(lruSeq);
                 pw.print(" lastPss="); pw.print(lastPss);
                 pw.print(" lastCachedPss="); pw.println(lastCachedPss);
-        pw.print(prefix); pw.print("keeping="); pw.print(keeping);
-                pw.print(" cached="); pw.print(cached);
+        pw.print(prefix); pw.print("cached="); pw.print(cached);
                 pw.print(" empty="); pw.println(empty);
         if (serviceb) {
             pw.print(prefix); pw.print("serviceb="); pw.print(serviceb);
@@ -275,16 +273,15 @@
         if (hasStartedServices) {
             pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
         }
-        if (!keeping) {
+        if (setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
             long wtime;
             synchronized (mBatteryStats) {
                 wtime = mBatteryStats.getProcessWakeTime(info.uid,
                         pid, SystemClock.elapsedRealtime());
             }
-            long timeUsed = wtime - lastWakeTime;
             pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
                     pw.print(" timeUsed=");
-                    TimeUtils.formatDuration(timeUsed, pw); pw.println("");
+                    TimeUtils.formatDuration(wtime-lastWakeTime, pw); pw.println("");
             pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime);
                     pw.print(" timeUsed=");
                     TimeUtils.formatDuration(curCpuTime-lastCpuTime, pw); pw.println("");
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 6fb8570..545723a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -148,8 +148,8 @@
     /**
      * Request ConnectivityService display provisioning notification.
      * arg1    = Whether to make the notification visible.
-     * obj     = Intent to be launched when notification selected by user.
-     * replyTo = NetworkAgentInfo.messenger so ConnectivityService can identify sender.
+     * arg2    = NetID.
+     * obj     = Intent to be launched when notification selected by user, null if !arg1.
      */
     public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 12;
 
@@ -447,9 +447,9 @@
             // Initiate notification to sign-in.
             Intent intent = new Intent(ACTION_SIGN_IN_REQUESTED);
             intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetworkAgentInfo.network.netId));
-            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1, 0,
+            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
+                    mNetworkAgentInfo.network.netId,
                     PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
-            message.replyTo = mNetworkAgentInfo.messenger;
             mConnectivityServiceHandler.sendMessage(message);
         }
 
@@ -470,8 +470,8 @@
 
         @Override
         public void exit() {
-            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0, 0, null);
-            message.replyTo = mNetworkAgentInfo.messenger;
+            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
+                    mNetworkAgentInfo.network.netId, null);
             mConnectivityServiceHandler.sendMessage(message);
             mContext.unregisterReceiver(mUserRespondedBroadcastReceiver);
             mUserRespondedBroadcastReceiver = null;
@@ -648,6 +648,7 @@
                         new InputStreamReader(socket.getInputStream()));
                 OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
                 writer.write("GET " + url.getFile() + " HTTP/1.1\r\nHost: " + url.getHost() +
+                        "\r\nUser-Agent: " + System.getProperty("http.agent") +
                         "\r\nConnection: close\r\n\r\n");
                 writer.flush();
                 String response = reader.readLine();
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 1b40cdf..08d6fc9 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1212,8 +1212,7 @@
             } else {
                 try {
                     mEventName = mSyncOperation.wakeLockName();
-                    mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_SYNC_START,
-                            mEventName, mSyncAdapterUid);
+                    mBatteryStats.noteSyncStart(mEventName, mSyncAdapterUid);
                 } catch (RemoteException e) {
                 }
             }
@@ -1232,8 +1231,7 @@
                 mBound = false;
                 mContext.unbindService(this);
                 try {
-                    mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_SYNC_FINISH,
-                            mEventName, mSyncAdapterUid);
+                    mBatteryStats.noteSyncFinish(mEventName, mSyncAdapterUid);
                 } catch (RemoteException e) {
                 }
             }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 7f8b232..caae9f5 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -35,16 +35,20 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.app.IBatteryStats;
 import com.android.server.job.controllers.BatteryController;
 import com.android.server.job.controllers.ConnectivityController;
 import com.android.server.job.controllers.IdleController;
@@ -52,8 +56,6 @@
 import com.android.server.job.controllers.StateController;
 import com.android.server.job.controllers.TimeController;
 
-import java.util.LinkedList;
-
 /**
  * Responsible for taking jobs representing work to be performed by a client app, and determining
  * based on the criteria specified when that job should be run against the client application's
@@ -74,7 +76,7 @@
     private static final int MAX_JOB_CONTEXTS_COUNT = 3;
     static final String TAG = "JobManagerService";
     /** Master list of jobs. */
-    private final JobStore mJobs;
+    final JobStore mJobs;
 
     static final int MSG_JOB_EXPIRED = 0;
     static final int MSG_CHECK_JOB = 1;
@@ -84,33 +86,41 @@
      * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
      * early.
      */
-    private static final int MIN_IDLE_COUNT = 1;
+    static final int MIN_IDLE_COUNT = 1;
     /**
      * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
      * things early.
      */
-    private static final int MIN_CONNECTIVITY_COUNT = 2;
+    static final int MIN_CONNECTIVITY_COUNT = 2;
     /**
      * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running
      * some work early.
      */
-    private static final int MIN_READY_JOBS_COUNT = 4;
+    static final int MIN_READY_JOBS_COUNT = 4;
 
     /**
      * Track Services that have currently active or pending jobs. The index is provided by
      * {@link JobStatus#getServiceToken()}
      */
-    private final List<JobServiceContext> mActiveServices = new LinkedList<JobServiceContext>();
+    final List<JobServiceContext> mActiveServices = new ArrayList<JobServiceContext>();
     /** List of controllers that will notify this service of updates to jobs. */
-    private List<StateController> mControllers;
+    List<StateController> mControllers;
     /**
      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
      * when ready to execute them.
      */
-    private final LinkedList<JobStatus> mPendingJobs = new LinkedList<JobStatus>();
+    final ArrayList<JobStatus> mPendingJobs = new ArrayList<JobStatus>();
 
-    private final JobHandler mHandler;
-    private final JobSchedulerStub mJobSchedulerStub;
+    final JobHandler mHandler;
+    final JobSchedulerStub mJobSchedulerStub;
+
+    IBatteryStats mBatteryStats;
+
+    /**
+     * Set to true once we are allowed to run third party apps.
+     */
+    boolean mReadyToRock;
+
     /**
      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
      * still clean up. On reinstall the package will have a new uid.
@@ -152,7 +162,9 @@
     public List<JobInfo> getPendingJobs(int uid) {
         ArrayList<JobInfo> outList = new ArrayList<JobInfo>();
         synchronized (mJobs) {
-            for (JobStatus job : mJobs.getJobs()) {
+            ArraySet<JobStatus> jobs = mJobs.getJobs();
+            for (int i=0; i<jobs.size(); i++) {
+                JobStatus job = jobs.valueAt(i);
                 if (job.getUid() == uid) {
                     outList.add(job.getJob());
                 }
@@ -164,7 +176,8 @@
     private void cancelJobsForUser(int userHandle) {
         synchronized (mJobs) {
             List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
-            for (JobStatus toRemove : jobsForUser) {
+            for (int i=0; i<jobsForUser.size(); i++) {
+                JobStatus toRemove = jobsForUser.get(i);
                 if (DEBUG) {
                     Slog.d(TAG, "Cancelling: " + toRemove);
                 }
@@ -183,7 +196,8 @@
         // Remove from master list.
         synchronized (mJobs) {
             List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
-            for (JobStatus toRemove : jobsForUid) {
+            for (int i=0; i<jobsForUid.size(); i++) {
+                JobStatus toRemove = jobsForUid.get(i);
                 if (DEBUG) {
                     Slog.d(TAG, "Cancelling: " + toRemove);
                 }
@@ -230,7 +244,7 @@
     public JobSchedulerService(Context context) {
         super(context);
         // Create the controllers.
-        mControllers = new LinkedList<StateController>();
+        mControllers = new ArrayList<StateController>();
         mControllers.add(ConnectivityController.get(this));
         mControllers.add(TimeController.get(this));
         mControllers.add(IdleController.get(this));
@@ -238,11 +252,6 @@
 
         mHandler = new JobHandler(context.getMainLooper());
         mJobSchedulerStub = new JobSchedulerStub();
-        // Create the "runners".
-        for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
-            mActiveServices.add(
-                    new JobServiceContext(this, context.getMainLooper()));
-        }
         mJobs = JobStore.initAndGet(this);
     }
 
@@ -262,6 +271,29 @@
             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
             getContext().registerReceiverAsUser(
                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+        } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            synchronized (mJobs) {
+                // Let's go!
+                mReadyToRock = true;
+                mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+                        BatteryStats.SERVICE_NAME));
+                // Create the "runners".
+                for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
+                    mActiveServices.add(
+                            new JobServiceContext(this, mBatteryStats,
+                                    getContext().getMainLooper()));
+                }
+                // Attach jobs to their controllers.
+                ArraySet<JobStatus> jobs = mJobs.getJobs();
+                for (int i=0; i<jobs.size(); i++) {
+                    JobStatus job = jobs.valueAt(i);
+                    for (int j=0; j<mControllers.size(); j++) {
+                        mControllers.get(i).maybeStartTrackingJob(job);
+                    }
+                }
+                // GO GO GO!
+                mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+            }
         }
     }
 
@@ -272,14 +304,19 @@
      */
     private void startTrackingJob(JobStatus jobStatus) {
         boolean update;
+        boolean rocking;
         synchronized (mJobs) {
             update = mJobs.add(jobStatus);
+            rocking = mReadyToRock;
         }
-        for (StateController controller : mControllers) {
-            if (update) {
-                controller.maybeStopTrackingJob(jobStatus);
+        if (rocking) {
+            for (int i=0; i<mControllers.size(); i++) {
+                StateController controller = mControllers.get(i);
+                if (update) {
+                    controller.maybeStopTrackingJob(jobStatus);
+                }
+                controller.maybeStartTrackingJob(jobStatus);
             }
-            controller.maybeStartTrackingJob(jobStatus);
         }
     }
 
@@ -289,12 +326,15 @@
      */
     private boolean stopTrackingJob(JobStatus jobStatus) {
         boolean removed;
+        boolean rocking;
         synchronized (mJobs) {
             // Remove from store as well as controllers.
             removed = mJobs.remove(jobStatus);
+            rocking = mReadyToRock;
         }
-        if (removed) {
-            for (StateController controller : mControllers) {
+        if (removed && rocking) {
+            for (int i=0; i<mControllers.size(); i++) {
+                StateController controller = mControllers.get(i);
                 controller.maybeStopTrackingJob(jobStatus);
             }
         }
@@ -302,7 +342,8 @@
     }
 
     private boolean stopJobOnServiceContextLocked(JobStatus job) {
-        for (JobServiceContext jsc : mActiveServices) {
+        for (int i=0; i<mActiveServices.size(); i++) {
+            JobServiceContext jsc = mActiveServices.get(i);
             final JobStatus executing = jsc.getRunningJob();
             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
                 jsc.cancelExecutingJob();
@@ -318,7 +359,8 @@
      * is pending.
      */
     private boolean isCurrentlyActiveLocked(JobStatus job) {
-        for (JobServiceContext serviceContext : mActiveServices) {
+        for (int i=0; i<mActiveServices.size(); i++) {
+            JobServiceContext serviceContext = mActiveServices.get(i);
             final JobStatus running = serviceContext.getRunningJob();
             if (running != null && running.matches(job.getUid(), job.getJobId())) {
                 return true;
@@ -431,8 +473,13 @@
      */
     @Override
     public void onControllerStateChanged() {
-        // Post a message to to run through the list of jobs and start/stop any that are eligible.
-        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+        synchronized (mJobs) {
+            if (mReadyToRock) {
+                // Post a message to to run through the list of jobs and start/stop any that
+                // are eligible.
+                mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+            }
+        }
     }
 
     @Override
@@ -449,7 +496,8 @@
     @Override
     public void onJobMapReadFinished(List<JobStatus> jobs) {
         synchronized (mJobs) {
-            for (JobStatus js : jobs) {
+            for (int i=0; i<jobs.size(); i++) {
+                JobStatus js = jobs.get(i);
                 if (mJobs.containsJobIdForUid(js.getJobId(), js.getUid())) {
                     // An app with BOOT_COMPLETED *might* have decided to reschedule their job, in
                     // the same amount of time it took us to read it from disk. If this is the case
@@ -495,7 +543,9 @@
          */
         private void queueReadyJobsForExecutionH() {
             synchronized (mJobs) {
-                for (JobStatus job : mJobs.getJobs()) {
+                ArraySet<JobStatus> jobs = mJobs.getJobs();
+                for (int i=0; i<jobs.size(); i++) {
+                    JobStatus job = jobs.valueAt(i);
                     if (isReadyToBeExecutedLocked(job)) {
                         mPendingJobs.add(job);
                     } else if (isReadyToBeCancelledLocked(job)) {
@@ -520,7 +570,9 @@
                 int backoffCount = 0;
                 int connectivityCount = 0;
                 List<JobStatus> runnableJobs = new ArrayList<JobStatus>();
-                for (JobStatus job : mJobs.getJobs()) {
+                ArraySet<JobStatus> jobs = mJobs.getJobs();
+                for (int i=0; i<jobs.size(); i++) {
+                    JobStatus job = jobs.valueAt(i);
                     if (isReadyToBeExecutedLocked(job)) {
                         if (job.getNumFailures() > 0) {
                             backoffCount++;
@@ -539,8 +591,8 @@
                 if (backoffCount > 0 || idleCount >= MIN_IDLE_COUNT ||
                         connectivityCount >= MIN_CONNECTIVITY_COUNT ||
                         runnableJobs.size() >= MIN_READY_JOBS_COUNT) {
-                    for (JobStatus job : runnableJobs) {
-                        mPendingJobs.add(job);
+                    for (int i=0; i<runnableJobs.size(); i++) {
+                        mPendingJobs.add(runnableJobs.get(i));
                     }
                 }
             }
@@ -576,7 +628,8 @@
                 while (it.hasNext()) {
                     JobStatus nextPending = it.next();
                     JobServiceContext availableContext = null;
-                    for (JobServiceContext jsc : mActiveServices) {
+                    for (int i=0; i<mActiveServices.size(); i++) {
+                        JobServiceContext jsc = mActiveServices.get(i);
                         final JobStatus running = jsc.getRunningJob();
                         if (running != null && running.matches(nextPending.getUid(),
                                 nextPending.getJobId())) {
@@ -737,25 +790,28 @@
         synchronized (mJobs) {
             pw.println("Registered jobs:");
             if (mJobs.size() > 0) {
-                for (JobStatus job : mJobs.getJobs()) {
+                ArraySet<JobStatus> jobs = mJobs.getJobs();
+                for (int i=0; i<jobs.size(); i++) {
+                    JobStatus job = jobs.valueAt(i);
                     job.dump(pw, "  ");
                 }
             } else {
                 pw.println();
                 pw.println("No jobs scheduled.");
             }
-            for (StateController controller : mControllers) {
+            for (int i=0; i<mControllers.size(); i++) {
                 pw.println();
-                controller.dumpControllerState(pw);
+                mControllers.get(i).dumpControllerState(pw);
             }
             pw.println();
             pw.println("Pending");
-            for (JobStatus jobStatus : mPendingJobs) {
-                pw.println(jobStatus.hashCode());
+            for (int i=0; i<mPendingJobs.size(); i++) {
+                pw.println(mPendingJobs.get(i).hashCode());
             }
             pw.println();
             pw.println("Active jobs:");
-            for (JobServiceContext jsc : mActiveServices) {
+            for (int i=0; i<mActiveServices.size(); i++) {
+                JobServiceContext jsc = mActiveServices.get(i);
                 if (jsc.isAvailable()) {
                     continue;
                 } else {
@@ -765,6 +821,8 @@
                             "timeout: " + jsc.getTimeoutElapsed());
                 }
             }
+            pw.println();
+            pw.print("mReadyToRock="); pw.println(mReadyToRock);
         }
         pw.println();
     }
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 534faba3..eaf5480 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -39,6 +39,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IBatteryStats;
 import com.android.server.job.controllers.JobStatus;
 
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -57,8 +58,6 @@
     private static final long EXECUTING_TIMESLICE_MILLIS = 60 * 1000;
     /** Amount of time the JobScheduler will wait for a response from an app for a message. */
     private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
-    /** String prefix for all wakelock names. */
-    private static final String JS_WAKELOCK_PREFIX = "*job*/";
 
     private static final String[] VERB_STRINGS = {
             "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_PENDING"
@@ -87,6 +86,7 @@
     private final JobCompletedListener mCompletedListener;
     /** Used for service binding, etc. */
     private final Context mContext;
+    private final IBatteryStats mBatteryStats;
     private PowerManager.WakeLock mWakeLock;
 
     // Execution state.
@@ -109,13 +109,15 @@
     /** Track when job will timeout. */
     private long mTimeoutElapsed;
 
-    JobServiceContext(JobSchedulerService service, Looper looper) {
-        this(service.getContext(), service, looper);
+    JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, Looper looper) {
+        this(service.getContext(), batteryStats, service, looper);
     }
 
     @VisibleForTesting
-    JobServiceContext(Context context, JobCompletedListener completedListener, Looper looper) {
+    JobServiceContext(Context context, IBatteryStats batteryStats,
+            JobCompletedListener completedListener, Looper looper) {
         mContext = context;
+        mBatteryStats = batteryStats;
         mCallbackHandler = new JobServiceHandler(looper);
         mCompletedListener = completedListener;
         mAvailable = true;
@@ -152,6 +154,11 @@
                 mExecutionStartTimeElapsed = 0L;
                 return false;
             }
+            try {
+                mBatteryStats.noteJobStart(job.getName(), job.getUid());
+            } catch (RemoteException e) {
+                // Whatever.
+            }
             mAvailable = false;
             return true;
         }
@@ -228,8 +235,7 @@
         mCallbackHandler.removeMessages(MSG_TIMEOUT);
         final PowerManager pm =
                 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                JS_WAKELOCK_PREFIX + mRunningJob.getServiceComponent().getPackageName());
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
         mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
         mWakeLock.setReferenceCounted(false);
         mWakeLock.acquire();
@@ -483,6 +489,11 @@
             removeMessages(MSG_TIMEOUT);
             mCompletedListener.onJobCompleted(mRunningJob, reschedule);
             synchronized (mLock) {
+                try {
+                    mBatteryStats.noteJobFinish(mRunningJob.getName(), mRunningJob.getUid());
+                } catch (RemoteException e) {
+                    // Whatever.
+                }
                 mWakeLock.release();
                 mContext.unbindService(JobServiceContext.this);
                 mWakeLock = null;
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 8736980..48312b0 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -136,7 +136,8 @@
      * Whether this jobStatus object already exists in the JobStore.
      */
     public boolean containsJobIdForUid(int jobId, int uId) {
-        for (JobStatus ts : mJobSet) {
+        for (int i=mJobSet.size()-1; i>=0; i--) {
+            JobStatus ts = mJobSet.valueAt(i);
             if (ts.getUid() == uId && ts.getJobId() == jobId) {
                 return true;
             }
@@ -267,7 +268,8 @@
             List<JobStatus> mStoreCopy = new ArrayList<JobStatus>();
             synchronized (JobStore.this) {
                 // Copy over the jobs so we can release the lock before writing.
-                for (JobStatus jobStatus : mJobSet) {
+                for (int i=0; i<mJobSet.size(); i++) {
+                    JobStatus jobStatus = mJobSet.valueAt(i);
                     JobStatus copy = new JobStatus(jobStatus.getJob(), jobStatus.getUid(),
                             jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed());
                     mStoreCopy.add(copy);
@@ -290,7 +292,8 @@
 
                 out.startTag(null, "job-info");
                 out.attribute(null, "version", Integer.toString(JOBS_FILE_VERSION));
-                for (JobStatus jobStatus : jobList) {
+                for (int i=0; i<jobList.size(); i++) {
+                    JobStatus jobStatus = jobList.get(i);
                     if (DEBUG) {
                         Slog.d(TAG, "Saving job " + jobStatus.getJobId());
                     }
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 9ee2869..652d8f8 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -42,6 +42,8 @@
 
     final JobInfo job;
     final int uId;
+    final String name;
+    final String tag;
 
     // Constraints.
     final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
@@ -72,6 +74,8 @@
     private JobStatus(JobInfo job, int uId, int numFailures) {
         this.job = job;
         this.uId = uId;
+        this.name = job.getService().flattenToShortString();
+        this.tag = "*job*/" + this.name;
         this.numFailures = numFailures;
     }
 
@@ -140,6 +144,14 @@
         return uId;
     }
 
+    public String getName() {
+        return name;
+    }
+
+    public String getTag() {
+        return tag;
+    }
+
     public PersistableBundle getExtras() {
         return job.getExtras();
     }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 08c8271..f4248da 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -53,6 +53,7 @@
     private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
 
     private Uri mExitConditionId;
+    private ComponentName mExitConditionComponent;
 
     public ConditionProviders(Context context, Handler handler,
             UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
@@ -95,6 +96,7 @@
                 }
             }
         }
+        mCountdown.dump(pw, filter);
     }
 
     @Override
@@ -120,20 +122,20 @@
             // we tried
         }
         synchronized (mMutex) {
+            if (info.component.equals(mExitConditionComponent)) {
+                // ensure record exists, we'll wire it up and subscribe below
+                final ConditionRecord manualRecord =
+                        getRecordLocked(mExitConditionId, mExitConditionComponent);
+                manualRecord.isManual = true;
+            }
             final int N = mRecords.size();
             for(int i = 0; i < N; i++) {
                 final ConditionRecord r = mRecords.get(i);
                 if (!r.component.equals(info.component)) continue;
                 r.info = info;
-                // if automatic, auto-subscribe
-                if (r.isAutomatic) {
-                    try {
-                        final Uri id = r.id;
-                        if (DEBUG) Slog.d(TAG, "Auto-subscribing to configured condition " + id);
-                        provider.onSubscribe(id);
-                    } catch (RemoteException e) {
-                        // we tried
-                    }
+                // if automatic or manual, auto-subscribe
+                if (r.isAutomatic || r.isManual) {
+                    subscribeLocked(r);
                 }
             }
         }
@@ -274,6 +276,7 @@
     public void setZenModeCondition(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
         synchronized(mMutex) {
+            ComponentName conditionComponent = null;
             if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
                 // constructed by the client, make sure the record exists...
                 final ConditionRecord r = getRecordLocked(conditionId,
@@ -296,9 +299,13 @@
                     subscribeLocked(r);
                     r.isManual = true;
                 }
+                if (idEqual) {
+                    conditionComponent = r.component;
+                }
             }
             if (!Objects.equals(mExitConditionId, conditionId)) {
                 mExitConditionId = conditionId;
+                mExitConditionComponent = conditionComponent;
                 saveZenConfigLocked();
             }
         }
@@ -404,6 +411,13 @@
         for (ManagedServiceInfo info : mServices) {
             final IConditionProvider provider = provider(info);
             if (provider == null) continue;
+            // clear all stored conditions from this provider that we no longer care about
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
+                final ConditionRecord r = mRecords.get(i);
+                if (r.info != info) continue;
+                if (r.isManual || r.isAutomatic) continue;
+                mRecords.remove(i);
+            }
             try {
                 provider.onRequestConditions(flags);
             } catch (RemoteException e) {
@@ -420,6 +434,7 @@
         }
         synchronized (mMutex) {
             mExitConditionId = config.exitConditionId;
+            mExitConditionComponent = config.exitConditionComponent;
             if (config.conditionComponents == null || config.conditionIds == null
                     || config.conditionComponents.length != config.conditionIds.length) {
                 if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
@@ -467,6 +482,7 @@
             }
         }
         config.exitConditionId = mExitConditionId;
+        config.exitConditionComponent = mExitConditionComponent;
         if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
         mZenModeHelper.setConfig(config);
     }
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index 0884f76..aaf7cfc 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -31,6 +31,9 @@
 import android.text.format.DateUtils;
 import android.util.Slog;
 
+import com.android.server.notification.NotificationManagerService.DumpFilter;
+
+import java.io.PrintWriter;
 import java.util.Date;
 
 /** Built-in zen condition provider for simple time-based conditions */
@@ -49,11 +52,18 @@
     private final Receiver mReceiver = new Receiver();
 
     private boolean mConnected;
+    private long mTime;
 
     public CountdownConditionProvider() {
         if (DEBUG) Slog.d(TAG, "new CountdownConditionProvider()");
     }
 
+    public void dump(PrintWriter pw, DumpFilter filter) {
+        pw.println("    CountdownConditionProvider:");
+        pw.print("      mConnected="); pw.println(mConnected);
+        pw.print("      mTime="); pw.println(mTime);
+    }
+
     @Override
     public void onConnected() {
         if (DEBUG) Slog.d(TAG, "onConnected");
@@ -79,7 +89,7 @@
     @Override
     public void onSubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
-        final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
+        mTime = ZenModeConfig.tryParseCountdownConditionId(conditionId);
         final AlarmManager alarms = (AlarmManager)
                 mContext.getSystemService(Context.ALARM_SERVICE);
         final Intent intent = new Intent(ACTION).putExtra(EXTRA_CONDITION_ID, conditionId)
@@ -87,14 +97,21 @@
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
                 intent, PendingIntent.FLAG_UPDATE_CURRENT);
         alarms.cancel(pendingIntent);
-        if (time > 0) {
+        if (mTime > 0) {
             final long now = System.currentTimeMillis();
             final CharSequence span =
-                    DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
+                    DateUtils.getRelativeTimeSpanString(mTime, now, DateUtils.MINUTE_IN_MILLIS);
+            if (mTime <= now) {
+                // in the past, already false
+                notifyCondition(newCondition(mTime, Condition.STATE_FALSE));
+            } else {
+                // in the future, set an alarm
+                alarms.setExact(AlarmManager.RTC_WAKEUP, mTime, pendingIntent);
+            }
             if (DEBUG) Slog.d(TAG, String.format(
-                    "Scheduling %s for %s, %s in the future (%s), now=%s",
-                    ACTION, ts(time), time - now, span, ts(now)));
-            alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+                    "%s %s for %s, %s in the future (%s), now=%s",
+                    (mTime <= now ? "Not scheduling" : "Scheduling"),
+                    ACTION, ts(mTime), mTime - now, span, ts(now)));
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 6cd4019..0546a55 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -28,6 +28,12 @@
         if (lhs.isRecentlyIntrusive() != rhs.isRecentlyIntrusive()) {
             return lhs.isRecentlyIntrusive() ? -1 : 1;
         }
+        final int leftPackagePriority = lhs.getPackagePriority();
+        final int rightPackagePriority = rhs.getPackagePriority();
+        if (leftPackagePriority != rightPackagePriority) {
+            // by priority, high to low
+            return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
+        }
         final int leftScore = lhs.sbn.getScore();
         final int rightScore = rhs.sbn.getScore();
         if (leftScore != rightScore) {
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index d8ab9d7..1335706 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -63,4 +63,9 @@
             }
         };
     }
-}
\ No newline at end of file
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        // ignore: config has no relevant information yet.
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 892f61f..b15ac8d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -103,11 +103,9 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
-import java.util.concurrent.TimeUnit;
 
 /** {@hide} */
 public class NotificationManagerService extends SystemService {
@@ -120,7 +118,8 @@
     static final int MESSAGE_TIMEOUT = 2;
     static final int MESSAGE_SAVE_POLICY_FILE = 3;
     static final int MESSAGE_RECONSIDER_RANKING = 4;
-    static final int MESSAGE_SEND_RANKING_UPDATE = 5;
+    static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
+    static final int MESSAGE_SEND_RANKING_UPDATE = 6;
 
     static final int LONG_DELAY = 3500; // 3.5 seconds
     static final int SHORT_DELAY = 2000; // 2 seconds
@@ -152,7 +151,6 @@
     private WorkerHandler mHandler;
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
-    private Handler mRankingHandler = null;
 
     private Light mNotificationLight;
     Light mAttentionLight;
@@ -178,7 +176,6 @@
     // used as a mutex for access to all active notifications & listeners
     final ArrayList<NotificationRecord> mNotificationList =
             new ArrayList<NotificationRecord>();
-    final NotificationComparator mRankingComparator = new NotificationComparator();
     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
             new ArrayMap<String, NotificationRecord>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
@@ -203,7 +200,7 @@
     private static final String TAG_PACKAGE = "package";
     private static final String ATTR_NAME = "name";
 
-    final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>();
+    private RankingHelper mRankingHelper;
 
     private final UserProfiles mUserProfiles = new UserProfiles();
     private NotificationListeners mListeners;
@@ -360,6 +357,7 @@
                         }
                     }
                     mZenModeHelper.readXml(parser);
+                    mRankingHelper.readXml(parser);
                 }
             } catch (FileNotFoundException e) {
                 // No data yet
@@ -398,6 +396,7 @@
                 out.startTag(null, TAG_BODY);
                 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
                 mZenModeHelper.writeXml(out);
+                mRankingHelper.writeXml(out);
                 out.endTag(null, TAG_BODY);
                 out.endDocument();
                 mPolicyFile.finishWrite(stream);
@@ -752,13 +751,23 @@
 
     @Override
     public void onStart() {
+        Resources resources = getContext().getResources();
+
         mAm = ActivityManagerNative.getDefault();
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
 
         mHandler = new WorkerHandler();
         mRankingThread.start();
-        mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper());
+        String[] extractorNames;
+        try {
+            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
+        } catch (Resources.NotFoundException e) {
+            extractorNames = new String[0];
+        }
+        mRankingHelper = new RankingHelper(getContext(),
+                new RankingWorkerHandler(mRankingThread.getLooper()),
+                extractorNames);
         mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
@@ -782,7 +791,6 @@
         mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
         mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
 
-        Resources resources = getContext().getResources();
         mDefaultNotificationColor = resources.getColor(
                 R.color.config_defaultNotificationColor);
         mDefaultNotificationLedOn = resources.getInteger(
@@ -837,25 +845,6 @@
 
         mSettingsObserver = new SettingsObserver(mHandler);
 
-        // spin up NotificationSignalExtractors
-        String[] extractorNames = resources.getStringArray(
-                R.array.config_notificationSignalExtractors);
-        for (String extractorName : extractorNames) {
-            try {
-                Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName);
-                NotificationSignalExtractor extractor =
-                        (NotificationSignalExtractor) extractorClass.newInstance();
-                extractor.initialize(getContext());
-                mSignalExtractors.add(extractor);
-            } catch (ClassNotFoundException e) {
-                Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e);
-            } catch (InstantiationException e) {
-                Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e);
-            } catch (IllegalAccessException e) {
-                Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e);
-            }
-        }
-
         mArchive = new Archive(resources.getInteger(
                 R.integer.config_notificationServiceArchiveSize));
 
@@ -1062,6 +1051,19 @@
                     == AppOpsManager.MODE_ALLOWED);
         }
 
+        @Override
+        public void setPackagePriority(String pkg, int uid, int priority) {
+            checkCallerIsSystem();
+            mRankingHelper.setPackagePriority(pkg, uid, priority);
+            savePolicyFile();
+        }
+
+        @Override
+        public int getPackagePriority(String pkg, int uid) {
+            checkCallerIsSystem();
+            return mRankingHelper.getPackagePriority(pkg, uid);
+        }
+
         /**
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
@@ -1338,72 +1340,84 @@
     void dumpImpl(PrintWriter pw, DumpFilter filter) {
         pw.print("Current Notification Manager state");
         if (filter != null) {
-            pw.print(" (filtered to '"); pw.print(filter.pkgFilter); pw.print("')");
+            pw.print(" (filtered to "); pw.print(filter); pw.print(")");
         }
         pw.println(':');
         int N;
+        final boolean zenOnly = filter != null && filter.zen;
 
-        synchronized (mToastQueue) {
-            N = mToastQueue.size();
-            if (N > 0) {
-                pw.println("  Toast Queue:");
-                for (int i=0; i<N; i++) {
-                    mToastQueue.get(i).dump(pw, "    ", filter);
+        if (!zenOnly) {
+            synchronized (mToastQueue) {
+                N = mToastQueue.size();
+                if (N > 0) {
+                    pw.println("  Toast Queue:");
+                    for (int i=0; i<N; i++) {
+                        mToastQueue.get(i).dump(pw, "    ", filter);
+                    }
+                    pw.println("  ");
                 }
-                pw.println("  ");
             }
         }
 
         synchronized (mNotificationList) {
-            N = mNotificationList.size();
-            if (N > 0) {
-                pw.println("  Notification List:");
-                for (int i=0; i<N; i++) {
-                    final NotificationRecord nr = mNotificationList.get(i);
-                    if (filter != null && !filter.matches(nr.sbn)) continue;
-                    nr.dump(pw, "    ", getContext());
-                }
-                pw.println("  ");
-            }
-
-            if (filter == null) {
-                N = mLights.size();
+            if (!zenOnly) {
+                N = mNotificationList.size();
                 if (N > 0) {
-                    pw.println("  Lights List:");
+                    pw.println("  Notification List:");
                     for (int i=0; i<N; i++) {
-                        pw.println("    " + mLights.get(i));
+                        final NotificationRecord nr = mNotificationList.get(i);
+                        if (filter != null && !filter.matches(nr.sbn)) continue;
+                        nr.dump(pw, "    ", getContext());
                     }
                     pw.println("  ");
                 }
 
-                pw.println("  mSoundNotification=" + mSoundNotification);
-                pw.println("  mVibrateNotification=" + mVibrateNotification);
-                pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
-                pw.println("  mSystemReady=" + mSystemReady);
-            }
-            pw.println("  mArchive=" + mArchive.toString());
-            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
-            int i=0;
-            while (iter.hasNext()) {
-                final StatusBarNotification sbn = iter.next();
-                if (filter != null && !filter.matches(sbn)) continue;
-                pw.println("    " + sbn);
-                if (++i >= 5) {
-                    if (iter.hasNext()) pw.println("    ...");
-                    break;
+                if (filter == null) {
+                    N = mLights.size();
+                    if (N > 0) {
+                        pw.println("  Lights List:");
+                        for (int i=0; i<N; i++) {
+                            pw.println("    " + mLights.get(i));
+                        }
+                        pw.println("  ");
+                    }
+
+                    pw.println("  mSoundNotification=" + mSoundNotification);
+                    pw.println("  mVibrateNotification=" + mVibrateNotification);
+                    pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
+                    pw.println("  mSystemReady=" + mSystemReady);
+                }
+                pw.println("  mArchive=" + mArchive.toString());
+                Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
+                int i=0;
+                while (iter.hasNext()) {
+                    final StatusBarNotification sbn = iter.next();
+                    if (filter != null && !filter.matches(sbn)) continue;
+                    pw.println("    " + sbn);
+                    if (++i >= 5) {
+                        if (iter.hasNext()) pw.println("    ...");
+                        break;
+                    }
                 }
             }
 
-            pw.println("\n  Usage Stats:");
-            mUsageStats.dump(pw, "    ", filter);
+            if (!zenOnly) {
+                pw.println("\n  Usage Stats:");
+                mUsageStats.dump(pw, "    ", filter);
+            }
 
-            if (filter == null) {
+            if (filter == null || zenOnly) {
                 pw.println("\n  Zen Mode:");
                 mZenModeHelper.dump(pw, "    ");
             }
 
-            pw.println("\n  Notification listeners:");
-            mListeners.dump(pw, filter);
+            if (!zenOnly) {
+                pw.println("\n  Ranking Config:");
+                mRankingHelper.dump(pw, "    ", filter);
+
+                pw.println("\n  Notification listeners:");
+                mListeners.dump(pw, filter);
+            }
 
             pw.println("\n  Condition providers:");
             mConditionProviders.dump(pw, filter);
@@ -1509,16 +1523,7 @@
                     // Retain ranking information from previous record
                     r.copyRankingInformation(old);
                 }
-                if (!mSignalExtractors.isEmpty()) {
-                    for (NotificationSignalExtractor extractor : mSignalExtractors) {
-                        try {
-                            RankingReconsideration recon = extractor.process(r);
-                            scheduleRankingReconsideration(recon);
-                        } catch (Throwable t) {
-                            Slog.w(TAG, "NotificationSignalExtractor failed.", t);
-                        }
-                    }
-                }
+                mRankingHelper.extractSignals(r);
 
                 // 3. Apply local rules
 
@@ -1563,7 +1568,7 @@
 
                     applyZenModeLocked(r);
 
-                    Collections.sort(mNotificationList, mRankingComparator);
+                    mRankingHelper.sort(mNotificationList);
 
                     if (notification.icon != 0) {
                         mListeners.notifyPostedLocked(n);
@@ -1838,14 +1843,6 @@
         }
     }
 
-    private void scheduleRankingReconsideration(RankingReconsideration recon) {
-        if (recon != null) {
-            Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, recon);
-            long delay = recon.getDelay(TimeUnit.MILLISECONDS);
-            mRankingHandler.sendMessageDelayed(m, delay);
-        }
-    }
-
     private void handleRankingReconsideration(Message message) {
         if (!(message.obj instanceof RankingReconsideration)) return;
         RankingReconsideration recon = (RankingReconsideration) message.obj;
@@ -1860,7 +1857,7 @@
             boolean interceptBefore = record.isIntercepted();
             recon.applyChangesLocked(record);
             applyZenModeLocked(record);
-            Collections.sort(mNotificationList, mRankingComparator);
+            mRankingHelper.sort(mNotificationList);
             int indexAfter = findNotificationRecordIndexLocked(record);
             boolean interceptAfter = record.isIntercepted();
             changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
@@ -1873,6 +1870,25 @@
         }
     }
 
+    private void handleRankingConfigChange() {
+        synchronized (mNotificationList) {
+            final int N = mNotificationList.size();
+            ArrayList<String> orderBefore = new ArrayList<String>(N);
+            for (int i = 0; i < N; i++) {
+                final NotificationRecord r = mNotificationList.get(i);
+                orderBefore.add(r.getKey());
+                mRankingHelper.extractSignals(r);
+            }
+            mRankingHelper.sort(mNotificationList);
+            for (int i = 0; i < N; i++) {
+                if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
+                    scheduleSendRankingUpdate();
+                    return;
+                }
+            }
+        }
+    }
+
     // let zen mode evaluate this record
     private void applyZenModeLocked(NotificationRecord record) {
         record.setIntercepted(mZenModeHelper.shouldIntercept(record));
@@ -1880,7 +1896,7 @@
 
     // lock on mNotificationList
     private int findNotificationRecordIndexLocked(NotificationRecord target) {
-        return Collections.binarySearch(mNotificationList, target, mRankingComparator);
+        return mRankingHelper.indexOf(mNotificationList, target);
     }
 
     private void scheduleSendRankingUpdate() {
@@ -1928,6 +1944,9 @@
                 case MESSAGE_RECONSIDER_RANKING:
                     handleRankingReconsideration(msg);
                     break;
+                case MESSAGE_RANKING_CONFIG_CHANGE:
+                    handleRankingConfigChange();
+                    break;
             }
         }
     }
@@ -2461,27 +2480,39 @@
 
     public static final class DumpFilter {
         public String pkgFilter;
+        public boolean zen;
 
         public static DumpFilter parseFromArguments(String[] args) {
-            if (args == null || args.length != 2 || !"p".equals(args[0])
-                    || args[1] == null || args[1].trim().isEmpty()) {
-                return null;
+            if (args != null && args.length == 2 && "p".equals(args[0])
+                    && args[1] != null && !args[1].trim().isEmpty()) {
+                final DumpFilter filter = new DumpFilter();
+                filter.pkgFilter = args[1].trim().toLowerCase();
+                return filter;
             }
-            final DumpFilter filter = new DumpFilter();
-            filter.pkgFilter = args[1].trim().toLowerCase();
-            return filter;
+            if (args != null && args.length == 1 && "zen".equals(args[0])) {
+                final DumpFilter filter = new DumpFilter();
+                filter.zen = true;
+                return filter;
+            }
+            return null;
         }
 
         public boolean matches(StatusBarNotification sbn) {
-            return sbn != null && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
+            return zen ? true : sbn != null
+                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
         }
 
         public boolean matches(ComponentName component) {
-            return component != null && matches(component.getPackageName());
+            return zen ? true : component != null && matches(component.getPackageName());
         }
 
         public boolean matches(String pkg) {
-            return pkg != null && pkg.toLowerCase().contains(pkgFilter);
+            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
+        }
+
+        @Override
+        public String toString() {
+            return zen ? "zen" : ('\'' + pkgFilter + '\'');
         }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0e6265c..088b813 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -58,6 +58,7 @@
 
     // Is this record an update of an old record?
     public boolean isUpdate;
+    private int mPackagePriority;
 
     NotificationRecord(StatusBarNotification sbn, int score)
     {
@@ -70,6 +71,7 @@
     public void copyRankingInformation(NotificationRecord previous) {
         mContactAffinity = previous.mContactAffinity;
         mRecentlyIntrusive = previous.mRecentlyIntrusive;
+        mPackagePriority = previous.mPackagePriority;
         mIntercept = previous.mIntercept;
         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
     }
@@ -141,6 +143,7 @@
         pw.println(prefix + "  stats=" + stats.toString());
         pw.println(prefix + "  mContactAffinity=" + mContactAffinity);
         pw.println(prefix + "  mRecentlyIntrusive=" + mRecentlyIntrusive);
+        pw.println(prefix + "  mPackagePriority=" + mPackagePriority);
         pw.println(prefix + "  mIntercept=" + mIntercept);
         pw.println(prefix + "  mRankingTimeMs=" + mRankingTimeMs);
     }
@@ -193,6 +196,14 @@
         return mRecentlyIntrusive;
     }
 
+    public void setPackagePriority(int packagePriority) {
+      mPackagePriority = packagePriority;
+    }
+
+    public int getPackagePriority() {
+        return mPackagePriority;
+    }
+
     public boolean setIntercepted(boolean intercept) {
         mIntercept = intercept;
         return mIntercept;
@@ -230,5 +241,4 @@
         }
         return sbn.getPostTime();
     }
-
 }
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index 1537ea9..43d05d0 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -38,4 +38,10 @@
      */
     public RankingReconsideration process(NotificationRecord notification);
 
+    /**
+     * Called whenever the {@link RankingConfig} changes.
+     *
+     * @param config information about which signals are important.
+     */
+    void setConfig(RankingConfig config);
 }
diff --git a/services/core/java/com/android/server/notification/PackagePriorityExtractor.java b/services/core/java/com/android/server/notification/PackagePriorityExtractor.java
new file mode 100644
index 0000000..9cdb3e1
--- /dev/null
+++ b/services/core/java/com/android/server/notification/PackagePriorityExtractor.java
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.android.server.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.util.Slog;
+
+public class PackagePriorityExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "ImportantPackageExtractor";
+    private static final boolean DBG = false;
+
+    private RankingConfig mConfig;
+
+    public void initialize(Context ctx) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        if (mConfig == null) {
+            if (DBG) Slog.d(TAG, "missing config");
+            return null;
+        }
+
+        final int packagePriority = mConfig.getPackagePriority(
+                record.sbn.getPackageName(), record.sbn.getUid());
+        record.setPackagePriority(packagePriority);
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        mConfig = config;
+    }
+}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
new file mode 100644
index 0000000..7d0bd59
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+public interface RankingConfig {
+    int getPackagePriority(String packageName, int uid);
+
+    void setPackagePriority(String packageName, int uid, int priority);
+}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
new file mode 100644
index 0000000..fc03c17
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -0,0 +1,250 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+public class RankingHelper implements RankingConfig {
+    private static final String TAG = "RankingHelper";
+    private static final boolean DEBUG = false;
+
+    private static final int XML_VERSION = 1;
+
+    private static final String TAG_RANKING = "ranking";
+    private static final String TAG_PACKAGE = "package";
+    private static final String ATT_VERSION = "version";
+
+    private static final String ATT_NAME = "name";
+    private static final String ATT_UID = "uid";
+    private static final String ATT_PRIORITY = "priority";
+
+    private static final String VALUE_HIGH = "high";
+
+    private final NotificationSignalExtractor[] mSignalExtractors;
+    private final NotificationComparator mRankingComparator = new NotificationComparator();
+
+    // Package name to uid, to priority. Would be better as Table<String, Int, Int>
+    private final ArrayMap<String, SparseIntArray> mPackagePriorities;
+
+    private final Context mContext;
+    private final Handler mRankingHandler;
+
+    public RankingHelper(Context context, Handler rankingHandler, String[] extractorNames) {
+        mContext = context;
+        mRankingHandler = rankingHandler;
+        mPackagePriorities = new ArrayMap<String, SparseIntArray>();
+
+        final int N = extractorNames.length;
+        mSignalExtractors = new NotificationSignalExtractor[N];
+        for (int i = 0; i < N; i++) {
+            try {
+                Class<?> extractorClass = mContext.getClassLoader().loadClass(extractorNames[i]);
+                NotificationSignalExtractor extractor =
+                        (NotificationSignalExtractor) extractorClass.newInstance();
+                extractor.initialize(mContext);
+                extractor.setConfig(this);
+                mSignalExtractors[i] = extractor;
+            } catch (ClassNotFoundException e) {
+                Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
+            } catch (InstantiationException e) {
+                Slog.w(TAG, "Couldn't instantiate extractor " + extractorNames[i] + ".", e);
+            } catch (IllegalAccessException e) {
+                Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
+            }
+        }
+    }
+
+    public void extractSignals(NotificationRecord r) {
+        final int N = mSignalExtractors.length;
+        for (int i = 0; i < N; i++) {
+            NotificationSignalExtractor extractor = mSignalExtractors[i];
+            try {
+                RankingReconsideration recon = extractor.process(r);
+                if (recon != null) {
+                    Message m = Message.obtain(mRankingHandler,
+                            NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
+                    long delay = recon.getDelay(TimeUnit.MILLISECONDS);
+                    mRankingHandler.sendMessageDelayed(m, delay);
+                }
+            } catch (Throwable t) {
+                Slog.w(TAG, "NotificationSignalExtractor failed.", t);
+            }
+        }
+    }
+
+    public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
+        int type = parser.getEventType();
+        if (type != XmlPullParser.START_TAG) return;
+        String tag = parser.getName();
+        if (!TAG_RANKING.equals(tag)) return;
+        mPackagePriorities.clear();
+        final int version = safeInt(parser, ATT_VERSION, XML_VERSION);
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            tag = parser.getName();
+            if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
+                return;
+            }
+            if (type == XmlPullParser.START_TAG) {
+                if (TAG_PACKAGE.equals(tag)) {
+                    int uid = safeInt(parser, ATT_UID, UserHandle.USER_ALL);
+                    int priority = safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT);
+                    String name = parser.getAttributeValue(null, ATT_NAME);
+
+                    if (!TextUtils.isEmpty(name) && priority != Notification.PRIORITY_DEFAULT) {
+                        SparseIntArray priorityByUid = mPackagePriorities.get(name);
+                        if (priorityByUid == null) {
+                            priorityByUid = new SparseIntArray();
+                            mPackagePriorities.put(name, priorityByUid);
+                        }
+                        priorityByUid.put(uid, priority);
+                    }
+                }
+            }
+        }
+        throw new IllegalStateException("Failed to reach END_DOCUMENT");
+    }
+
+    public void writeXml(XmlSerializer out) throws IOException {
+        out.startTag(null, TAG_RANKING);
+        out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
+
+        final int N = mPackagePriorities.size();
+        for (int i = 0; i < N; i ++) {
+            String name = mPackagePriorities.keyAt(i);
+            SparseIntArray priorityByUid = mPackagePriorities.get(name);
+            final int M = priorityByUid.size();
+            for (int j = 0; j < M; j++) {
+                int uid = priorityByUid.keyAt(j);
+                int priority = priorityByUid.get(uid);
+                out.startTag(null, TAG_PACKAGE);
+                out.attribute(null, ATT_NAME, name);
+                out.attribute(null, ATT_UID, Integer.toString(uid));
+                out.attribute(null, ATT_PRIORITY, Integer.toString(priority));
+                out.endTag(null, TAG_PACKAGE);
+            }
+        }
+        out.endTag(null, TAG_RANKING);
+    }
+
+    private void updateConfig() {
+        final int N = mSignalExtractors.length;
+        for (int i = 0; i < N; i++) {
+            mSignalExtractors[i].setConfig(this);
+        }
+        mRankingHandler.sendEmptyMessage(NotificationManagerService.MESSAGE_RANKING_CONFIG_CHANGE);
+    }
+
+    public void sort(ArrayList<NotificationRecord> notificationList) {
+        Collections.sort(notificationList, mRankingComparator);
+    }
+
+    public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
+        return Collections.binarySearch(notificationList, target, mRankingComparator);
+    }
+
+    private static int safeInt(XmlPullParser parser, String att, int defValue) {
+        final String val = parser.getAttributeValue(null, att);
+        return tryParseInt(val, defValue);
+    }
+
+    private static int tryParseInt(String value, int defValue) {
+        if (TextUtils.isEmpty(value)) return defValue;
+        try {
+            return Integer.valueOf(value);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
+    }
+
+    @Override
+    public int getPackagePriority(String packageName, int uid) {
+        int priority = Notification.PRIORITY_DEFAULT;
+        SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
+        if (priorityByUid != null) {
+            priority = priorityByUid.get(uid, Notification.PRIORITY_DEFAULT);
+        }
+        return priority;
+    }
+
+    @Override
+    public void setPackagePriority(String packageName, int uid, int priority) {
+        if (priority == getPackagePriority(packageName, uid)) {
+            return;
+        }
+        SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
+        if (priorityByUid == null) {
+            priorityByUid = new SparseIntArray();
+            mPackagePriorities.put(packageName, priorityByUid);
+        }
+        priorityByUid.put(uid, priority);
+        updateConfig();
+    }
+
+    public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
+        if (filter == null) {
+            final int N = mSignalExtractors.length;
+            pw.print(prefix);
+            pw.print("mSignalExtractors.length = ");
+            pw.println(N);
+            for (int i = 0; i < N; i++) {
+                pw.print(prefix);
+                pw.print("  ");
+                pw.println(mSignalExtractors[i]);
+            }
+        }
+        final int N = mPackagePriorities.size();
+        if (filter == null) {
+            pw.print(prefix);
+            pw.println("package priorities:");
+        }
+        for (int i = 0; i < N; i++) {
+            String name = mPackagePriorities.keyAt(i);
+            if (filter == null || filter.matches(name)) {
+                SparseIntArray priorityByUid = mPackagePriorities.get(name);
+                final int M = priorityByUid.size();
+                for (int j = 0; j < M; j++) {
+                    int uid = priorityByUid.keyAt(j);
+                    int priority = priorityByUid.get(uid);
+                    pw.print(prefix);
+                    pw.print("  ");
+                    pw.print(name);
+                    pw.print(" (");
+                    pw.print(uid);
+                    pw.print(") has priority: ");
+                    pw.println(priority);
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 4ac2dcc..bdc364c 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -264,6 +264,11 @@
         return validatePeople(record);
     }
 
+    @Override
+    public void setConfig(RankingConfig config) {
+        // ignore: config has no relevant information yet.
+    }
+
     private static class LookupResult {
         private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000;  // 1hr
         public static final int INVALID_ID = -1;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2215182..56b5b7e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -165,6 +165,7 @@
 import android.util.AtomicFile;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
+import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.LogPrinter;
 import android.util.PrintStreamPrinter;
@@ -1713,7 +1714,7 @@
                 // NOTE: We ignore potential failures here during a system scan (like
                 // the rest of the commands above) because there's precious little we
                 // can do about it. A settings error is reported, though.
-                adjustCpuAbisForSharedUserLPw(setting.packages, null,
+                adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
                         false /* force dexopt */, false /* defer dexopt */);
             }
 
@@ -4636,7 +4637,7 @@
             return DEX_OPT_SKIPPED;
         }
 
-        final Collection<String> paths = pkg.getAllCodePaths();
+        final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
         boolean performedDexOpt = false;
         // There are three basic cases here:
         // 1.) we need to dexopt, either because we are forced or it is needed
@@ -4648,7 +4649,8 @@
                     final boolean isDexOptNeeded = DexFile.isDexOptNeededInternal(path,
                             pkg.packageName, instructionSet, defer);
                     if (forceDex || (!defer && isDexOptNeeded)) {
-                        Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName + " isa=" + instructionSet);
+                        Log.i(TAG, "Running dexopt on: " + path + " pkg="
+                                + pkg.applicationInfo.packageName + " isa=" + instructionSet);
                         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
                         final int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
                                 pkg.packageName, instructionSet);
@@ -5483,6 +5485,8 @@
 
                     if (copyRet >= 0) {
                         pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
+                    } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && abiOverride != null) {
+                        pkg.applicationInfo.primaryCpuAbi = abiOverride;
                     }
                 }
             } catch (IOException ioe) {
@@ -5538,11 +5542,8 @@
             // We also do this *before* we perform dexopt on this package, so that
             // we can avoid redundant dexopts, and also to make sure we've got the
             // code and package path correct.
-            if (!adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
-                    pkg, forceDex, (scanMode & SCAN_DEFER_DEX) != 0)) {
-                throw new PackageManagerException(INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
-                        "scanPackageLI");
-            }
+            adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
+                    pkg, forceDex, (scanMode & SCAN_DEFER_DEX) != 0);
         }
 
         if ((scanMode&SCAN_NO_DEX) == 0) {
@@ -6041,7 +6042,7 @@
      * NOTE: We currently only match for the primary CPU abi string. Matching the secondary
      * adds unnecessary complexity.
      */
-    private boolean adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
+    private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
             PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt) {
         String requiredInstructionSet = null;
         if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
@@ -6055,27 +6056,23 @@
             // when scannedPackage is an update of an existing package. Without this check,
             // we will never be able to change the ABI of any package belonging to a shared
             // user, even if it's compatible with other packages.
-            if (scannedPackage == null || ! scannedPackage.packageName.equals(ps.name)) {
+            if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
                 if (ps.primaryCpuAbiString == null) {
                     continue;
                 }
 
                 final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString);
-                if (requiredInstructionSet != null) {
-                    if (!instructionSet.equals(requiredInstructionSet)) {
-                        // We have a mismatch between instruction sets (say arm vs arm64).
-                        // bail out.
-                        String errorMessage = "Instruction set mismatch, "
-                                + ((requirer == null) ? "[caller]" : requirer)
-                                + " requires " + requiredInstructionSet + " whereas " + ps
-                                + " requires " + instructionSet;
-                        Slog.e(TAG, errorMessage);
+                if (requiredInstructionSet != null && !instructionSet.equals(requiredInstructionSet)) {
+                    // We have a mismatch between instruction sets (say arm vs arm64) warn about
+                    // this but there's not much we can do.
+                    String errorMessage = "Instruction set mismatch, "
+                            + ((requirer == null) ? "[caller]" : requirer)
+                            + " requires " + requiredInstructionSet + " whereas " + ps
+                            + " requires " + instructionSet;
+                    Slog.w(TAG, errorMessage);
+                }
 
-                        reportSettingsProblem(Log.WARN, errorMessage);
-                        // Give up, don't bother making any other changes to the package settings.
-                        return false;
-                    }
-                } else {
+                if (requiredInstructionSet == null) {
                     requiredInstructionSet = instructionSet;
                     requirer = ps;
                 }
@@ -6112,7 +6109,7 @@
                         if (performDexOptLI(ps.pkg, forceDexOpt, deferDexOpt, true) == DEX_OPT_FAILED) {
                             ps.primaryCpuAbiString = null;
                             ps.pkg.applicationInfo.primaryCpuAbi = null;
-                            return false;
+                            return;
                         } else {
                             mInstaller.rmdex(ps.codePathString, getPreferredInstructionSet());
                         }
@@ -6120,8 +6117,6 @@
                 }
             }
         }
-
-        return true;
     }
 
     private void setUpCustomResolverActivity(PackageParser.Package pkg) {
@@ -9863,6 +9858,18 @@
             Slog.w(TAG, msg);
         }
 
+        public void setError(String msg, PackageParserException e) {
+            returnCode = e.error;
+            returnMsg = ExceptionUtils.getCompleteMessage(msg, e);
+            Slog.w(TAG, msg, e);
+        }
+
+        public void setError(String msg, PackageManagerException e) {
+            returnCode = e.error;
+            returnMsg = ExceptionUtils.getCompleteMessage(msg, e);
+            Slog.w(TAG, msg, e);
+        }
+
         // In some error cases we want to convey more info back to the observer
         String origPackage;
         String origPermission;
@@ -9916,8 +9923,7 @@
             }
 
         } catch (PackageManagerException e) {
-            res.setError(e.error,
-                    "Package couldn't be installed in " + pkg.codePath + ": " + e.getMessage());
+            res.setError("Package couldn't be installed in " + pkg.codePath, e);
         }
     }
 
@@ -10015,8 +10021,7 @@
                 updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
                 updatedSettings = true;
             } catch (PackageManagerException e) {
-                res.setError(e.error,
-                        "Package couldn't be installed in " + pkg.codePath + ": " + e.getMessage());
+                res.setError("Package couldn't be installed in " + pkg.codePath, e);
             }
         }
 
@@ -10147,8 +10152,7 @@
             }
 
         } catch (PackageManagerException e) {
-            res.setError(e.error,
-                    "Package couldn't be installed in " + pkg.codePath + ": " + e.getMessage());
+            res.setError("Package couldn't be installed in " + pkg.codePath, e);
         }
 
         if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -10286,7 +10290,7 @@
         try {
             pkg = pp.parsePackage(tmpPackageFile, parseFlags);
         } catch (PackageParserException e) {
-            res.setError(e.error, "Failed parse during installPackageLI: " + e.getMessage());
+            res.setError("Failed parse during installPackageLI", e);
             return;
         }
 
@@ -10302,7 +10306,7 @@
             pp.collectCertificates(pkg, parseFlags);
             pp.collectManifestDigest(pkg);
         } catch (PackageParserException e) {
-            res.setError(e.error, "Failed collect during installPackageLI: " + e.getMessage());
+            res.setError("Failed collect during installPackageLI", e);
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index e007600..0e1340c 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -46,6 +46,8 @@
 import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
+import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindBackgroundAnimation;
+import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation;
@@ -72,44 +74,42 @@
             WindowManagerService.DEBUG_APP_TRANSITIONS;
     private static final boolean DEBUG_ANIM = WindowManagerService.DEBUG_ANIM;
 
-    /** Bit mask that is set for all enter transition. */
-    public static final int TRANSIT_ENTER_MASK = 0x1000;
-
-    /** Bit mask that is set for all exit transitions. */
-    public static final int TRANSIT_EXIT_MASK = 0x2000;
 
     /** Not set up for a transition. */
     public static final int TRANSIT_UNSET = -1;
     /** No animation for transition. */
     public static final int TRANSIT_NONE = 0;
     /** A window in a new activity is being opened on top of an existing one in the same task. */
-    public static final int TRANSIT_ACTIVITY_OPEN = 6 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_ACTIVITY_OPEN = 6;
     /** The window in the top-most activity is being closed to reveal the
      * previous activity in the same task. */
-    public static final int TRANSIT_ACTIVITY_CLOSE = 7 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_ACTIVITY_CLOSE = 7;
     /** A window in a new task is being opened on top of an existing one
      * in another activity's task. */
-    public static final int TRANSIT_TASK_OPEN = 8 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_TASK_OPEN = 8;
     /** A window in the top-most activity is being closed to reveal the
      * previous activity in a different task. */
-    public static final int TRANSIT_TASK_CLOSE = 9 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_TASK_CLOSE = 9;
     /** A window in an existing task is being displayed on top of an existing one
      * in another activity's task. */
-    public static final int TRANSIT_TASK_TO_FRONT = 10 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_TASK_TO_FRONT = 10;
     /** A window in an existing task is being put below all other tasks. */
-    public static final int TRANSIT_TASK_TO_BACK = 11 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_TASK_TO_BACK = 11;
     /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
      * does, effectively closing the wallpaper. */
-    public static final int TRANSIT_WALLPAPER_CLOSE = 12 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_WALLPAPER_CLOSE = 12;
     /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
      * effectively opening the wallpaper. */
-    public static final int TRANSIT_WALLPAPER_OPEN = 13 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_WALLPAPER_OPEN = 13;
     /** A window in a new activity is being opened on top of an existing one, and both are on top
      * of the wallpaper. */
-    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
     /** The window in the top-most activity is being closed to reveal the previous activity, and
      * both are on top of the wallpaper. */
-    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
+    /** A window in a new task is being opened behind an existing one in another activity's task.
+     * The new window will show briefly and then be gone. */
+    public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
 
     /** Fraction of animation at which the recents thumbnail becomes completely transparent */
     private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f;
@@ -811,6 +811,10 @@
                             ? WindowAnimation_wallpaperIntraCloseEnterAnimation
                             : WindowAnimation_wallpaperIntraCloseExitAnimation;
                     break;
+                case TRANSIT_TASK_OPEN_BEHIND:
+                    animAttr = enter
+                            ? WindowAnimation_launchTaskBehindSourceAnimation
+                            : WindowAnimation_launchTaskBehindBackgroundAnimation;
             }
             a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
@@ -896,9 +900,6 @@
             case TRANSIT_NONE: {
                 return "TRANSIT_NONE";
             }
-            case TRANSIT_EXIT_MASK: {
-                return "TRANSIT_EXIT_MASK";
-            }
             case TRANSIT_ACTIVITY_OPEN: {
                 return "TRANSIT_ACTIVITY_OPEN";
             }
@@ -929,6 +930,9 @@
             case TRANSIT_WALLPAPER_INTRA_CLOSE: {
                 return "TRANSIT_WALLPAPER_INTRA_CLOSE";
             }
+            case TRANSIT_TASK_OPEN_BEHIND: {
+                return "TRANSIT_TASK_OPEN_BEHIND";
+            }
             default: {
                 return "<UNKNOWN>";
             }
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 63ae98e..874e105 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -16,7 +16,9 @@
 
 package com.android.server.wm;
 
+import android.graphics.Bitmap;
 import android.graphics.Matrix;
+import android.os.RemoteException;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.view.Display;
@@ -281,9 +283,21 @@
 
         final int N = mAllAppWinAnimators.size();
         for (int i=0; i<N; i++) {
-            mAllAppWinAnimators.get(i).finishExit();
+            final WindowStateAnimator winAnim = mAllAppWinAnimators.get(i);
+            if (mAppToken.mLaunchTaskBehind) {
+                winAnim.mWin.mExiting = true;
+            }
+            winAnim.finishExit();
         }
-        mAppToken.updateReportedVisibilityLocked();
+        if (mAppToken.mLaunchTaskBehind) {
+            try {
+                mService.mActivityManager.notifyLaunchTaskBehindComplete(mAppToken.token);
+            } catch (RemoteException e) {
+            }
+            mAppToken.mLaunchTaskBehind = false;
+        } else {
+            mAppToken.updateReportedVisibilityLocked();
+        }
 
         return false;
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 12c15e2..312689b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -109,6 +109,8 @@
 
     boolean mDeferRemoval;
 
+    boolean mLaunchTaskBehind;
+
     AppWindowToken(WindowManagerService _service, IApplicationToken _token,
             boolean _voiceInteraction) {
         super(_service, _token.asBinder(),
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f1d0585..27fff1d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -248,7 +248,7 @@
         for (int i = windows.size() - 1; i >= 0; i--) {
             WindowState win = windows.get(i);
             WindowStateAnimator winAnimator = win.mWinAnimator;
-            final int flags = winAnimator.mAttrFlags;
+            final int flags = win.mAttrs.flags;
 
             if (winAnimator.mSurfaceControl != null) {
                 final boolean wasAnimating = winAnimator.mWasAnimating;
@@ -309,8 +309,7 @@
                             + " hidden=" + win.mRootToken.hidden
                             + " anim=" + win.mWinAnimator.mAnimation);
                 } else if (mPolicy.canBeForceHidden(win, win.mAttrs)) {
-                    final boolean hideWhenLocked =
-                            (winAnimator.mAttrFlags & FLAG_SHOW_WHEN_LOCKED) == 0;
+                    final boolean hideWhenLocked = (flags & FLAG_SHOW_WHEN_LOCKED) == 0;
                     final boolean changed;
                     if (((mForceHiding == KEYGUARD_ANIMATING_IN)
                                 && (!winAnimator.isAnimating() || hideWhenLocked))
@@ -416,7 +415,7 @@
                 continue;
             }
 
-            final int flags = winAnimator.mAttrFlags;
+            final int flags = win.mAttrs.flags;
 
             // If this window is animating, make a note that we have
             // an animating window and take care of a request to run
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 396ec8f..a5959d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -497,8 +497,8 @@
     boolean mStartingIconInTransition = false;
     boolean mSkipAppTransitionAnimation = false;
 
-    final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
-    final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
+    final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<AppWindowToken>();
+    final ArraySet<AppWindowToken> mClosingApps = new ArraySet<AppWindowToken>();
 
     boolean mIsTouchDevice;
 
@@ -3234,6 +3234,12 @@
                                 SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
             }
 
+            if (atoken.mLaunchTaskBehind) {
+                // Differentiate the two animations. This one which is briefly on the screen
+                // gets the !enter animation, and the other activity which remains on the
+                // screen gets the enter animation. Both appear in the mOpeningApps set.
+                enter = false;
+            }
             Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
                     mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen,
                     isVoiceInteraction);
@@ -3449,14 +3455,14 @@
         EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
         Task task = new Task(atoken, stack, userId);
         mTaskIdToTask.put(taskId, task);
-        stack.addTask(task, true);
+        stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */);
         return task;
     }
 
     @Override
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
-            int configChanges, boolean voiceInteraction) {
+            int configChanges, boolean voiceInteraction, boolean launchTaskBehind) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3490,6 +3496,7 @@
             atoken.requestedOrientation = requestedOrientation;
             atoken.layoutConfigChanges = (configChanges &
                     (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
+            atoken.mLaunchTaskBehind = launchTaskBehind;
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
                     + " to stack=" + stackId + " task=" + taskId + " at " + addPos);
 
@@ -3954,16 +3961,16 @@
         }
 
         synchronized(mWindowMap) {
-            if (DEBUG_APP_TRANSITIONS) {
-                RuntimeException e = new RuntimeException("here");
-                e.fillInStackTrace();
-                Slog.w(TAG, "Execute app transition: " + mAppTransition, e);
-            }
+            if (DEBUG_APP_TRANSITIONS) Slog.w(TAG, "Execute app transition: " + mAppTransition,
+                    new RuntimeException("here").fillInStackTrace());
             if (mAppTransition.isTransitionSet()) {
                 mAppTransition.setReady();
                 final long origId = Binder.clearCallingIdentity();
-                performLayoutAndPlaceSurfacesLocked();
-                Binder.restoreCallingIdentity(origId);
+                try {
+                    performLayoutAndPlaceSurfacesLocked();
+                } finally {
+                    Binder.restoreCallingIdentity(origId);
+                }
             }
         }
     }
@@ -4370,17 +4377,11 @@
                 return;
             }
 
-            if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
-                RuntimeException e = null;
-                if (!HIDE_STACK_CRAWLS) {
-                    e = new RuntimeException();
-                    e.fillInStackTrace();
-                }
-                Slog.v(TAG, "setAppVisibility(" + token + ", visible=" + visible
-                        + "): " + mAppTransition
-                        + " hidden=" + wtoken.hidden
-                        + " hiddenRequested=" + wtoken.hiddenRequested, e);
-            }
+            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());
 
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
@@ -4428,6 +4429,21 @@
                         wtoken.waitingToHide = true;
                     }
                 }
+                if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
+                    // We're launchingBehind, add the launching activity to mOpeningApps.
+                    final WindowState win =
+                            findFocusedWindowLocked(getDefaultDisplayContentLocked());
+                    if (win != null) {
+                        final AppWindowToken focusedToken = win.mAppToken;
+                        if (focusedToken != null) {
+                            if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "TRANSIT_TASK_OPEN_BEHIND, " +
+                                    " adding " + focusedToken + " to mOpeningApps");
+                            // Force animation to be loaded.
+                            focusedToken.hidden = true;
+                            mOpeningApps.add(focusedToken);
+                        }
+                    }
+                }
                 return;
             }
 
@@ -8558,7 +8574,7 @@
             // all of the apps are ready.  Otherwise just go because
             // we'll unfreeze the display when everyone is ready.
             for (i=0; i<NN && goodToGo; i++) {
-                AppWindowToken wtoken = mOpeningApps.get(i);
+                AppWindowToken wtoken = mOpeningApps.valueAt(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                         "Check opening app=" + wtoken + ": allDrawn="
                         + wtoken.allDrawn + " startingDisplayed="
@@ -8631,12 +8647,12 @@
             for (i=0; i<NN; i++) {
                 final AppWindowToken wtoken;
                 if (i < NC) {
-                    wtoken = mClosingApps.get(i);
+                    wtoken = mClosingApps.valueAt(i);
                     if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
                         closingAppHasWallpaper = true;
                     }
                 } else {
-                    wtoken = mOpeningApps.get(i - NC);
+                    wtoken = mOpeningApps.valueAt(i - NC);
                     if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
                         openingAppHasWallpaper = true;
                     }
@@ -8710,7 +8726,7 @@
 
             NN = mOpeningApps.size();
             for (i=0; i<NN; i++) {
-                AppWindowToken wtoken = mOpeningApps.get(i);
+                AppWindowToken wtoken = mOpeningApps.valueAt(i);
                 final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
                 appAnimator.clearThumbnail();
@@ -8743,7 +8759,7 @@
             }
             NN = mClosingApps.size();
             for (i=0; i<NN; i++) {
-                AppWindowToken wtoken = mClosingApps.get(i);
+                AppWindowToken wtoken = mClosingApps.valueAt(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
                 wtoken.mAppAnimator.clearThumbnail();
                 wtoken.inPendingTransaction = false;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b24072f..49d4ae9 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -172,7 +172,6 @@
     /** Was this window last hidden? */
     boolean mLastHidden;
 
-    int mAttrFlags;
     int mAttrType;
 
     public WindowStateAnimator(final WindowState win) {
@@ -197,7 +196,6 @@
                 ? null : win.mAttachedWindow.mWinAnimator;
         mAppAnimator = win.mAppToken == null ? null : win.mAppToken.mAppAnimator;
         mSession = win.mSession;
-        mAttrFlags = win.mAttrs.flags;
         mAttrType = win.mAttrs.type;
         mIsWallpaper = win.mIsWallpaper;
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5445dc0..92d3d95 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3741,6 +3741,114 @@
     }
 
     @Override
+    public void enableSystemApp(ComponentName who, String packageName) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+
+            // This API can only be called by an active device admin,
+            // so try to retrieve it to check that the caller is one.
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+            int userId = UserHandle.getCallingUserId();
+            long id = Binder.clearCallingIdentity();
+
+            try {
+                UserManager um = UserManager.get(mContext);
+                if (!um.getUserInfo(userId).isManagedProfile()) {
+                    throw new IllegalStateException(
+                            "Only call this method from a managed profile.");
+                }
+
+                UserInfo primaryUser = um.getProfileParent(userId);
+
+                if (DBG) {
+                    Slog.v(LOG_TAG, "installing " + packageName + " for "
+                            + userId);
+                }
+
+                IPackageManager pm = AppGlobals.getPackageManager();
+                if (!isSystemApp(pm, packageName, primaryUser.id)) {
+                    throw new IllegalArgumentException("Only system apps can be enabled this way.");
+                }
+
+                // Install the app.
+                pm.installExistingPackageAsUser(packageName, userId);
+
+            } catch (RemoteException re) {
+                // shouldn't happen
+                Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
+
+    @Override
+    public int enableSystemAppWithIntent(ComponentName who, Intent intent) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+
+            // This API can only be called by an active device admin,
+            // so try to retrieve it to check that the caller is one.
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+            int userId = UserHandle.getCallingUserId();
+            long id = Binder.clearCallingIdentity();
+
+            try {
+                UserManager um = UserManager.get(mContext);
+                if (!um.getUserInfo(userId).isManagedProfile()) {
+                    throw new IllegalStateException(
+                            "Only call this method from a managed profile.");
+                }
+
+                UserInfo primaryUser = um.getProfileParent(userId);
+
+                IPackageManager pm = AppGlobals.getPackageManager();
+                List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent,
+                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                        0, // no flags
+                        primaryUser.id);
+
+                if (DBG) Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
+                int numberOfAppsInstalled = 0;
+                if (activitiesToEnable != null) {
+                    for (ResolveInfo info : activitiesToEnable) {
+                        if (info.activityInfo != null) {
+
+                            if (!isSystemApp(pm, info.activityInfo.packageName, primaryUser.id)) {
+                                throw new IllegalArgumentException(
+                                        "Only system apps can be enabled this way.");
+                            }
+
+
+                            numberOfAppsInstalled++;
+                            pm.installExistingPackageAsUser(info.activityInfo.packageName, userId);
+                        }
+                    }
+                }
+                return numberOfAppsInstalled;
+            } catch (RemoteException e) {
+                // shouldn't happen
+                Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
+                return 0;
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
+
+    private boolean isSystemApp(IPackageManager pm, String packageName, int userId)
+            throws RemoteException {
+        ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId);
+        return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0;
+    }
+
+    @Override
     public void setAccountManagementDisabled(ComponentName who, String accountType,
             boolean disabled) {
         if (!mHasFeature) {
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
index 1db3c5e..4fe30e6 100644
--- a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -23,17 +23,13 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.admin.IDevicePolicyManager;
-import android.content.AbstractRestrictionsProvider;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.IPermissionResponseCallback;
-import android.content.IRestrictionsProvider;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IRestrictionsManager;
 import android.content.RestrictionsManager;
-import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -127,58 +123,12 @@
                     enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
                             " match caller ");
                     // Prepare and broadcast the intent to the provider
-                    Intent intent = new Intent();
+                    Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION);
                     intent.setComponent(restrictionsProvider);
-                    new ProviderServiceConnection(intent, null, userHandle) {
-                        @Override
-                        public void run() throws RemoteException {
-                            if (DEBUG) {
-                                Log.i(LOG_TAG, "calling requestPermission for " + packageName
-                                        + ", type=" + requestType + ", data=" + requestData);
-                            }
-                            mRestrictionsProvider.requestPermission(packageName,
-                                    requestType, requestData);
-                        }
-                    }.bind();
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        }
-
-        @Override
-        public void getPermissionResponse(final String packageName, final String requestId,
-                final IPermissionResponseCallback callback) throws RemoteException {
-            int callingUid = Binder.getCallingUid();
-            int userHandle = UserHandle.getUserId(callingUid);
-            if (mDpm != null) {
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    ComponentName restrictionsProvider =
-                            mDpm.getRestrictionsProvider(userHandle);
-                    // Check if there is a restrictions provider
-                    if (restrictionsProvider == null) {
-                        throw new IllegalStateException(
-                            "Cannot fetch permission without a restrictions provider registered");
-                    }
-                    // Check that the packageName matches the caller.
-                    enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
-                            " match caller ");
-                    // Prepare and broadcast the intent to the provider
-                    Intent intent = new Intent();
-                    intent.setComponent(restrictionsProvider);
-                    new ProviderServiceConnection(intent, callback.asBinder(), userHandle) {
-                        @Override
-                        public void run() throws RemoteException {
-                            if (DEBUG) {
-                                Log.i(LOG_TAG, "calling getPermissionResponse for " + packageName
-                                        + ", id=" + requestId);
-                            }
-                            Bundle response = mRestrictionsProvider.getPermissionResponse(
-                                    packageName, requestId);
-                            callback.onResponse(response);
-                        }
-                    }.bind();
+                    intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
+                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_TYPE, requestType);
+                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
+                    mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -226,81 +176,5 @@
                 // Shouldn't happen
             }
         }
-
-        abstract class ProviderServiceConnection
-                implements IBinder.DeathRecipient, ServiceConnection {
-
-            protected IRestrictionsProvider mRestrictionsProvider;
-            private Intent mIntent;
-            protected int mUserHandle;
-            protected IBinder mResponse;
-            private boolean mAbort;
-
-            public ProviderServiceConnection(Intent intent, IBinder response, int userHandle) {
-                mIntent = intent;
-                mResponse = response;
-                mUserHandle = userHandle;
-                if (mResponse != null) {
-                    try {
-                        mResponse.linkToDeath(this, 0 /* flags */);
-                    } catch (RemoteException re) {
-                        close();
-                    }
-                }
-            }
-
-            /** Bind to the RestrictionsProvider process */
-            public void bind() {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "binding to service: " + mIntent);
-                }
-                mContext.bindServiceAsUser(mIntent, this, Context.BIND_AUTO_CREATE,
-                        new UserHandle(mUserHandle));
-            }
-
-            private void close() {
-                mAbort = true;
-                unbind();
-            }
-
-            private void unbind() {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "unbinding from service");
-                }
-                mContext.unbindService(this);
-            }
-
-            /** Implement this to call the appropriate method on the service */
-            public abstract void run() throws RemoteException;
-
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "connected to " + name);
-                }
-                mRestrictionsProvider = IRestrictionsProvider.Stub.asInterface(service);
-                if (!mAbort) {
-                    try {
-                        run();
-                    } catch (RemoteException re) {
-                        Log.w("RestrictionsProvider", "Remote exception: " + re);
-                    }
-                }
-                close();
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "disconnected from " + name);
-                }
-                mRestrictionsProvider = null;
-            }
-
-            @Override
-            public void binderDied() {
-                mAbort = true;
-            }
-        }
     }
 }
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index ba7e253..b17f929 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -73,6 +73,7 @@
         private final String mDisconnectCauseMsg;
         private final long mConnectTimeMillis;
         private final GatewayInfo mGatewayInfo;
+        private final int mVideoState;
 
         /**
          * @return The handle (e.g., phone number) to which the {@code Call} is currently
@@ -167,7 +168,8 @@
                         Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
                         Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) &&
                         Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
-                        Objects.equals(mGatewayInfo, d.mGatewayInfo);
+                        Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
+                        Objects.equals(mVideoState, d.mVideoState);
             }
             return false;
         }
@@ -184,7 +186,8 @@
                     Objects.hashCode(mDisconnectCauseCode) +
                     Objects.hashCode(mDisconnectCauseMsg) +
                     Objects.hashCode(mConnectTimeMillis) +
-                    Objects.hashCode(mGatewayInfo);
+                    Objects.hashCode(mGatewayInfo) +
+                    Objects.hashCode(mVideoState);
         }
 
         /** {@hide} */
@@ -198,7 +201,8 @@
                 int disconnectCauseCode,
                 String disconnectCauseMsg,
                 long connectTimeMillis,
-                GatewayInfo gatewayInfo) {
+                GatewayInfo gatewayInfo,
+                int videoState) {
             mHandle = handle;
             mHandlePresentation = handlePresentation;
             mCallerDisplayName = callerDisplayName;
@@ -209,6 +213,7 @@
             mDisconnectCauseMsg = disconnectCauseMsg;
             mConnectTimeMillis = connectTimeMillis;
             mGatewayInfo = gatewayInfo;
+            mVideoState = videoState;
         }
     }
 
@@ -545,7 +550,8 @@
                 inCallCall.getDisconnectCauseCode(),
                 inCallCall.getDisconnectCauseMsg(),
                 inCallCall.getConnectTimeMillis(),
-                inCallCall.getGatewayInfo());
+                inCallCall.getGatewayInfo(),
+                inCallCall.getVideoState());
         boolean detailsChanged = !Objects.equals(mDetails, details);
         if (detailsChanged) {
             mDetails = details;
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index 355c260..db8395c 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -50,6 +50,7 @@
     private final String mParentCallId;
     private final List<String> mChildCallIds;
     private final StatusHints mStatusHints;
+    private final int mVideoState;
 
     /** @hide */
     public InCallCall(
@@ -69,7 +70,8 @@
             ICallVideoProvider callVideoProvider,
             String parentCallId,
             List<String> childCallIds,
-            StatusHints statusHints) {
+            StatusHints statusHints,
+            int videoState) {
         mId = id;
         mState = state;
         mDisconnectCauseCode = disconnectCauseCode;
@@ -87,6 +89,7 @@
         mParentCallId = parentCallId;
         mChildCallIds = childCallIds;
         mStatusHints = statusHints;
+        mVideoState = videoState;
     }
 
     /** The unique ID of the call. */
@@ -204,6 +207,14 @@
         return mStatusHints;
     }
 
+    /**
+     * The video state.
+     * @return The video state of the call.
+     */
+    public int getVideoState() {
+        return mVideoState;
+    }
+
     /** Responsible for creating InCallCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<InCallCall> CREATOR =
             new Parcelable.Creator<InCallCall> () {
@@ -230,10 +241,12 @@
             List<String> childCallIds = new ArrayList<>();
             source.readList(childCallIds, classLoader);
             StatusHints statusHints = source.readParcelable(classLoader);
+            int videoState = source.readInt();
             return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg,
                     cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation,
                     callerDisplayName, callerDisplayNamePresentation, gatewayInfo,
-                    account, callVideoProvider, parentCallId, childCallIds, statusHints);
+                    account, callVideoProvider, parentCallId, childCallIds, statusHints,
+                    videoState);
         }
 
         @Override
@@ -269,6 +282,7 @@
         destination.writeString(mParentCallId);
         destination.writeList(mChildCallIds);
         destination.writeParcelable(mStatusHints, 0);
+        destination.writeInt(mVideoState);
     }
 
     @Override
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index a6c09f3..62c92a1 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
         }
         
         try {
-            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false);
+            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false);
             fail("IWindowManager.addAppToken did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 963c796..4f1d15e 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -931,6 +931,13 @@
 
     // Build an empty <application> tag (required).
     sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application"));
+
+    // Add the 'hasCode' attribute which is never true for resource splits.
+    if (!addTagAttribute(app, RESOURCES_ANDROID_NAMESPACE, "hasCode",
+            "false", true, true)) {
+        return UNKNOWN_ERROR;
+    }
+
     manifest->addChild(app);
     root->addChild(manifest);
 
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index 1942831..ead3b13 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -61,7 +61,7 @@
 	$(hide) mkdir -p $(dir $@)
 	$(hide) rm -f $@
 	$(hide) ls -l $(built_framework_classes)
-	$(hide) java -jar $(built_layoutlib_create_jar) \
+	$(hide) java -ea -jar $(built_layoutlib_create_jar) \
 	             $@ \
 	             $(built_core_classes) \
 	             $(built_framework_classes) \
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 2f40003..6927b26 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -72,7 +72,7 @@
 
     @Override
     public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
-            boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9)
+            boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10)
             throws RemoteException {
         // TODO Auto-generated method stub
 
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
similarity index 60%
rename from tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
index 112250d..607e628 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
@@ -14,24 +14,23 @@
  * limitations under the License.
  */
 
-package android.content.res;
+package com.android.layoutlib.bridge.android;
 
 import java.util.Locale;
 
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 import com.ibm.icu.util.ULocale;
 
 /**
- * Delegate used to provide new implementation of a select few methods of {@link Resources}
+ * This class provides an alternate implementation for {@code java.util.Locale#toLanguageTag}
+ * which is only available after Java 6.
  *
- * Through the layoutlib_create tool, the original  methods of Resources have been replaced
- * by calls to methods of the same name in this delegate class.
- *
+ * The create tool re-writes references to the above mentioned method to this one. Hence it's
+ * imperative that this class is not deleted unless the create tool is modified.
  */
-public class Resources_Delegate {
+@SuppressWarnings("UnusedDeclaration")
+public class AndroidLocale {
 
-    @LayoutlibDelegate
-    /*package*/ static String localeToLanguageTag(Resources res, Locale locale)  {
+    public static String toLanguageTag(Locale locale)  {
         return ULocale.forLocale(locale).toLanguageTag();
     }
 }
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
index 8de64db..727b194 100644
--- a/tools/layoutlib/create/README.txt
+++ b/tools/layoutlib/create/README.txt
@@ -39,7 +39,8 @@
 
 The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the configuration
 is done in the main() method and the CreateInfo structure is expected to change with the Android
-platform as new classes are added, changed or removed.
+platform as new classes are added, changed or removed. Some configuration that may be platform
+dependent is also present elsewhere in code.
 
 The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that
 provides all the necessary missing implementation for rendering graphics in Eclipse.
@@ -95,7 +96,7 @@
 - specific classes to refactor.
 
 Each of these are specific strategies we use to be able to modify the Android code to fit within the
-Eclipse renderer. These strategies are explained beow.
+Eclipse renderer. These strategies are explained below.
 
 The core method of the generator is transform(): it takes an input ASM ClassReader and modifies it
 to produce a byte array suitable for the final JAR file.
@@ -130,9 +131,11 @@
 valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on
 Mac has horrible font rendering support.
 
-ReplaceMethodCallsAdapter replaces calls to certain methods. Currently, it only rewrites calls to
-specialized versions of java.lang.System.arraycopy(), which are not part of the Desktop VM to call
-the more general method java.lang.System.arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V.
+ReplaceMethodCallsAdapter replaces calls to certain methods. This is different from the
+DelegateMethodAdapter since it doesn't preserve the original copy of the method and more importantly
+changes the calls to a method in each class instead of changing the implementation of the method.
+This is useful for methods in the Java namespace where we cannot add delegates. The configuration
+for this is not done through the CreateInfo class, but done in the ReplaceMethodAdapter.
 
 The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7
 Transformation chains in the asm user guide, link in the References.) The order of execution of
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index 767e597..e043d4d 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -724,9 +724,8 @@
                 considerDesc(desc);
 
 
-                // Check if method is a specialized version of java.lang.System.arrayCopy()
-                if (owner.equals("java/lang/System") && name.equals("arraycopy")
-                        && !desc.equals("(Ljava/lang/Object;ILjava/lang/Object;II)V")) {
+                // Check if method needs to replaced by a call to a different method.
+                if (ReplaceMethodCallsAdapter.isReplacementNeeded(owner, name, desc)) {
                     mReplaceMethodCallClasses.add(mOwnerClass);
                 }
             }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 552fb6c..8fb8928 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -126,7 +126,6 @@
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "android.content.res.Resources$Theme#resolveAttribute",
         "android.content.res.Resources$Theme#resolveAttributes",
-        "android.content.res.Resources#localeToLanguageTag",
         "android.content.res.AssetManager#newTheme",
         "android.content.res.AssetManager#deleteTheme",
         "android.content.res.AssetManager#applyThemeStyle",
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index ae17417..0b5fb46 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -20,12 +20,15 @@
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 /**
- * Replaces calls to certain methods that do not exist in the Desktop VM.
+ * Replaces calls to certain methods that do not exist in the Desktop VM. Useful for methods in the
+ * "java" package.
  */
 public class ReplaceMethodCallsAdapter extends ClassVisitor {
 
@@ -37,6 +40,60 @@
             "([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
             "([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
 
+    private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<MethodReplacer>(2);
+
+    // Static initialization block to initialize METHOD_REPLACERS.
+    static {
+        // Case 1: java.lang.System.arraycopy()
+        METHOD_REPLACERS.add(new MethodReplacer() {
+            @Override
+            public boolean isNeeded(String owner, String name, String desc) {
+                return owner.equals("java/lang/System") && name.equals("arraycopy") &&
+                        ARRAYCOPY_DESCRIPTORS.contains(desc);
+            }
+
+            @Override
+            public void replace(int opcode, String owner, String name, String desc,
+                    int[] opcodeOut, String[] output) {
+                assert isNeeded(owner, name, desc) && output.length == 3
+                        && opcodeOut.length == 1;
+                opcodeOut[0] = opcode;
+                output[0] = owner;
+                output[1] = name;
+                output[2] = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+            }
+        });
+
+        // Case 2: java.util.Locale.toLanguageTag()
+        METHOD_REPLACERS.add(new MethodReplacer() {
+            @Override
+            public boolean isNeeded(String owner, String name, String desc) {
+                return owner.equals("java/util/Locale") && name.equals("toLanguageTag") &&
+                        "()Ljava/lang/String;".equals(desc);
+            }
+
+            @Override
+            public void replace(int opcode, String owner, String name, String desc,
+                    int[] opcodeOut, String[] output) {
+                assert isNeeded(owner, name, desc) && output.length == 3
+                        && opcodeOut.length == 1;
+                opcodeOut[0] = Opcodes.INVOKESTATIC;
+                output[0] = "com.android.layoutlib.bridge.android.AndroidLocale";
+                output[1] = name;
+                output[2] = "(Ljava/util/Locale;)Ljava/lang/String;";
+            }
+        });
+    }
+
+    public static boolean isReplacementNeeded(String owner, String name, String desc) {
+        for (MethodReplacer replacer : METHOD_REPLACERS) {
+            if (replacer.isNeeded(owner, name, desc)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public ReplaceMethodCallsAdapter(ClassVisitor cv) {
         super(Opcodes.ASM4, cv);
     }
@@ -56,13 +113,34 @@
         @Override
         public void visitMethodInsn(int opcode, String owner, String name, String desc) {
             // Check if method is a specialized version of java.lang.System.arrayCopy
-            if (owner.equals("java/lang/System") && name.equals("arraycopy")) {
-
-                if (ARRAYCOPY_DESCRIPTORS.contains(desc)) {
-                    desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+            for (MethodReplacer replacer : METHOD_REPLACERS) {
+                if (replacer.isNeeded(owner, name, desc)) {
+                    String[] output = new String[3];
+                    int[] opcodeOut = new int[1];
+                    replacer.replace(opcode, owner, name, desc, opcodeOut, output);
+                    opcode = opcodeOut[0];
+                    owner = output[0];
+                    name = output[1];
+                    desc = output[2];
+                    break;
                 }
             }
             super.visitMethodInsn(opcode, owner, name, desc);
         }
     }
+
+    private interface MethodReplacer {
+        public boolean isNeeded(String owner, String name, String desc);
+
+        /**
+         * This method must update the values of the output arrays with the new values of method
+         * attributes - opcode, owner, name and desc.
+         * @param opcodeOut An array that will contain the new value of the opcode. The size of
+         *                  the array must be 1.
+         * @param output An array that will contain the new values of the owner, name and desc in
+         *               that order. The size of the array must be 3.
+         */
+        public void replace(int opcode, String owner, String name, String desc, int[] opcodeOut,
+                String[] output);
+    }
 }