Merge "Add non-blocking reads to AudioRecord"
diff --git a/api/current.txt b/api/current.txt
index 99a44f7..6f2cf45 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19690,6 +19690,7 @@
 
   public final class NfcEvent {
     field public final android.nfc.NfcAdapter nfcAdapter;
+    field public final byte peerLlcpVersion;
   }
 
   public final class NfcManager {
diff --git a/api/system-current.txt b/api/system-current.txt
index 690efce..b853a38 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21578,6 +21578,7 @@
 
   public final class NfcEvent {
     field public final android.nfc.NfcAdapter nfcAdapter;
+    field public final byte peerLlcpVersion;
   }
 
   public final class NfcManager {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index fa28143..8ba2a5a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -857,6 +857,11 @@
                             "Error: Activity not started, voice control not allowed for: "
                                     + intent);
                     break;
+                case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
+                    out.println(
+                            "Error: Not allowed to start background user activity"
+                            + " that shouldn't be displayed for all users.");
+                    break;
                 default:
                     out.println(
                             "Error: Activity not started, unknown error code " + res);
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index dd5e0ea..ce6d7b5 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -56,6 +56,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
 LOCAL_MODULE_STEM := app_process
 LOCAL_ADDRESS_SANITIZER := true
+LOCAL_CLANG := true
 
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 134ecdd..576a046 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -86,6 +86,13 @@
     public static final String META_HOME_ALTERNATE = "android.app.home.alternate";
 
     /**
+     * Result for IActivityManager.startActivity: trying to start a background user
+     * activity that shouldn't be displayed for all users.
+     * @hide
+     */
+    public static final int START_NOT_CURRENT_USER_ACTIVITY = -8;
+
+    /**
      * Result for IActivityManager.startActivity: trying to start an activity under voice
      * control when that activity does not support the VOICE category.
      * @hide
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index fc96464..b77dec5 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1797,6 +1797,10 @@
             case ActivityManager.START_NOT_VOICE_COMPATIBLE:
                 throw new SecurityException(
                         "Starting under voice control not allowed for: " + intent);
+            case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
+                throw new SecurityException(
+                        "Not allowed to start background user activity that shouldn't be"
+                        + " displayed for all users.");
             default:
                 throw new AndroidRuntimeException("Unknown error code "
                         + res + " when starting " + intent);
diff --git a/core/java/android/nfc/IAppCallback.aidl b/core/java/android/nfc/IAppCallback.aidl
index 9599308..c027d54 100644
--- a/core/java/android/nfc/IAppCallback.aidl
+++ b/core/java/android/nfc/IAppCallback.aidl
@@ -24,7 +24,7 @@
  */
 interface IAppCallback
 {
-    BeamShareData createBeamShareData();
-    void onNdefPushComplete();
+    BeamShareData createBeamShareData(byte peerLlcpVersion);
+    void onNdefPushComplete(byte peerLlcpVersion);
     void onTagDiscovered(in Tag tag);
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index d009295..76bd0ec 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -46,7 +46,6 @@
     static final Boolean DBG = false;
 
     final NfcAdapter mAdapter;
-    final NfcEvent mDefaultEvent;  // cached NfcEvent (its currently always the same)
 
     // All objects in the lists are protected by this
     final List<NfcApplicationState> mApps;  // Application(s) that have NFC state. Usually one
@@ -200,7 +199,6 @@
         mAdapter = adapter;
         mActivities = new LinkedList<NfcActivityState>();
         mApps = new ArrayList<NfcApplicationState>(1);  // Android VM usually has 1 app
-        mDefaultEvent = new NfcEvent(mAdapter);
     }
 
     public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
@@ -354,13 +352,14 @@
 
     /** Callback from NFC service, usually on binder thread */
     @Override
-    public BeamShareData createBeamShareData() {
+    public BeamShareData createBeamShareData(byte peerLlcpVersion) {
         NfcAdapter.CreateNdefMessageCallback ndefCallback;
         NfcAdapter.CreateBeamUrisCallback urisCallback;
         NdefMessage message;
         Activity activity;
         Uri[] uris;
         int flags;
+        NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findResumedActivityState();
             if (state == null) return null;
@@ -375,10 +374,10 @@
 
         // Make callbacks without lock
         if (ndefCallback != null) {
-            message  = ndefCallback.createNdefMessage(mDefaultEvent);
+            message  = ndefCallback.createNdefMessage(event);
         }
         if (urisCallback != null) {
-            uris = urisCallback.createBeamUris(mDefaultEvent);
+            uris = urisCallback.createBeamUris(event);
             if (uris != null) {
                 ArrayList<Uri> validUris = new ArrayList<Uri>();
                 for (Uri uri : uris) {
@@ -412,7 +411,7 @@
 
     /** Callback from NFC service, usually on binder thread */
     @Override
-    public void onNdefPushComplete() {
+    public void onNdefPushComplete(byte peerLlcpVersion) {
         NfcAdapter.OnNdefPushCompleteCallback callback;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findResumedActivityState();
@@ -420,10 +419,10 @@
 
             callback = state.onNdefPushCompleteCallback;
         }
-
+        NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
         // Make callback without lock
         if (callback != null) {
-            callback.onNdefPushComplete(mDefaultEvent);
+            callback.onNdefPushComplete(event);
         }
     }
 
diff --git a/core/java/android/nfc/NfcEvent.java b/core/java/android/nfc/NfcEvent.java
index 860700a..cf1d71a 100644
--- a/core/java/android/nfc/NfcEvent.java
+++ b/core/java/android/nfc/NfcEvent.java
@@ -38,7 +38,14 @@
      */
     public final NfcAdapter nfcAdapter;
 
-    NfcEvent(NfcAdapter nfcAdapter) {
+    /**
+     * The LLCP version of the peer associated with the NFC event.
+     * The major version is in the top nibble, the minor version is in the bottom nibble.
+     */
+    public final byte peerLlcpVersion;
+
+    NfcEvent(NfcAdapter nfcAdapter, byte peerLlcpVersion) {
         this.nfcAdapter = nfcAdapter;
+        this.peerLlcpVersion = peerLlcpVersion;
     }
 }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 10724b0..8f2be99 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -494,6 +494,11 @@
      */
     private int mNestedScrollAxes;
 
+    // Used to manage the list of transient views, added by addTransientView()
+    private List<Integer> mTransientIndices = null;
+    private List<View> mTransientViews = null;
+
+
     /**
      * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild.
      *
@@ -2822,6 +2827,11 @@
             child.dispatchAttachedToWindow(info,
                     visibility | (child.mViewFlags & VISIBILITY_MASK));
         }
+        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
+        for (int i = 0; i < transientCount; ++i) {
+            View view = mTransientViews.get(i);
+            view.dispatchAttachedToWindow(info, visibility | (view.mViewFlags & VISIBILITY_MASK));
+        }
     }
 
     @Override
@@ -2991,6 +3001,11 @@
             children[i].dispatchDetachedFromWindow();
         }
         clearDisappearingChildren();
+        final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
+        for (int i = 0; i < transientCount; ++i) {
+            View view = mTransientViews.get(i);
+            view.dispatchDetachedFromWindow();
+        }
         super.dispatchDetachedFromWindow();
     }
 
@@ -3291,6 +3306,8 @@
         final long drawingTime = getDrawingTime();
 
         if (usingRenderNodeProperties) canvas.insertReorderBarrier();
+        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
+        int transientIndex = transientCount != 0 ? 0 : -1;
         // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
         // draw reordering internally
         final ArrayList<View> preorderedList = usingRenderNodeProperties
@@ -3298,6 +3315,17 @@
         final boolean customOrder = preorderedList == null
                 && isChildrenDrawingOrderEnabled();
         for (int i = 0; i < childrenCount; i++) {
+            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
+                final View transientChild = mTransientViews.get(transientIndex);
+                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
+                        transientChild.getAnimation() != null) {
+                    more |= drawChild(canvas, transientChild, drawingTime);
+                }
+                transientIndex++;
+                if (transientIndex >= transientCount) {
+                    transientIndex = -1;
+                }
+            }
             int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
             final View child = (preorderedList == null)
                     ? children[childIndex] : preorderedList.get(childIndex);
@@ -3305,6 +3333,18 @@
                 more |= drawChild(canvas, child, drawingTime);
             }
         }
+        while (transientIndex >= 0) {
+            // there may be additional transient views after the normal views
+            final View transientChild = mTransientViews.get(transientIndex);
+            if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
+                    transientChild.getAnimation() != null) {
+                more |= drawChild(canvas, transientChild, drawingTime);
+            }
+            transientIndex++;
+            if (transientIndex >= transientCount) {
+                break;
+            }
+        }
         if (preorderedList != null) preorderedList.clear();
 
         // Draw any disappearing views that have animations
@@ -3785,6 +3825,145 @@
     }
 
     /**
+     * This method adds a view to this container at the specified index purely for the
+     * purposes of allowing that view to draw even though it is not a normal child of
+     * the container. That is, the view does not participate in layout, focus, accessibility,
+     * input, or other normal view operations; it is purely an item to be drawn during the normal
+     * rendering operation of this container. The index that it is added at is the order
+     * in which it will be drawn, with respect to the other views in the container.
+     * For example, a transient view added at index 0 will be drawn before all other views
+     * in the container because it will be drawn first (including before any real view
+     * at index 0). There can be more than one transient view at any particular index;
+     * these views will be drawn in the order in which they were added to the list of
+     * transient views. The index of transient views can also be greater than the number
+     * of normal views in the container; that just means that they will be drawn after all
+     * other views are drawn.
+     *
+     * <p>Note that since transient views do not participate in layout, they must be sized
+     * manually or, more typically, they should just use the size that they had before they
+     * were removed from their container.</p>
+     *
+     * <p>Transient views are useful for handling animations of views that have been removed
+     * from the container, but which should be animated out after the removal. Adding these
+     * views as transient views allows them to participate in drawing without side-effecting
+     * the layout of the container.</p>
+     *
+     * <p>Transient views must always be explicitly {@link #removeTransientView(View) removed}
+     * from the container when they are no longer needed. For example, a transient view
+     * which is added in order to fade it out in its old location should be removed
+     * once the animation is complete.</p>
+     *
+     * @param view The view to be added
+     * @param index The index at which this view should be drawn, must be >= 0.
+     * This value is relative to the {@link #getChildAt(int) index} values in the normal
+     * child list of this container, where any transient view at a particular index will
+     * be drawn before any normal child at that same index.
+     *
+     * @hide
+     */
+    public void addTransientView(View view, int index) {
+        if (index < 0) {
+            return;
+        }
+        if (mTransientIndices == null) {
+            mTransientIndices = new ArrayList<Integer>();
+            mTransientViews = new ArrayList<View>();
+        }
+        final int oldSize = mTransientIndices.size();
+        if (oldSize > 0) {
+            int insertionIndex;
+            for (insertionIndex = 0; insertionIndex < oldSize; ++insertionIndex) {
+                if (index < mTransientIndices.get(insertionIndex)) {
+                    break;
+                }
+            }
+            mTransientIndices.add(insertionIndex, index);
+            mTransientViews.add(insertionIndex, view);
+        } else {
+            mTransientIndices.add(index);
+            mTransientViews.add(view);
+        }
+        view.mParent = this;
+        view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
+        invalidate(true);
+    }
+
+    /**
+     * Removes a view from the list of transient views in this container. If there is no
+     * such transient view, this method does nothing.
+     *
+     * @param view The transient view to be removed
+     *
+     * @hide
+     */
+    public void removeTransientView(View view) {
+        if (mTransientViews == null) {
+            return;
+        }
+        final int size = mTransientViews.size();
+        for (int i = 0; i < size; ++i) {
+            if (view == mTransientViews.get(i)) {
+                mTransientViews.remove(i);
+                mTransientIndices.remove(i);
+                view.mParent = null;
+                view.dispatchDetachedFromWindow();
+                invalidate(true);
+                return;
+            }
+        }
+    }
+
+    /**
+     * Returns the number of transient views in this container. Specific transient
+     * views and the index at which they were added can be retrieved via
+     * {@link #getTransientView(int)} and {@link #getTransientViewIndex(int)}.
+     *
+     * @see #addTransientView(View, int)
+     * @return The number of transient views in this container
+     *
+     * @hide
+     */
+    public int getTransientViewCount() {
+        return mTransientIndices == null ? 0 : mTransientIndices.size();
+    }
+
+    /**
+     * Given a valid position within the list of transient views, returns the index of
+     * the transient view at that position.
+     *
+     * @param position The position of the index being queried. Must be at least 0
+     * and less than the value returned by {@link #getTransientViewCount()}.
+     * @return The index of the transient view stored in the given position if the
+     * position is valid, otherwise -1
+     *
+     * @hide
+     */
+    public int getTransientViewIndex(int position) {
+        if (position < 0 || mTransientIndices == null || position >= mTransientIndices.size()) {
+            return -1;
+        }
+        return mTransientIndices.get(position);
+    }
+
+    /**
+     * Given a valid position within the list of transient views, returns the
+     * transient view at that position.
+     *
+     * @param position The position of the view being queried. Must be at least 0
+     * and less than the value returned by {@link #getTransientViewCount()}.
+     * @return The transient view stored in the given position if the
+     * position is valid, otherwise null
+     *
+     * @hide
+     */
+    public View getTransientView(int position) {
+        if (mTransientViews == null || position >= mTransientViews.size()) {
+            return null;
+        }
+        return mTransientViews.get(position);
+    }
+
+    /**
      * <p>Adds a child view. If no layout parameters are already set on the child, the
      * default parameters for this ViewGroup are set on the child.</p>
      * 
@@ -4096,6 +4275,16 @@
         if (child.getVisibility() != View.GONE) {
             notifySubtreeAccessibilityStateChangedIfNeeded();
         }
+
+        if (mTransientIndices != null) {
+            final int transientCount = mTransientIndices.size();
+            for (int i = 0; i < transientCount; ++i) {
+                final int oldIndex = mTransientIndices.get(i);
+                if (index <= oldIndex) {
+                    mTransientIndices.set(i, oldIndex + 1);
+                }
+            }
+        }
     }
 
     private void addInArray(View child, int index) {
@@ -4340,6 +4529,14 @@
         if (view.getVisibility() != View.GONE) {
             notifySubtreeAccessibilityStateChangedIfNeeded();
         }
+
+        int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
+        for (int i = 0; i < transientCount; ++i) {
+            final int oldIndex = mTransientIndices.get(i);
+            if (index < oldIndex) {
+                mTransientIndices.set(i, oldIndex - 1);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
index 7bce756..0ed75d5 100644
--- a/core/java/android/widget/CalendarViewMaterialDelegate.java
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -19,6 +19,7 @@
 import android.annotation.StyleRes;
 import android.content.Context;
 import android.util.AttributeSet;
+import android.widget.DayPickerView.OnDaySelectedListener;
 
 import java.util.Calendar;
 
@@ -109,8 +110,7 @@
         mOnDateChangeListener = listener;
     }
 
-    private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener =
-            new DayPickerView.OnDaySelectedListener() {
+    private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() {
         @Override
         public void onDaySelected(DayPickerView view, Calendar day) {
             if (mOnDateChangeListener != null) {
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 06a5bd2..d38a225 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -34,8 +34,6 @@
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
 import android.widget.DayPickerView.OnDaySelectedListener;
 import android.widget.YearPickerView.OnYearSelectedListener;
 
@@ -549,7 +547,7 @@
         final int listPosition = ss.getListPosition();
         if (listPosition != -1) {
             if (currentView == VIEW_MONTH_DAY) {
-                mDayPickerView.setCurrentItem(listPosition);
+                mDayPickerView.setPosition(listPosition);
             } else if (currentView == VIEW_YEAR) {
                 final int listPositionOffset = ss.getListPositionOffset();
                 mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
similarity index 92%
rename from core/java/android/widget/DayPickerAdapter.java
rename to core/java/android/widget/DayPickerPagerAdapter.java
index 9a4b6f5..478fa00 100644
--- a/core/java/android/widget/DayPickerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -36,7 +36,7 @@
 /**
  * An adapter for a list of {@link android.widget.SimpleMonthView} items.
  */
-class DayPickerAdapter extends PagerAdapter {
+class DayPickerPagerAdapter extends PagerAdapter {
     private static final int MONTHS_IN_YEAR = 12;
 
     private final Calendar mMinDate = Calendar.getInstance();
@@ -63,7 +63,7 @@
     private int mCount;
     private int mFirstDayOfWeek;
 
-    public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId,
+    public DayPickerPagerAdapter(@NonNull Context context, @LayoutRes int layoutResId,
             @IdRes int calendarViewId) {
         mInflater = LayoutInflater.from(context);
         mLayoutResId = layoutResId;
@@ -200,7 +200,8 @@
 
         final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
         final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH));
-        return yearOffset * MONTHS_IN_YEAR + monthOffset;
+        final int position = yearOffset * MONTHS_IN_YEAR + monthOffset;
+        return position;
     }
 
     @Override
@@ -253,8 +254,6 @@
 
         v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
                 enabledDayRangeStart, enabledDayRangeEnd);
-        v.setPrevEnabled(position > 0);
-        v.setNextEnabled(position < mCount - 1);
 
         final ViewHolder holder = new ViewHolder(position, itemView, v);
         mItems.put(position, holder);
@@ -298,17 +297,10 @@
                 setSelectedDay(day);
 
                 if (mOnDaySelectedListener != null) {
-                    mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day);
+                    mOnDaySelectedListener.onDaySelected(DayPickerPagerAdapter.this, day);
                 }
             }
         }
-
-        @Override
-        public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) {
-            if (mOnDaySelectedListener != null) {
-                mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate);
-            }
-        }
     };
 
     private static class ViewHolder {
@@ -324,7 +316,6 @@
     }
 
     public interface OnDaySelectedListener {
-        public void onDaySelected(DayPickerAdapter view, Calendar day);
-        public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate);
+        public void onDaySelected(DayPickerPagerAdapter view, Calendar day);
     }
 }
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 0e0b2d3..c6b4d7e 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,37 +16,44 @@
 
 package android.widget;
 
-import com.android.internal.widget.ViewPager;
 import com.android.internal.R;
+import com.android.internal.widget.ViewPager;
+import com.android.internal.widget.ViewPager.OnPageChangeListener;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.MathUtils;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
 
-import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Locale;
 
 import libcore.icu.LocaleData;
 
-/**
- * This displays a list of months in a calendar format with selectable days.
- */
-class DayPickerView extends ViewPager {
+class DayPickerView extends ViewGroup {
+    private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material;
     private static final int DEFAULT_START_YEAR = 1900;
     private static final int DEFAULT_END_YEAR = 2100;
 
+    private static final int[] ATTRS_TEXT_COLOR = new int[] { R.attr.textColor };
+
     private final Calendar mSelectedDay = Calendar.getInstance();
     private final Calendar mMinDate = Calendar.getInstance();
     private final Calendar mMaxDate = Calendar.getInstance();
 
-    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+    private final AccessibilityManager mAccessibilityManager;
 
-    private final DayPickerAdapter mAdapter;
+    private final ViewPager mViewPager;
+    private final ImageButton mPrevButton;
+    private final ImageButton mNextButton;
+
+    private final DayPickerPagerAdapter mAdapter;
 
     /** Temporary calendar used for date calculations. */
     private Calendar mTempCalendar;
@@ -57,17 +64,21 @@
         this(context, null);
     }
 
-    public DayPickerView(Context context, AttributeSet attrs) {
+    public DayPickerView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.calendarViewStyle);
     }
 
-    public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
+        mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+
         final TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.CalendarView, defStyleAttr, defStyleRes);
 
@@ -93,14 +104,44 @@
         a.recycle();
 
         // Set up adapter.
-        mAdapter = new DayPickerAdapter(context,
+        mAdapter = new DayPickerPagerAdapter(context,
                 R.layout.date_picker_month_item_material, R.id.month_view);
         mAdapter.setMonthTextAppearance(monthTextAppearanceResId);
         mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId);
         mAdapter.setDayTextAppearance(dayTextAppearanceResId);
         mAdapter.setDaySelectorColor(daySelectorColor);
 
-        setAdapter(mAdapter);
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        final ViewGroup content = (ViewGroup) inflater.inflate(DEFAULT_LAYOUT, this, false);
+
+        // Transfer all children from content to here.
+        while (content.getChildCount() > 0) {
+            final View child = content.getChildAt(0);
+            content.removeViewAt(0);
+            addView(child);
+        }
+
+        mPrevButton = (ImageButton) findViewById(R.id.prev);
+        mPrevButton.setOnClickListener(mOnClickListener);
+
+        mNextButton = (ImageButton) findViewById(R.id.next);
+        mNextButton.setOnClickListener(mOnClickListener);
+
+        mViewPager = (ViewPager) findViewById(R.id.day_picker_view_pager);
+        mViewPager.setAdapter(mAdapter);
+        mViewPager.setOnPageChangeListener(mOnPageChangedListener);
+
+        // Proxy the month text color into the previous and next buttons.
+        if (monthTextAppearanceResId != 0) {
+            final TypedArray ta = mContext.obtainStyledAttributes(null,
+                    ATTRS_TEXT_COLOR, 0, monthTextAppearanceResId);
+            final ColorStateList monthColor = ta.getColorStateList(0);
+            if (monthColor != null) {
+                mPrevButton.setImageTintList(monthColor);
+                mNextButton.setImageTintList(monthColor);
+            }
+            ta.recycle();
+        }
 
         // Set up min and max dates.
         final Calendar tempDate = Calendar.getInstance();
@@ -127,109 +168,68 @@
         setDate(setDateMillis, false);
 
         // Proxy selection callbacks to our own listener.
-        mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() {
+        mAdapter.setOnDaySelectedListener(new DayPickerPagerAdapter.OnDaySelectedListener() {
             @Override
-            public void onDaySelected(DayPickerAdapter adapter, Calendar day) {
+            public void onDaySelected(DayPickerPagerAdapter adapter, Calendar day) {
                 if (mOnDaySelectedListener != null) {
                     mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
                 }
             }
-
-            @Override
-            public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate) {
-                // ViewPager clamps input values, so we don't need to worry
-                // about passing invalid indices.
-                final int nextItem = getCurrentItem() + direction;
-                setCurrentItem(nextItem, animate);
-            }
         });
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        populate();
+        final ViewPager viewPager = mViewPager;
+        measureChild(viewPager, widthMeasureSpec, heightMeasureSpec);
 
-        // Everything below is mostly copied from FrameLayout.
-        int count = getChildCount();
+        final int measuredWidthAndState = viewPager.getMeasuredWidthAndState();
+        final int measuredHeightAndState = viewPager.getMeasuredHeightAndState();
+        setMeasuredDimension(measuredWidthAndState, measuredHeightAndState);
 
-        final boolean measureMatchParentChildren =
-                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
-                        MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
+        final int pagerWidth = viewPager.getMeasuredWidth();
+        final int pagerHeight = viewPager.getMeasuredHeight();
+        final int buttonWidthSpec = MeasureSpec.makeMeasureSpec(pagerWidth, MeasureSpec.AT_MOST);
+        final int buttonHeightSpec = MeasureSpec.makeMeasureSpec(pagerHeight, MeasureSpec.AT_MOST);
+        mPrevButton.measure(buttonWidthSpec, buttonHeightSpec);
+        mNextButton.measure(buttonWidthSpec, buttonHeightSpec);
+    }
 
-        int maxHeight = 0;
-        int maxWidth = 0;
-        int childState = 0;
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        final ImageButton leftButton = mPrevButton;
+        final ImageButton rightButton = mNextButton;
 
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                measureChild(child, widthMeasureSpec, heightMeasureSpec);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
-                maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
-                childState = combineMeasuredStates(childState, child.getMeasuredState());
-                if (measureMatchParentChildren) {
-                    if (lp.width == LayoutParams.MATCH_PARENT ||
-                            lp.height == LayoutParams.MATCH_PARENT) {
-                        mMatchParentChildren.add(child);
-                    }
-                }
-            }
+        final int width = right - left;
+        final int height = bottom - top;
+        mViewPager.layout(0, 0, width, height);
+
+        if (mViewPager.getChildCount() < 1) {
+            leftButton.setVisibility(View.INVISIBLE);
+            rightButton.setVisibility(View.INVISIBLE);
+            return;
         }
 
-        // Account for padding too
-        maxWidth += getPaddingLeft() + getPaddingRight();
-        maxHeight += getPaddingTop() + getPaddingBottom();
+        final SimpleMonthView monthView = (SimpleMonthView) mViewPager.getChildAt(0);
+        final int monthHeight = monthView.getMonthHeight();
+        final int cellWidth = monthView.getCellWidth();
 
-        // Check against our minimum height and width
-        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
-        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+        // Vertically center the previous/next buttons within the month
+        // header, horizontally center within the day cell.
+        final int leftDW = leftButton.getMeasuredWidth();
+        final int leftDH = leftButton.getMeasuredHeight();
+        final int leftIconTop = monthView.getPaddingTop() + (monthHeight - leftDH) / 2;
+        final int leftIconLeft = monthView.getPaddingLeft() + (cellWidth - leftDW) / 2;
+        leftButton.layout(leftIconLeft, leftIconTop, leftIconLeft + leftDW, leftIconTop + leftDH);
+        leftButton.setVisibility(View.VISIBLE);
 
-        // Check against our foreground's minimum height and width
-        final Drawable drawable = getForeground();
-        if (drawable != null) {
-            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
-            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
-        }
-
-        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        childState << MEASURED_HEIGHT_STATE_SHIFT));
-
-        count = mMatchParentChildren.size();
-        if (count > 1) {
-            for (int i = 0; i < count; i++) {
-                final View child = mMatchParentChildren.get(i);
-
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                final int childWidthMeasureSpec;
-                final int childHeightMeasureSpec;
-
-                if (lp.width == LayoutParams.MATCH_PARENT) {
-                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
-                            MeasureSpec.EXACTLY);
-                } else {
-                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
-                            getPaddingLeft() + getPaddingRight(),
-                            lp.width);
-                }
-
-                if (lp.height == LayoutParams.MATCH_PARENT) {
-                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
-                            MeasureSpec.EXACTLY);
-                } else {
-                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
-                            getPaddingTop() + getPaddingBottom(),
-                            lp.height);
-                }
-
-                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-            }
-        }
-
-        mMatchParentChildren.clear();
+        final int rightDW = rightButton.getMeasuredWidth();
+        final int rightDH = rightButton.getMeasuredHeight();
+        final int rightIconTop = monthView.getPaddingTop() + (monthHeight - rightDH) / 2;
+        final int rightIconRight = width - monthView.getPaddingRight() - (cellWidth - rightDW) / 2;
+        rightButton.layout(rightIconRight - rightDW, rightIconTop,
+                rightIconRight, rightIconTop + rightDH);
+        rightButton.setVisibility(View.VISIBLE);
     }
 
     public void setDayOfWeekTextAppearance(int resId) {
@@ -284,8 +284,8 @@
         }
 
         final int position = getPositionFromDay(timeInMillis);
-        if (position != getCurrentItem()) {
-            setCurrentItem(position, animate);
+        if (position != mViewPager.getCurrentItem()) {
+            mViewPager.setCurrentItem(position, animate);
         }
 
         mTempCalendar.setTimeInMillis(timeInMillis);
@@ -365,10 +365,57 @@
      * Gets the position of the view that is most prominently displayed within the list view.
      */
     public int getMostVisiblePosition() {
-        return getCurrentItem();
+        return mViewPager.getCurrentItem();
     }
 
+    public void setPosition(int position) {
+        mViewPager.setCurrentItem(position, false);
+    }
+
+    private final OnPageChangeListener mOnPageChangedListener = new OnPageChangeListener() {
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            final float alpha = Math.abs(0.5f - positionOffset) * 2.0f;
+            mPrevButton.setAlpha(alpha);
+            mNextButton.setAlpha(alpha);
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {}
+
+        @Override
+        public void onPageSelected(int position) {
+            mPrevButton.setVisibility(
+                    position > 0 ? View.VISIBLE : View.INVISIBLE);
+            mNextButton.setVisibility(
+                    position < (mAdapter.getCount() - 1) ? View.VISIBLE : View.INVISIBLE);
+        }
+    };
+
+    private final OnClickListener mOnClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            final int direction;
+            if (v == mPrevButton) {
+                direction = -1;
+            } else if (v == mNextButton) {
+                direction = 1;
+            } else {
+                return;
+            }
+
+            // Animation is expensive for accessibility services since it sends
+            // lots of scroll and content change events.
+            final boolean animate = !mAccessibilityManager.isEnabled();
+
+            // ViewPager clamps input values, so we don't need to worry
+            // about passing invalid indices.
+            final int nextItem = mViewPager.getCurrentItem() + direction;
+            mViewPager.setCurrentItem(nextItem, animate);
+        }
+    };
+
     public interface OnDaySelectedListener {
-        public void onDaySelected(DayPickerView view, Calendar day);
+        void onDaySelected(DayPickerView view, Calendar day);
     }
 }
diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java
new file mode 100644
index 0000000..bb6e3a4
--- /dev/null
+++ b/core/java/android/widget/DayPickerViewPager.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.internal.widget.ViewPager;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.ArrayList;
+
+/**
+ * This displays a list of months in a calendar format with selectable days.
+ */
+class DayPickerViewPager extends ViewPager {
+    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+
+    public DayPickerViewPager(Context context) {
+        this(context, null);
+    }
+
+    public DayPickerViewPager(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        populate();
+
+        // Everything below is mostly copied from FrameLayout.
+        int count = getChildCount();
+
+        final boolean measureMatchParentChildren =
+                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
+                        MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
+
+        int maxHeight = 0;
+        int maxWidth = 0;
+        int childState = 0;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                measureChild(child, widthMeasureSpec, heightMeasureSpec);
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
+                maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+                childState = combineMeasuredStates(childState, child.getMeasuredState());
+                if (measureMatchParentChildren) {
+                    if (lp.width == LayoutParams.MATCH_PARENT ||
+                            lp.height == LayoutParams.MATCH_PARENT) {
+                        mMatchParentChildren.add(child);
+                    }
+                }
+            }
+        }
+
+        // Account for padding too
+        maxWidth += getPaddingLeft() + getPaddingRight();
+        maxHeight += getPaddingTop() + getPaddingBottom();
+
+        // Check against our minimum height and width
+        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+        // Check against our foreground's minimum height and width
+        final Drawable drawable = getForeground();
+        if (drawable != null) {
+            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
+            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
+        }
+
+        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+                resolveSizeAndState(maxHeight, heightMeasureSpec,
+                        childState << MEASURED_HEIGHT_STATE_SHIFT));
+
+        count = mMatchParentChildren.size();
+        if (count > 1) {
+            for (int i = 0; i < count; i++) {
+                final View child = mMatchParentChildren.get(i);
+
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                final int childWidthMeasureSpec;
+                final int childHeightMeasureSpec;
+
+                if (lp.width == LayoutParams.MATCH_PARENT) {
+                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
+                            MeasureSpec.EXACTLY);
+                } else {
+                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+                            getPaddingLeft() + getPaddingRight(),
+                            lp.width);
+                }
+
+                if (lp.height == LayoutParams.MATCH_PARENT) {
+                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
+                            MeasureSpec.EXACTLY);
+                } else {
+                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
+                            getPaddingTop() + getPaddingBottom(),
+                            lp.height);
+                }
+
+                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+            }
+        }
+
+        mMatchParentChildren.clear();
+    }
+}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index aa7f0b6..0249c22 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -26,7 +26,6 @@
 import android.graphics.Paint.Style;
 import android.graphics.Rect;
 import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.TextPaint;
 import android.text.format.DateFormat;
@@ -60,12 +59,6 @@
     private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
     private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
 
-    /** Virtual view ID for previous button. */
-    private static final int ITEM_ID_PREV = 0x101;
-
-    /** Virtual view ID for next button. */
-    private static final int ITEM_ID_NEXT = 0x100;
-
     private final TextPaint mMonthPaint = new TextPaint();
     private final TextPaint mDayOfWeekPaint = new TextPaint();
     private final TextPaint mDayPaint = new TextPaint();
@@ -87,14 +80,6 @@
     private final int mDesiredCellWidth;
     private final int mDesiredDaySelectorRadius;
 
-    // Next/previous drawables.
-    private final Drawable mPrevDrawable;
-    private final Drawable mNextDrawable;
-    private final Rect mPrevHitArea;
-    private final Rect mNextHitArea;
-    private final CharSequence mPrevContentDesc;
-    private final CharSequence mNextContentDesc;
-
     private CharSequence mTitle;
 
     private int mMonth;
@@ -137,9 +122,6 @@
     /** The day of month for the last (inclusive) enabled day. */
     private int mEnabledDayEnd = 31;
 
-    /** The number of week rows needed to display the current month. */
-    private int mNumWeeks = MAX_WEEKS_IN_MONTH;
-
     /** Optional listener for handling day click actions. */
     private OnDayClickListener mOnDayClickListener;
 
@@ -147,9 +129,6 @@
 
     private int mTouchedItem = -1;
 
-    private boolean mPrevEnabled;
-    private boolean mNextEnabled;
-
     public SimpleMonthView(Context context) {
         this(context, null);
     }
@@ -170,14 +149,8 @@
         mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
         mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
         mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
-        mDesiredDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
-
-        mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left);
-        mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right);
-        mPrevHitArea = mPrevDrawable != null ? new Rect() : null;
-        mNextHitArea = mNextDrawable != null ? new Rect() : null;
-        mPrevContentDesc = res.getText(R.string.date_picker_prev_month_button);
-        mNextContentDesc = res.getText(R.string.date_picker_next_month_button);
+        mDesiredDaySelectorRadius = res.getDimensionPixelSize(
+                R.dimen.date_picker_day_selector_radius);
 
         // Set up accessibility components.
         mTouchHelper = new MonthViewTouchHelper(this);
@@ -193,18 +166,6 @@
         initPaints(res);
     }
 
-    public void setNextEnabled(boolean enabled) {
-        mNextEnabled = enabled;
-        mTouchHelper.invalidateRoot();
-        invalidate();
-    }
-
-    public void setPrevEnabled(boolean enabled) {
-        mPrevEnabled = enabled;
-        mTouchHelper.invalidateRoot();
-        invalidate();
-    }
-
     /**
      * Applies the specified text appearance resource to a paint, returning the
      * text color if one is set in the text appearance.
@@ -236,16 +197,16 @@
         return textColor;
     }
 
+    public int getMonthHeight() {
+        return mMonthHeight;
+    }
+
+    public int getCellWidth() {
+        return mCellWidth;
+    }
+
     public void setMonthTextAppearance(int resId) {
-        final ColorStateList monthColor = applyTextAppearance(mMonthPaint, resId);
-        if (monthColor != null) {
-            if (mPrevDrawable != null) {
-                mPrevDrawable.setTintList(monthColor);
-            }
-            if (mNextDrawable != null) {
-                mNextDrawable.setTintList(monthColor);
-            }
-        }
+        applyTextAppearance(mMonthPaint, resId);
 
         invalidate();
     }
@@ -360,7 +321,7 @@
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_MOVE:
-                final int touchedItem = getItemAtLocation(x, y);
+                final int touchedItem = getDayAtLocation(x, y);
                 if (mTouchedItem != touchedItem) {
                     mTouchedItem = touchedItem;
                     invalidate();
@@ -368,8 +329,8 @@
                 break;
 
             case MotionEvent.ACTION_UP:
-                final int clickedItem = getItemAtLocation(x, y);
-                onItemClicked(clickedItem, true);
+                final int clickedDay = getDayAtLocation(x, y);
+                onDayClicked(clickedDay);
                 // Fall through.
             case MotionEvent.ACTION_CANCEL:
                 // Reset touched day on stream end.
@@ -389,7 +350,6 @@
         drawMonth(canvas);
         drawDaysOfWeek(canvas);
         drawDays(canvas);
-        drawButtons(canvas);
 
         canvas.translate(-paddingLeft, -paddingTop);
     }
@@ -482,16 +442,6 @@
         }
     }
 
-    private void drawButtons(Canvas canvas) {
-        if (mPrevEnabled && mPrevDrawable != null) {
-            mPrevDrawable.draw(canvas);
-        }
-
-        if (mNextEnabled && mNextDrawable != null) {
-            mNextDrawable.draw(canvas);
-        }
-    }
-
     private static boolean isValidDayOfWeek(int day) {
         return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY;
     }
@@ -674,33 +624,6 @@
         mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius,
                 Math.min(maxSelectorWidth, maxSelectorHeight));
 
-        // Vertically center the previous/next drawables within the month
-        // header, horizontally center within the day cell, then expand the
-        // hit area to ensure it's at least 48x48dp.
-        final Drawable prevDrawable = mPrevDrawable;
-        if (prevDrawable != null) {
-            final int dW = prevDrawable.getIntrinsicWidth();
-            final int dH = prevDrawable.getIntrinsicHeight();
-            final int iconTop = (monthHeight - dH) / 2;
-            final int iconLeft = (cellWidth - dW) / 2;
-
-            // Button bounds don't include padding, but hit area does.
-            prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH);
-            mPrevHitArea.set(0, 0, paddingLeft + cellWidth, paddingTop + monthHeight);
-        }
-
-        final Drawable nextDrawable = mNextDrawable;
-        if (nextDrawable != null) {
-            final int dW = nextDrawable.getIntrinsicWidth();
-            final int dH = nextDrawable.getIntrinsicHeight();
-            final int iconTop = (monthHeight - dH) / 2;
-            final int iconRight = paddedWidth - (cellWidth - dW) / 2;
-
-            // Button bounds don't include padding, but hit area does.
-            nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH);
-            mNextHitArea.set(paddedRight - cellWidth, 0, w, paddingTop + monthHeight);
-        }
-
         // Invalidate cached accessibility information.
         mTouchHelper.invalidateRoot();
     }
@@ -714,22 +637,15 @@
     }
 
     /**
-     * Calculates the day of the month or item identifier at the specified
-     * touch position. Returns the day of the month or -1 if the position
-     * wasn't in a valid day.
+     * Calculates the day of the month at the specified touch position. Returns
+     * the day of the month or -1 if the position wasn't in a valid day.
      *
      * @param x the x position of the touch event
      * @param y the y position of the touch event
-     * @return the day of the month at (x, y), an item identifier, or -1 if the
-     *         position wasn't in a valid day or item
+     * @return the day of the month at (x, y), or -1 if the position wasn't in
+     *         a valid day
      */
-    private int getItemAtLocation(int x, int y) {
-        if (mNextEnabled && mNextDrawable != null && mNextHitArea.contains(x, y)) {
-            return ITEM_ID_NEXT;
-        } else if (mPrevEnabled && mPrevDrawable != null && mPrevHitArea.contains(x, y)) {
-            return ITEM_ID_PREV;
-        }
-
+    private int getDayAtLocation(int x, int y) {
         final int paddedX = x - getPaddingLeft();
         if (paddedX < 0 || paddedX >= mPaddedWidth) {
             return -1;
@@ -755,22 +671,10 @@
     /**
      * Calculates the bounds of the specified day.
      *
-     * @param id the day of the month, or an item identifier
+     * @param id the day of the month
      * @param outBounds the rect to populate with bounds
      */
-    private boolean getBoundsForItem(int id, Rect outBounds) {
-        if (mNextEnabled && id == ITEM_ID_NEXT) {
-            if (mNextDrawable != null) {
-                outBounds.set(mNextHitArea);
-                return true;
-            }
-        } else if (mPrevEnabled && id == ITEM_ID_PREV) {
-            if (mPrevDrawable != null) {
-                outBounds.set(mPrevHitArea);
-                return true;
-            }
-        }
-
+    private boolean getBoundsForDay(int id, Rect outBounds) {
         if (id < 1 || id > mDaysInMonth) {
             return false;
         }
@@ -789,16 +693,8 @@
         final int top = getPaddingTop() + headerHeight + row * rowHeight;
 
         outBounds.set(left, top, left + colWidth, top + rowHeight);
-        return true;
-    }
 
-    /**
-     * Called when an item is clicked.
-     *
-     * @param id the day number or item identifier
-     */
-    private boolean onItemClicked(int id, boolean animate) {
-        return onNavigationClicked(id, animate) || onDayClicked(id);
+        return true;
     }
 
     /**
@@ -824,31 +720,6 @@
     }
 
     /**
-     * Called when the user clicks on a navigation button. Handles callbacks to
-     * the {@link OnDayClickListener} if one is set.
-     *
-     * @param id the item identifier
-     */
-    private boolean onNavigationClicked(int id, boolean animate) {
-        final int direction;
-        if (id == ITEM_ID_NEXT) {
-            direction = 1;
-        } else if (id == ITEM_ID_PREV) {
-            direction = -1;
-        } else {
-            return false;
-        }
-
-        if (mOnDayClickListener != null) {
-            mOnDayClickListener.onNavigationClick(this, direction, animate);
-        }
-
-        // This is a no-op if accessibility is turned off.
-        mTouchHelper.sendEventForVirtualView(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
-        return true;
-    }
-
-    /**
      * Provides a virtual view hierarchy for interfacing with an accessibility
      * service.
      */
@@ -864,7 +735,7 @@
 
         @Override
         protected int getVirtualViewAt(float x, float y) {
-            final int day = getItemAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
+            final int day = getDayAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
             if (day >= 0) {
                 return day;
             }
@@ -873,14 +744,6 @@
 
         @Override
         protected void getVisibleVirtualViews(IntArray virtualViewIds) {
-            if (mNextEnabled && mNextDrawable != null) {
-                virtualViewIds.add(ITEM_ID_PREV);
-            }
-
-            if (mPrevEnabled && mPrevDrawable != null) {
-                virtualViewIds.add(ITEM_ID_NEXT);
-            }
-
             for (int day = 1; day <= mDaysInMonth; day++) {
                 virtualViewIds.add(day);
             }
@@ -888,12 +751,12 @@
 
         @Override
         protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
-            event.setContentDescription(getItemDescription(virtualViewId));
+            event.setContentDescription(getDayDescription(virtualViewId));
         }
 
         @Override
         protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
-            final boolean hasBounds = getBoundsForItem(virtualViewId, mTempRect);
+            final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
 
             if (!hasBounds) {
                 // The day is invalid, kill the node.
@@ -904,8 +767,8 @@
                 return;
             }
 
-            node.setText(getItemText(virtualViewId));
-            node.setContentDescription(getItemDescription(virtualViewId));
+            node.setText(getDayText(virtualViewId));
+            node.setContentDescription(getDayDescription(virtualViewId));
             node.setBoundsInParent(mTempRect);
             node.addAction(AccessibilityAction.ACTION_CLICK);
 
@@ -921,7 +784,7 @@
                 Bundle arguments) {
             switch (action) {
                 case AccessibilityNodeInfo.ACTION_CLICK:
-                    return onItemClicked(virtualViewId, false);
+                    return onDayClicked(virtualViewId);
             }
 
             return false;
@@ -930,15 +793,11 @@
         /**
          * Generates a description for a given virtual view.
          *
-         * @param id the day or item identifier to generate a description for
+         * @param id the day to generate a description for
          * @return a description of the virtual view
          */
-        private CharSequence getItemDescription(int id) {
-            if (id == ITEM_ID_NEXT) {
-                return mNextContentDesc;
-            } else if (id == ITEM_ID_PREV) {
-                return mPrevContentDesc;
-            } else if (id >= 1 && id <= mDaysInMonth) {
+        private CharSequence getDayDescription(int id) {
+            if (id >= 1 && id <= mDaysInMonth) {
                 mTempCalendar.set(mYear, mMonth, id);
                 return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis());
             }
@@ -949,13 +808,11 @@
         /**
          * Generates displayed text for a given virtual view.
          *
-         * @param id the day or item identifier to generate text for
+         * @param id the day to generate text for
          * @return the visible text of the virtual view
          */
-        private CharSequence getItemText(int id) {
-            if (id == ITEM_ID_NEXT || id == ITEM_ID_PREV) {
-                return null;
-            } else if (id >= 1 && id <= mDaysInMonth) {
+        private CharSequence getDayText(int id) {
+            if (id >= 1 && id <= mDaysInMonth) {
                 return Integer.toString(id);
             }
 
@@ -967,7 +824,6 @@
      * Handles callbacks when the user clicks on a time object.
      */
     public interface OnDayClickListener {
-        public void onDayClick(SimpleMonthView view, Calendar day);
-        public void onNavigationClick(SimpleMonthView view, int direction, boolean animate);
+        void onDayClick(SimpleMonthView view, Calendar day);
     }
 }
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 0b1e0e5..1e96945 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1070,7 +1070,8 @@
     private static PopupWindow createPopupWindow(View content) {
         ViewGroup popupContentHolder = new LinearLayout(content.getContext());
         PopupWindow popupWindow = new PopupWindow(popupContentHolder);
-        popupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+        popupWindow.setWindowLayoutType(
+                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
         popupWindow.setAnimationStyle(0);
         popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
         content.setLayoutParams(new ViewGroup.LayoutParams(
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 123d1ac..aa60eba 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -472,6 +472,8 @@
             updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
         }
 
+        setCredentialRequiredToDecrypt(false);
+
         getDevicePolicyManager().setActivePasswordState(
                 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle);
 
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index bda7de9..8125544 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -45,6 +45,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
+        android:gravity="start"
         android:maxLines="2"
         android:ellipsize="none"
         tools:text="Thu, Sep 30"
diff --git a/core/res/res/layout/day_picker_content_material.xml b/core/res/res/layout/day_picker_content_material.xml
new file mode 100644
index 0000000..1852bfa
--- /dev/null
+++ b/core/res/res/layout/day_picker_content_material.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+
+    <android.widget.DayPickerViewPager
+        android:id="@+id/day_picker_view_pager"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <ImageButton
+        android:id="@+id/prev"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        android:src="@drawable/ic_chevron_left"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:contentDescription="@string/date_picker_prev_month_button"
+        android:visibility="invisible" />
+
+    <ImageButton
+        android:id="@+id/next"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        android:src="@drawable/ic_chevron_right"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:contentDescription="@string/date_picker_next_month_button"
+        android:visibility="invisible" />
+
+</FrameLayout>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 180b415..98c1b8f 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2242,4 +2242,6 @@
   <java-symbol type="layout" name="chooser_grid" />
   <java-symbol type="layout" name="resolve_grid_item" />
   <java-symbol type="id" name="title_icon" />
+  <java-symbol type="id" name="day_picker_view_pager" />
+  <java-symbol type="layout" name="day_picker_content_material" />
 </resources>
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
index 5fa2405..6ee6ffa 100644
--- a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
@@ -5,7 +5,6 @@
 
 LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk
 
-LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS := -0
 LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES := true
 
 include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/docs/html/images/tools/hierarchicalviewer-icon.png b/docs/html/images/tools/hierarchicalviewer-icon.png
new file mode 100644
index 0000000..061f952
--- /dev/null
+++ b/docs/html/images/tools/hierarchicalviewer-icon.png
Binary files differ
diff --git a/docs/html/images/tools/studio-DDMS-open-perspective-icon.png b/docs/html/images/tools/studio-DDMS-open-perspective-icon.png
new file mode 100644
index 0000000..f8e6d1a
--- /dev/null
+++ b/docs/html/images/tools/studio-DDMS-open-perspective-icon.png
Binary files differ
diff --git a/docs/html/images/tools/studio-gradle-panel.png b/docs/html/images/tools/studio-gradle-panel.png
deleted file mode 100644
index 4a76a8d..0000000
--- a/docs/html/images/tools/studio-gradle-panel.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/tools/studio-gradle-tab.png b/docs/html/images/tools/studio-gradle-tab.png
deleted file mode 100644
index b0f302c..0000000
--- a/docs/html/images/tools/studio-gradle-tab.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/tools/building/building-studio.jd b/docs/html/tools/building/building-studio.jd
index 68800da..4431194 100644
--- a/docs/html/tools/building/building-studio.jd
+++ b/docs/html/tools/building/building-studio.jd
@@ -67,26 +67,13 @@
 <a href="{@docRoot}sdk/installing/studio-build.html">Build System</a> guide.</p>
 
 <p>To view the list of all available build tasks in Android Studio, click <strong>Gradle</strong>
-on the right side of the IDE window. The <em>Gradle tasks</em> panel appears as shown in
-figure 2. Double-click any build task to run it in Android Studio. To hide the <em>Gradle tasks</em>
-panel, click <strong>Gradle</strong> again.</p>
+on the right side of the IDE window. The <em>Gradle tasks</em> panel appears.</p>
 
-<img src="{@docRoot}images/tools/studio-gradle-panel.png" alt="" />
-<p class="img-caption"><strong>Figure 2.</strong> The list of build tasks in Android Studio.</p>
 
 <h3 id="buildRelease">Build a release version</h3>
 
-<p>You can now build the release version of your application for distribution. To build it from Android
-Studio:</p>
-
-<ol>
-    <li>Click <strong>Gradle</strong> on the right side of the IDE window.</li>
-    <li>On the <em>All tasks</em> section of the sidebar that appears, expand
-        <strong>BuildSystemExample</strong>.</li>
-    <li>Expand <strong>:app</strong> and double-click <strong>assembleRelease</strong>.</li>
-</ol>
-
-<p>You can use this procedure to invoke any build task from Android Studio.</p>
+<p>You can now use the <strong>Build</strong> menu options to build the release version of your
+application for distribution. </p>
 
 <p>The build generates an APK for each build variant:
 the <code>app/build/apk/</code> directory contains packages named
diff --git a/docs/html/tools/building/configuring-gradle.jd b/docs/html/tools/building/configuring-gradle.jd
index 8379508..7cca5b4 100644
--- a/docs/html/tools/building/configuring-gradle.jd
+++ b/docs/html/tools/building/configuring-gradle.jd
@@ -470,7 +470,11 @@
 <li>fullRelease</li>
 </ul>
 
-<p>To build this example, invoke the <code>assemble</code> task from Android Studio or from the
-command line.</p>
+<p>To build this example, click the <strong>Build</strong> menu option in Android Studio or invoke
+the <code>assemble</code> task from the command line. </p>
+
+<p class="note"><strong>Note:</strong> The <strong>Build &gt; Make Project</strong> option compiles
+all the source files in the entire project that have been modified since the last compilation. The
+<strong>Build &gt; Rebuild Project</strong> option recomplies all the source files in the project.</p>
 
 <p>Separate output folders are created for each build variant. </p>
diff --git a/docs/html/tools/building/plugin-for-gradle.jd b/docs/html/tools/building/plugin-for-gradle.jd
index 54a03fd..a497c1b 100644
--- a/docs/html/tools/building/plugin-for-gradle.jd
+++ b/docs/html/tools/building/plugin-for-gradle.jd
@@ -15,7 +15,7 @@
 <li><a href="{@docRoot}sdk/installing/studio-build.html">
 Build System Overview</a></li>
 <li><a href="{@docRoot}tools/building/index.html">
-Buidling and Running</a></li>
+Building and Running</a></li>
 <li><a href="{@docRoot}tools/building/building-studio.html">
 Building and Running from Android Studio</a></li>
 </ul>
@@ -273,16 +273,9 @@
    <dd><p>Performs the clean.</p></dd>
 </dl>
 
-<p>The Android plugin provides additional tasks for <em>connectedCheck</em> and <em>deviceCheck</em>
+<p>The Android plugin provides the <em>connectedCheck</em> and <em>deviceCheck</em> tasks
 for checks run on connected, emulated, and remote devices. Gradle tasks can be viewed by clicking
-the Gradle tab</a> in the right margin.
-<img src="{@docRoot}images/tools/studio-gradle-tab.png"></p>
-<p class="img-caption"><strong>Figure 1:</strong> Gradle tab</p>
-
-<p>Running a top-level task, runs all the dependent tasks. For example, the <em>assemble</em> task
-has dependent tasks for <em>assembleDebug</em> and <em>assembleRelease</em> to make the debug and
-release APKs. The <em>assemble</em> task depends on these tasks so calling it builds both APKs.
-These tasks can also be called independently to build the debug or release APK separately. </p>
+the Gradle tab</a> in the right margin.</p>
 
 <p>You can view the list of available tasks and invoke any task from Android Studio and from
 the command line, as described in
diff --git a/docs/html/tools/help/hierarchy-viewer.jd b/docs/html/tools/help/hierarchy-viewer.jd
index 4a346e0..da4cc1e 100644
--- a/docs/html/tools/help/hierarchy-viewer.jd
+++ b/docs/html/tools/help/hierarchy-viewer.jd
@@ -3,16 +3,27 @@
 parent.link=index.html
 @jd:body
 
-<p>Hierarchy Viewer allows you to debug and optimize your user 
-interface. It provides a visual representation of the layout's View hierarchy 
-(the Layout View) and a magnified inspector of the display (the Pixel Perfect View). 
+<p>The Hierarchy Viewer allows you to debug and optimize your user
+interface. It provides a visual representation of the layout's View hierarchy
+(the Layout View) and a magnified inspector of the display (the Pixel Perfect View). </p>
+
+<p>To start the Hierarchy Viewer, do one of the following: </p>
+
+<ul>
+<li> From Android Studio, choose <strong>Tools &gt; Android Device Monitor</strong> or click the
+Android Device Monitor icon
+<img src="{@docRoot}images/tools/hierarchicalviewer-icon.png" alt=""/>. Click the Open Perspectives
+icon <img src="{@docRoot}images/tools/studio-DDMS-open-perspective-icon.png" alt=""/> and select
+<strong>Hierarchy View</strong>. </li>
+<li>From the SDK <code>tools/</code> directory, enter:
+  <pre>monitor</pre> </li>
+</ul>
+
+<p>For more information on how to use the Hierarchy Viewer, see
+<a href="{@docRoot}tools/debugging/debugging-ui.html">Optimizing Your UI</a>.
 </p>
 
-<p>To start Hierarchy Viewer, enter the following command from the SDK <code>tools/</code> directory:</p>
-  <pre>hierarchyviewer</pre>
-</ol>
+<p class="note"><strong>Note:</strong> The command line version of Hierarchy Viewer has been
+deprecated. </p>
 
-<p>For more information on how to use Hierarchy Viewer, see 
-<a href="{@docRoot}tools/debugging/debugging-ui.html">Debugging and Profiling UIs</a>
-</p>
 
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index e8b8c77..22ff3e74 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -572,7 +572,7 @@
      * Specifies a tint blending mode for this drawable.
      * <p>
      * Defines how this drawable's tint color should be blended into the drawable
-     * before it is drawn to screen. Default tint mode is {@link PorterDuff.Mode#MULTIPLY}.
+     * before it is drawn to screen. Default tint mode is {@link PorterDuff.Mode#SRC_IN}.
      * </p>
      * <p class="note"><strong>Note:</strong> Setting a color filter via
      * {@link #setColorFilter(ColorFilter)} or
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index aa722d0..d06534e 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1009,12 +1009,13 @@
         TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
         TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
     }
+    Rect modelRect = Rect(rect.getWidth(), rect.getHeight());
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
             .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
             .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
-            .setModelViewOffsetRectSnap(0, 0, rect)
+            .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect)
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
     DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsInfo.java
new file mode 100644
index 0000000..60b5ba5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsInfo.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.applications;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class PermissionsInfo {
+
+    private static final String TAG = "PermissionsInfo";
+
+    private final PackageManager mPm;
+    private final ArrayList<PermissionGroup> mGroups = new ArrayList<>();
+    private final Map<String, PermissionGroup> mGroupLookup = new ArrayMap<>();
+    private final Callback mCallback;
+    private final Context mContext;
+    // Count of apps that request runtime permissions.
+    private int mRuntimePermAppsCt;
+    // Count of apps that are granted runtime permissions.
+    private int mRuntimePermAppsGrantedCt;
+
+    public PermissionsInfo(Context context, Callback callback) {
+        mContext = context;
+        mPm = context.getPackageManager();
+        mCallback = callback;
+        new PermissionsLoader().execute();
+    }
+
+    public List<PermissionGroup> getGroups() {
+        synchronized (mGroups) {
+            return new ArrayList<>(mGroups);
+        }
+    }
+
+    public int getRuntimePermAppsCount() {
+        return mRuntimePermAppsCt;
+    }
+
+    public int getRuntimePermAppsGrantedCount() {
+        return mRuntimePermAppsGrantedCt;
+    }
+
+    private PermissionGroup getOrCreateGroup(String permission) {
+        PermissionGroup group = mGroupLookup.get(permission);
+        if (group == null) {
+            // Some permissions don't have a group, in that case treat them like a group
+            // and create their own PermissionGroup (only if they are runtime).
+            try {
+                PermissionInfo info = mPm.getPermissionInfo(permission, 0);
+                if (info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) {
+                    group = new PermissionGroup();
+                    // TODO: Add default permission icon.
+                    group.icon = info.icon != 0 ? info.loadIcon(mPm) : new ShapeDrawable();
+                    group.name = info.name;
+                    group.label = info.loadLabel(mPm).toString();
+                    mGroups.add(group);
+                    mGroupLookup.put(permission, group);
+                }
+            } catch (NameNotFoundException e) {
+                Log.w(TAG, "Unknown permission " + permission, e);
+            }
+        }
+        return group;
+    }
+
+    private class PermissionsLoader extends AsyncTask<Void, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            List<PermissionGroupInfo> groups =
+                    mPm.getAllPermissionGroups(PackageManager.GET_META_DATA);
+            // Get the groups.
+            for (PermissionGroupInfo groupInfo : groups) {
+                PermissionGroup group = new PermissionGroup();
+                // TODO: Add default permission icon.
+                group.icon = groupInfo.icon != 0 ? groupInfo.loadIcon(mPm) : new ShapeDrawable();
+                group.name = groupInfo.name;
+                group.label = groupInfo.loadLabel(mPm).toString();
+                synchronized (mGroups) {
+                    mGroups.add(group);
+                }
+            }
+            // Load permissions and which are runtime.
+            for (PermissionGroup group : mGroups) {
+                try {
+                    List<PermissionInfo> permissions =
+                            mPm.queryPermissionsByGroup(group.name, 0);
+                    for (PermissionInfo info : permissions) {
+                        if (info.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) continue;
+                        mGroupLookup.put(info.name, group);
+                    }
+                } catch (NameNotFoundException e) {
+                    Log.w(TAG, "Problem getting permissions", e);
+                }
+            }
+            // Load granted info.
+            for (UserHandle user : UserManager.get(mContext).getUserProfiles()) {
+                List<PackageInfo> allApps = mPm.getInstalledPackages(
+                        PackageManager.GET_PERMISSIONS, user.getIdentifier());
+                for (PackageInfo info : allApps) {
+                    if (info.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
+                            || info.requestedPermissions == null)  {
+                        continue;
+                    }
+                    final int N = info.requestedPermissionsFlags.length;
+                    boolean appHasRuntimePerms = false;
+                    boolean appGrantedRuntimePerms = false;
+                    for (int i = 0; i < N; i++) {
+                        boolean granted = (info.requestedPermissionsFlags[i]
+                                & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
+                        PermissionGroup group = getOrCreateGroup(info.requestedPermissions[i]);
+                        String key = Integer.toString(info.applicationInfo.uid);
+                        if (group != null && !group.possibleApps.contains(key)) {
+                            appHasRuntimePerms = true;
+                            group.possibleApps.add(key);
+                            if (granted) {
+                                appGrantedRuntimePerms = true;
+                                group.grantedApps.add(key);
+                            }
+                        }
+                    }
+                    if (appHasRuntimePerms) {
+                        mRuntimePermAppsCt++;
+                        if (appGrantedRuntimePerms) {
+                            mRuntimePermAppsGrantedCt++;
+                        }
+                    }
+                }
+            }
+            Collections.sort(mGroups);
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            mCallback.onPermissionLoadComplete();
+        }
+    }
+
+    public static class PermissionGroup implements Comparable<PermissionGroup> {
+        public final List<String> possibleApps = new ArrayList<>();
+        public final List<String> grantedApps = new ArrayList<>();
+        public String name;
+        public String label;
+        public Drawable icon;
+
+        @Override
+        public int compareTo(PermissionGroup another) {
+            return label.compareTo(another.label);
+        }
+    }
+
+    public interface Callback {
+        void onPermissionLoadComplete();
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 9f86475..de4874f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -687,13 +687,7 @@
         setHeadsUpUser(newUserId);
     }
 
-    private void setHeadsUpUser(int newUserId) {
-        mHeadsUpManager.setUser(newUserId);
-    }
-
-    public boolean isHeadsUp(String key) {
-      return mHeadsUpManager.isHeadsUp(key);
-    }
+    protected abstract void setHeadsUpUser(int newUserId);
 
     @Override  // NotificationData.Environment
     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
@@ -1578,7 +1572,7 @@
                             mCurrentUserId);
             dismissKeyguardThenExecute(new OnDismissAction() {
                 public boolean onDismiss() {
-                    if (mHeadsUpManager.isHeadsUp(mNotificationKey)) {
+                    if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(mNotificationKey)) {
                         // Release the HUN notification to the shade.
                         //
                         // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
@@ -1941,20 +1935,8 @@
         setAreThereNotifications();
     }
 
-    private void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
-            boolean alertAgain) {
-        final boolean wasHeadsUp = isHeadsUp(key);
-        if (wasHeadsUp) {
-            mHeadsUpManager.updateNotification(entry, alertAgain);
-            if (!shouldInterrupt) {
-                // We don't want this to be interrupting anymore, lets remove it
-                mHeadsUpManager.removeNotification(key);
-            }
-        } else if (shouldInterrupt && alertAgain) {
-            // This notification was updated to be a heads-up, show it!
-            mHeadsUpManager.showNotification(entry);
-        }
-    }
+    protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+            boolean alertAgain);
 
     private void logUpdate(Entry oldEntry, Notification n) {
         StatusBarNotification oldNotification = oldEntry.notification;
@@ -2075,7 +2057,7 @@
             return false;
         }
 
-        if (mHeadsUpManager.isSnoozed(sbn.getPackageName())) {
+        if (isSnoozedPackage(sbn)) {
             return false;
         }
 
@@ -2109,6 +2091,8 @@
         return interrupt;
     }
 
+    protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
+
     public void setInteracting(int barWindow, boolean interacting) {
         // hook for subclasses
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index a5dad92..7f65f73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1867,6 +1867,35 @@
 
     }
 
+    protected void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+            boolean alertAgain) {
+        final boolean wasHeadsUp = isHeadsUp(key);
+        if (wasHeadsUp) {
+            mHeadsUpManager.updateNotification(entry, alertAgain);
+            if (!shouldInterrupt) {
+                // We don't want this to be interrupting anymore, lets remove it
+                mHeadsUpManager.removeNotification(key);
+            }
+        } else if (shouldInterrupt && alertAgain) {
+            // This notification was updated to be a heads-up, show it!
+            mHeadsUpManager.showNotification(entry);
+        }
+    }
+
+    protected void setHeadsUpUser(int newUserId) {
+        if (mHeadsUpManager != null) {
+            mHeadsUpManager.setUser(newUserId);
+        }
+    }
+
+    public boolean isHeadsUp(String key) {
+        return mHeadsUpManager.isHeadsUp(key);
+    }
+
+    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 78122d6..dce695d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -20,8 +20,6 @@
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -166,4 +164,17 @@
     @Override
     public void appTransitionStarting(long startTime, long duration) {
     }
+
+    @Override
+    protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldInterrupt,
+            boolean alertAgain) {
+    }
+
+    @Override
+    protected void setHeadsUpUser(int newUserId) {
+    }
+
+    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 16689ee..a31a1a7 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -134,7 +134,11 @@
 
     public void systemReady() {
         migrateOldData();
-        getGateKeeperService();
+        try {
+            getGateKeeperService();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
+        }
         mStorage.prefetchUser(UserHandle.USER_OWNER);
     }
 
@@ -695,7 +699,16 @@
         return null;
     }
 
-    private synchronized IGateKeeperService getGateKeeperService() {
+    private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            mGateKeeperService.asBinder().unlinkToDeath(this, 0);
+            mGateKeeperService = null;
+        }
+    }
+
+    private synchronized IGateKeeperService getGateKeeperService()
+            throws RemoteException {
         if (mGateKeeperService != null) {
             return mGateKeeperService;
         }
@@ -703,6 +716,7 @@
         final IBinder service =
             ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
         if (service != null) {
+            service.linkToDeath(new GateKeeperDiedRecipient(), 0);
             mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
             return mGateKeeperService;
         }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 4c937f7..7e4df58 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -61,6 +61,7 @@
 import android.os.storage.StorageVolume;
 import android.os.storage.VolumeInfo;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.DebugUtils;
@@ -533,7 +534,11 @@
                     break;
                 }
                 case H_FSTRIM: {
-                    waitForReady();
+                    if (!isReady()) {
+                        Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
+                        sendMessageDelayed(obtainMessage(H_FSTRIM), DateUtils.SECOND_IN_MILLIS);
+                    }
+
                     Slog.i(TAG, "Running fstrim idle maintenance");
 
                     // Remember when we kicked it off
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5b1543e..33f915f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -363,19 +363,9 @@
         mOverrideConfig = Configuration.EMPTY;
     }
 
-    /**
-     * Checks whether the userid is a profile of the current user.
-     */
-    private boolean isCurrentProfileLocked(int userId) {
-        if (userId == mCurrentUser) return true;
-        for (int i = 0; i < mService.mCurrentProfileIds.length; i++) {
-            if (mService.mCurrentProfileIds[i] == userId) return true;
-        }
-        return false;
-    }
-
     boolean okToShowLocked(ActivityRecord r) {
-        return isCurrentProfileLocked(r.userId) || (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
+        return mStackSupervisor.isCurrentProfileLocked(r.userId)
+                || (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
     }
 
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
@@ -619,7 +609,8 @@
 
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
-            final boolean notCurrentUserTask = !isCurrentProfileLocked(task.userId);
+            final boolean notCurrentUserTask =
+                    !mStackSupervisor.isCurrentProfileLocked(task.userId);
             final ArrayList<ActivityRecord> activities = task.mActivities;
 
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -655,7 +646,7 @@
 
             // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
             // okay to show the activity when locked.
-            if (isCurrentProfileLocked(task.userId)
+            if (mStackSupervisor.isCurrentProfileLocked(task.userId)
                     || task.topRunningActivityLocked(null) != null) {
                 if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
                         " moving " + task + " to top");
@@ -817,8 +808,14 @@
     final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
             boolean dontWait) {
         if (mPausingActivity != null) {
-            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity);
-            completePauseLocked(false);
+            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+                    + " state=" + mPausingActivity.state);
+            if (!mService.isSleeping()) {
+                // Avoid recursion among check for sleep and complete pause during sleeping.
+                // Because activity will be paused immediately after resume, just let pause
+                // be completed by the order of activity paused from clients.
+                completePauseLocked(false);
+            }
         }
         ActivityRecord prev = mResumedActivity;
         if (prev == null) {
@@ -2026,11 +2023,11 @@
         final boolean notShownWhenLocked =
                 (newActivity != null && (newActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) == 0)
                 || (newActivity == null && task.topRunningActivityLocked(null) == null);
-        if (!isCurrentProfileLocked(task.userId) && notShownWhenLocked) {
+        if (!mStackSupervisor.isCurrentProfileLocked(task.userId) && notShownWhenLocked) {
             // Put non-current user tasks below current user tasks.
             while (--taskNdx >= 0) {
                 final TaskRecord tmpTask = mTaskHistory.get(taskNdx);
-                if (!isCurrentProfileLocked(tmpTask.userId)
+                if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
                         || tmpTask.topRunningActivityLocked(null) == null) {
                     break;
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 58bdc28..1585f61 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -24,6 +24,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -1381,8 +1382,9 @@
             }
         }
 
+        final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
+
         if (err == ActivityManager.START_SUCCESS) {
-            final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
             Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
                     + "} from uid " + callingUid
                     + " on display " + (container == null ? (mFocusedStack == null ?
@@ -1406,7 +1408,7 @@
 
         final int launchFlags = intent.getFlags();
 
-        if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
+        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
             // Transfer the result target from the source activity to the new
             // one being started, including any failures.
             if (requestCode >= 0) {
@@ -1450,6 +1452,13 @@
             err = ActivityManager.START_CLASS_NOT_FOUND;
         }
 
+        if (err == ActivityManager.START_SUCCESS
+                && !isCurrentProfileLocked(userId)
+                && (aInfo.flags & FLAG_SHOW_FOR_ALL_USERS) == 0) {
+            // Trying to launch a background activity that doesn't show for all users.
+            err = ActivityManager.START_NOT_CURRENT_USER_ACTIVITY;
+        }
+
         if (err == ActivityManager.START_SUCCESS && sourceRecord != null
                 && sourceRecord.task.voiceSession != null) {
             // If this activity is being launched as part of a voice session, we need
@@ -3231,6 +3240,15 @@
         mStartingBackgroundUsers.add(uss);
     }
 
+    /** Checks whether the userid is a profile of the current user. */
+    boolean isCurrentProfileLocked(int userId) {
+        if (userId == mCurrentUser) return true;
+        for (int i = 0; i < mService.mCurrentProfileIds.length; i++) {
+            if (mService.mCurrentProfileIds[i] == userId) return true;
+        }
+        return false;
+    }
+
     final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
         ArrayList<ActivityRecord> stops = null;
 
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 0ca0c0e..770df82 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -143,75 +143,67 @@
                     + ", mAuthClients = " + mAuthClient + ", mEnrollClient = " + mEnrollClient);
         if (mEnrollClient != null) {
             final IBinder token = mEnrollClient.token;
-            if (doNotify(mEnrollClient, type, arg1, arg2, arg3)) {
-                stopEnrollment(token);
+            if (dispatchNotify(mEnrollClient, type, arg1, arg2, arg3)) {
+                stopEnrollment(token, false);
+                removeClient(mEnrollClient);
             }
         }
         if (mAuthClient != null) {
             final IBinder token = mAuthClient.token;
-            if (doNotify(mAuthClient, type, arg1, arg2, arg3)) {
-                stopAuthentication(token);
+            if (dispatchNotify(mAuthClient, type, arg1, arg2, arg3)) {
+                stopAuthentication(token, false);
+                removeClient(mAuthClient);
             }
         }
         if (mRemoveClient != null) {
-            if (doNotify(mRemoveClient, type, arg1, arg2, arg3)) {
+            if (dispatchNotify(mRemoveClient, type, arg1, arg2, arg3)) {
                 removeClient(mRemoveClient);
             }
         }
     }
 
-    // Returns true if the operation is done, i.e. authentication completed
-    boolean doNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) {
+    /*
+     * Dispatch notify events to clients.
+     *
+     * @return true if the operation is done, i.e. authentication completed
+     */
+    boolean dispatchNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) {
         ContentResolver contentResolver = mContext.getContentResolver();
         boolean operationCompleted = false;
+        int fpId;
+        int groupId;
+        int remaining;
+        int acquireInfo;
         switch (type) {
             case FINGERPRINT_ERROR:
-                {
-                    final int error = arg1;
-                    clientMonitor.sendError(error);
-                    removeClient(clientMonitor);
-                    operationCompleted = true; // any error means the operation is done
-                }
+                fpId = arg1;
+                operationCompleted = clientMonitor.sendError(fpId);
                 break;
             case FINGERPRINT_ACQUIRED:
-                clientMonitor.sendAcquired(arg1 /* acquireInfo */);
+                acquireInfo = arg1;
+                operationCompleted = clientMonitor.sendAcquired(acquireInfo);
                 break;
             case FINGERPRINT_AUTHENTICATED:
-                {
-                    final int fpId = arg1;
-                    final int groupId = arg2;
-                    clientMonitor.sendAuthenticated(fpId, groupId);
-                    if (fpId == 0) {
-                        if (clientMonitor == mAuthClient) {
-                            operationCompleted = handleFailedAttempt(clientMonitor);
-                        }
-                    } else {
-                        mLockoutReset.run(); // a valid fingerprint resets lockout
-                    }
-                }
+                fpId = arg1;
+                groupId = arg2;
+                operationCompleted = clientMonitor.sendAuthenticated(fpId, groupId);
                 break;
             case FINGERPRINT_TEMPLATE_ENROLLING:
-                {
-                    final int fpId = arg1;
-                    final int groupId = arg2;
-                    final int remaining = arg3;
-                    clientMonitor.sendEnrollResult(fpId, groupId, remaining);
-                    if (remaining == 0) {
-                        addTemplateForUser(clientMonitor, contentResolver, fpId);
-                        operationCompleted = true; // enroll completed
-                    }
+                fpId = arg1;
+                groupId = arg2;
+                remaining = arg3;
+                operationCompleted = clientMonitor.sendEnrollResult(fpId, groupId, remaining);
+                if (remaining == 0) {
+                    addTemplateForUser(clientMonitor, contentResolver, fpId);
+                    operationCompleted = true; // enroll completed
                 }
                 break;
             case FINGERPRINT_TEMPLATE_REMOVED:
-                {
-                    final int fingerId = arg1;
-                    final int groupId = arg2;
-                    removeTemplateForUser(clientMonitor, contentResolver, fingerId);
-                    if (fingerId == 0) {
-                        operationCompleted = true; // remove completed
-                    } else {
-                        clientMonitor.sendRemoved(fingerId, groupId);
-                    }
+                fpId = arg1;
+                groupId = arg2;
+                operationCompleted = clientMonitor.sendRemoved(fpId, groupId);
+                if (fpId != 0) {
+                    removeTemplateForUser(clientMonitor, contentResolver, fpId);
                 }
                 break;
         }
@@ -235,7 +227,9 @@
     }
 
     private void resetFailedAttempts() {
-        if (DEBUG) Slog.v(TAG, "Reset fingerprint lockout");
+        if (DEBUG && inLockoutMode()) {
+            Slog.v(TAG, "Reset fingerprint lockout");
+        }
         mFailedAttempts = 0;
     }
 
@@ -283,19 +277,21 @@
 
     private void stopPendingOperations() {
         if (mEnrollClient != null) {
-            stopEnrollment(mEnrollClient.token);
+            stopEnrollment(mEnrollClient.token, true);
         }
         if (mAuthClient != null) {
-            stopAuthentication(mAuthClient.token);
+            stopAuthentication(mAuthClient.token, true);
         }
         // mRemoveClient is allowed to continue
     }
 
-    void stopEnrollment(IBinder token) {
+    void stopEnrollment(IBinder token, boolean notify) {
         final ClientMonitor client = mEnrollClient;
         if (client == null || client.token != token) return;
         int result = nativeStopEnrollment();
-        client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        if (notify) {
+            client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
         removeClient(mEnrollClient);
         if (result != 0) {
             Slog.w(TAG, "startEnrollCancel failed, result=" + result);
@@ -321,11 +317,13 @@
         }
     }
 
-    void stopAuthentication(IBinder token) {
+    void stopAuthentication(IBinder token, boolean notify) {
         final ClientMonitor client = mAuthClient;
         if (client == null || client.token != token) return;
         int result = nativeStopAuthentication();
-        client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        if (notify) {
+            client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
         removeClient(mAuthClient);
         if (result != 0) {
             Slog.w(TAG, "stopAuthentication failed, result=" + result);
@@ -408,74 +406,89 @@
             }
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendRemoved(int fingerId, int groupId) {
             IFingerprintServiceReceiver rx = receiver.get();
-            if (rx != null) {
-                try {
-                    rx.onRemoved(mHalDeviceId, fingerId, groupId);
-                    return true;
-                } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendRemoved:", e);
-                }
+            if (rx == null) return true; // client not listening
+            try {
+                rx.onRemoved(mHalDeviceId, fingerId, groupId);
+                return fingerId == 0;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to notify Removed:", e);
             }
-            removeClient(this);
             return false;
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
             IFingerprintServiceReceiver rx = receiver.get();
-            if (rx != null) {
-                try {
-                    rx.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
-                    return true;
-                } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendEnrollResult:", e);
-                }
+            if (rx == null) return true; // client not listening
+            try {
+                rx.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
+                return remaining == 0;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to notify EnrollResult:", e);
+                return true;
             }
-            removeClient(this);
-            return false;
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendAuthenticated(int fpId, int groupId) {
             IFingerprintServiceReceiver rx = receiver.get();
+            boolean result = false;
             if (rx != null) {
                 try {
                     rx.onAuthenticated(mHalDeviceId, fpId, groupId);
-                    return true;
                 } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendProcessed:", e);
+                    Slog.w(TAG, "Failed to notify Authenticated:", e);
+                    result = true; // client failed
                 }
+            } else {
+                result = true; // client not listening
             }
-            removeClient(this);
-            return false;
+            if (fpId <= 0) {
+                result |= handleFailedAttempt(this);
+            } else {
+                result |= true; // we have a valid fingerprint
+                mLockoutReset.run();
+            }
+            return result;
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendAcquired(int acquiredInfo) {
             IFingerprintServiceReceiver rx = receiver.get();
-            if (rx != null) {
-                try {
-                    rx.onAcquired(mHalDeviceId, acquiredInfo);
-                    return true;
-                } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendAcquired:", e);
-                }
+            if (rx == null) return true; // client not listening
+            try {
+                rx.onAcquired(mHalDeviceId, acquiredInfo);
+                return false; // acquisition continues...
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to invoke sendAcquired:", e);
+                return true; // client failed
             }
-            removeClient(this);
-            return false;
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendError(int error) {
             IFingerprintServiceReceiver rx = receiver.get();
             if (rx != null) {
                 try {
                     rx.onError(mHalDeviceId, error);
-                    return true;
                 } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendError:", e);
+                    Slog.w(TAG, "Failed to invoke sendError:", e);
                 }
             }
-            removeClient(this);
-            return false;
+            return true; // errors always terminate progress
         }
     }
 
@@ -507,7 +520,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    stopEnrollment(token);
+                    stopEnrollment(token, true);
                 }
             });
         }
@@ -533,7 +546,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    stopAuthentication(token);
+                    stopAuthentication(token, true);
                 }
             });
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 964dcc2..f29d524 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -709,6 +709,11 @@
     private WindowContentFrameStats mTempWindowRenderStats;
 
     final class DragInputEventReceiver extends InputEventReceiver {
+        // Set, if stylus button was down at the start of the drag.
+        private boolean mStylusButtonDownAtStart;
+        // Indicates the first event to check for button state.
+        private boolean mIsStartEvent = true;
+
         public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
         }
@@ -724,6 +729,18 @@
                     boolean endDrag = false;
                     final float newX = motionEvent.getRawX();
                     final float newY = motionEvent.getRawY();
+                    final boolean isStylusButtonDown =
+                            (motionEvent.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS)
+                            && (motionEvent.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0;
+
+                    if (mIsStartEvent) {
+                        if (isStylusButtonDown) {
+                            // First event and the button was down, check for the button being
+                            // lifted in the future, if that happens we'll drop the item.
+                            mStylusButtonDownAtStart = true;
+                        }
+                        mIsStartEvent = false;
+                    }
 
                     switch (motionEvent.getAction()) {
                     case MotionEvent.ACTION_DOWN: {
@@ -733,9 +750,17 @@
                     } break;
 
                     case MotionEvent.ACTION_MOVE: {
-                        synchronized (mWindowMap) {
-                            // move the surface and tell the involved window(s) where we are
-                            mDragState.notifyMoveLw(newX, newY);
+                        if (mStylusButtonDownAtStart && !isStylusButtonDown) {
+                            if (DEBUG_DRAG) Slog.d(TAG, "Button no longer pressed; dropping at "
+                                    + newX + "," + newY);
+                            synchronized (mWindowMap) {
+                                endDrag = mDragState.notifyDropLw(newX, newY);
+                            }
+                        } else {
+                            synchronized (mWindowMap) {
+                                // move the surface and tell the involved window(s) where we are
+                                mDragState.notifyMoveLw(newX, newY);
+                            }
                         }
                     } break;
 
@@ -759,6 +784,8 @@
                         synchronized (mWindowMap) {
                             mDragState.endDragLw();
                         }
+                        mStylusButtonDownAtStart = false;
+                        mIsStartEvent = true;
                     }
 
                     handled = true;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 7c5980a..f3edbd1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -752,7 +752,7 @@
 
 void NativeInputManager::reloadCalibration() {
     mInputManager->getReader()->requestRefreshConfiguration(
-            InputReaderConfiguration::TOUCH_AFFINE_TRANSFORMATION);
+            InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION);
 }
 
 TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(