Merge "Distance based animation duration"
diff --git a/api/current.txt b/api/current.txt
index 0f36163..686a6e8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3982,6 +3982,48 @@
     field public java.lang.String serviceDetails;
   }
 
+  public final class AssistAction {
+    method public static void updateAssistData(android.os.Bundle, android.os.Bundle);
+    field public static final java.lang.String ASSIST_ACTION_KEY = "android:assist_action";
+    field public static final java.lang.String KEY_ACTION_OBJECT = "object";
+    field public static final java.lang.String KEY_ACTION_STATUS = "actionStatus";
+    field public static final java.lang.String KEY_DESCRIPTION = "description";
+    field public static final java.lang.String KEY_ID = "@id";
+    field public static final java.lang.String KEY_NAME = "name";
+    field public static final java.lang.String KEY_TYPE = "@type";
+    field public static final java.lang.String KEY_URL = "url";
+    field public static final java.lang.String STATUS_TYPE_ACTIVE = "ActiveActionStatus";
+    field public static final java.lang.String STATUS_TYPE_COMPLETED = "CompletedActionStatus";
+    field public static final java.lang.String TYPE_ADD_ACTION = "AddAction";
+    field public static final java.lang.String TYPE_BOOKMARK_ACTION = "BookmarkAction";
+    field public static final java.lang.String TYPE_LIKE_ACTION = "LikeAction";
+    field public static final java.lang.String TYPE_LISTEN_ACTION = "ListenAction";
+    field public static final java.lang.String TYPE_VIEW_ACTION = "ViewAction";
+    field public static final java.lang.String TYPE_WANT_ACTION = "WantAction";
+    field public static final java.lang.String TYPE_WATCH_ACTION = "WatchAction";
+  }
+
+  public static final class AssistAction.ActionBuilder {
+    ctor public AssistAction.ActionBuilder();
+    method public android.os.Bundle build();
+    method public android.app.AssistAction.ActionBuilder set(java.lang.String, java.lang.String);
+    method public android.app.AssistAction.ActionBuilder set(java.lang.String, android.os.Bundle);
+    method public android.app.AssistAction.ActionBuilder setObject(android.os.Bundle);
+    method public android.app.AssistAction.ActionBuilder setType(java.lang.String);
+  }
+
+  public static final class AssistAction.ThingBuilder {
+    ctor public AssistAction.ThingBuilder();
+    method public android.os.Bundle build();
+    method public android.app.AssistAction.ThingBuilder set(java.lang.String, java.lang.String);
+    method public android.app.AssistAction.ThingBuilder set(java.lang.String, android.os.Bundle);
+    method public android.app.AssistAction.ThingBuilder setDescription(java.lang.String);
+    method public android.app.AssistAction.ThingBuilder setId(java.lang.String);
+    method public android.app.AssistAction.ThingBuilder setName(java.lang.String);
+    method public android.app.AssistAction.ThingBuilder setType(java.lang.String);
+    method public android.app.AssistAction.ThingBuilder setUrl(android.net.Uri);
+  }
+
   public class AssistContent implements android.os.Parcelable {
     ctor public AssistContent();
     method public int describeContents();
@@ -9173,6 +9215,7 @@
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
     field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad";
+    field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
     field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
     field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
     field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
@@ -15286,6 +15329,7 @@
     method public static final boolean isCryptoSchemeSupported(java.util.UUID);
     method public final void release();
     method public final boolean requiresSecureDecoderComponent(java.lang.String);
+    method public final void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException;
   }
 
   public final class MediaCryptoException extends java.lang.Exception {
@@ -15488,6 +15532,7 @@
     field public static final java.lang.String KEY_MAX_INPUT_SIZE = "max-input-size";
     field public static final java.lang.String KEY_MAX_WIDTH = "max-width";
     field public static final java.lang.String KEY_MIME = "mime";
+    field public static final java.lang.String KEY_OPERATING_RATE = "operating-rate";
     field public static final java.lang.String KEY_PRIORITY = "priority";
     field public static final java.lang.String KEY_PROFILE = "profile";
     field public static final java.lang.String KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
diff --git a/api/system-current.txt b/api/system-current.txt
index 60a95e3..9a637ed 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4073,6 +4073,48 @@
     field public java.lang.String serviceDetails;
   }
 
+  public final class AssistAction {
+    method public static void updateAssistData(android.os.Bundle, android.os.Bundle);
+    field public static final java.lang.String ASSIST_ACTION_KEY = "android:assist_action";
+    field public static final java.lang.String KEY_ACTION_OBJECT = "object";
+    field public static final java.lang.String KEY_ACTION_STATUS = "actionStatus";
+    field public static final java.lang.String KEY_DESCRIPTION = "description";
+    field public static final java.lang.String KEY_ID = "@id";
+    field public static final java.lang.String KEY_NAME = "name";
+    field public static final java.lang.String KEY_TYPE = "@type";
+    field public static final java.lang.String KEY_URL = "url";
+    field public static final java.lang.String STATUS_TYPE_ACTIVE = "ActiveActionStatus";
+    field public static final java.lang.String STATUS_TYPE_COMPLETED = "CompletedActionStatus";
+    field public static final java.lang.String TYPE_ADD_ACTION = "AddAction";
+    field public static final java.lang.String TYPE_BOOKMARK_ACTION = "BookmarkAction";
+    field public static final java.lang.String TYPE_LIKE_ACTION = "LikeAction";
+    field public static final java.lang.String TYPE_LISTEN_ACTION = "ListenAction";
+    field public static final java.lang.String TYPE_VIEW_ACTION = "ViewAction";
+    field public static final java.lang.String TYPE_WANT_ACTION = "WantAction";
+    field public static final java.lang.String TYPE_WATCH_ACTION = "WatchAction";
+  }
+
+  public static final class AssistAction.ActionBuilder {
+    ctor public AssistAction.ActionBuilder();
+    method public android.os.Bundle build();
+    method public android.app.AssistAction.ActionBuilder set(java.lang.String, java.lang.String);
+    method public android.app.AssistAction.ActionBuilder set(java.lang.String, android.os.Bundle);
+    method public android.app.AssistAction.ActionBuilder setObject(android.os.Bundle);
+    method public android.app.AssistAction.ActionBuilder setType(java.lang.String);
+  }
+
+  public static final class AssistAction.ThingBuilder {
+    ctor public AssistAction.ThingBuilder();
+    method public android.os.Bundle build();
+    method public android.app.AssistAction.ThingBuilder set(java.lang.String, java.lang.String);
+    method public android.app.AssistAction.ThingBuilder set(java.lang.String, android.os.Bundle);
+    method public android.app.AssistAction.ThingBuilder setDescription(java.lang.String);
+    method public android.app.AssistAction.ThingBuilder setId(java.lang.String);
+    method public android.app.AssistAction.ThingBuilder setName(java.lang.String);
+    method public android.app.AssistAction.ThingBuilder setType(java.lang.String);
+    method public android.app.AssistAction.ThingBuilder setUrl(android.net.Uri);
+  }
+
   public class AssistContent implements android.os.Parcelable {
     ctor public AssistContent();
     method public int describeContents();
@@ -9429,6 +9471,7 @@
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
     field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad";
+    field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
     field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
     field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
     field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
@@ -16495,6 +16538,7 @@
     method public static final boolean isCryptoSchemeSupported(java.util.UUID);
     method public final void release();
     method public final boolean requiresSecureDecoderComponent(java.lang.String);
+    method public final void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException;
   }
 
   public final class MediaCryptoException extends java.lang.Exception {
@@ -16698,6 +16742,7 @@
     field public static final java.lang.String KEY_MAX_INPUT_SIZE = "max-input-size";
     field public static final java.lang.String KEY_MAX_WIDTH = "max-width";
     field public static final java.lang.String KEY_MIME = "mime";
+    field public static final java.lang.String KEY_OPERATING_RATE = "operating-rate";
     field public static final java.lang.String KEY_PRIORITY = "priority";
     field public static final java.lang.String KEY_PROFILE = "profile";
     field public static final java.lang.String KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index b0944ff..275e78e 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1481,6 +1481,12 @@
         anim.mInitialized = false;
         anim.mPlayingState = STOPPED;
         anim.mStartedDelay = false;
+        anim.mStarted = false;
+        anim.mRunning = false;
+        anim.mPaused = false;
+        anim.mResumed = false;
+        anim.mStartListenersCalled = false;
+
         PropertyValuesHolder[] oldValues = mValues;
         if (oldValues != null) {
             int numValues = oldValues.length;
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 2dbbc38..abe12dc 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -22,6 +22,7 @@
 import android.annotation.AttrRes;
 import android.annotation.DrawableRes;
 import android.annotation.StringRes;
+import android.annotation.StyleRes;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
@@ -191,11 +192,11 @@
      *                   {@code context}'s default alert dialog theme
      * @see android.R.styleable#Theme_alertDialogTheme
      */
-    protected AlertDialog(Context context, @AttrRes int themeResId) {
+    protected AlertDialog(Context context, @StyleRes int themeResId) {
         this(context, themeResId, true);
     }
 
-    AlertDialog(Context context, @AttrRes int themeResId, boolean createContextThemeWrapper) {
+    AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
         super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
                 createContextThemeWrapper);
 
@@ -204,9 +205,7 @@
     }
 
     static int resolveDialogTheme(Context context, int themeResId) {
-        if (themeResId == 0) {
-            return 0;
-        } else if (themeResId == THEME_TRADITIONAL) {
+        if (themeResId == THEME_TRADITIONAL) {
             return R.style.Theme_Dialog_Alert;
         } else if (themeResId == THEME_HOLO_DARK) {
             return R.style.Theme_Holo_Dialog_Alert;
diff --git a/core/java/android/app/AssistAction.java b/core/java/android/app/AssistAction.java
new file mode 100644
index 0000000..eb33542
--- /dev/null
+++ b/core/java/android/app/AssistAction.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Helper class for building a {@link Bundle} representing an action being performed by the user,
+ * to be included in the Bundle generated by {@link Activity#onProvideAssistData}.
+ *
+ * @see Activity#onProvideAssistData
+ */
+public final class AssistAction {
+
+    /**
+     * Key name for the Bundle containing the schema.org representation of
+     * an action performed, and should be stored in the Bundle generated by
+     * {@link Activity#onProvideAssistData}.
+     */
+    public static final String ASSIST_ACTION_KEY = "android:assist_action";
+
+    /** Bundle key to specify the schema.org ID of the content. */
+    public static final String KEY_ID = "@id";
+
+    /** Bundle key to specify the schema.org type of the content. */
+    public static final String KEY_TYPE = "@type";
+
+    /** Bundle key to specify the name of the content. */
+    public static final String KEY_NAME = "name";
+
+    /** Bundle key to specify the description of the content. */
+    public static final String KEY_DESCRIPTION = "description";
+
+    /** Bundle key to specify the URL of the content. */
+    public static final String KEY_URL = "url";
+
+    /** Bundle key to specify the object of an action. */
+    public static final String KEY_ACTION_OBJECT = "object";
+
+    /** Bundle key to specify the action's status. */
+    public static final String KEY_ACTION_STATUS = "actionStatus";
+
+    /** The act of editing by adding an object to a collection. */
+    public static final String TYPE_ADD_ACTION = "AddAction";
+
+    /** The act of bookmarking an object. */
+    public static final String TYPE_BOOKMARK_ACTION = "BookmarkAction";
+
+    /** The act of liking an object. */
+    public static final String TYPE_LIKE_ACTION = "LikeAction";
+
+    /** The act of consuming audio content. */
+    public static final String TYPE_LISTEN_ACTION = "ListenAction";
+
+    /** The act of consuming static visual content. */
+    public static final String TYPE_VIEW_ACTION = "ViewAction";
+
+    /** The act of expressing a desire about the object. */
+    public static final String TYPE_WANT_ACTION = "WantAction";
+
+    /** The act of watching an object. */
+    public static final String TYPE_WATCH_ACTION = "WatchAction";
+
+    /** The status of an active action. */
+    public static final String STATUS_TYPE_ACTIVE = "ActiveActionStatus";
+
+    /** The status of a completed action. */
+    public static final String STATUS_TYPE_COMPLETED = "CompletedActionStatus";
+
+    private AssistAction() {
+    }
+
+    /**
+     * Update the Bundle passed into {@link Activity#onProvideAssistData} with the action Bundle,
+     * built with {@link ActionBuilder}.
+     *
+     * @param assistDataBundle The Bundle provided to {@link Activity#onProvideAssistData}.
+     * @param actionBundle The Bundle representing an schema.org action.
+     */
+    public static void updateAssistData(Bundle assistDataBundle, Bundle actionBundle) {
+        Preconditions.checkNotNull(assistDataBundle);
+        Preconditions.checkNotNull(actionBundle);
+
+        Preconditions.checkNotNull(actionBundle.getString(KEY_TYPE),
+                "The '@type' property is required in the provided actionBundle");
+        assistDataBundle.putParcelable(ASSIST_ACTION_KEY, actionBundle);
+    }
+
+    /**
+     * Builds a {@link Bundle} representing a schema.org entity.
+     */
+    public static final class ThingBuilder {
+        private final Bundle mBundle;
+
+        public ThingBuilder() {
+            mBundle = new Bundle();
+        }
+
+        /**
+         * Sets the name of the content.
+         *
+         * @param name The name of the content.
+         */
+        public ThingBuilder setName(@Nullable String name) {
+            set(KEY_NAME, name);
+            return this;
+        }
+
+        /**
+         * Sets the app URI of the content.
+         *
+         * @param uri The app URI of the content.
+         */
+        public ThingBuilder setUrl(@Nullable Uri uri) {
+            if (uri != null) {
+                set(KEY_URL, uri.toString());
+            }
+            return this;
+        }
+
+        /**
+         * Sets the ID of the content.
+         *
+         * @param id Set the ID of the content.
+         */
+        public ThingBuilder setId(@Nullable String id) {
+            set(KEY_ID, id);
+            return this;
+        }
+
+        /**
+        * Sets the schema.org type of the content.
+        *
+        * @param type The schema.org type.
+        */
+        public ThingBuilder setType(@Nullable String type) {
+            set(KEY_TYPE, type);
+            return this;
+        }
+
+        /**
+         * Sets the optional description of the content.
+         *
+         * @param description The description of the content.
+         */
+        public ThingBuilder setDescription(@Nullable String description) {
+            set(KEY_DESCRIPTION, description);
+            return this;
+        }
+
+        /**
+         * Sets a property of the content.
+         *
+         * @param key The schema.org property. Must not be null.
+         * @param value The value of the schema.org property.
+         *              If null, the value will be ignored.
+         */
+        public ThingBuilder set(@NonNull String key, @Nullable String value) {
+            if (value != null) {
+                mBundle.putString(key, value);
+            }
+            return this;
+        }
+
+        /**
+         * Sets a property of the content.
+         *
+         * @param key The schema.org property. Must not be null.
+         * @param value The value of the schema.org property represented as a bundle.
+         *              If null, the value will be ignored.
+         */
+        public ThingBuilder set(@NonNull String key, @Nullable Bundle value) {
+            if (value != null) {
+                mBundle.putParcelable(key, value);
+            }
+            return this;
+        }
+
+        /**
+         * Build the {@link Bundle} object representing the schema.org entity.
+         */
+        public Bundle build() {
+            return mBundle;
+        }
+    }
+
+    /**
+     * Builds a {@link Bundle} representing a schema.org action.
+     */
+    public static final class ActionBuilder {
+        private final Bundle mBundle;
+
+        public ActionBuilder() {
+            mBundle = new Bundle();
+        }
+
+        /**
+         * Sets the schema.org type of the action.
+         *
+         * @param type The schema.org type.
+         */
+        public ActionBuilder setType(@Nullable String type) {
+            set(KEY_TYPE, type);
+            return this;
+        }
+
+        /**
+         * Sets the schema.org object of the action.
+         *
+         * @param object The schema.org object of the action.
+         */
+        public ActionBuilder setObject(@Nullable Bundle object) {
+            set(KEY_ACTION_OBJECT, object);
+            return this;
+        }
+
+        /**
+         * Sets a property of the action.
+         *
+         * @param key The schema.org property. Must not be null.
+         * @param value The value of the schema.org property.
+         *              If null, the value will be ignored.
+         */
+        public ActionBuilder set(@NonNull String key, @Nullable String value) {
+            if (value != null) {
+                mBundle.putString(key, value);
+            }
+            return this;
+        }
+
+        /**
+         * Sets a property of the action.
+         *
+         * @param key The schema.org property. Must not be null.
+         * @param value The value of the schema.org property represented as a bundle.
+         *              If null, the value will be ignored.
+         */
+        public ActionBuilder set(@NonNull String key, @Nullable Bundle value) {
+            if (value != null) {
+                mBundle.putParcelable(key, value);
+            }
+            return this;
+        }
+
+        /**
+         * Build the {@link Bundle} object representing the schema.org action.
+         */
+        public Bundle build() {
+            if (TextUtils.isEmpty(mBundle.getString(KEY_TYPE, null))) {
+                // Defaults to the base action type http://schema.org/Action.
+                setType("Action");
+            }
+
+            return mBundle;
+        }
+    }
+}
diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java
index ace4af7..cb1a3f5 100644
--- a/core/java/android/app/AssistContent.java
+++ b/core/java/android/app/AssistContent.java
@@ -51,7 +51,7 @@
     /**
      * Sets the Intent associated with the content, describing the current top-level context of
      * the activity.  If this contains a reference to a piece of data related to the activity,
-     * be sure to set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} so the accessibilty
+     * be sure to set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} so the accessibility
      * service can access it.
      */
     public void setIntent(Intent intent) {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 6a2d207..786a52f 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -20,9 +20,11 @@
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
+import android.annotation.NonNull;
 import android.annotation.StringRes;
 
 import android.annotation.Nullable;
+import android.annotation.StyleRes;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -140,7 +142,7 @@
      * @param context the context in which the dialog should run
      * @see android.R.styleable#Theme_dialogTheme
      */
-    public Dialog(Context context) {
+    public Dialog(@NonNull Context context) {
         this(context, 0, true);
     }
 
@@ -156,26 +158,26 @@
      * using styles.
      *
      * @param context the context in which the dialog should run
-     * @param theme a style resource describing the theme to use for the
+     * @param themeResId a style resource describing the theme to use for the
      *              window, or {@code 0} to use the default dialog theme
      */
-    public Dialog(Context context, int theme) {
-        this(context, theme, true);
+    public Dialog(@NonNull Context context, @StyleRes int themeResId) {
+        this(context, themeResId, true);
     }
 
-    Dialog(Context context, int theme, boolean createContextThemeWrapper) {
+    Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
         if (createContextThemeWrapper) {
-            if (theme == 0) {
+            if (themeResId == 0) {
                 final TypedValue outValue = new TypedValue();
                 context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
-                theme = outValue.resourceId;
+                themeResId = outValue.resourceId;
             }
-            mContext = new ContextThemeWrapper(context, theme);
+            mContext = new ContextThemeWrapper(context, themeResId);
         } else {
             mContext = context;
         }
 
-        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
 
         final Window w = new PhoneWindow(mContext);
         mWindow = w;
@@ -192,14 +194,13 @@
      * @hide
      */
     @Deprecated
-    protected Dialog(Context context, boolean cancelable,
-            Message cancelCallback) {
+    protected Dialog(@NonNull Context context, boolean cancelable, Message cancelCallback) {
         this(context);
         mCancelable = cancelable;
         mCancelMessage = cancelCallback;
     }
 
-    protected Dialog(Context context, boolean cancelable,
+    protected Dialog(@NonNull Context context, boolean cancelable,
             OnCancelListener cancelListener) {
         this(context);
         mCancelable = cancelable;
@@ -211,6 +212,7 @@
      * 
      * @return Context The Context used by the Dialog.
      */
+    @NonNull
     public final Context getContext() {
         return mContext;
     }
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 93ea299..5dbfa6a 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -36,7 +36,7 @@
 
 /**
  * This class provides methods to perform scan related operations for Bluetooth LE devices. An
- * application can scan for a particular type of Bluetotoh LE devices using {@link ScanFilter}. It
+ * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It
  * can also request different types of callbacks for delivering the result.
  * <p>
  * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 303b709..d5461eba 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1318,6 +1318,15 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports high fidelity sensor processing
+     * capabilities.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_HIFI_SENSORS =
+            "android.hardware.sensor.hifi_sensors";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a telephony radio with data
      * communication support.
      */
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1566985..c7edb1a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1947,6 +1947,13 @@
     public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_POWER_DRAIN + 1;
 
     /**
+     * Returns true if the BatteryStats object has detailed bluetooth power reports.
+     * When true, calling {@link #getBluetoothControllerActivity(int, int)} will yield the
+     * actual power data.
+     */
+    public abstract boolean hasBluetoothActivityReporting();
+
+    /**
      * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
      * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
      * respective state.
@@ -1956,6 +1963,13 @@
     public abstract long getBluetoothControllerActivity(int type, int which);
 
     /**
+     * Returns true if the BatteryStats object has detailed WiFi power reports.
+     * When true, calling {@link #getWifiControllerActivity(int, int)} will yield the
+     * actual power data.
+     */
+    public abstract boolean hasWifiActivityReporting();
+
+    /**
      * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
      * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
      * respective state.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index dc5770b..e16691c 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -96,18 +96,9 @@
     public static final int KM_MODE_FIRST_UNAUTHENTICATED = 1;
     public static final int KM_MODE_ECB = KM_MODE_FIRST_UNAUTHENTICATED;
     public static final int KM_MODE_CBC = 2;
-    public static final int KM_MODE_CBC_CTS = 3;
     public static final int KM_MODE_CTR = 4;
-    public static final int KM_MODE_OFB = 5;
-    public static final int KM_MODE_CFB = 6;
-    public static final int KM_MODE_XTS = 7;
     public static final int KM_MODE_FIRST_AUTHENTICATED = 32;
     public static final int KM_MODE_GCM = KM_MODE_FIRST_AUTHENTICATED;
-    public static final int KM_MODE_OCB = 33;
-    public static final int KM_MODE_CCM = 34;
-    public static final int KM_MODE_FIRST_MAC = 128;
-    public static final int KM_MODE_CMAC = KM_MODE_FIRST_MAC;
-    public static final int KM_MODE_POLY1305 = 129;
 
     // Padding modes.
     public static final int KM_PAD_NONE = 1;
@@ -115,11 +106,7 @@
     public static final int KM_PAD_RSA_PSS = 3;
     public static final int KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4;
     public static final int KM_PAD_RSA_PKCS1_1_5_SIGN = 5;
-    public static final int KM_PAD_ANSI_X923 = 32;
-    public static final int KM_PAD_ISO_10126 = 33;
-    public static final int KM_PAD_ZERO = 64;
-    public static final int KM_PAD_PKCS7 = 65;
-    public static final int KM_PAD_ISO_7816_4 = 66;
+    public static final int KM_PAD_PKCS7 = 64;
 
     // Digest modes.
     public static final int KM_DIGEST_NONE = 0;
@@ -129,9 +116,6 @@
     public static final int KM_DIGEST_SHA_2_256 = 4;
     public static final int KM_DIGEST_SHA_2_384 = 5;
     public static final int KM_DIGEST_SHA_2_512 = 6;
-    public static final int KM_DIGEST_SHA_3_256 = 7;
-    public static final int KM_DIGEST_SHA_3_384 = 8;
-    public static final int KM_DIGEST_SHA_3_512 = 9;
 
     // Key origins.
     public static final int KM_ORIGIN_HARDWARE = 0;
@@ -151,7 +135,6 @@
     // Key formats.
     public static final int KM_KEY_FORMAT_X509 = 0;
     public static final int KM_KEY_FORMAT_PKCS8 = 1;
-    public static final int KM_KEY_FORMAT_PKCS12 = 2;
     public static final int KM_KEY_FORMAT_RAW = 3;
 
     // User authenticators.
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 024b7c5..59dbec6 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -21,7 +21,6 @@
 import android.content.IntentFilter;
 import android.hardware.SensorManager;
 import android.net.ConnectivityManager;
-import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.Uid;
 import android.os.Bundle;
@@ -130,16 +129,11 @@
         return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
     }
 
-    public static boolean checkHasWifiPowerReporting(Context context, PowerProfile profile) {
-        WifiManager manager = context.getSystemService(WifiManager.class);
-        if (manager.isEnhancedPowerReportingSupported()) {
-            if (profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
-                    profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
-                    profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0) {
-                return true;
-            }
-        }
-        return false;
+    public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
+        return stats.hasWifiActivityReporting() &&
+                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
+                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
+                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
     }
 
     public BatteryStatsHelper(Context context) {
@@ -339,7 +333,7 @@
         mMobileRadioPowerCalculator.reset(mStats);
 
         if (mWifiPowerCalculator == null) {
-            if (checkHasWifiPowerReporting(mContext, mPowerProfile)) {
+            if (checkHasWifiPowerReporting(mStats, mPowerProfile)) {
                 mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
             } else {
                 mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c5c0ba6..fbb2dfc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -472,6 +472,8 @@
     private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
 
     private PowerProfile mPowerProfile;
+    private boolean mHasWifiEnergyReporting = false;
+    private boolean mHasBluetoothEnergyReporting = false;
 
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
@@ -4298,6 +4300,10 @@
         return mBluetoothStateTimer[bluetoothState].getCountLocked(which);
     }
 
+    @Override public boolean hasBluetoothActivityReporting() {
+        return mHasBluetoothEnergyReporting;
+    }
+
     @Override public long getBluetoothControllerActivity(int type, int which) {
         if (type >= 0 && type < mBluetoothActivityCounters.length) {
             return mBluetoothActivityCounters[type].getCountLocked(which);
@@ -4305,6 +4311,10 @@
         return 0;
     }
 
+    @Override public boolean hasWifiActivityReporting() {
+        return mHasWifiEnergyReporting;
+    }
+
     @Override public long getWifiControllerActivity(int type, int which) {
         if (type >= 0 && type < mWifiActivityCounters.length) {
             return mWifiActivityCounters[type].getCountLocked(which);
@@ -7567,6 +7577,8 @@
         }
 
         if (info != null) {
+            mHasWifiEnergyReporting = true;
+
             // Measured in mAms
             final long txTimeMs = info.getControllerTxTimeMillis();
             final long rxTimeMs = info.getControllerRxTimeMillis();
@@ -7778,6 +7790,7 @@
      */
     public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
         if (info != null && mOnBatteryInternal && false) {
+            mHasBluetoothEnergyReporting = true;
             mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
                     info.getControllerRxTimeMillis());
             mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
@@ -9533,6 +9546,8 @@
             mWifiActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
         }
 
+        mHasWifiEnergyReporting = in.readInt() != 0;
+        mHasBluetoothEnergyReporting = in.readInt() != 0;
         mNumConnectivityChange = in.readInt();
         mLoadedNumConnectivityChange = in.readInt();
         mUnpluggedNumConnectivityChange = in.readInt();
@@ -9686,6 +9701,8 @@
         for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
             mWifiActivityCounters[i].writeToParcel(out);
         }
+        out.writeInt(mHasWifiEnergyReporting ? 1 : 0);
+        out.writeInt(mHasBluetoothEnergyReporting ? 1 : 0);
         out.writeInt(mNumConnectivityChange);
         out.writeInt(mLoadedNumConnectivityChange);
         out.writeInt(mUnpluggedNumConnectivityChange);
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 4e77f6b..4fb8b55 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -16,12 +16,15 @@
 package com.android.internal.os;
 
 import android.os.BatteryStats;
+import android.util.Log;
 
 /**
  * WiFi power calculator for when BatteryStats supports energy reporting
  * from the WiFi controller.
  */
 public class WifiPowerCalculator extends PowerCalculator {
+    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+    private static final String TAG = "WifiPowerCalculator";
     private final double mIdleCurrentMa;
     private final double mTxCurrentMa;
     private final double mRxCurrentMa;
@@ -75,6 +78,10 @@
                     + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
         }
         app.wifiPowerMah = Math.max(0, powerDrain - mTotalAppPowerDrain);
+
+        if (DEBUG) {
+            Log.d(TAG, "left over WiFi power: " + BatteryStatsHelper.makemAh(app.wifiPowerMah));
+        }
     }
 
     @Override
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 526885f..4c4a39d 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -543,6 +543,11 @@
     return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
 }
 
+jobject decodeBitmap(JNIEnv* env, void* data, size_t size) {
+    SkMemoryStream  stream(data, size);
+    return doDecode(env, &stream, NULL, NULL);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static JNINativeMethod gMethods[] = {
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
index a54da43..22a955f 100644
--- a/core/jni/android/graphics/BitmapFactory.h
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -21,4 +21,6 @@
 
 jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
 
+jobject decodeBitmap(JNIEnv* env, void* data, size_t size);
+
 #endif  // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp
index 655b400..e9f18a6 100644
--- a/core/jni/android_emoji_EmojiFactory.cpp
+++ b/core/jni/android_emoji_EmojiFactory.cpp
@@ -5,6 +5,7 @@
 #include <utils/Log.h>
 #include <ScopedUtfChars.h>
 
+#include "BitmapFactory.h"
 #include "EmojiFactory.h"
 #include "GraphicsJNI.h"
 #include <nativehelper/JNIHelp.h>
@@ -164,14 +165,7 @@
     return NULL;
   }
 
-  SkBitmap *bitmap = new SkBitmap;
-  if (!SkImageDecoder::DecodeMemory(bytes, size, bitmap)) {
-    ALOGE("SkImageDecoder::DecodeMemory() failed.");
-    return NULL;
-  }
-
-  return GraphicsJNI::createBitmap(env, bitmap,
-      GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
+  return decodeBitmap(env, (void*)bytes, size);
 }
 
 static void android_emoji_EmojiFactory_destructor(
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fa86a45..453cb74 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3021,6 +3021,12 @@
         android:description="@string/permdesc_accessVoiceInteractionService"
         android:label="@string/permlab_accessVoiceInteractionService" />
 
+    <!-- Allows an app that has this permission and a permissions to install packages
+         to request all runtime permissions to be granted at installation.
+     @hide -->
+    <permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"
+        android:protectionLevel="signature" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 78eee37..05a81de 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -163,7 +163,6 @@
         inflateLayers(r, parser, attrs, theme);
 
         ensurePadding();
-        onStateChange(getState());
     }
 
     /**
@@ -307,7 +306,6 @@
         }
 
         ensurePadding();
-        onStateChange(getState());
     }
 
     @Override
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index c9a140c..6e3f8be 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -798,7 +798,7 @@
         // TODO: Verify we have an RSA public key that's well formed.
     }
 
-    public void testAesOcbEncryptSuccess() throws Exception {
+    public void testAesGcmEncryptSuccess() throws Exception {
         String name = "test";
         KeymasterArguments args = new KeymasterArguments();
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
@@ -806,7 +806,7 @@
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
-        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
         args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
         args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
         args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
@@ -903,9 +903,7 @@
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
-        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
-        args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
-        args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
         args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
@@ -935,11 +933,9 @@
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
-        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
-        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
-        args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
-        args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
         args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1);
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
diff --git a/media/java/android/media/MediaCrypto.java b/media/java/android/media/MediaCrypto.java
index c7c3fc2..da81b37 100644
--- a/media/java/android/media/MediaCrypto.java
+++ b/media/java/android/media/MediaCrypto.java
@@ -70,6 +70,20 @@
      */
     public final native boolean requiresSecureDecoderComponent(String mime);
 
+    /**
+     * Associate a MediaDrm session with this MediaCrypto instance.  The
+     * MediaDrm session is used to securely load decryption keys for a
+     * crypto scheme.  The crypto keys loaded through the MediaDrm session
+     * may be selected for use during the decryption operation performed
+     * by {@link android.media.MediaCodec#queueSecureInputBuffer} by specifying
+     * their key ids in the {@link android.media.MediaCodec.CryptoInfo#key} field.
+     * @param sessionId the MediaDrm sessionId to associate with this
+     * MediaCrypto instance
+     * @throws MediaCryptoException on failure to set the sessionId
+     */
+    public final native void setMediaDrmSession(byte[] sessionId)
+        throws MediaCryptoException;
+
     @Override
     protected void finalize() {
         native_finalize();
diff --git a/media/java/android/media/MediaCryptoException.java b/media/java/android/media/MediaCryptoException.java
index 44c5222..703e96f 100644
--- a/media/java/android/media/MediaCryptoException.java
+++ b/media/java/android/media/MediaCryptoException.java
@@ -17,8 +17,8 @@
 package android.media;
 
 /**
- * Exception thrown if MediaCrypto object could not be instantiated for
- * whatever reason.
+ * Exception thrown if MediaCrypto object could not be instantiated or
+ * if unable to perform an operation on the MediaCrypto object.
  */
 public final class MediaCryptoException extends Exception {
     public MediaCryptoException(String detailMessage) {
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 0c1c7e9..726622f 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -439,6 +439,22 @@
     public static final String KEY_PRIORITY = "priority";
 
     /**
+     * A key describing the desired operating frame rate for video or sample rate for audio
+     * that the codec will need to operate at.
+     * <p>
+     * The associated value is an integer or a float representing frames-per-second or
+     * samples-per-second
+     * <p>
+     * This is used for cases like high-speed/slow-motion video capture, where the video encoder
+     * format contains the target playback rate (e.g. 30fps), but the component must be able to
+     * handle the high operating capture rate (e.g. 240fps).
+     * <p>
+     * This rate will be used by codec for resource planning and setting the operating points.
+     *
+     */
+    public static final String KEY_OPERATING_RATE = "operating-rate";
+
+    /**
      * A key describing the desired profile to be used by an encoder.
      * Constants are declared in {@link MediaCodecInfo.CodecProfileLevel}.
      * This key is only supported for codecs that specify a profile.
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index d2216fb..a9accb02 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -140,6 +140,15 @@
     return jcrypto->mCrypto;
 }
 
+// JNI conversion utilities
+static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) {
+    Vector<uint8_t> vector;
+    size_t length = env->GetArrayLength(byteArray);
+    vector.insertAt((size_t)0, length);
+    env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
+    return vector;
+}
+
 }  // namespace android
 
 using namespace android;
@@ -274,6 +283,37 @@
     return result ? JNI_TRUE : JNI_FALSE;
 }
 
+static void android_media_MediaCrypto_setMediaDrmSession(
+        JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
+    if (jsessionId == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    sp<ICrypto> crypto = JCrypto::GetCrypto(env, thiz);
+
+    if (crypto == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
+
+    status_t err = crypto->setMediaDrmSession(sessionId);
+
+    String8 msg("setMediaDrmSession failed");
+    if (err == ERROR_DRM_SESSION_NOT_OPENED) {
+        msg += ": session not opened";
+    } else if (err == ERROR_UNSUPPORTED) {
+        msg += ": not supported by this crypto scheme";
+    } else if (err == NO_INIT) {
+        msg += ": crypto plugin not initialized";
+    } else if (err != OK) {
+        msg.appendFormat(": general failure (%d)", err);
+    }
+    jniThrowException(env, "android/media/MediaCryptoException", msg.string());
+}
+
 static JNINativeMethod gMethods[] = {
     { "release", "()V", (void *)android_media_MediaCrypto_release },
     { "native_init", "()V", (void *)android_media_MediaCrypto_native_init },
@@ -289,6 +329,9 @@
 
     { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
       (void *)android_media_MediaCrypto_requiresSecureDecoderComponent },
+
+    { "setMediaDrmSession", "([B)V",
+      (void *)android_media_MediaCrypto_setMediaDrmSession },
 };
 
 int register_android_media_Crypto(JNIEnv *env) {
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 310ccf0..3ca239a 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -123,5 +123,7 @@
       <item quantity="one">Copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
       <item quantity="other">Copying <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
     </plurals>
+    <!-- Text shown on the copy notification while DocumentsUI performs setup in preparation for copying files [CHAR LIMIT=32] -->
+    <string name="copy_preparing">Preparing for copy\u2026</string>
 
 </resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index f135af49b..c826aba 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -16,19 +16,24 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
 import android.app.IntentService;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.ContentResolver;
+import android.content.ContentProviderClient;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.net.Uri;
 import android.os.CancellationSignal;
-import android.os.Environment;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
 import android.text.format.DateUtils;
 import android.util.Log;
 
@@ -36,12 +41,13 @@
 
 import libcore.io.IoUtils;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.text.NumberFormat;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
 
 public class CopyService extends IntentService {
     public static final String TAG = "CopyService";
@@ -56,6 +62,7 @@
     private volatile boolean mIsCancelled;
     // Parameters of the copy job. Requests to an IntentService are serialized so this code only
     // needs to deal with one job at a time.
+    private final List<Uri> mFailedFiles;
     private long mBatchSize;
     private long mBytesCopied;
     private long mStartTime;
@@ -65,9 +72,15 @@
     private long mSampleTime;
     private long mSpeed;
     private long mRemainingTime;
+    // Provider clients are acquired for the duration of each copy job. Note that there is an
+    // implicit assumption that all srcs come from the same authority.
+    private ContentProviderClient mSrcClient;
+    private ContentProviderClient mDstClient;
 
     public CopyService() {
         super("CopyService");
+
+        mFailedFiles = new ArrayList<Uri>();
     }
 
     @Override
@@ -88,27 +101,34 @@
         ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
         Uri destinationUri = intent.getData();
 
-        setupCopyJob(srcs, destinationUri);
+        try {
+            // Acquire content providers.
+            mSrcClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(),
+                    srcs.get(0).authority);
+            mDstClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(),
+                    destinationUri.getAuthority());
 
-        ArrayList<String> failedFilenames = new ArrayList<String>();
-        for (int i = 0; i < srcs.size() && !mIsCancelled; ++i) {
-            DocumentInfo src = srcs.get(i);
-            try {
-                copyFile(src, destinationUri);
-            } catch (IOException e) {
-                Log.e(TAG, "Failed to copy " + src.displayName, e);
-                failedFilenames.add(src.displayName);
+            setupCopyJob(srcs, destinationUri);
+
+            for (int i = 0; i < srcs.size() && !mIsCancelled; ++i) {
+                copy(srcs.get(i), destinationUri);
             }
+        } catch (Exception e) {
+            // Catch-all to prevent any copy errors from wedging the app.
+            Log.e(TAG, "Exceptions occurred during copying", e);
+        } finally {
+            ContentProviderClient.releaseQuietly(mSrcClient);
+            ContentProviderClient.releaseQuietly(mDstClient);
+
+            // Dismiss the ongoing copy notification when the copy is done.
+            mNotificationManager.cancel(mJobId, 0);
+
+            if (mFailedFiles.size() > 0) {
+                // TODO: Display a notification when an error has occurred.
+            }
+
+            // TODO: Display a toast if the copy was cancelled.
         }
-
-        if (failedFilenames.size() > 0) {
-            // TODO: Display a notification when an error has occurred.
-        }
-
-        // Dismiss the ongoing copy notification when the copy is done.
-        mNotificationManager.cancel(mJobId, 0);
-
-        // TODO: Display a toast if the copy was cancelled.
     }
 
     @Override
@@ -123,8 +143,10 @@
      *
      * @param srcs A list of src files to copy.
      * @param destinationUri The URI of the destination directory.
+     * @throws RemoteException
      */
-    private void setupCopyJob(ArrayList<DocumentInfo> srcs, Uri destinationUri) {
+    private void setupCopyJob(ArrayList<DocumentInfo> srcs, Uri destinationUri)
+            throws RemoteException {
         // Create an ID for this copy job. Use the timestamp.
         mJobId = String.valueOf(SystemClock.elapsedRealtime());
         // Reset the cancellation flag.
@@ -144,13 +166,13 @@
         // TODO: Add a content intent to open the destination folder.
 
         // Send an initial progress notification.
+        mProgressBuilder.setProgress(0, 0, true); // Indeterminate progress while setting up.
+        mProgressBuilder.setContentText(getString(R.string.copy_preparing));
         mNotificationManager.notify(mJobId, 0, mProgressBuilder.build());
 
         // Reset batch parameters.
-        mBatchSize = 0;
-        for (DocumentInfo doc : srcs) {
-            mBatchSize += doc.size;
-        }
+        mFailedFiles.clear();
+        mBatchSize = calculateFileSizes(srcs);
         mBytesCopied = 0;
         mStartTime = SystemClock.elapsedRealtime();
         mLastNotificationTime = 0;
@@ -165,6 +187,66 @@
     }
 
     /**
+     * Calculates the cumulative size of all the documents in the list. Directories are recursed
+     * into and totaled up.
+     *
+     * @param srcs
+     * @return Size in bytes.
+     * @throws RemoteException
+     */
+    private long calculateFileSizes(List<DocumentInfo> srcs) throws RemoteException {
+        long result = 0;
+        for (DocumentInfo src : srcs) {
+            if (Document.MIME_TYPE_DIR.equals(src.mimeType)) {
+                // Directories need to be recursed into.
+                result += calculateFileSizesHelper(src.derivedUri);
+            } else {
+                result += src.size;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Calculates (recursively) the cumulative size of all the files under the given directory.
+     *
+     * @throws RemoteException
+     */
+    private long calculateFileSizesHelper(Uri uri) throws RemoteException {
+        final String authority = uri.getAuthority();
+        final Uri queryUri = DocumentsContract.buildChildDocumentsUri(authority,
+                DocumentsContract.getDocumentId(uri));
+        final String queryColumns[] = new String[] {
+                Document.COLUMN_DOCUMENT_ID,
+                Document.COLUMN_MIME_TYPE,
+                Document.COLUMN_SIZE
+        };
+
+        long result = 0;
+        Cursor cursor = null;
+        try {
+            cursor = mSrcClient.query(queryUri, queryColumns, null, null, null);
+            while (cursor.moveToNext()) {
+                if (Document.MIME_TYPE_DIR.equals(
+                        getCursorString(cursor, Document.COLUMN_MIME_TYPE))) {
+                    // Recurse into directories.
+                    final Uri subdirUri = DocumentsContract.buildDocumentUri(authority,
+                            getCursorString(cursor, Document.COLUMN_DOCUMENT_ID));
+                    result += calculateFileSizesHelper(subdirUri);
+                } else {
+                    // This may return -1 if the size isn't defined. Ignore those cases.
+                    long size = getCursorLong(cursor, Document.COLUMN_SIZE);
+                    result += size > 0 ? size : 0;
+                }
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+
+        return result;
+    }
+
+    /**
      * Cancels the current copy job, if its ID matches the given ID.
      *
      * @param intent The cancellation intent.
@@ -173,7 +255,7 @@
         final String cancelledId = intent.getStringExtra(EXTRA_CANCEL);
         // Do nothing if the cancelled ID doesn't match the current job ID. This prevents racey
         // cancellation requests from affecting unrelated copy jobs.
-        if (java.util.Objects.equals(mJobId, cancelledId)) {
+        if (Objects.equals(mJobId, cancelledId)) {
             // Set the cancel flag. This causes the copy loops to exit.
             mIsCancelled = true;
             // Dismiss the progress notification here rather than in the copy loop. This preserves
@@ -237,21 +319,78 @@
     }
 
     /**
-     * Copies a file to a given location.
+     * Copies a the given documents to the given location.
      *
-     * @param srcInfo The source file.
-     * @param destinationUri The URI of the destination directory.
-     * @throws IOException
+     * @param srcInfo DocumentInfos for the documents to copy.
+     * @param dstDirUri The URI of the destination directory.
+     * @throws RemoteException
      */
-    private void copyFile(DocumentInfo srcInfo, Uri destinationUri) throws IOException {
-        final Context context = getApplicationContext();
-        final ContentResolver resolver = context.getContentResolver();
-
-        final Uri writableDstUri = DocumentsContract.buildDocumentUriUsingTree(destinationUri,
-                DocumentsContract.getTreeDocumentId(destinationUri));
-        final Uri dstFileUri = DocumentsContract.createDocument(resolver, writableDstUri,
+    private void copy(DocumentInfo srcInfo, Uri dstDirUri) throws RemoteException {
+        final Uri dstUri = DocumentsContract.createDocument(mDstClient, dstDirUri,
                 srcInfo.mimeType, srcInfo.displayName);
+        if (dstUri == null) {
+            // If this is a directory, the entire subdir will not be copied over.
+            Log.e(TAG, "Error while copying " + srcInfo.displayName);
+            mFailedFiles.add(srcInfo.derivedUri);
+            return;
+        }
 
+        if (Document.MIME_TYPE_DIR.equals(srcInfo.mimeType)) {
+            copyDirectoryHelper(srcInfo.derivedUri, dstUri);
+        } else {
+            copyFileHelper(srcInfo.derivedUri, dstUri);
+        }
+    }
+
+    /**
+     * Handles recursion into a directory and copying its contents. Note that in linux terms, this
+     * does the equivalent of "cp src/* dst", not "cp -r src dst".
+     *
+     * @param srcDirUri URI of the directory to copy from. The routine will copy the directory's
+     *            contents, not the directory itself.
+     * @param dstDirUri URI of the directory to copy to. Must be created beforehand.
+     * @throws RemoteException
+     */
+    private void copyDirectoryHelper(Uri srcDirUri, Uri dstDirUri) throws RemoteException {
+        // Recurse into directories. Copy children into the new subdirectory.
+        final String queryColumns[] = new String[] {
+                Document.COLUMN_DISPLAY_NAME,
+                Document.COLUMN_DOCUMENT_ID,
+                Document.COLUMN_MIME_TYPE,
+                Document.COLUMN_SIZE
+        };
+        final Uri queryUri = DocumentsContract.buildChildDocumentsUri(srcDirUri.getAuthority(),
+                DocumentsContract.getDocumentId(srcDirUri));
+        Cursor cursor = null;
+        try {
+            // Iterate over srcs in the directory; copy to the destination directory.
+            cursor = mSrcClient.query(queryUri, queryColumns, null, null, null);
+            while (cursor.moveToNext()) {
+                final String childMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+                final Uri dstUri = DocumentsContract.createDocument(mDstClient, dstDirUri,
+                        childMimeType, getCursorString(cursor, Document.COLUMN_DISPLAY_NAME));
+                final Uri childUri = DocumentsContract.buildDocumentUri(srcDirUri.getAuthority(),
+                        getCursorString(cursor, Document.COLUMN_DOCUMENT_ID));
+                if (Document.MIME_TYPE_DIR.equals(childMimeType)) {
+                    copyDirectoryHelper(childUri, dstUri);
+                } else {
+                    copyFileHelper(childUri, dstUri);
+                }
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+    }
+
+    /**
+     * Handles copying a single file.
+     *
+     * @param srcUri URI of the file to copy from.
+     * @param dstUri URI of the *file* to copy to. Must be created beforehand.
+     * @throws RemoteException
+     */
+    private void copyFileHelper(Uri srcUri, Uri dstUri) throws RemoteException {
+        // Copy an individual file.
         CancellationSignal canceller = new CancellationSignal();
         ParcelFileDescriptor srcFile = null;
         ParcelFileDescriptor dstFile = null;
@@ -260,8 +399,8 @@
 
         boolean errorOccurred = false;
         try {
-            srcFile = resolver.openFileDescriptor(srcInfo.derivedUri, "r", canceller);
-            dstFile = resolver.openFileDescriptor(dstFileUri, "w", canceller);
+            srcFile = mSrcClient.openFile(srcUri, "r", canceller);
+            dstFile = mDstClient.openFile(dstUri, "w", canceller);
             src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
             dst = new ParcelFileDescriptor.AutoCloseOutputStream(dstFile);
 
@@ -275,7 +414,8 @@
             dstFile.checkError();
         } catch (IOException e) {
             errorOccurred = true;
-            Log.e(TAG, "Error while copying " + srcInfo.displayName, e);
+            Log.e(TAG, "Error while copying " + srcUri.toString(), e);
+            mFailedFiles.add(srcUri);
         } finally {
             // This also ensures the file descriptors are closed.
             IoUtils.closeQuietly(src);
@@ -285,8 +425,13 @@
         if (errorOccurred || mIsCancelled) {
             // Clean up half-copied files.
             canceller.cancel();
-            if (!DocumentsContract.deleteDocument(resolver, dstFileUri)) {
-                Log.w(TAG, "Failed to clean up: " + srcInfo.displayName);
+            try {
+                DocumentsContract.deleteDocument(mDstClient, dstUri);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to clean up: " + srcUri, e);
+                // RemoteExceptions usually signal that the connection is dead, so there's no point
+                // attempting to continue. Propagate the exception up so the copy job is cancelled.
+                throw e;
             }
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 83071bd..0e3016d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -357,7 +357,12 @@
             return;
         }
 
-        Uri destination = data.getData();
+        // Because the destination picker is launched using an open tree intent, the URI returned is
+        // a tree URI. Convert it to a document URI.
+        // TODO: Remove this step when the destination picker returns a document URI.
+        final Uri destinationTree = data.getData();
+        final Uri destination = DocumentsContract.buildDocumentUriUsingTree(destinationTree,
+                DocumentsContract.getTreeDocumentId(destinationTree));
 
         List<DocumentInfo> docs = mSelectedDocumentsForCopy;
         Intent copyIntent = new Intent(context, CopyService.class);
@@ -506,8 +511,10 @@
             open.setVisible(!manageMode);
             share.setVisible(manageMode);
             delete.setVisible(manageMode);
-            // Hide the copy feature by default.
-            copy.setVisible(SystemProperties.getBoolean("debug.documentsui.enable_copy", false));
+            // Hide the copy menu item in the recents folder. For now, also hide it by default
+            // unless the debug flag is enabled.
+            copy.setVisible((mType != TYPE_RECENT_OPEN) &&
+                    SystemProperties.getBoolean("debug.documentsui.enable_copy", false));
 
             return true;
         }
@@ -575,9 +582,7 @@
                 if (cursor != null) {
                     final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
                     final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
-                    if (!Document.MIME_TYPE_DIR.equals(docMimeType)) {
-                        valid = isDocumentEnabled(docMimeType, docFlags);
-                    }
+                    valid = isDocumentEnabled(docMimeType, docFlags);
                 }
 
                 if (!valid) {
@@ -606,8 +611,17 @@
 
     private void onShareDocuments(List<DocumentInfo> docs) {
         Intent intent;
-        if (docs.size() == 1) {
-            final DocumentInfo doc = docs.get(0);
+
+        // Filter out directories - those can't be shared.
+        List<DocumentInfo> docsForSend = Lists.newArrayList();
+        for (DocumentInfo doc: docs) {
+            if (!Document.MIME_TYPE_DIR.equals(doc.mimeType)) {
+                docsForSend.add(doc);
+            }
+        }
+
+        if (docsForSend.size() == 1) {
+            final DocumentInfo doc = docsForSend.get(0);
 
             intent = new Intent(Intent.ACTION_SEND);
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -615,14 +629,14 @@
             intent.setType(doc.mimeType);
             intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri);
 
-        } else if (docs.size() > 1) {
+        } else if (docsForSend.size() > 1) {
             intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
             intent.addCategory(Intent.CATEGORY_DEFAULT);
 
             final ArrayList<String> mimeTypes = Lists.newArrayList();
             final ArrayList<Uri> uris = Lists.newArrayList();
-            for (DocumentInfo doc : docs) {
+            for (DocumentInfo doc : docsForSend) {
                 mimeTypes.add(doc.mimeType);
                 uris.add(doc.derivedUri);
             }
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index e835536..df221fa 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -303,4 +303,7 @@
     <!-- Description of airplane mode -->
     <string name="airplane_mode">Airplane mode</string>
 
+    <!-- Fingerprint hint message when finger was not recognized.-->
+    <string name="fingerprint_not_recognized">Not recognized</string>
+
 </resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 82dec30..5a4d5cd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -149,6 +149,7 @@
     private boolean mScreenOn;
     private SubscriptionManager mSubscriptionManager;
     private List<SubscriptionInfo> mSubscriptionInfo;
+    private boolean mFingerprintDetectionRunning;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -332,27 +333,35 @@
     }
 
     private void handleFingerprintAuthenticated(int fingerId, int groupId) {
-        if (fingerId == 0) return; // not a valid fingerprint
+        if (fingerId == 0) {
+            // not a valid fingerprint
+            handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized));
+            return;
+        }
 
-        final int userId;
         try {
-            userId = ActivityManagerNative.getDefault().getCurrentUser().id;
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get current user id: ", e);
-            return;
-        }
-        if (isFingerprintDisabled(userId)) {
-            Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
-            return;
-        }
-        final ContentResolver res = mContext.getContentResolver();
-        final int ids[] = FingerprintUtils.getFingerprintIdsForUser(res, userId);
-        for (int i = 0; i < ids.length; i++) {
-            // TODO: fix once HAL supports storing group id
-            final boolean isCorrectUser = true || (groupId == userId);
-            if (ids[i] == fingerId && isCorrectUser) {
-                onFingerprintAuthenticated(userId);
+            final int userId;
+            try {
+                userId = ActivityManagerNative.getDefault().getCurrentUser().id;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to get current user id: ", e);
+                return;
             }
+            if (isFingerprintDisabled(userId)) {
+                Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
+                return;
+            }
+            final ContentResolver res = mContext.getContentResolver();
+            final int ids[] = FingerprintUtils.getFingerprintIdsForUser(res, userId);
+            for (int i = 0; i < ids.length; i++) {
+                // TODO: fix once HAL supports storing group id
+                final boolean isCorrectUser = true || (groupId == userId);
+                if (ids[i] == fingerId && isCorrectUser) {
+                    onFingerprintAuthenticated(userId);
+                }
+            }
+        } finally {
+            setFingerprintRunningDetectionRunning(false);
         }
     }
 
@@ -366,6 +375,7 @@
     }
 
     private void handleFingerprintError(int msgId, String errString) {
+        setFingerprintRunningDetectionRunning(false);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -374,6 +384,21 @@
         }
     }
 
+    private void setFingerprintRunningDetectionRunning(boolean running) {
+        if (running != mFingerprintDetectionRunning) {
+            mFingerprintDetectionRunning = running;
+            notifyFingerprintRunningStateChanged();
+        }
+    }
+
+    private void notifyFingerprintRunningStateChanged() {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onFingerprintRunningStateChanged(mFingerprintDetectionRunning);
+            }
+        }
+    }
     private void handleFaceUnlockStateChanged(boolean running, int userId) {
         mUserFaceUnlockRunning.put(userId, running);
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -388,6 +413,10 @@
         return mUserFaceUnlockRunning.get(userId);
     }
 
+    public boolean isFingerprintDetectionRunning() {
+        return mFingerprintDetectionRunning;
+    }
+
     private boolean isTrustDisabled(int userId) {
         // Don't allow trust agent if device is secured with a SIM PIN. This is here
         // mainly because there's no other way to prompt the user to enter their SIM PIN
@@ -736,13 +765,15 @@
     }
 
     private void startListeningForFingerprint(Context context) {
-        if (mFpm != null && mFpm.isHardwareDetected()) {
+        if (mFpm != null && mFpm.isHardwareDetected()
+                && mFpm.getEnrolledFingerprints().size() > 0) {
             if (mFingerprintCancelSignal == null) {
                 mFingerprintCancelSignal = new CancellationSignal();
             } else {
                 mFingerprintCancelSignal.cancel();
             }
             mFpm.authenticate(null, mAuthenticationCallback, mFingerprintCancelSignal, 0);
+            setFingerprintRunningDetectionRunning(true);
         }
     }
 
@@ -750,6 +781,7 @@
         if (mFingerprintCancelSignal != null) {
             mFingerprintCancelSignal.cancel();
         }
+        setFingerprintRunningDetectionRunning(false);
     }
 
     private boolean isDeviceProvisionedInSettingsDb() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 8d2562d..756a7a4 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -200,4 +200,9 @@
      * Called when the state of face unlock changed.
      */
     public void onFaceUnlockStateChanged(boolean running, int userId) { }
+
+    /**
+     * Called when the fingerprint running state changed.
+     */
+    public void onFingerprintRunningStateChanged(boolean running) { }
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 9d16501..35e9636 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -93,6 +93,7 @@
     <uses-permission android:name="android.permission.BIND_APPWIDGET" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
+    <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
 
     <application android:label="@string/app_label">
         <provider
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 47ef42a..51fea2a 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -20,6 +20,11 @@
     $(LOCAL_PATH)/res
 LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.keyguard
 
+ifneq ($(SYSTEM_UI_INCREMENTAL_BUILDS),)
+    LOCAL_PROGUARD_ENABLED := disabled
+    LOCAL_JACK_ENABLED := incremental
+endif
+
 include frameworks/base/packages/SettingsLib/common.mk
 
 include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/res/drawable/ic_fingerprint.xml b/packages/SystemUI/res/drawable/ic_fingerprint.xml
new file mode 100644
index 0000000..ee2cf03
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_fingerprint.xml
@@ -0,0 +1,36 @@
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0">
+    <path
+        android:fillColor="#80ffffff"
+        android:pathData="M23.7,5.9c-0.1,0.0 -0.2,0.0 -0.3,-0.1C21.0,4.5 18.6,3.9 16.0,3.9c-2.5,0.0 -4.6,0.6 -6.9,1.9C8.8,6.0 8.3,5.9 8.1,5.5C7.9,5.2 8.0,4.7 8.4,4.5c2.5,-1.4 4.9,-2.1 7.7,-2.1c2.8,0.0 5.4,0.7 8.0,2.1c0.4,0.2 0.5,0.6 0.3,1.0C24.2,5.7 24.0,5.9 23.7,5.9z"/>
+    <path
+        android:fillColor="#80ffffff"
+        android:pathData="M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z"/>
+    <path
+        android:fillColor="#80ffffff"
+        android:pathData="M13.3,29.6c-0.2,0.0 -0.4,-0.1 -0.5,-0.2c-1.1,-1.2 -1.7,-2.0 -2.6,-3.6c-0.9,-1.7 -1.4,-3.7 -1.4,-5.9c0.0,-4.1 3.3,-7.4 7.4,-7.4c4.1,0.0 7.4,3.3 7.4,7.4c0.0,0.4 -0.3,0.7 -0.7,0.7s-0.7,-0.3 -0.7,-0.7c0.0,-3.3 -2.7,-5.9 -5.9,-5.9c-3.3,0.0 -5.9,2.7 -5.9,5.9c0.0,2.0 0.4,3.8 1.2,5.2c0.8,1.6 1.4,2.2 2.4,3.3c0.3,0.3 0.3,0.8 0.0,1.0C13.7,29.5 13.5,29.6 13.3,29.6z"/>
+    <path
+        android:fillColor="#80ffffff"
+        android:pathData="M22.6,27.1c-1.6,0.0 -2.9,-0.4 -4.1,-1.2c-1.9,-1.4 -3.1,-3.6 -3.1,-6.0c0.0,-0.4 0.3,-0.7 0.7,-0.7s0.7,0.3 0.7,0.7c0.0,1.9 0.9,3.7 2.5,4.8c0.9,0.6 1.9,1.0 3.2,1.0c0.3,0.0 0.8,0.0 1.3,-0.1c0.4,-0.1 0.8,0.2 0.8,0.6c0.1,0.4 -0.2,0.8 -0.6,0.8C23.4,27.1 22.8,27.1 22.6,27.1z"/>
+    <path
+        android:fillColor="#80ffffff"
+        android:pathData="M20.0,29.9c-0.1,0.0 -0.1,0.0 -0.2,0.0c-2.1,-0.6 -3.4,-1.4 -4.8,-2.9c-1.8,-1.9 -2.8,-4.4 -2.8,-7.1c0.0,-2.2 1.8,-4.1 4.1,-4.1c2.2,0.0 4.1,1.8 4.1,4.1c0.0,1.4 1.2,2.6 2.6,2.6c1.4,0.0 2.6,-1.2 2.6,-2.6c0.0,-5.1 -4.2,-9.3 -9.3,-9.3c-3.6,0.0 -6.9,2.1 -8.4,5.4C7.3,17.1 7.0,18.4 7.0,19.8c0.0,1.1 0.1,2.7 0.9,4.9c0.1,0.4 -0.1,0.8 -0.4,0.9c-0.4,0.1 -0.8,-0.1 -0.9,-0.4c-0.6,-1.8 -0.9,-3.6 -0.9,-5.4c0.0,-1.6 0.3,-3.1 0.9,-4.4c1.7,-3.8 5.6,-6.3 9.8,-6.3c5.9,0.0 10.7,4.8 10.7,10.7c0.0,2.2 -1.8,4.1 -4.1,4.1s-4.0,-1.8 -4.0,-4.1c0.0,-1.4 -1.2,-2.6 -2.6,-2.6c-1.4,0.0 -2.6,1.2 -2.6,2.6c0.0,2.3 0.9,4.5 2.4,6.1c1.2,1.3 2.4,2.0 4.2,2.5c0.4,0.1 0.6,0.5 0.5,0.9C20.6,29.7 20.3,29.9 20.0,29.9z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_fingerprint_error.xml b/packages/SystemUI/res/drawable/ic_fingerprint_error.xml
new file mode 100644
index 0000000..11e83a1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_fingerprint_error.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0">
+    <path
+        android:fillColor="@color/system_warning_color"
+        android:pathData="M15.99,2.5C8.53,2.5 2.5,8.54 2.5,16.0s6.03,13.5 13.49,13.5S29.5,23.46 29.5,16.0S23.45,2.5 15.99,2.5zM16.0,26.8c-5.97,0.0 -10.8,-4.83 -10.8,-10.8S10.03,5.2 16.0,5.2S26.8,10.03 26.8,16.0S21.97,26.8 16.0,26.8z"/>
+    <path
+        android:fillColor="@color/system_warning_color"
+        android:pathData="M14.65,20.05l2.7,0.0l0.0,2.7l-2.7,0.0z"/>
+    <path
+        android:fillColor="@color/system_warning_color"
+        android:pathData="M14.65,9.25l2.7,0.0l0.0,8.1l-2.7,0.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 01b2713..fca8231 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -68,7 +68,6 @@
         android:layout_gravity="bottom|center_horizontal"
         android:src="@drawable/ic_lock_24dp"
         android:scaleType="center"
-        android:tint="#ffffffff"
         android:contentDescription="@string/accessibility_unlock_button" />
 
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c9e1fee..b6ff1ce 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -573,4 +573,7 @@
     <!-- Screen pinning inner nav bar outer circle size -->
     <dimen name="screen_pinning_nav_highlight_outer_size">84dp</dimen>
 
+    <!-- Padding to be used on the bottom of the fingerprint icon on Keyguard so it better aligns
+         with the other icons. -->
+    <dimen name="fingerprint_icon_additional_padding">12dp</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f75dd73..d2837b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1628,7 +1628,7 @@
 
                     // close the shade if it was open
                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
-                            true /* force */);
+                            true /* force */, true /* delayed */);
                     visibilityChanged(false);
 
                     return mIntent != null && mIntent.isActivity();
@@ -1640,6 +1640,9 @@
     public void animateCollapsePanels(int flags, boolean force) {
     }
 
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
+    }
+
     public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
         if (keyguardShowing) {
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index e2464c2..583184f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -34,6 +34,7 @@
 import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
 
 /**
  * An ImageView which does not have overlapping renderings commands and therefore does not need a
@@ -75,6 +76,7 @@
     private float mCircleStartRadius;
     private float mMaxCircleSize;
     private Animator mPreviewClipper;
+    private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
     private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
@@ -394,6 +396,17 @@
         }
     }
 
+    public void setRestingAlpha(float alpha) {
+        mRestingAlpha = alpha;
+
+        // TODO: Handle the case an animation is playing.
+        setImageAlpha(alpha, false);
+    }
+
+    public float getRestingAlpha() {
+        return mRestingAlpha;
+    }
+
     public void setImageAlpha(float alpha, boolean animate) {
         setImageAlpha(alpha, animate, -1, null, null);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 58067c3..07a055c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Color;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Handler;
@@ -53,6 +54,7 @@
 
     private String mRestingIndication;
     private String mTransientIndication;
+    private int mTransientTextColor;
     private boolean mVisible;
 
     private boolean mPowerPluggedIn;
@@ -105,7 +107,15 @@
      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
      */
     public void showTransientIndication(String transientIndication) {
+        showTransientIndication(transientIndication, Color.WHITE);
+    }
+
+    /**
+     * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
+     */
+    public void showTransientIndication(String transientIndication, int textColor) {
         mTransientIndication = transientIndication;
+        mTransientTextColor = textColor;
         mHandler.removeMessages(MSG_HIDE_TRANSIENT);
         updateIndication();
     }
@@ -124,9 +134,17 @@
     private void updateIndication() {
         if (mVisible) {
             mTextView.switchIndication(computeIndication());
+            mTextView.setTextColor(computeColor());
         }
     }
 
+    private int computeColor() {
+        if (!TextUtils.isEmpty(mTransientIndication)) {
+            return mTransientTextColor;
+        }
+        return Color.WHITE;
+    }
+
     private String computeIndication() {
         if (!TextUtils.isEmpty(mTransientIndication)) {
             return mTransientIndication;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index cab152f..9d892f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -83,9 +83,9 @@
         mContext = context;
         mCallback = callback;
         initIcons();
-        updateIcon(mLeftIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false, false);
-        updateIcon(mCenterIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false, false);
-        updateIcon(mRightIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false, false);
+        updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false);
+        updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false);
+        updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false);
         initDimens();
     }
 
@@ -344,22 +344,23 @@
             float alpha = absTranslation / getMinTranslationAmount();
 
             // We interpolate the alpha of the other icons to 0
-            float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha);
-            fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
-
-            // We interpolate the alpha of the targetView to 1
-            alpha = fadeOutAlpha + alpha;
+            float fadeOutAlpha = 1.0f - alpha;
+            fadeOutAlpha = Math.min(1.0f, Math.max(0.0f, fadeOutAlpha));
 
             boolean animateIcons = isReset && animateReset;
             float radius = getRadiusFromTranslation(absTranslation);
             boolean slowAnimation = isReset && isBelowFalsingThreshold();
             if (!isReset) {
-                updateIcon(targetView, radius, alpha, false, false);
+                updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
+                        false, false);
             } else {
-                updateIcon(targetView, 0.0f, fadeOutAlpha, animateIcons, slowAnimation);
+                updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
+                        animateIcons, slowAnimation);
             }
-            updateIcon(otherView, 0.0f, fadeOutAlpha, animateIcons, slowAnimation);
-            updateIcon(mCenterIcon, 0.0f, fadeOutAlpha, animateIcons, slowAnimation);
+            updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
+                    animateIcons, slowAnimation);
+            updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
+                    animateIcons, slowAnimation);
 
             mTranslation = translation;
         }
@@ -369,15 +370,14 @@
         float alpha = newRadius / mMinBackgroundRadius;
 
         // We interpolate the alpha of the other icons to 0
-        float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha);
+        float fadeOutAlpha =  1.0f - alpha;
         fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
 
         // We interpolate the alpha of the targetView to 1
-        alpha = fadeOutAlpha + alpha;
         KeyguardAffordanceView otherView = targetView == mRightIcon ? mLeftIcon : mRightIcon;
-        updateIconAlpha(targetView, alpha, false);
-        updateIconAlpha(otherView, fadeOutAlpha, false);
-        updateIconAlpha(mCenterIcon, fadeOutAlpha, false);
+        updateIconAlpha(targetView, alpha + fadeOutAlpha * targetView.getRestingAlpha(), false);
+        updateIconAlpha(otherView, fadeOutAlpha * otherView.getRestingAlpha(), false);
+        updateIconAlpha(mCenterIcon, fadeOutAlpha * mCenterIcon.getRestingAlpha(), false);
     }
 
     private float getTranslationFromRadius(float circleSize) {
@@ -404,14 +404,14 @@
     }
 
     private void updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate) {
-        float scale = getScale(alpha);
+        float scale = getScale(alpha, view);
         alpha = Math.min(1.0f, alpha);
         view.setImageAlpha(alpha, animate);
         view.setImageScale(scale, animate);
     }
 
-    private float getScale(float alpha) {
-        float scale = alpha / SWIPE_RESTING_ALPHA_AMOUNT * 0.2f +
+    private float getScale(float alpha, KeyguardAffordanceView icon) {
+        float scale = alpha / icon.getRestingAlpha() * 0.2f +
                 KeyguardAffordanceView.MIN_ICON_SCALE_AMOUNT;
         return Math.min(scale, KeyguardAffordanceView.MAX_ICON_SCALE_AMOUNT);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 628ae84..410a7e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -25,13 +25,16 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.Vibrator;
 import android.provider.MediaStore;
+import android.provider.Settings;
 import android.telecom.TelecomManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -78,6 +81,9 @@
     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
     private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
     private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
+    private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
+    private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30};
+    private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30};
 
     private KeyguardAffordanceView mCameraImageView;
     private KeyguardAffordanceView mPhoneImageView;
@@ -101,6 +107,7 @@
     private final Interpolator mLinearOutSlowInInterpolator;
     private int mLastUnlockIconRes = 0;
     private boolean mPrewarmSent;
+    private boolean mTransientFpError;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -431,28 +438,41 @@
             return;
         }
         // TODO: Real icon for facelock.
-        int iconRes = mUnlockMethodCache.isFaceUnlockRunning()
-                ? com.android.internal.R.drawable.ic_account_circle
+        boolean isFingerprintIcon =
+                KeyguardUpdateMonitor.getInstance(mContext).isFingerprintDetectionRunning();
+        boolean anyFingerprintIcon = isFingerprintIcon || mTransientFpError;
+        int iconRes = mTransientFpError ? R.drawable.ic_fingerprint_error
+                : isFingerprintIcon ? R.drawable.ic_fingerprint
+                : mUnlockMethodCache.isFaceUnlockRunning()
+                        ? com.android.internal.R.drawable.ic_account_circle
                 : mUnlockMethodCache.isCurrentlyInsecure() ? R.drawable.ic_lock_open_24dp
                 : R.drawable.ic_lock_24dp;
+
         if (mLastUnlockIconRes != iconRes) {
             Drawable icon = mContext.getDrawable(iconRes);
             int iconHeight = getResources().getDimensionPixelSize(
                     R.dimen.keyguard_affordance_icon_height);
             int iconWidth = getResources().getDimensionPixelSize(
                     R.dimen.keyguard_affordance_icon_width);
-            if (icon.getIntrinsicHeight() != iconHeight || icon.getIntrinsicWidth() != iconWidth) {
+            if (!anyFingerprintIcon && (icon.getIntrinsicHeight() != iconHeight
+                    || icon.getIntrinsicWidth() != iconWidth)) {
                 icon = new IntrinsicSizeDrawable(icon, iconWidth, iconHeight);
             }
             mLockIcon.setImageDrawable(icon);
+            mLockIcon.setPaddingRelative(0, 0, 0, anyFingerprintIcon
+                    ? getResources().getDimensionPixelSize(
+                            R.dimen.fingerprint_icon_additional_padding)
+                    : 0);
+            mLockIcon.setRestingAlpha(
+                    anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT);
         }
-        boolean trustManaged = mUnlockMethodCache.isTrustManaged();
+
+        // Hide trust circle when fingerprint is running.
+        boolean trustManaged = mUnlockMethodCache.isTrustManaged() && !anyFingerprintIcon;
         mTrustDrawable.setTrustManaged(trustManaged);
         updateLockIconClickability();
     }
 
-
-
     public KeyguardAffordanceView getPhoneView() {
         return mPhoneImageView;
     }
@@ -530,6 +550,14 @@
                 .setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
     }
 
+    private void vibrateFingerprintError() {
+        mContext.getSystemService(Vibrator.class).vibrate(FP_ERROR_VIBRATE_PATTERN, -1);
+    }
+
+    private void vibrateFingerprintSuccess() {
+        mContext.getSystemService(Vibrator.class).vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1);
+    }
+
     private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             post(new Runnable() {
@@ -541,6 +569,15 @@
         }
     };
 
+    private final Runnable mTransientFpErrorClearRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mTransientFpError = false;
+            mIndicationController.hideTransientIndication();
+            updateLockIcon();
+        }
+    };
+
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
         @Override
@@ -562,6 +599,33 @@
         public void onKeyguardVisibilityChanged(boolean showing) {
             updateLockIcon();
         }
+
+        @Override
+        public void onFingerprintAuthenticated(int userId) {
+            vibrateFingerprintSuccess();
+        }
+
+        @Override
+        public void onFingerprintRunningStateChanged(boolean running) {
+            updateLockIcon();
+        }
+
+        @Override
+        public void onFingerprintHelp(int msgId, String helpString) {
+            vibrateFingerprintError();
+            mTransientFpError = true;
+            mIndicationController.showTransientIndication(helpString,
+                    getResources().getColor(R.color.system_warning_color, null));
+            removeCallbacks(mTransientFpErrorClearRunnable);
+            postDelayed(mTransientFpErrorClearRunnable, TRANSIENT_FP_ERROR_TIMEOUT);
+            updateLockIcon();
+        }
+
+        @Override
+        public void onFingerprintError(int msgId, String errString) {
+            // TODO: Go to bouncer if this is "too many attempts" (lockout) error.
+            Log.i(TAG, "FP Error: " + errString);
+        }
     };
 
     public void setKeyguardIndicationController(
@@ -569,7 +633,6 @@
         mIndicationController = keyguardIndicationController;
     }
 
-
     /**
      * A wrapper around another Drawable that overrides the intrinsic size.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 216730b..02b6c19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1505,7 +1505,7 @@
                 lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
                         mFastOutLinearInterpolator);
             } else if (!active && mUnlockIconActive && mTracking) {
-                lockIcon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT, true,
+                lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */,
                         150, mFastOutLinearInterpolator, null);
                 lockIcon.setImageScale(1.0f, true, 150,
                         mFastOutLinearInterpolator);
@@ -1796,8 +1796,8 @@
                 mFastOutSlowInInterpolator, new Runnable() {
                     @Override
                     public void run() {
-                        icon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT,
-                                true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
+                        icon.setImageAlpha(icon.getRestingAlpha(),
+                                true /* animate */, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
                                 mFastOutSlowInInterpolator, null);
                     }
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 3efaaff..c6e1be9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -187,11 +187,11 @@
                 (fullyOpenedPanel!=null)?" fullyOpened":"", fullyClosed?" fullyClosed":"");
     }
 
-    public void collapseAllPanels(boolean animate) {
+    public void collapseAllPanels(boolean animate, boolean delayed) {
         boolean waiting = false;
         for (PanelView pv : mPanels) {
             if (animate && !pv.isFullyCollapsed()) {
-                pv.collapse(true /* delayed */);
+                pv.collapse(delayed);
                 waiting = true;
             } else {
                 pv.resetViews();
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 974ce41..7c199cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1992,10 +1992,14 @@
     }
 
     public void animateCollapsePanels(int flags) {
-        animateCollapsePanels(flags, false /* force */);
+        animateCollapsePanels(flags, false /* force */, false /* delayed */);
     }
 
     public void animateCollapsePanels(int flags, boolean force) {
+        animateCollapsePanels(flags, force, false /* delayed*/);
+    }
+
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
         if (!force &&
                 (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
             runPostCollapseRunnables();
@@ -2019,7 +2023,7 @@
             mStatusBarWindowManager.setStatusBarFocusable(false);
 
             mStatusBarWindow.cancelExpandHelper();
-            mStatusBarView.collapseAllPanels(true);
+            mStatusBarView.collapseAllPanels(true /* animate */, delayed);
         }
     }
 
@@ -2062,7 +2066,7 @@
 
     public void animateCollapseQuickSettings() {
         if (mState == StatusBarState.SHADE) {
-            mStatusBarView.collapseAllPanels(true);
+            mStatusBarView.collapseAllPanels(true, false /* delayed */);
         }
     }
 
@@ -2075,7 +2079,7 @@
         }
 
         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
-        mStatusBarView.collapseAllPanels(/*animate=*/ false);
+        mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/);
 
         // reset things to their proper state
         mStackScroller.setVisibility(View.VISIBLE);
@@ -2169,7 +2173,7 @@
             mStatusBarWindowState = state;
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
             if (!showing && mState == StatusBarState.SHADE) {
-                mStatusBarView.collapseAllPanels(false);
+                mStatusBarView.collapseAllPanels(false /* animate */, false /* delayed */);
             }
         }
         if (mNavigationBarView != null
@@ -2643,8 +2647,8 @@
                     }
                 });
                 if (dismissShade) {
-                    animateCollapsePanels(
-                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
+                            true /* delayed*/);
                 }
                 return true;
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7193384..0581e22 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15498,8 +15498,9 @@
         }
 
         synchronized (this) {
-            if (callerApp != null && callerApp.pid == 0) {
-                // Caller already died
+            if (callerApp != null && (callerApp.thread == null
+                    || callerApp.thread.asBinder() != caller.asBinder())) {
+                // Original caller already died
                 return null;
             }
             ReceiverList rl
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index c97bdb6..a3c9738 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -348,6 +348,10 @@
         private int mActualBacklight = INITIAL_BACKLIGHT;
         private boolean mChangeInProgress;
 
+        public PhotonicModulator() {
+            super("PhotonicModulator");
+        }
+
         public boolean setState(int state, int backlight) {
             synchronized (mLock) {
                 if (state != mPendingState || backlight != mPendingBacklight) {
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 9dcc529..2da9d8e 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -106,7 +106,8 @@
                 mMode = mode;
                 mOnMS = onMS;
                 mOffMS = offMS;
-                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", " + color + ")");
+                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
+                        + Integer.toHexString(color) + ")");
                 try {
                     setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
                 } finally {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 591dbee..89fa320 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -30,6 +30,7 @@
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.Manifest;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -528,6 +529,15 @@
             params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
         }
 
+        // Only system components can circumvent runtime permissions when installing.
+        if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
+                && mContext.checkCallingOrSelfPermission(Manifest.permission
+                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException("You need the "
+                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
+        }
+
         // Defensively resize giant app icons
         if (params.appIcon != null) {
             final ActivityManager am = (ActivityManager) mContext.getSystemService(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f5042ed..11e1ccf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8612,6 +8612,15 @@
             user = new UserHandle(userId);
         }
 
+        // Only system components can circumvent runtime permissions when installing.
+        if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
+                && mContext.checkCallingOrSelfPermission(Manifest.permission
+                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException("You need the "
+                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
+        }
+
         verificationParams.setInstallerUid(callingUid);
 
         final File originFile = new File(originPath);
@@ -8769,7 +8778,6 @@
         long callingId = Binder.clearCallingIdentity();
         try {
             boolean sendAdded = false;
-            Bundle extras = new Bundle(1);
 
             // writer
             synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ed14569..936840a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -990,7 +990,7 @@
                     launchHomeFromHotKey();
                     break;
                 case SHORT_PRESS_POWER_GO_HOME:
-                    launchHomeFromHotKey();
+                    launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
                     break;
             }
         }
@@ -1068,7 +1068,7 @@
                         PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
                 break;
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
-                launchHomeFromHotKey(false /* awakenDreams */);
+                launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
                 mPowerManager.goToSleep(event.getEventTime(),
                         PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
                 break;
@@ -3059,50 +3059,56 @@
     }
 
     void launchHomeFromHotKey() {
-        launchHomeFromHotKey(true /* awakenFromDreams */);
+        launchHomeFromHotKey(true /* awakenFromDreams */, true /*respectKeyguard*/);
     }
 
     /**
      * A home key -> launch home action was detected.  Take the appropriate action
      * given the situation with the keyguard.
      */
-    void launchHomeFromHotKey(final boolean awakenFromDreams) {
-        if (isKeyguardShowingAndNotOccluded()) {
-            // don't launch home if keyguard showing
-        } else if (!mHideLockScreen && mKeyguardDelegate.isInputRestricted()) {
-            // when in keyguard restricted mode, must first verify unlock
-            // before launching home
-            mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
-                @Override
-                public void onKeyguardExitResult(boolean success) {
-                    if (success) {
-                        try {
-                            ActivityManagerNative.getDefault().stopAppSwitches();
-                        } catch (RemoteException e) {
+    void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
+        if (respectKeyguard) {
+            if (isKeyguardShowingAndNotOccluded()) {
+                // don't launch home if keyguard showing
+                return;
+            }
+
+            if (!mHideLockScreen && mKeyguardDelegate.isInputRestricted()) {
+                // when in keyguard restricted mode, must first verify unlock
+                // before launching home
+                mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
+                    @Override
+                    public void onKeyguardExitResult(boolean success) {
+                        if (success) {
+                            try {
+                                ActivityManagerNative.getDefault().stopAppSwitches();
+                            } catch (RemoteException e) {
+                            }
+                            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+                            startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
                         }
-                        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-                        startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
                     }
-                }
-            });
+                });
+                return;
+            }
+        }
+
+        // no keyguard stuff to worry about, just launch home!
+        try {
+            ActivityManagerNative.getDefault().stopAppSwitches();
+        } catch (RemoteException e) {
+        }
+        if (mRecentsVisible) {
+            // Hide Recents and notify it to launch Home
+            if (awakenFromDreams) {
+                awakenDreams();
+            }
+            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+            hideRecentApps(false, true);
         } else {
-            // no keyguard stuff to worry about, just launch home!
-            try {
-                ActivityManagerNative.getDefault().stopAppSwitches();
-            } catch (RemoteException e) {
-            }
-            if (mRecentsVisible) {
-                // Hide Recents and notify it to launch Home
-                if (awakenFromDreams) {
-                    awakenDreams();
-                }
-                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-                hideRecentApps(false, true);
-            } else {
-                // Otherwise, just launch Home
-                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-                startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
-            }
+            // Otherwise, just launch Home
+            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+            startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 70ac268..24bdb7a 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -157,6 +157,34 @@
             in PendingIntent deliveryIntent);
 
     /**
+     * Send a data SMS. Only for use internally.
+     *
+     * @param smsc the SMSC to send the message through, or NULL for the
+     *  default SMSC
+     * @param data the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is sucessfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+     *  the extra "errorCode" containing a radio technology specific value,
+     *  generally only useful for troubleshooting.<br>
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applicaitons,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     * @param subId the subId id.
+     */
+    void sendDataForSubscriberWithSelfPermissions(int subId, String callingPkg, in String destAddr,
+            in String scAddr, in int destPort, in byte[] data, in PendingIntent sentIntent,
+            in PendingIntent deliveryIntent);
+
+    /**
      * Send an SMS.
      *
      * @param smsc the SMSC to send the message through, or NULL for the
@@ -211,6 +239,34 @@
             in PendingIntent deliveryIntent);
 
     /**
+     * Send an SMS. Internal use only.
+     *
+     * @param smsc the SMSC to send the message through, or NULL for the
+     *  default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is sucessfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+     *  the extra "errorCode" containing a radio technology specific value,
+     *  generally only useful for troubleshooting.<br>
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     * @param subId the subId on which the SMS has to be sent.
+     */
+    void sendTextForSubscriberWithSelfPermissions(in int subId, String callingPkg,
+            in String destAddr, in String scAddr, in String text, in PendingIntent sentIntent,
+            in PendingIntent deliveryIntent);
+
+    /**
      * Inject an SMS PDU into the android platform.
      *
      * @param pdu is the byte array of pdu to be injected into android application framework
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 72ee343..df76bc9 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -518,7 +518,7 @@
             if m.name.startswith("create") and m.name.endswith("Intent"):
                 pass
             else:
-                error(clazz, m, "FW1", "Methods creating an Intent must be named createFooIntent()")
+                warn(clazz, m, "FW1", "Methods creating an Intent should be named createFooIntent()")
 
 
 def verify_helper_classes(clazz):