Merge "Implement DayNight theme for DocumentsUI"
diff --git a/api/current.txt b/api/current.txt
index 85813f0..e509a00 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7774,6 +7774,7 @@
     field public static final java.lang.String MEDIA_PROJECTION_SERVICE = "media_projection";
     field public static final java.lang.String MEDIA_ROUTER_SERVICE = "media_router";
     field public static final java.lang.String MEDIA_SESSION_SERVICE = "media_session";
+    field public static final java.lang.String MIDI_SERVICE = "midi";
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -9311,6 +9312,7 @@
     field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
     field public static final java.lang.String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
     field public static final java.lang.String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+    field public static final java.lang.String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
     field public static final java.lang.String FEATURE_BACKUP = "android.software.backup";
     field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
     field public static final java.lang.String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
@@ -12412,7 +12414,9 @@
     method public void draw(android.graphics.Canvas);
     method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
     method public int findIndexByLayerId(int);
+    method public int getBottomPadding();
     method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getEndPadding();
     method public int getId(int);
     method public int getLayerGravity(int);
     method public int getLayerHeight(int);
@@ -12423,9 +12427,13 @@
     method public int getLayerInsetStart(int);
     method public int getLayerInsetTop(int);
     method public int getLayerWidth(int);
+    method public int getLeftPadding();
     method public int getNumberOfLayers();
     method public int getOpacity();
     method public int getPaddingMode();
+    method public int getRightPadding();
+    method public int getStartPadding();
+    method public int getTopPadding();
     method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
     method public void setAlpha(int);
@@ -12446,7 +12454,9 @@
     method public void setLayerSize(int, int, int);
     method public void setLayerWidth(int, int);
     method public void setOpacity(int);
+    method public void setPadding(int, int, int, int);
     method public void setPaddingMode(int);
+    method public void setPaddingRelative(int, int, int, int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
@@ -14987,6 +14997,7 @@
     method public int getPlayState();
     method public int getPlaybackHeadPosition();
     method public int getPlaybackRate();
+    method public android.media.PlaybackSettings getPlaybackSettings();
     method public int getPositionNotificationPeriod();
     method public android.media.AudioDeviceInfo getPreferredOutputDevice();
     method public int getSampleRate();
@@ -15004,6 +15015,7 @@
     method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener);
     method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
     method public int setPlaybackRate(int);
+    method public void setPlaybackSettings(android.media.PlaybackSettings);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredOutputDevice(android.media.AudioDeviceInfo);
     method protected deprecated void setState(int);
@@ -15733,7 +15745,7 @@
     method public final void release();
     method public void seekTo(long, int);
     method public void selectTrack(int);
-    method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException, java.lang.IllegalArgumentException;
+    method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
     method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
     method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
     method public final void setDataSource(java.lang.String) throws java.io.IOException;
@@ -16367,6 +16379,24 @@
     method public abstract void onAudioDeviceConnection();
   }
 
+  public final class PlaybackSettings {
+    ctor public PlaybackSettings();
+    method public android.media.PlaybackSettings allowDefaults();
+    method public int getAudioFallbackMode();
+    method public int getAudioStretchMode();
+    method public float getPitch();
+    method public float getSpeed();
+    method public android.media.PlaybackSettings setAudioFallbackMode(int);
+    method public android.media.PlaybackSettings setAudioStretchMode(int);
+    method public android.media.PlaybackSettings setPitch(float);
+    method public android.media.PlaybackSettings setSpeed(float);
+    field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
+    field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
+    field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+    field public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; // 0x0
+    field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
+  }
+
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -30888,6 +30918,7 @@
     method public int getEvdoSnr();
     method public int getGsmBitErrorRate();
     method public int getGsmSignalStrength();
+    method public int getLevel();
     method public boolean isGsm();
     method public void writeToParcel(android.os.Parcel, int);
   }
@@ -35568,10 +35599,10 @@
     method public long getTimeDelta();
     method public boolean isInProgress();
     method public boolean isQuickScaleEnabled();
-    method public boolean isSecondaryButtonScaleEnabled();
+    method public boolean isStylusScaleEnabled();
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public void setQuickScaleEnabled(boolean);
-    method public void setSecondaryButtonScaleEnabled(boolean);
+    method public void setStylusScaleEnabled(boolean);
   }
 
   public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index 043a55b..8b4052b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7991,6 +7991,7 @@
     field public static final java.lang.String MEDIA_PROJECTION_SERVICE = "media_projection";
     field public static final java.lang.String MEDIA_ROUTER_SERVICE = "media_router";
     field public static final java.lang.String MEDIA_SESSION_SERVICE = "media_session";
+    field public static final java.lang.String MIDI_SERVICE = "midi";
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -9570,6 +9571,7 @@
     field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
     field public static final java.lang.String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
     field public static final java.lang.String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+    field public static final java.lang.String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
     field public static final java.lang.String FEATURE_BACKUP = "android.software.backup";
     field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
     field public static final java.lang.String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
@@ -12706,7 +12708,9 @@
     method public void draw(android.graphics.Canvas);
     method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
     method public int findIndexByLayerId(int);
+    method public int getBottomPadding();
     method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getEndPadding();
     method public int getId(int);
     method public int getLayerGravity(int);
     method public int getLayerHeight(int);
@@ -12717,9 +12721,13 @@
     method public int getLayerInsetStart(int);
     method public int getLayerInsetTop(int);
     method public int getLayerWidth(int);
+    method public int getLeftPadding();
     method public int getNumberOfLayers();
     method public int getOpacity();
     method public int getPaddingMode();
+    method public int getRightPadding();
+    method public int getStartPadding();
+    method public int getTopPadding();
     method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
     method public void setAlpha(int);
@@ -12740,7 +12748,9 @@
     method public void setLayerSize(int, int, int);
     method public void setLayerWidth(int, int);
     method public void setOpacity(int);
+    method public void setPadding(int, int, int, int);
     method public void setPaddingMode(int);
+    method public void setPaddingRelative(int, int, int, int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
@@ -16199,6 +16209,7 @@
     method public int getPlayState();
     method public int getPlaybackHeadPosition();
     method public int getPlaybackRate();
+    method public android.media.PlaybackSettings getPlaybackSettings();
     method public int getPositionNotificationPeriod();
     method public android.media.AudioDeviceInfo getPreferredOutputDevice();
     method public int getSampleRate();
@@ -16216,6 +16227,7 @@
     method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener);
     method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
     method public int setPlaybackRate(int);
+    method public void setPlaybackSettings(android.media.PlaybackSettings);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredOutputDevice(android.media.AudioDeviceInfo);
     method protected deprecated void setState(int);
@@ -16946,7 +16958,7 @@
     method public final void release();
     method public void seekTo(long, int);
     method public void selectTrack(int);
-    method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException, java.lang.IllegalArgumentException;
+    method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
     method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
     method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
     method public final void setDataSource(java.lang.String) throws java.io.IOException;
@@ -17582,6 +17594,24 @@
     method public abstract void onAudioDeviceConnection();
   }
 
+  public final class PlaybackSettings {
+    ctor public PlaybackSettings();
+    method public android.media.PlaybackSettings allowDefaults();
+    method public int getAudioFallbackMode();
+    method public int getAudioStretchMode();
+    method public float getPitch();
+    method public float getSpeed();
+    method public android.media.PlaybackSettings setAudioFallbackMode(int);
+    method public android.media.PlaybackSettings setAudioStretchMode(int);
+    method public android.media.PlaybackSettings setPitch(float);
+    method public android.media.PlaybackSettings setSpeed(float);
+    field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
+    field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
+    field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+    field public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; // 0x0
+    field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
+  }
+
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -33041,6 +33071,7 @@
     method public int getEvdoSnr();
     method public int getGsmBitErrorRate();
     method public int getGsmSignalStrength();
+    method public int getLevel();
     method public boolean isGsm();
     method public void writeToParcel(android.os.Parcel, int);
   }
@@ -37767,10 +37798,10 @@
     method public long getTimeDelta();
     method public boolean isInProgress();
     method public boolean isQuickScaleEnabled();
-    method public boolean isSecondaryButtonScaleEnabled();
+    method public boolean isStylusScaleEnabled();
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public void setQuickScaleEnabled(boolean);
-    method public void setSecondaryButtonScaleEnabled(boolean);
+    method public void setStylusScaleEnabled(boolean);
   }
 
   public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
diff --git a/core/java/android/annotation/BinderThread.java b/core/java/android/annotation/BinderThread.java
new file mode 100644
index 0000000..c69ba10
--- /dev/null
+++ b/core/java/android/annotation/BinderThread.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on the binder thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the binder thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  (&#64;BinderThread
+ *  public BeamShareData createBeamShareData() { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface BinderThread {
+}
\ No newline at end of file
diff --git a/core/java/android/annotation/MainThread.java b/core/java/android/annotation/MainThread.java
new file mode 100644
index 0000000..18a4283
--- /dev/null
+++ b/core/java/android/annotation/MainThread.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on the main thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the main thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  &#64;MainThread
+ *  public void deliverResult(D data) { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface MainThread {
+}
\ No newline at end of file
diff --git a/core/java/android/annotation/UiThread.java b/core/java/android/annotation/UiThread.java
new file mode 100644
index 0000000..b814600
--- /dev/null
+++ b/core/java/android/annotation/UiThread.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method or constructor should only be called on the UI thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the UI thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  &#64;UiThread
+ *  public abstract void setText(&#64;NonNull String text) { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface UiThread {
+}
\ No newline at end of file
diff --git a/core/java/android/annotation/WorkerThread.java b/core/java/android/annotation/WorkerThread.java
new file mode 100644
index 0000000..dd12e05
--- /dev/null
+++ b/core/java/android/annotation/WorkerThread.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on a worker thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on a worker thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  (&#64;WorkerThread
+ *  protected abstract FilterResults performFiltering(CharSequence constraint);
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface WorkerThread {
+}
\ No newline at end of file
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 10d6d01..ab5f811 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2541,11 +2541,16 @@
             if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
                 data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity));
                 AssistContent content = new AssistContent();
-                Intent intent = new Intent(r.activity.getIntent());
-                intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
-                intent.removeUnsafeExtras();
-                content.setIntent(intent);
+                Intent activityIntent = r.activity.getIntent();
+                if (activityIntent != null) {
+                    Intent intent = new Intent(activityIntent);
+                    intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+                    intent.removeUnsafeExtras();
+                    content.setIntent(intent);
+                } else {
+                    content.setIntent(new Intent());
+                }
                 r.activity.onProvideAssistContent(content);
                 data.putParcelable(AssistContent.ASSIST_KEY, content);
             }
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 179957d..9d1d312 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -597,6 +597,15 @@
         }
     }
 
+    /** @hide */
+    public long getNextWakeFromIdleTime() {
+        try {
+            return mService.getNextWakeFromIdleTime();
+        } catch (RemoteException ex) {
+            return Long.MAX_VALUE;
+        }
+    }
+
     /**
      * Gets information about the next alarm clock currently scheduled.
      *
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index d5719f5..327c00b 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -33,6 +33,7 @@
     boolean setTime(long millis);
     void setTimeZone(String zone);
     void remove(in PendingIntent operation);
+    long getNextWakeFromIdleTime();
     AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
 }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3bf3f85..5eacce3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3098,7 +3098,6 @@
      * {@link android.media.midi.MidiManager} for accessing the MIDI service.
      *
      * @see #getSystemService
-     * @hide
      */
     public static final String MIDI_SERVICE = "midi";
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a0cec50..e4108b1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1574,6 +1574,21 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: This is a device dedicated to showing UI
+     * on a vehicle headunit. A headunit here is defined to be inside a
+     * vehicle that may or may not be moving. A headunit uses either a
+     * primary display in the center console and/or additional displays in
+     * the instrument cluster or elsewhere in the vehicle. Headunit display(s)
+     * have limited size and resolution. The user will likely be focused on
+     * driving so limiting driver distraction is a primary concern. User input
+     * can be a variety of hard buttons, touch, rotary controllers and even mouse-
+     * like interfaces.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a device dedicated to showing UI
      * on a television.  Television here is defined to be a typical living
      * room television experience: displayed on a big screen, where the user
      * is sitting far away from it, and the dominant form of input will be
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 0d7b261..2257b0a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -587,6 +587,25 @@
         return false;
     }
 
+    /**
+     * Retrieves the authenticator token for binding keys to the lifecycle
+     * of the current set of fingerprints. Used only by internal clients.
+     *
+     * @hide
+     */
+    public long getAuthenticatorId() {
+        if (mService != null) {
+            try {
+                return mService.getAuthenticatorId();
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
+            }
+        } else {
+            Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+        }
+        return 0;
+    }
+
     private Handler mHandler = new Handler() {
         public void handleMessage(android.os.Message msg) {
             switch(msg.what) {
@@ -792,4 +811,5 @@
         }
     };
 
-}
\ No newline at end of file
+}
+
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 51a0e4c..c5ec08c 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -63,4 +63,6 @@
     // Gets the unique device id for hardware enumerated at i
     // long getHardwareDevice(int i);
 
+    // Gets the authenticator ID for fingerprint
+    long getAuthenticatorId();
 }
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 5cf2c5c..b055efe 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -130,7 +130,7 @@
     private float mFocusY;
 
     private boolean mQuickScaleEnabled;
-    private boolean mButtonScaleEnabled;
+    private boolean mStylusScaleEnabled;
 
     private float mCurrSpan;
     private float mPrevSpan;
@@ -162,7 +162,7 @@
     private static final float SCALE_FACTOR = .5f;
     private static final int ANCHORED_SCALE_MODE_NONE = 0;
     private static final int ANCHORED_SCALE_MODE_DOUBLE_TAP = 1;
-    private static final int ANCHORED_SCALE_MODE_BUTTON = 2;
+    private static final int ANCHORED_SCALE_MODE_STYLUS = 2;
 
 
     /**
@@ -212,9 +212,14 @@
         mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan);
         mHandler = handler;
         // Quick scale is enabled by default after JB_MR2
-        if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+        final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+        if (targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
             setQuickScaleEnabled(true);
         }
+        // Stylus scale is enabled by default after LOLLIPOP_MR1
+        if (targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+            setStylusScaleEnabled(true);
+        }
     }
 
     /**
@@ -315,14 +320,11 @@
         }
 
         final int count = event.getPointerCount();
-        final int toolType = event.getToolType(0);
-        final boolean isButtonTool = toolType == MotionEvent.TOOL_TYPE_STYLUS
-                || toolType == MotionEvent.TOOL_TYPE_MOUSE;
-        final boolean isAnchoredScaleButtonDown = isButtonTool && (count == 1)
+        final boolean isStylusButtonDown = (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS)
                 && (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0;
 
         final boolean anchoredScaleCancelled =
-                mAnchoredScaleMode == ANCHORED_SCALE_MODE_BUTTON && !isAnchoredScaleButtonDown;
+                mAnchoredScaleMode == ANCHORED_SCALE_MODE_STYLUS && !isStylusButtonDown;
         final boolean streamComplete = action == MotionEvent.ACTION_UP ||
                 action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;
 
@@ -347,12 +349,12 @@
             }
         }
 
-        if (!mInProgress && mButtonScaleEnabled && !inAnchoredScaleMode()
-                && !streamComplete && isAnchoredScaleButtonDown) {
+        if (!mInProgress && mStylusScaleEnabled && !inAnchoredScaleMode()
+                && !streamComplete && isStylusButtonDown) {
             // Start of a button scale gesture
             mAnchoredScaleStartX = event.getX();
             mAnchoredScaleStartY = event.getY();
-            mAnchoredScaleMode = ANCHORED_SCALE_MODE_BUTTON;
+            mAnchoredScaleMode = ANCHORED_SCALE_MODE_STYLUS;
             mInitialSpan = 0;
         }
 
@@ -503,24 +505,22 @@
     }
 
     /**
-     * Sets whether the associates {@link OnScaleGestureListener} should receive onScale callbacks
-     * when the user presses a {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus
-     * first button) and drags the pointer on the screen. Note that this is enabled by default if
-     * the app targets API 23 and newer.
+     * Sets whether the associates {@link OnScaleGestureListener} should receive
+     * onScale callbacks when the user uses a stylus and presses the button.
+     * Note that this is enabled by default if the app targets API 23 and newer.
      *
-     * @param scales true to enable stylus or mouse scaling, false to disable.
+     * @param scales true to enable stylus scaling, false to disable.
      */
-    public void setSecondaryButtonScaleEnabled(boolean scales) {
-        mButtonScaleEnabled = scales;
+    public void setStylusScaleEnabled(boolean scales) {
+        mStylusScaleEnabled = scales;
     }
 
     /**
-     * Return whether the button scale gesture, in which the user presses a
-     * {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus first button) and drags the
-     * pointer on the screen, should perform scaling. {@see #setButtonScaleEnabled(boolean)}.
+     * Return whether the stylus scale gesture, in which the user uses a stylus
+     * and presses the button, should preform scaling. {@see #setButtonScaleEnabled(boolean)}.
      */
-    public boolean isSecondaryButtonScaleEnabled() {
-        return mButtonScaleEnabled;
+    public boolean isStylusScaleEnabled() {
+        return mStylusScaleEnabled;
     }
 
     /**
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 0747969..44037dd 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -358,7 +358,7 @@
     return legacyBitmapConfigToColorType(c);
 }
 
-SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
+android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
     SkASSERT(env);
     SkASSERT(canvas);
     SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
@@ -366,9 +366,7 @@
     if (!canvasHandle) {
         return NULL;
     }
-    SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->asSkCanvas();
-    SkASSERT(c);
-    return c;
+    return reinterpret_cast<android::Canvas*>(canvasHandle);
 }
 
 SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 422d3f1..d73507e 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -8,6 +8,7 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkImageDecoder.h"
+#include <Canvas.h>
 #include <jni.h>
 
 class SkBitmapRegionDecoder;
@@ -47,7 +48,7 @@
     static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
     static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
 
-    static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
+    static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
     static SkBitmap* getSkBitmap(JNIEnv*, jobject bitmap);
     static SkRegion* getNativeRegion(JNIEnv*, jobject region);
 
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index a2c1609..3ae829b 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -39,17 +39,23 @@
 }
 
 // Native wrapper constructor used by Canvas(Bitmap)
-static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
+static jlong initRaster(JNIEnv* env, jobject, jobject jbitmap) {
+    SkBitmap* bitmap = nullptr;
+    if (jbitmap != NULL) {
+        bitmap = GraphicsJNI::getSkBitmap(env, jbitmap);
+    }
+    return reinterpret_cast<jlong>(Canvas::create_canvas(
+            bitmap ? *bitmap : SkBitmap()));
 }
 
 // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
 // optionally copying canvas matrix & clip state.
-static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
-                      jboolean copyState) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    get_canvas(canvasHandle)->setBitmap(bitmap, copyState);
+static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap) {
+    SkBitmap* bitmap = nullptr;
+    if (jbitmap != NULL) {
+        bitmap = GraphicsJNI::getSkBitmap(env, jbitmap);
+    }
+    get_canvas(canvasHandle)->setBitmap(bitmap ? *bitmap : SkBitmap());
 }
 
 static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
@@ -658,8 +664,8 @@
 
 static JNINativeMethod gMethods[] = {
     {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
-    {"initRaster", "(J)J", (void*) CanvasJNI::initRaster},
-    {"native_setBitmap", "(JJZ)V", (void*) CanvasJNI::setBitmap},
+    {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
+    {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
     {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
     {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
     {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 8d3a9aa..8b2c269 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -677,6 +677,63 @@
 
 
 // ----------------------------------------------------------------------------
+static void android_media_AudioTrack_set_playback_settings(JNIEnv *env,  jobject thiz,
+        jfloatArray floatArray, jintArray intArray) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "AudioTrack not initialized");
+        return;
+    }
+
+    // NOTE: Get<Primitive>ArrayRegion throws ArrayIndexOutOfBoundsException if not valid.
+    // TODO: consider the actual occupancy.
+    float farray[2];
+    int iarray[2];
+    if ((env->GetFloatArrayRegion(floatArray, 0, 2, farray), env->ExceptionCheck()) == JNI_FALSE
+            &&
+        (env->GetIntArrayRegion(intArray, 0, 2, iarray), env->ExceptionCheck()) == JNI_FALSE) {
+        // arrays retrieved OK
+        AudioPlaybackRate playbackRate;
+        playbackRate.mSpeed = farray[0];
+        playbackRate.mPitch = farray[1];
+        playbackRate.mFallbackMode = (AudioTimestretchFallbackMode)iarray[0];
+        playbackRate.mStretchMode = (AudioTimestretchStretchMode)iarray[1];
+        if (lpTrack->setPlaybackRate(playbackRate) != OK) {
+            jniThrowException(env, "java/lang/IllegalArgumentException",
+                    "arguments out of range");
+        }
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static void android_media_AudioTrack_get_playback_settings(JNIEnv *env,  jobject thiz,
+        jfloatArray floatArray, jintArray intArray) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "AudioTrack not initialized");
+        return;
+    }
+
+    AudioPlaybackRate playbackRate = lpTrack->getPlaybackRate();
+
+    float farray[2] = {
+            playbackRate.mSpeed,
+            playbackRate.mPitch,
+    };
+    int iarray[2] = {
+            playbackRate.mFallbackMode,
+            playbackRate.mStretchMode,
+    };
+    // NOTE: Set<Primitive>ArrayRegion throws ArrayIndexOutOfBoundsException if not valid.
+    env->SetFloatArrayRegion(floatArray, 0, 2, farray);
+    env->SetIntArrayRegion(intArray, 0, 2, iarray);
+}
+
+
+// ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
         jint markerPos) {
     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
@@ -942,6 +999,10 @@
                              "(I)I",     (void *)android_media_AudioTrack_set_playback_rate},
     {"native_get_playback_rate",
                              "()I",      (void *)android_media_AudioTrack_get_playback_rate},
+    {"native_set_playback_settings",
+                             "([F[I)V",  (void *)android_media_AudioTrack_set_playback_settings},
+    {"native_get_playback_settings",
+                             "([F[I)V",  (void *)android_media_AudioTrack_get_playback_settings},
     {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
     {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
     {"native_set_pos_update_period",
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index a12629f..aa79d70 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -67,11 +67,6 @@
     jfieldID bottom;
 } gRectClassInfo;
 
-static struct {
-    jfieldID mSurfaceFormat;
-    jmethodID setNativeBitmap;
-} gCanvasClassInfo;
-
 #define GET_INT(object, field) \
     env->GetIntField(object, field)
 
@@ -196,13 +191,9 @@
         bitmap.setPixels(NULL);
     }
 
-    SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat());
-    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap));
-
-    SkRect clipRect;
-    clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
-    SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->clipRect(clipRect);
+    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
+    nativeCanvas->setBitmap(bitmap);
+    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom);
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -217,7 +208,8 @@
 
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
-    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
+    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
+    nativeCanvas->setBitmap(SkBitmap());
 
     if (wrapper) {
         status_t status = wrapper->buffer->unlock();
@@ -302,10 +294,6 @@
     gRectClassInfo.right = GetFieldIDOrDie(env, clazz, "right", "I");
     gRectClassInfo.bottom = GetFieldIDOrDie(env, clazz, "bottom", "I");
 
-    clazz = FindClassOrDie(env, "android/graphics/Canvas");
-    gCanvasClassInfo.mSurfaceFormat = GetFieldIDOrDie(env, clazz, "mSurfaceFormat", "I");
-    gCanvasClassInfo.setNativeBitmap = GetMethodIDOrDie(env, clazz, "setNativeBitmap", "(J)V");
-
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4fcb361..c4cd7ff 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -73,11 +73,6 @@
     jfieldID bottom;
 } gRectClassInfo;
 
-static struct {
-    jfieldID mSurfaceFormat;
-    jmethodID setNativeBitmap;
-} gCanvasClassInfo;
-
 // ----------------------------------------------------------------------------
 
 // this is just a pointer we use to pass to inc/decStrong
@@ -318,9 +313,6 @@
         return 0;
     }
 
-    // Associate a SkCanvas object to this surface
-    env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);
-
     SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
                                          convertPixelFormat(outBuffer.format),
                                          kPremul_SkAlphaType);
@@ -338,12 +330,12 @@
         bitmap.setPixels(NULL);
     }
 
-    env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap,
-                        reinterpret_cast<jlong>(&bitmap));
+    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
+    nativeCanvas->setBitmap(bitmap);
 
     if (dirtyRectPtr) {
-        SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
-        nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) );
+        nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
+                dirtyRect.right, dirtyRect.bottom);
     }
 
     if (dirtyRectObj) {
@@ -369,7 +361,8 @@
     }
 
     // detach the canvas from the surface
-    env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, (jlong)0);
+    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
+    nativeCanvas->setBitmap(SkBitmap());
 
     // unlock surface
     status_t err = surface->unlockAndPost();
@@ -564,10 +557,6 @@
             gSurfaceClassInfo.clazz, "mLock", "Ljava/lang/Object;");
     gSurfaceClassInfo.ctor = GetMethodIDOrDie(env, gSurfaceClassInfo.clazz, "<init>", "(J)V");
 
-    clazz = FindClassOrDie(env, "android/graphics/Canvas");
-    gCanvasClassInfo.mSurfaceFormat = GetFieldIDOrDie(env, clazz, "mSurfaceFormat", "I");
-    gCanvasClassInfo.setNativeBitmap = GetMethodIDOrDie(env, clazz, "setNativeBitmap", "(J)V");
-
     clazz = FindClassOrDie(env, "android/graphics/Rect");
     gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I");
     gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I");
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index c2bd0b3c4..7e05793 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -48,11 +48,6 @@
 } gRectClassInfo;
 
 static struct {
-    jfieldID mSurfaceFormat;
-    jmethodID setNativeBitmap;
-} gCanvasClassInfo;
-
-static struct {
     jfieldID nativeWindow;
 } gTextureViewClassInfo;
 
@@ -172,13 +167,9 @@
         bitmap.setPixels(NULL);
     }
 
-    SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format);
-    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap));
-
-    SkRect clipRect;
-    clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
-    SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->clipRect(clipRect);
+    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
+    nativeCanvas->setBitmap(bitmap);
+    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom);
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -191,7 +182,8 @@
 static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
         jlong nativeWindow, jobject canvas) {
 
-    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
+    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
+    nativeCanvas->setBitmap(SkBitmap());
 
     if (nativeWindow) {
         sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
@@ -225,10 +217,6 @@
     gRectClassInfo.right = GetFieldIDOrDie(env, clazz, "right", "I");
     gRectClassInfo.bottom = GetFieldIDOrDie(env, clazz, "bottom", "I");
 
-    clazz = FindClassOrDie(env, "android/graphics/Canvas");
-    gCanvasClassInfo.mSurfaceFormat = GetFieldIDOrDie(env, clazz, "mSurfaceFormat", "I");
-    gCanvasClassInfo.setNativeBitmap = GetMethodIDOrDie(env, clazz, "setNativeBitmap", "(J)V");
-
     clazz = FindClassOrDie(env, "android/view/TextureView");
     gTextureViewClassInfo.nativeWindow = GetFieldIDOrDie(env, clazz, "mNativeWindow", "J");
 
diff --git a/core/res/res/drawable/ic_spinner_caret.xml b/core/res/res/drawable/ic_spinner_caret.xml
new file mode 100644
index 0000000..6a18f89
--- /dev/null
+++ b/core/res/res/drawable/ic_spinner_caret.xml
@@ -0,0 +1,26 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:pathData="M7,10l5,5,5-5z"
+        android:fillColor="@color/white"/>
+</vector>
diff --git a/core/res/res/drawable/spinner_background_material.xml b/core/res/res/drawable/spinner_background_material.xml
index d5b509f..892dbc5 100644
--- a/core/res/res/drawable/spinner_background_material.xml
+++ b/core/res/res/drawable/spinner_background_material.xml
@@ -15,21 +15,24 @@
 -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-            android:paddingMode="stack">
-    <item android:drawable="@drawable/item_background_borderless_material"
-          android:gravity="end|center_vertical"
-          android:width="24dp"
-          android:height="24dp" />
-    <item android:gravity="end|center_vertical">
-        <vector android:width="24dp"
-                android:height="24dp"
-                android:viewportWidth="24.0"
-                android:viewportHeight="24.0"
-                android:tint="?attr/colorControlNormal">
-            <path android:pathData="M7,10l5,5,5-5z"
-                  android:fillColor="@color/white"/>
-        </vector>
+            android:paddingMode="stack"
+            android:paddingStart="0dp"
+            android:paddingEnd="48dp"
+            android:paddingLeft="0dp"
+            android:paddingRight="0dp">
+    <item
+        android:gravity="end|center_vertical"
+        android:width="48dp"
+        android:height="48dp">
+        <ripple
+            android:color="?attr/colorControlHighlight"
+            android:radius="24dp" />
     </item>
-    <item android:end="48dp"
-          android:drawable="@color/transparent" />
+
+    <item
+        android:drawable="@drawable/ic_spinner_caret"
+        android:gravity="end|center_vertical"
+        android:width="24dp"
+        android:height="24dp"
+        android:end="12dp" />
 </layer-list>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 551c083..a5c6f84 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5183,6 +5183,20 @@
             <!-- Stack each layer directly atop the previous layer. -->
             <enum name="stack" value="1" />
         </attr>
+        <!-- Explicit top padding. Overrides child padding. -->
+        <attr name="paddingTop" />
+        <!-- Explicit bottom padding. Overrides child padding. -->
+        <attr name="paddingBottom" />
+        <!-- Explicit left padding. Overrides child padding. -->
+        <attr name="paddingLeft" />
+        <!-- Explicit right padding. Overrides child padding. -->
+        <attr name="paddingRight" />
+        <!-- Explicit start padding. Overrides child padding. Takes precedence
+             over absolute padding (e.g. left when layout direction is LTR). -->
+        <attr name="paddingStart" />
+        <!-- Explicit end padding. Overrides child padding. Takes precedence
+             over absolute padding (e.g. right when layout direction is LTR). -->
+        <attr name="paddingEnd" />
     </declare-styleable>
 
     <!-- Describes an item (or child) of a LayerDrawable. -->
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index 56a3954..1aef0c1 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -62,6 +62,211 @@
 <div class="toggle-content opened">
   <p id="rev21"><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+/>Android Support Library, revision 22.1.0</a> <em>(April 2015)</em>
+  </p>
+  <div class="toggle-content-toggleme">
+    <dl>
+      <dt>Changes for <a href="features.html#v4">v4 support library:</a></dt>
+      <dd>
+        <ul>
+
+          <li>Added the {@link android.support.v4.graphics.ColorUtils ColorUtils} class
+            to provide a set of color-related utility methods.
+          </li>
+          <li>Added the {@link android.support.v4.graphics.drawable.DrawableCompat#unwrap unwrap()} and
+            {@link android.support.v4.graphics.drawable.DrawableCompat#wrap wrap()} methods to the
+            {@link android.support.v4.graphics.drawable.DrawableCompat} class , allowing you to use
+            {@link android.support.v4.graphics.drawable.DrawableCompat#setTint setTint()},
+            {@link android.support.v4.graphics.drawable.DrawableCompat#setTintList setTintList()},
+            and {@link android.support.v4.graphics.drawable.DrawableCompat#setTintMode setTintMode()}
+            on all API level 4 or higher devices.
+          </li>
+          <li>Added the {@link android.support.v4.os.TraceCompat} class to
+            write trace events to the system trace buffer, which can then be collected and
+            visualized using the <a href="{@docRoot}tools/help/systrace.html">Systrace</a> tool.
+          </li>
+          <li>Added the {@link android.support.v4.util.CircularIntArray} class
+            to create circular integer array data structures.
+          </li>
+          <li>Added the {@link android.support.v4.util.CircularArray#clear clear()},
+            {@link android.support.v4.util.CircularArray#removeFromStart removeFromStart()},
+            and {@link android.support.v4.util.CircularArray#removeFromEnd removeFromEnd()}
+            methods to the {@link android.support.v4.util.CircularArray} class. Also, changed the
+            existing methods in this class to be non-final.
+          </li>
+          <li>Added the {@link android.support.v4.view.InputDeviceCompat}
+            as a helper class to access data in the
+            {@link android.support.v4.view.InputDeviceCompat} class.
+          </li>
+          <li>Added the {@link android.support.v4.view.LayoutInflaterCompat}
+            class as a helper class to access data in the
+            {@link android.support.v4.view.LayoutInflaterCompat} class
+            and added the {@link android.support.v4.view.LayoutInflaterFactory} interface.
+          </li>
+          <li>Added classes, methods, and interfaces to support nested scrolling.
+             <ul>
+               <li>Added the {@link android.support.v4.view.NestedScrollingChildHelper}
+               and {@link android.support.v4.view.NestedScrollingParentHelper}
+               helper classes for implementing nested scrolling parent and child views.</li>
+               <li>Added the {@link android.support.v4.view.NestedScrollingChild}
+               interface to be implemented by {@link android.view.View} subclasses.</li>
+               <li>Added the {@link android.support.v4.view.NestedScrollingParent}
+               and {@link android.support.v4.view.ScrollingView} interfaces to support
+               scrolling operations and provide scroll related APIs.</li>
+               <li>Added the
+                {@link android.support.v4.view.ViewGroupCompat#getNestedScrollAxes
+                getNestedScrollAxes()} method to the {@link android.support.v4.view.ViewGroupCompat}
+                class.</li>
+               <li>Added methods to the {@link android.support.v4.view.ViewParentCompat} class to
+                support nested scrolling.
+               </li>
+               <li>Added the {@link android.support.v4.widget.NestedScrollView}
+                 class to support nested scrolling parent and child on both new and old versions of
+                 Android.
+               </li>
+              </ul>
+          </li>
+          <li>Added methods and constants to the {@link android.support.v4.view.MotionEventCompat}
+            class for getting axis values and event source.
+         </li>
+         <li>Updated the {@link android.support.v4.view.accessibility.AccessibilityNodeInfoCompat}
+          class to add methods for errors, content invalidation and labels.
+         </li>
+         <li>Added the following interpolation classses for animation:
+           {@link android.support.v4.view.animation.FastOutLinearInInterpolator},
+           {@link android.support.v4.view.animation.FastOutSlowInInterpolator},
+           {@link android.support.v4.view.animation.LinearOutSlowInInterpolator},
+           {@link android.support.v4.view.animation.LinearOutSlowInInterpolator}, and
+           {@link android.support.v4.view.animation.PathInterpolatorCompat}.
+         </li>
+         <li>Added the {@link android.support.v4.widget.Space} class to create gaps between
+           components in general purpose layouts. This class is deprecated in the gridlayout library.
+         </li>
+         <li>Added the {@link android.support.v4.widget.TextViewCompat} class for accessing
+           features in a {@link android.widget.TextView}.
+         </li>
+         <li>Added a displacement parameter to the
+           {@link android.support.v4.widget.EdgeEffectCompat#onPull onPull()} method in the
+           {@link android.support.v4.widget.EdgeEffectCompat} class.
+         </li>
+
+       </ul>
+      </dd>
+
+
+  <dt>Changes for <a href="features.html#v7-appcompat">v7 appcompat library</a>:</dt>
+      <dd>
+        <ul>
+          <li>Added tint support to appcompat widgets, including
+            {@link android.support.v7.widget.AppCompatAutoCompleteTextView},
+            {@link android.support.v7.widget.AppCompatButton},
+            {@link android.support.v7.widget.AppCompatCheckBox},
+            {@link android.support.v7.widget.AppCompatCheckedTextView},
+            {@link android.support.v7.widget.AppCompatEditText},
+            {@link android.support.v7.widget.AppCompatMultiAutoCompleteTextView},
+            {@link android.support.v7.widget.AppCompatRadioButton},
+            {@link android.support.v7.widget.AppCompatRatingBar},
+            {@link android.support.v7.widget.AppCompatSpinner}, and
+            {@link android.support.v7.widget.AppCompatTextView}.
+          </li>
+          <li>Updated the {@link android.support.v7.app.AppCompatActivity} as the base
+            class for activities that use the support library action bar features. This class
+            replaces the deprecated {@link android.support.v7.app.ActionBarActivity}.
+          </li>
+          <li>Added the
+            {@link android.support.v7.app.AppCompatCallback} interface
+            to be implemented for AppCompat to be able to perform callbacks.
+          </li>
+            <li>Added the
+            {@link android.support.v7.app.AppCompatDelegate} abstract class
+            as a delegate you can use to extend AppCompat's support to any activity.
+          </li>
+          <li>Added the
+            {@link android.support.v7.app.AppCompatDialog} class
+            as the base class for AppCompat themed dialogs.
+          </li>
+          <li>Added the spinner style
+            {@link android.support.v7.app.AlertDialog} and
+            {@link android.support.v7.app.AlertDialog.Builder} classes to provide an AppCompat
+            themed {@link android.app.AlertDialog}.
+          </li>
+          <li>Added the {@link android.support.v7.graphics.Palette.Builder} class
+            for generating {@link android.support.v7.graphics.Palette} instances.
+            <ul>
+              <li>Added the
+                {@link android.support.v7.graphics.Palette#from}
+                method to the {@link android.support.v7.graphics.Palette} class to
+                start generating a Palette with the returned
+                {@link android.support.v7.graphics.Palette.Builder} instance.
+              </li>
+              <li>Deprecated the {@link android.support.v7.graphics.Palette#generate generate()} and
+                {@link android.support.v7.graphics.Palette#generateAsync generateAsync()} methods.
+              </li>
+             </ul>
+           </li>
+
+           <li>Added the
+             {@link android.support.v7.widget.GridLayout.Spec#getAbsoluteAlignment
+             getAbsoluteAlignment()} method to the {@link android.support.v7.widget.GridLayout.Spec}
+             class.
+           </li>
+           <li>Deprecated use of <code>app:theme</code> for styling
+             {@link android.support.v7.widget.Toolbar}. You can now use
+             <code>android:theme</code> for toolbars on all API level 7 and higher devices and
+             <code>android:theme</code> support for all widgets on API level 11 and higher devices.
+           </li>
+        </ul>
+      </dd>
+
+
+      <dt>Changes for <a href="features.html#v17-leanback">v17 leanback library</a>:</dt>
+      <dd>
+        <ul>
+          <li> Added {@link android.support.v17.leanback.app.GuidedStepFragment},
+            {@link android.support.v17.leanback.widget.GuidanceStylist} and
+            {@link android.support.v17.leanback.widget.GuidedActionsStylist} to support
+            creating multi-step decision flows.
+          </li>
+        </ul>
+      </dd>
+
+
+      <dt>Changes for <a href="features.html#v7-recyclerview">v7 recyclerview library</a>:</dt>
+      <dd>
+        <ul>
+           <li>Added {@link android.support.v7.util.SortedList} classes to display items in
+             a list order and provide notification of changes to the list.
+           </li>
+           <li>Added the {@link android.support.v7.widget.util.SortedListAdapterCallback} class
+             that can bind a sorted list to a
+             {@link android.support.v7.widget.RecyclerView.Adapter} class.
+           </li>
+        </ul>
+      </dd>
+
+
+      <dt>Changes for v8 renderscript library:</dt>
+      <dd>
+        <ul>
+          <li>Added the {@link android.support.v8.renderscript.ScriptIntrinsicHistogram} class for
+            use as a histogram filter.</li>
+          <li>Added the {@link android.support.v8.renderscript.ScriptIntrinsicResize} class for
+            performing a resize of a 2D allocation.
+          </li>
+        </ul>
+      </dd>
+
+    </dl>
+
+
+  </div>
+</div> <!-- end of collapsible section -->
+
+
+
+<div class="toggle-content closed">
+  <p id="rev21"><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""
 />Android Support Library, revision 22</a> <em>(March 2015)</em>
   </p>
   <div class="toggle-content-toggleme">
@@ -159,6 +364,10 @@
         </ul>
       </dd>
 
+  </div>
+</div>
+
+
 
 
 <div class="toggle-content closed">
@@ -1113,4 +1322,3 @@
       <p>Initial release with the v4 library.</p>
   </div>
 </div>
-
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 48afcbf..2acb8ba 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -81,10 +81,6 @@
      */
     protected int mScreenDensity = Bitmap.DENSITY_NONE;
 
-    // Used by native code
-    @SuppressWarnings("UnusedDeclaration")
-    private int mSurfaceFormat;
-
     /**
      * Flag for drawTextRun indicating left-to-right run direction.
      * @hide
@@ -137,7 +133,7 @@
     public Canvas() {
         if (!isHardwareAccelerated()) {
             // 0 means no native bitmap
-            mNativeCanvasWrapper = initRaster(0);
+            mNativeCanvasWrapper = initRaster(null);
             mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         } else {
             mFinalizer = null;
@@ -158,7 +154,7 @@
             throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
         }
         throwIfCannotDraw(bitmap);
-        mNativeCanvasWrapper = initRaster(bitmap.getSkBitmap());
+        mNativeCanvasWrapper = initRaster(bitmap);
         mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         mBitmap = bitmap;
         mDensity = bitmap.mDensity;
@@ -215,7 +211,7 @@
         }
 
         if (bitmap == null) {
-            native_setBitmap(mNativeCanvasWrapper, 0, false);
+            native_setBitmap(mNativeCanvasWrapper, null);
             mDensity = Bitmap.DENSITY_NONE;
         } else {
             if (!bitmap.isMutable()) {
@@ -223,7 +219,7 @@
             }
             throwIfCannotDraw(bitmap);
 
-            native_setBitmap(mNativeCanvasWrapper, bitmap.getSkBitmap(), true);
+            native_setBitmap(mNativeCanvasWrapper, bitmap);
             mDensity = bitmap.mDensity;
         }
 
@@ -231,13 +227,6 @@
     }
 
     /**
-     * setBitmap() variant for native callers with a raw bitmap handle.
-     */
-    private void setNativeBitmap(long bitmapHandle) {
-        native_setBitmap(mNativeCanvasWrapper, bitmapHandle, false);
-    }
-
-    /**
      * Set the viewport dimensions if this canvas is GL based. If it is not,
      * this method is ignored and no exception is thrown.
      *
@@ -1976,10 +1965,9 @@
      */
     public static native void freeTextLayoutCaches();
 
-    private static native long initRaster(long nativeBitmapOrZero);
+    private static native long initRaster(Bitmap bitmap);
     private static native void native_setBitmap(long canvasHandle,
-                                                long bitmapHandle,
-                                                boolean copyState);
+                                                Bitmap bitmap);
     private static native boolean native_isOpaque(long canvasHandle);
     private static native int native_getWidth(long canvasHandle);
     private static native int native_getHeight(long canvasHandle);
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 8468d9e..3bbbc71 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -86,7 +86,6 @@
 
     LayerState mLayerState;
 
-    private int mOpacityOverride = PixelFormat.UNKNOWN;
     private int[] mPaddingL;
     private int[] mPaddingT;
     private int[] mPaddingR;
@@ -177,12 +176,39 @@
         // Extract the theme attributes, if any.
         state.mThemeAttrs = a.extractThemeAttrs();
 
-        mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, mOpacityOverride);
-
-        state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored,
-                state.mAutoMirrored);
-        state.mPaddingMode = a.getInteger(R.styleable.LayerDrawable_paddingMode,
-                state.mPaddingMode);
+        final int N = a.getIndexCount();
+        for (int i = 0; i < N; i++) {
+            int attr = a.getIndex(i);
+            switch (attr) {
+                case R.styleable.LayerDrawable_opacity:
+                    state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride);
+                    break;
+                case R.styleable.LayerDrawable_paddingTop:
+                    state.mPaddingTop = a.getDimensionPixelOffset(attr, state.mPaddingTop);
+                    break;
+                case R.styleable.LayerDrawable_paddingBottom:
+                    state.mPaddingBottom = a.getDimensionPixelOffset(attr, state.mPaddingBottom);
+                    break;
+                case R.styleable.LayerDrawable_paddingLeft:
+                    state.mPaddingLeft = a.getDimensionPixelOffset(attr, state.mPaddingLeft);
+                    break;
+                case R.styleable.LayerDrawable_paddingRight:
+                    state.mPaddingRight = a.getDimensionPixelOffset(attr, state.mPaddingRight);
+                    break;
+                case R.styleable.LayerDrawable_paddingStart:
+                    state.mPaddingStart = a.getDimensionPixelOffset(attr, state.mPaddingStart);
+                    break;
+                case R.styleable.LayerDrawable_paddingEnd:
+                    state.mPaddingEnd = a.getDimensionPixelOffset(attr, state.mPaddingEnd);
+                    break;
+                case R.styleable.LayerDrawable_autoMirrored:
+                    state.mAutoMirrored = a.getBoolean(attr, state.mAutoMirrored);
+                    break;
+                case R.styleable.LayerDrawable_paddingMode:
+                    state.mPaddingMode = a.getInteger(attr, state.mPaddingMode);
+                    break;
+            }
+        }
     }
 
     /**
@@ -895,15 +921,210 @@
 
     @Override
     public boolean getPadding(Rect padding) {
-        if (mLayerState.mPaddingMode == PADDING_MODE_NEST) {
+        final LayerState layerState = mLayerState;
+        if (layerState.mPaddingMode == PADDING_MODE_NEST) {
             computeNestedPadding(padding);
         } else {
             computeStackedPadding(padding);
         }
 
+        // If padding was explicitly specified (e.g. not -1) then override the
+        // computed padding in that dimension.
+        if (layerState.mPaddingTop >= 0) {
+            padding.top = layerState.mPaddingTop;
+        }
+
+        if (layerState.mPaddingBottom >= 0) {
+            padding.bottom = layerState.mPaddingBottom;
+        }
+
+        final int paddingRtlLeft;
+        final int paddingRtlRight;
+        if (getLayoutDirection() == LayoutDirection.RTL) {
+            paddingRtlLeft = layerState.mPaddingEnd;
+            paddingRtlRight = layerState.mPaddingStart;
+        } else {
+            paddingRtlLeft = layerState.mPaddingStart;
+            paddingRtlRight = layerState.mPaddingEnd;
+        }
+
+        final int paddingLeft =  paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft;
+        if (paddingLeft >= 0) {
+            padding.left = paddingLeft;
+        }
+
+        final int paddingRight =  paddingRtlRight >= 0 ? paddingRtlRight : layerState.mPaddingRight;
+        if (paddingRight >= 0) {
+            padding.right = paddingRight;
+        }
+
         return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
     }
 
+    /**
+     * Sets the absolute padding.
+     * <p>
+     * If padding in a dimension is specified as {@code -1}, the resolved
+     * padding will use the value computed according to the padding mode (see
+     * {@link #setPaddingMode(int)}).
+     * <p>
+     * Calling this method clears any relative padding values previously set
+     * using {@link #setPaddingRelative(int, int, int, int)}.
+     *
+     * @param left the left padding in pixels, or -1 to use computed padding
+     * @param top the top padding in pixels, or -1 to use computed padding
+     * @param right the right padding in pixels, or -1 to use computed padding
+     * @param bottom the bottom padding in pixels, or -1 to use computed
+     *               padding
+     * @attr ref android.R.styleable#LayerDrawable_paddingLeft
+     * @attr ref android.R.styleable#LayerDrawable_paddingTop
+     * @attr ref android.R.styleable#LayerDrawable_paddingRight
+     * @attr ref android.R.styleable#LayerDrawable_paddingBottom
+     * @see #setPaddingRelative(int, int, int, int)
+     */
+    public void setPadding(int left, int top, int right, int bottom) {
+        final LayerState layerState = mLayerState;
+        layerState.mPaddingLeft = left;
+        layerState.mPaddingTop = top;
+        layerState.mPaddingRight = right;
+        layerState.mPaddingBottom = bottom;
+
+        // Clear relative padding values.
+        layerState.mPaddingStart = -1;
+        layerState.mPaddingEnd = -1;
+    }
+
+    /**
+     * Sets the relative padding.
+     * <p>
+     * If padding in a dimension is specified as {@code -1}, the resolved
+     * padding will use the value computed according to the padding mode (see
+     * {@link #setPaddingMode(int)}).
+     * <p>
+     * Calling this method clears any absolute padding values previously set
+     * using {@link #setPadding(int, int, int, int)}.
+     *
+     * @param start the start padding in pixels, or -1 to use computed padding
+     * @param top the top padding in pixels, or -1 to use computed padding
+     * @param end the end padding in pixels, or -1 to use computed padding
+     * @param bottom the bottom padding in pixels, or -1 to use computed
+     *               padding
+     * @attr ref android.R.styleable#LayerDrawable_paddingStart
+     * @attr ref android.R.styleable#LayerDrawable_paddingTop
+     * @attr ref android.R.styleable#LayerDrawable_paddingEnd
+     * @attr ref android.R.styleable#LayerDrawable_paddingBottom
+     * @see #setPadding(int, int, int, int)
+     */
+    public void setPaddingRelative(int start, int top, int end, int bottom) {
+        final LayerState layerState = mLayerState;
+        layerState.mPaddingStart = start;
+        layerState.mPaddingTop = top;
+        layerState.mPaddingEnd = end;
+        layerState.mPaddingBottom = bottom;
+
+        // Clear absolute padding values.
+        layerState.mPaddingLeft = -1;
+        layerState.mPaddingRight = -1;
+    }
+
+    /**
+     * Returns the left padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the left padding in pixels, or -1 if not explicitly specified
+     * @see #setPadding(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getLeftPadding() {
+        return mLayerState.mPaddingLeft;
+    }
+
+    /**
+     * Returns the right padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the right padding in pixels, or -1 if not explicitly specified
+     * @see #setPadding(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getRightPadding() {
+        return mLayerState.mPaddingRight;
+    }
+
+    /**
+     * Returns the start padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the start padding in pixels, or -1 if not explicitly specified
+     * @see #setPaddingRelative(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getStartPadding() {
+        return mLayerState.mPaddingStart;
+    }
+
+    /**
+     * Returns the end padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the end padding in pixels, or -1 if not explicitly specified
+     * @see #setPaddingRelative(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getEndPadding() {
+        return mLayerState.mPaddingEnd;
+    }
+
+    /**
+     * Returns the top padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the top padding in pixels, or -1 if not explicitly specified
+     * @see #setPadding(int, int, int, int)
+     * @see #setPaddingRelative(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getTopPadding() {
+        return mLayerState.mPaddingTop;
+    }
+
+    /**
+     * Returns the bottom padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the bottom padding in pixels, or -1 if not explicitly specified
+     * @see #setPadding(int, int, int, int)
+     * @see #setPaddingRelative(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getBottomPadding() {
+        return mLayerState.mPaddingBottom;
+    }
+
     private void computeNestedPadding(Rect padding) {
         padding.left = 0;
         padding.top = 0;
@@ -1109,8 +1330,8 @@
     }
 
     /**
-     * Sets the opacity of this drawable directly, instead of collecting the
-     * states from the layers
+     * Sets the opacity of this drawable directly instead of collecting the
+     * states from the layers.
      *
      * @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN
      *            PixelFormat.UNKNOWN} for the default behavior
@@ -1120,13 +1341,13 @@
      * @see PixelFormat#OPAQUE
      */
     public void setOpacity(int opacity) {
-        mOpacityOverride = opacity;
+        mLayerState.mOpacityOverride = opacity;
     }
 
     @Override
     public int getOpacity() {
-        if (mOpacityOverride != PixelFormat.UNKNOWN) {
-            return mOpacityOverride;
+        if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
+            return mLayerState.mOpacityOverride;
         }
         return mLayerState.getOpacity();
     }
@@ -1265,12 +1486,12 @@
      * dimension, defaults to START or TOP. Otherwise, defaults to FILL to
      * preserve legacy behavior.
      *
-     * @param gravity
-     * @param width
-     * @param height
-     * @return
+     * @param gravity layer gravity
+     * @param width width of the layer if set, -1 otherwise
+     * @param height height of the layer if set, -1 otherwise
+     * @return the default gravity for the layer
      */
-    private int resolveGravity(int gravity, int width, int height) {
+    private static int resolveGravity(int gravity, int width, int height) {
         if (!Gravity.isHorizontal(gravity)) {
             if (width < 0) {
                 gravity |= Gravity.FILL_HORIZONTAL;
@@ -1504,6 +1725,14 @@
         ChildDrawable[] mChildren;
         int[] mThemeAttrs;
 
+        int mPaddingTop = -1;
+        int mPaddingBottom = -1;
+        int mPaddingLeft = -1;
+        int mPaddingRight = -1;
+        int mPaddingStart = -1;
+        int mPaddingEnd = -1;
+        int mOpacityOverride = PixelFormat.UNKNOWN;
+
         int mChangingConfigurations;
         int mChildrenChangingConfigurations;
 
@@ -1540,6 +1769,13 @@
                 mAutoMirrored = orig.mAutoMirrored;
                 mPaddingMode = orig.mPaddingMode;
                 mThemeAttrs = orig.mThemeAttrs;
+                mPaddingTop = orig.mPaddingTop;
+                mPaddingBottom = orig.mPaddingBottom;
+                mPaddingLeft = orig.mPaddingLeft;
+                mPaddingRight = orig.mPaddingRight;
+                mPaddingStart = orig.mPaddingStart;
+                mPaddingEnd = orig.mPaddingEnd;
+                mOpacityOverride = orig.mOpacityOverride;
             } else {
                 mNum = 0;
                 mChildren = null;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 6731a17..f67dcb3 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -323,7 +323,21 @@
      */
     @Override
     public boolean isProjected() {
-        return getNumberOfLayers() == 0;
+        // If the maximum radius is contained entirely within the bounds, we
+        // don't need to project this ripple.
+        final int radius = mState.mMaxRadius;
+        final Rect bounds = getBounds();
+        if (radius != RADIUS_AUTO && radius <= bounds.width() / 2
+                && radius <= bounds.height() / 2) {
+            return false;
+        }
+
+        // Otherwise, if the layer is bounded then we don't need to project.
+        return !isBounded();
+    }
+
+    private boolean isBounded() {
+        return getNumberOfLayers() > 0;
     }
 
     @Override
@@ -545,7 +559,7 @@
                 y = mHotspotBounds.exactCenterY();
             }
 
-            final boolean isBounded = !isProjected();
+            final boolean isBounded = isBounded();
             mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded);
         }
 
@@ -866,7 +880,7 @@
 
     @Override
     public Rect getDirtyBounds() {
-        if (isProjected()) {
+        if (!isBounded()) {
             final Rect drawingBounds = mDrawingBounds;
             final Rect dirtyBounds = mDirtyBounds;
             dirtyBounds.set(drawingBounds);
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index 7ad0683..aa24673 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -29,7 +29,7 @@
 public:
     virtual ~Canvas() {};
 
-    static Canvas* create_canvas(SkBitmap* bitmap);
+    static Canvas* create_canvas(const SkBitmap& bitmap);
 
     /**
      *  Create a new Canvas object which delegates to an SkCanvas.
@@ -52,7 +52,7 @@
      */
     virtual SkCanvas* asSkCanvas() = 0;
 
-    virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
+    virtual void setBitmap(const SkBitmap& bitmap) = 0;
 
     virtual bool isOpaque() = 0;
     virtual int width() = 0;
@@ -87,7 +87,8 @@
     virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0;
     virtual bool quickRejectPath(const SkPath& path) const = 0;
 
-    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;
+    virtual bool clipRect(float left, float top, float right, float bottom,
+            SkRegion::Op op = SkRegion::kIntersect_Op) = 0;
     virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
     virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
 
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index a9ac57d..2b0b6b2 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -136,7 +136,7 @@
 // ----------------------------------------------------------------------------
     virtual SkCanvas* asSkCanvas() override;
 
-    virtual void setBitmap(SkBitmap* bitmap, bool copyState) override {
+    virtual void setBitmap(const SkBitmap& bitmap) override {
         LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap.");
     }
 
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 8b11757..a323065 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -31,7 +31,7 @@
 // Holds an SkCanvas reference plus additional native data.
 class SkiaCanvas : public Canvas {
 public:
-    explicit SkiaCanvas(SkBitmap* bitmap);
+    explicit SkiaCanvas(const SkBitmap& bitmap);
 
     /**
      *  Create a new SkiaCanvas.
@@ -49,7 +49,7 @@
         return mCanvas.get();
     }
 
-    virtual void setBitmap(SkBitmap* bitmap, bool copyState) override;
+    virtual void setBitmap(const SkBitmap& bitmap) override;
 
     virtual bool isOpaque() override;
     virtual int width() override;
@@ -145,19 +145,7 @@
     SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
 };
 
-// Construct an SkCanvas from the bitmap.
-static SkCanvas* createCanvas(SkBitmap* bitmap) {
-    if (bitmap) {
-        return SkNEW_ARGS(SkCanvas, (*bitmap));
-    }
-
-    // Create an empty bitmap device to prevent callers from crashing
-    // if they attempt to draw into this canvas.
-    SkBitmap emptyBitmap;
-    return new SkCanvas(emptyBitmap);
-}
-
-Canvas* Canvas::create_canvas(SkBitmap* bitmap) {
+Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
     return new SkiaCanvas(bitmap);
 }
 
@@ -165,8 +153,8 @@
     return new SkiaCanvas(skiaCanvas);
 }
 
-SkiaCanvas::SkiaCanvas(SkBitmap* bitmap) {
-    mCanvas.reset(createCanvas(bitmap));
+SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
+    mCanvas.reset(new SkCanvas(bitmap));
 }
 
 // ----------------------------------------------------------------------------
@@ -191,11 +179,11 @@
     SkCanvas* m_dstCanvas;
 };
 
-void SkiaCanvas::setBitmap(SkBitmap* bitmap, bool copyState) {
-    SkCanvas* newCanvas = createCanvas(bitmap);
+void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
+    SkCanvas* newCanvas = new SkCanvas(bitmap);
     SkASSERT(newCanvas);
 
-    if (copyState) {
+    if (!bitmap.isNull()) {
         // Copy the canvas matrix & clip state.
         newCanvas->setMatrix(mCanvas->getTotalMatrix());
         if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index ded9d31..093ff26 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -946,13 +946,30 @@
     }
 
     /**
-     * Returns the current playback rate in Hz.
+     * Returns the current playback sample rate rate in Hz.
      */
     public int getPlaybackRate() {
         return native_get_playback_rate();
     }
 
     /**
+     * Returns the current playback settings.
+     * See {@link #setPlaybackSettings(PlaybackSettings)} to set playback settings
+     * @return current {@link PlaybackSettings}.
+     * @throws IllegalStateException if track is not initialized.
+     */
+    public @NonNull PlaybackSettings getPlaybackSettings() {
+        float[] floatArray = new float[2];
+        int[] intArray = new int[2];
+        native_get_playback_settings(floatArray, intArray);
+        return new PlaybackSettings()
+                .setSpeed(floatArray[0])
+                .setPitch(floatArray[1])
+                .setAudioFallbackMode(intArray[0])
+                .setAudioStretchMode(intArray[1]);
+    }
+
+    /**
      * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT}
      * and {@link AudioFormat#ENCODING_PCM_8BIT}.
      */
@@ -1307,6 +1324,7 @@
      * playback to last twice as long, but will also result in a pitch shift down by one octave.
      * The valid sample rate range is from 1 Hz to twice the value returned by
      * {@link #getNativeOutputSampleRate(int)}.
+     * Use {@link #setPlaybackSettings(PlaybackSettings)} for speed control.
      * @param sampleRateInHz the sample rate expressed in Hz
      * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
      *    {@link #ERROR_INVALID_OPERATION}
@@ -1323,6 +1341,42 @@
 
 
     /**
+     * Sets the playback settings.
+     * This method returns failure if it cannot apply the playback settings.
+     * One possible cause is that the parameters for speed or pitch are out of range.
+     * Another possible cause is that the <code>AudioTrack</code> is streaming
+     * (see {@link #MODE_STREAM}) and the
+     * buffer size is too small. For speeds greater than 1.0f, the <code>AudioTrack</code> buffer
+     * on configuration must be larger than the speed multiplied by the minimum size
+     * {@link #getMinBufferSize(int, int, int)}) to allow proper playback.
+     * @param settings see {@link PlaybackSettings}. In particular,
+     * speed, pitch, and audio mode should be set.
+     * @throws IllegalArgumentException if the settings are invalid or not accepted.
+     * @throws IllegalStateException if track is not initialized.
+     */
+    public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
+        if (settings == null) {
+            throw new IllegalArgumentException("settings is null");
+        }
+        float[] floatArray;
+        int[] intArray;
+        try {
+            floatArray = new float[] {
+                    settings.getSpeed(),
+                    settings.getPitch(),
+            };
+            intArray = new int[] {
+                    settings.getAudioFallbackMode(),
+                    settings.getAudioStretchMode(),
+            };
+        } catch (IllegalStateException e) {
+            throw new IllegalArgumentException(e);
+        }
+        native_set_playback_settings(floatArray, intArray);
+    }
+
+
+    /**
      * Sets the position of the notification marker.  At most one marker can be active.
      * @param markerInFrames marker position in wrapping frame units similar to
      * {@link #getPlaybackHeadPosition}, or zero to disable the marker.
@@ -2207,6 +2261,15 @@
     private native final int native_set_playback_rate(int sampleRateInHz);
     private native final int native_get_playback_rate();
 
+    // floatArray must be a non-null array of length >= 2
+    // [0] is speed
+    // [1] is pitch
+    // intArray must be a non-null array of length >= 2
+    // [0] is audio fallback mode
+    // [1] is audio stretch mode
+    private native final void native_set_playback_settings(float[] floatArray, int[] intArray);
+    private native final void native_get_playback_settings(float[] floatArray, int[] intArray);
+
     private native final int native_set_marker_pos(int marker);
     private native final int native_get_marker_pos();
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d82afdf..b0cd3e4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -16,6 +16,9 @@
 
 package android.media;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.media.Image;
@@ -30,6 +33,8 @@
 import android.view.Surface;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.ReadOnlyBufferException;
 import java.util.Arrays;
@@ -253,7 +258,7 @@
          * {@link #BUFFER_FLAG_END_OF_STREAM}.
          */
         public void set(
-                int newOffset, int newSize, long newTimeUs, int newFlags) {
+                int newOffset, int newSize, long newTimeUs, @BufferFlag int newFlags) {
             offset = newOffset;
             size = newSize;
             presentationTimeUs = newTimeUs;
@@ -293,6 +298,7 @@
          * be an empty buffer, whose sole purpose is to carry the end-of-stream
          * marker.
          */
+        @BufferFlag
         public int flags;
     };
 
@@ -325,6 +331,18 @@
      */
     public static final int BUFFER_FLAG_END_OF_STREAM = 4;
 
+    /** @hide */
+    @IntDef(
+        flag = true,
+        value = {
+            BUFFER_FLAG_SYNC_FRAME,
+            BUFFER_FLAG_KEY_FRAME,
+            BUFFER_FLAG_CODEC_CONFIG,
+            BUFFER_FLAG_END_OF_STREAM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BufferFlag {}
+
     private EventHandler mEventHandler;
     private Callback mCallback;
 
@@ -339,13 +357,13 @@
     private class EventHandler extends Handler {
         private MediaCodec mCodec;
 
-        public EventHandler(MediaCodec codec, Looper looper) {
+        public EventHandler(@NonNull MediaCodec codec, @NonNull Looper looper) {
             super(looper);
             mCodec = codec;
         }
 
         @Override
-        public void handleMessage(Message msg) {
+        public void handleMessage(@NonNull Message msg) {
             switch (msg.what) {
                 case EVENT_CALLBACK:
                 {
@@ -364,7 +382,7 @@
             }
         }
 
-        private void handleCallback(Message msg) {
+        private void handleCallback(@NonNull Message msg) {
             if (mCallback == null) {
                 return;
             }
@@ -438,7 +456,8 @@
      * @throws IllegalArgumentException if type is not a valid mime type.
      * @throws NullPointerException if type is null.
      */
-    public static MediaCodec createDecoderByType(String type)
+    @NonNull
+    public static MediaCodec createDecoderByType(@NonNull String type)
             throws IOException {
         return new MediaCodec(type, true /* nameIsType */, false /* encoder */);
     }
@@ -450,7 +469,8 @@
      * @throws IllegalArgumentException if type is not a valid mime type.
      * @throws NullPointerException if type is null.
      */
-    public static MediaCodec createEncoderByType(String type)
+    @NonNull
+    public static MediaCodec createEncoderByType(@NonNull String type)
             throws IOException {
         return new MediaCodec(type, true /* nameIsType */, true /* encoder */);
     }
@@ -464,14 +484,15 @@
      * @throws IllegalArgumentException if name is not valid.
      * @throws NullPointerException if name is null.
      */
-    public static MediaCodec createByCodecName(String name)
+    @NonNull
+    public static MediaCodec createByCodecName(@NonNull String name)
             throws IOException {
         return new MediaCodec(
                 name, false /* nameIsType */, false /* unused */);
     }
 
     private MediaCodec(
-            String name, boolean nameIsType, boolean encoder) {
+            @NonNull String name, boolean nameIsType, boolean encoder) {
         Looper looper;
         if ((looper = Looper.myLooper()) != null) {
             mEventHandler = new EventHandler(this, looper);
@@ -524,15 +545,26 @@
      */
     public static final int CONFIGURE_FLAG_ENCODE = 1;
 
+    /** @hide */
+    @IntDef(flag = true, value = { CONFIGURE_FLAG_ENCODE })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ConfigureFlag {}
+
     /**
      * Configures a component.
      *
      * @param format The format of the input data (decoder) or the desired
-     *               format of the output data (encoder).
+     *               format of the output data (encoder). Passing {@code null}
+     *               as {@code format} is equivalent to passing an
+     *               {@link MediaFormat#MediaFormat an empty mediaformat}.
      * @param surface Specify a surface on which to render the output of this
-     *                decoder.
+     *                decoder. Pass {@code null} as {@code surface} if the
+     *                codec does not generate raw video output (e.g. not a video
+     *                decoder) and/or if you want to configure the codec for
+     *                {@link ByteBuffer} output.
      * @param crypto  Specify a crypto object to facilitate secure decryption
-     *                of the media data.
+     *                of the media data. Pass {@code null} as {@code crypto} for
+     *                non-secure codecs.
      * @param flags   Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
      *                component as an encoder.
      * @throws IllegalArgumentException if the surface has been released (or is invalid),
@@ -544,14 +576,14 @@
      * @throws CodecException upon codec error.
      */
     public void configure(
-            MediaFormat format,
-            Surface surface, MediaCrypto crypto, int flags) {
-        Map<String, Object> formatMap = format.getMap();
-
+            @Nullable MediaFormat format,
+            @Nullable Surface surface, @Nullable MediaCrypto crypto,
+            @ConfigureFlag int flags) {
         String[] keys = null;
         Object[] values = null;
 
         if (format != null) {
+            Map<String, Object> formatMap = format.getMap();
             keys = new String[formatMap.size()];
             values = new Object[formatMap.size()];
 
@@ -578,11 +610,11 @@
         native_configure(keys, values, surface, crypto, flags);
     }
 
-    private native final void native_setCallback(Callback cb);
+    private native final void native_setCallback(@Nullable Callback cb);
 
     private native final void native_configure(
-            String[] keys, Object[] values,
-            Surface surface, MediaCrypto crypto, int flags);
+            @Nullable String[] keys, @Nullable Object[] values,
+            @Nullable Surface surface, @Nullable MediaCrypto crypto, @ConfigureFlag int flags);
 
     /**
      * Requests a Surface to use as the input to an encoder, in place of input buffers.  This
@@ -596,6 +628,7 @@
      * unexpected results.
      * @throws IllegalStateException if not in the Configured state.
      */
+    @NonNull
     public native final Surface createInputSurface();
 
     /**
@@ -669,7 +702,7 @@
      * Thrown when an internal codec error occurs.
      */
     public final static class CodecException extends IllegalStateException {
-        CodecException(int errorCode, int actionCode, String detailMessage, int reason) {
+        CodecException(int errorCode, int actionCode, @Nullable String detailMessage, int reason) {
             super(detailMessage);
             mErrorCode = errorCode;
             mReason = reason;
@@ -704,6 +737,7 @@
          * The reason could be one of {@link #REASON_HARDWARE} or {@link #REASON_RECLAIMED}.
          *
          */
+        @ReasonCode
         public int getReason() {
             return mReason;
         }
@@ -725,7 +759,7 @@
          * since this string will not be localized or generally
          * comprehensible to end-users.
          */
-        public String getDiagnosticInfo() {
+        public @NonNull String getDiagnosticInfo() {
             return mDiagnosticInfo;
         }
 
@@ -742,6 +776,14 @@
          */
         public static final int REASON_RECLAIMED = 1;
 
+        /** @hide */
+        @IntDef({
+            REASON_HARDWARE,
+            REASON_RECLAIMED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ReasonCode {}
+
         /* Must be in sync with android_media_MediaCodec.cpp */
         private final static int ACTION_TRANSIENT = 1;
         private final static int ACTION_RECOVERABLE = 2;
@@ -756,7 +798,7 @@
      * Thrown when a crypto error occurs while queueing a secure input buffer.
      */
     public final static class CryptoException extends RuntimeException {
-        public CryptoException(int errorCode, String detailMessage) {
+        public CryptoException(int errorCode, @Nullable String detailMessage) {
             super(detailMessage);
             mErrorCode = errorCode;
         }
@@ -789,9 +831,20 @@
          */
         public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4;
 
+        /** @hide */
+        @IntDef({
+            ERROR_NO_KEY,
+            ERROR_KEY_EXPIRED,
+            ERROR_RESOURCE_BUSY,
+            ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface CryptoErrorCode {}
+
         /**
          * Retrieve the error code associated with a CryptoException
          */
+        @CryptoErrorCode
         public int getErrorCode() {
             return mErrorCode;
         }
@@ -885,10 +938,10 @@
     public final static class CryptoInfo {
         public void set(
                 int newNumSubSamples,
-                int[] newNumBytesOfClearData,
-                int[] newNumBytesOfEncryptedData,
-                byte[] newKey,
-                byte[] newIV,
+                @NonNull int[] newNumBytesOfClearData,
+                @NonNull int[] newNumBytesOfEncryptedData,
+                @NonNull byte[] newKey,
+                @NonNull byte[] newIV,
                 int newMode) {
             numSubSamples = newNumSubSamples;
             numBytesOfClearData = newNumBytesOfClearData;
@@ -970,7 +1023,7 @@
     public final void queueSecureInputBuffer(
             int index,
             int offset,
-            CryptoInfo info,
+            @NonNull CryptoInfo info,
             long presentationTimeUs,
             int flags) throws CryptoException {
         synchronized(mBufferLock) {
@@ -989,7 +1042,7 @@
     private native final void native_queueSecureInputBuffer(
             int index,
             int offset,
-            CryptoInfo info,
+            @NonNull CryptoInfo info,
             long presentationTimeUs,
             int flags) throws CryptoException;
 
@@ -1043,6 +1096,15 @@
      */
     public static final int INFO_OUTPUT_BUFFERS_CHANGED = -3;
 
+    /** @hide */
+    @IntDef({
+        INFO_TRY_AGAIN_LATER,
+        INFO_OUTPUT_FORMAT_CHANGED,
+        INFO_OUTPUT_BUFFERS_CHANGED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface OutputBufferInfo {}
+
     /**
      * Dequeue an output buffer, block at most "timeoutUs" microseconds.
      * Returns the index of an output buffer that has been successfully
@@ -1053,8 +1115,9 @@
      *         or codec is configured in asynchronous mode.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @OutputBufferInfo
     public final int dequeueOutputBuffer(
-            BufferInfo info, long timeoutUs) {
+            @NonNull BufferInfo info, long timeoutUs) {
         int res = native_dequeueOutputBuffer(info, timeoutUs);
         synchronized(mBufferLock) {
             if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
@@ -1067,7 +1130,7 @@
     }
 
     private native final int native_dequeueOutputBuffer(
-            BufferInfo info, long timeoutUs);
+            @NonNull BufferInfo info, long timeoutUs);
 
     /**
      * If you are done with a buffer, use this call to return the buffer to
@@ -1176,6 +1239,7 @@
      *                               Configured state.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @NonNull
     public final MediaFormat getOutputFormat() {
         return new MediaFormat(getFormatNative(false /* input */));
     }
@@ -1190,6 +1254,7 @@
      *                               Configured state.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @NonNull
     public final MediaFormat getInputFormat() {
         return new MediaFormat(getFormatNative(true /* input */));
     }
@@ -1203,12 +1268,15 @@
      * @return the format for the output buffer, or null if the index
      * is not a dequeued output buffer.
      */
+    @NonNull
     public final MediaFormat getOutputFormat(int index) {
         return new MediaFormat(getOutputFormatNative(index));
     }
 
+    @NonNull
     private native final Map<String, Object> getFormatNative(boolean input);
 
+    @NonNull
     private native final Map<String, Object> getOutputFormatNative(int index);
 
     // used to track dequeued buffers
@@ -1230,12 +1298,12 @@
                 }
             }
 
-            public void setImage(Image image) {
+            public void setImage(@Nullable Image image) {
                 free();
                 mImage = image;
             }
 
-            public void setByteBuffer(ByteBuffer buffer) {
+            public void setByteBuffer(@Nullable ByteBuffer buffer) {
                 free();
                 mByteBuffer = buffer;
             }
@@ -1252,7 +1320,7 @@
             }
         }
 
-        public void put(int index, ByteBuffer newBuffer) {
+        public void put(int index, @Nullable ByteBuffer newBuffer) {
             CodecBuffer buffer = mMap.get(index);
             if (buffer == null) { // likely
                 buffer = new CodecBuffer();
@@ -1261,7 +1329,7 @@
             buffer.setByteBuffer(newBuffer);
         }
 
-        public void put(int index, Image newImage) {
+        public void put(int index, @Nullable Image newImage) {
             CodecBuffer buffer = mMap.get(index);
             if (buffer == null) { // likely
                 buffer = new CodecBuffer();
@@ -1285,7 +1353,7 @@
     final private Object mBufferLock;
 
     private final void invalidateByteBuffer(
-            ByteBuffer[] buffers, int index) {
+            @Nullable ByteBuffer[] buffers, int index) {
         if (buffers != null && index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
@@ -1295,7 +1363,7 @@
     }
 
     private final void validateInputByteBuffer(
-            ByteBuffer[] buffers, int index) {
+            @Nullable ByteBuffer[] buffers, int index) {
         if (buffers != null && index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
@@ -1306,7 +1374,7 @@
     }
 
     private final void revalidateByteBuffer(
-            ByteBuffer[] buffers, int index) {
+            @Nullable ByteBuffer[] buffers, int index) {
         synchronized(mBufferLock) {
             if (buffers != null && index >= 0 && index < buffers.length) {
                 ByteBuffer buffer = buffers[index];
@@ -1318,7 +1386,7 @@
     }
 
     private final void validateOutputByteBuffer(
-            ByteBuffer[] buffers, int index, BufferInfo info) {
+            @Nullable ByteBuffer[] buffers, int index, @NonNull BufferInfo info) {
         if (buffers != null && index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
@@ -1328,7 +1396,7 @@
         }
     }
 
-    private final void invalidateByteBuffers(ByteBuffer[] buffers) {
+    private final void invalidateByteBuffers(@Nullable ByteBuffer[] buffers) {
         if (buffers != null) {
             for (ByteBuffer buffer: buffers) {
                 if (buffer != null) {
@@ -1338,14 +1406,14 @@
         }
     }
 
-    private final void freeByteBuffer(ByteBuffer buffer) {
+    private final void freeByteBuffer(@Nullable ByteBuffer buffer) {
         if (buffer != null /* && buffer.isDirect() */) {
             // all of our ByteBuffers are direct
             java.nio.NioUtils.freeDirectBuffer(buffer);
         }
     }
 
-    private final void freeByteBuffers(ByteBuffer[] buffers) {
+    private final void freeByteBuffers(@Nullable ByteBuffer[] buffers) {
         if (buffers != null) {
             for (ByteBuffer buffer: buffers) {
                 freeByteBuffer(buffer);
@@ -1388,13 +1456,14 @@
      * @deprecated Use the new {@link #getInputBuffer} method instead
      * each time an input buffer is dequeued.
      *
-     * <b>Note:</b>As of API 21, dequeued input buffers are
+     * <b>Note:</b> As of API 21, dequeued input buffers are
      * automatically {@link java.nio.Buffer#clear cleared}.
      *
      * @throws IllegalStateException if not in the Executing state,
      *         or codec is configured in asynchronous mode.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @NonNull
     public ByteBuffer[] getInputBuffers() {
         if (mCachedInputBuffers == null) {
             throw new IllegalStateException();
@@ -1415,7 +1484,7 @@
      * each time an output buffer is dequeued.  This method is not
      * supported if codec is configured in asynchronous mode.
      *
-     * <b>Note:</b>As of API 21, the position and limit of output
+     * <b>Note:</b> As of API 21, the position and limit of output
      * buffers that are dequeued will be set to the valid data
      * range.
      *
@@ -1423,6 +1492,7 @@
      *         or codec is configured in asynchronous mode.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @NonNull
     public ByteBuffer[] getOutputBuffers() {
         if (mCachedOutputBuffers == null) {
             throw new IllegalStateException();
@@ -1449,6 +1519,7 @@
      * @throws IllegalStateException if not in the Executing state.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @Nullable
     public ByteBuffer getInputBuffer(int index) {
         ByteBuffer newBuffer = getBuffer(true /* input */, index);
         synchronized(mBufferLock) {
@@ -1477,6 +1548,7 @@
      * @throws IllegalStateException if not in the Executing state.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @Nullable
     public Image getInputImage(int index) {
         Image newImage = getImage(true /* input */, index);
         synchronized(mBufferLock) {
@@ -1505,6 +1577,7 @@
      * @throws IllegalStateException if not in the Executing state.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @Nullable
     public ByteBuffer getOutputBuffer(int index) {
         ByteBuffer newBuffer = getBuffer(false /* input */, index);
         synchronized(mBufferLock) {
@@ -1532,6 +1605,7 @@
      * @throws IllegalStateException if not in the Executing state.
      * @throws MediaCodec.CodecException upon codec error.
      */
+    @Nullable
     public Image getOutputImage(int index) {
         Image newImage = getImage(false /* input */, index);
         synchronized(mBufferLock) {
@@ -1552,19 +1626,28 @@
      */
     public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
 
+    /** @hide */
+    @IntDef({
+        VIDEO_SCALING_MODE_SCALE_TO_FIT,
+        VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VideoScalingMode {}
+
     /**
      * If a surface has been specified in a previous call to {@link #configure}
      * specifies the scaling mode to use. The default is "scale to fit".
      * @throws IllegalArgumentException if mode is not recognized.
      * @throws IllegalStateException if in the Uninitialized state.
      */
-    public native final void setVideoScalingMode(int mode);
+    public native final void setVideoScalingMode(@VideoScalingMode int mode);
 
     /**
      * Get the component name. If the codec was created by createDecoderByType
      * or createEncoderByType, what component is chosen is not known beforehand.
      * @throws IllegalStateException if in the Uninitialized state.
      */
+    @NonNull
     public native final String getName();
 
     /**
@@ -1592,9 +1675,12 @@
 
     /**
      * Communicate additional parameter changes to the component instance.
+     * <b>Note:</b> Some of these parameter changes may silently fail to apply.
+     *
+     * @param params The bundle of parameters to set.
      * @throws IllegalStateException if in the Uninitialized state.
      */
-    public final void setParameters(Bundle params) {
+    public final void setParameters(@Nullable Bundle params) {
         if (params == null) {
             return;
         }
@@ -1626,9 +1712,11 @@
      * {@code flush}, you must call {@link #start} to "resume" receiving input buffers,
      * even if an input surface was created.
      *
-     * @param cb The callback that will run.
+     * @param cb The callback that will run.  Use {@code null} to clear a previously
+     *           set callback (before {@link #configure configure} is called and run
+     *           in synchronous mode).
      */
-    public void setCallback(/* MediaCodec. */ Callback cb) {
+    public void setCallback(@Nullable /* MediaCodec. */ Callback cb) {
         if (mEventHandler != null) {
             // set java callback on handler
             Message msg = mEventHandler.obtainMessage(EVENT_SET_CALLBACK, 0, 0, cb);
@@ -1652,7 +1740,7 @@
          * @param codec The MediaCodec object.
          * @param index The index of the available input buffer.
          */
-        public abstract void onInputBufferAvailable(MediaCodec codec, int index);
+        public abstract void onInputBufferAvailable(@NonNull MediaCodec codec, int index);
 
         /**
          * Called when an output buffer becomes available.
@@ -1661,7 +1749,8 @@
          * @param index The index of the available output buffer.
          * @param info Info regarding the available output buffer {@link MediaCodec.BufferInfo}.
          */
-        public abstract void onOutputBufferAvailable(MediaCodec codec, int index, BufferInfo info);
+        public abstract void onOutputBufferAvailable(
+                @NonNull MediaCodec codec, int index, @NonNull BufferInfo info);
 
         /**
          * Called when the MediaCodec encountered an error
@@ -1669,7 +1758,7 @@
          * @param codec The MediaCodec object.
          * @param e The {@link MediaCodec.CodecException} object describing the error.
          */
-        public abstract void onError(MediaCodec codec, CodecException e);
+        public abstract void onError(@NonNull MediaCodec codec, @NonNull CodecException e);
 
         /**
          * Called when the output format has changed
@@ -1677,18 +1766,19 @@
          * @param codec The MediaCodec object.
          * @param format The new output format.
          */
-        public abstract void onOutputFormatChanged(MediaCodec codec, MediaFormat format);
+        public abstract void onOutputFormatChanged(
+                @NonNull MediaCodec codec, @NonNull MediaFormat format);
     }
 
     private void postEventFromNative(
-            int what, int arg1, int arg2, Object obj) {
+            int what, int arg1, int arg2, @Nullable Object obj) {
         if (mEventHandler != null) {
             Message msg = mEventHandler.obtainMessage(what, arg1, arg2, obj);
             mEventHandler.sendMessage(msg);
         }
     }
 
-    private native final void setParameters(String[] keys, Object[] values);
+    private native final void setParameters(@NonNull String[] keys, @NonNull Object[] values);
 
     /**
      * Get the codec info. If the codec was created by createDecoderByType
@@ -1696,20 +1786,24 @@
      * and thus the caller does not have the MediaCodecInfo.
      * @throws IllegalStateException if in the Uninitialized state.
      */
+    @NonNull
     public MediaCodecInfo getCodecInfo() {
         return MediaCodecList.getInfoFor(getName());
     }
 
+    @NonNull
     private native final ByteBuffer[] getBuffers(boolean input);
 
+    @Nullable
     private native final ByteBuffer getBuffer(boolean input, int index);
 
+    @Nullable
     private native final Image getImage(boolean input, int index);
 
     private static native final void native_init();
 
     private native final void native_setup(
-            String name, boolean nameIsType, boolean encoder);
+            @NonNull String name, boolean nameIsType, boolean encoder);
 
     private native final void native_finalize();
 
@@ -1756,6 +1850,7 @@
             return mTimestamp;
         }
 
+        @NonNull
         public Plane[] getPlanes() {
             checkValid();
             return Arrays.copyOf(mPlanes, mPlanes.length);
@@ -1774,7 +1869,7 @@
          * The crop rectangle specifies the region of valid pixels in the image,
          * using coordinates in the largest-resolution plane.
          */
-        public void setCropRect(Rect cropRect) {
+        public void setCropRect(@Nullable Rect cropRect) {
             if (mIsReadOnly) {
                 throw new ReadOnlyBufferException();
             }
@@ -1787,7 +1882,7 @@
             }
         }
 
-        private int readInt(ByteBuffer buffer, boolean asLong) {
+        private int readInt(@NonNull ByteBuffer buffer, boolean asLong) {
             if (asLong) {
                 return (int)buffer.getLong();
             } else {
@@ -1796,8 +1891,8 @@
         }
 
         public MediaImage(
-                ByteBuffer buffer, ByteBuffer info, boolean readOnly,
-                long timestamp, int xOffset, int yOffset, Rect cropRect) {
+                @NonNull ByteBuffer buffer, @NonNull ByteBuffer info, boolean readOnly,
+                long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
             mFormat = ImageFormat.YUV_420_888;
             mTimestamp = timestamp;
             mIsValid = true;
@@ -1863,7 +1958,7 @@
         }
 
         private class MediaPlane extends Plane {
-            public MediaPlane(ByteBuffer buffer, int rowInc, int colInc) {
+            public MediaPlane(@NonNull ByteBuffer buffer, int rowInc, int colInc) {
                 mData = buffer;
                 mRowInc = rowInc;
                 mColInc = colInc;
@@ -1882,6 +1977,7 @@
             }
 
             @Override
+            @NonNull
             public ByteBuffer getBuffer() {
                 checkValid();
                 return mData;
diff --git a/media/java/android/media/MediaCrypto.java b/media/java/android/media/MediaCrypto.java
index da81b37..474d8b9 100644
--- a/media/java/android/media/MediaCrypto.java
+++ b/media/java/android/media/MediaCrypto.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.media.MediaCryptoException;
 import java.util.UUID;
 
@@ -34,11 +35,12 @@
      * this device.
      * @param uuid The UUID of the crypto scheme.
      */
-    public static final boolean isCryptoSchemeSupported(UUID uuid) {
+    public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) {
         return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid));
     }
 
-    private static final byte[] getByteArrayFromUUID(UUID uuid) {
+    @NonNull
+    private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
         long msb = uuid.getMostSignificantBits();
         long lsb = uuid.getLeastSignificantBits();
 
@@ -51,7 +53,7 @@
         return uuidBytes;
     }
 
-    private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid);
+    private static final native boolean isCryptoSchemeSupportedNative(@NonNull byte[] uuid);
 
     /**
      * Instantiate a MediaCrypto object using opaque, crypto scheme specific
@@ -59,7 +61,7 @@
      * @param uuid The UUID of the crypto scheme.
      * @param initData Opaque initialization data specific to the crypto scheme.
      */
-    public MediaCrypto(UUID uuid, byte[] initData) throws MediaCryptoException {
+    public MediaCrypto(@NonNull UUID uuid, @NonNull byte[] initData) throws MediaCryptoException {
         native_setup(getByteArrayFromUUID(uuid), initData);
     }
 
@@ -68,7 +70,7 @@
      * to decode data of the given mime type.
      * @param mime The mime type of the media data
      */
-    public final native boolean requiresSecureDecoderComponent(String mime);
+    public final native boolean requiresSecureDecoderComponent(@NonNull String mime);
 
     /**
      * Associate a MediaDrm session with this MediaCrypto instance.  The
@@ -81,7 +83,7 @@
      * MediaCrypto instance
      * @throws MediaCryptoException on failure to set the sessionId
      */
-    public final native void setMediaDrmSession(byte[] sessionId)
+    public final native void setMediaDrmSession(@NonNull byte[] sessionId)
         throws MediaCryptoException;
 
     @Override
@@ -92,7 +94,7 @@
     public native final void release();
     private static native final void native_init();
 
-    private native final void native_setup(byte[] uuid, byte[] initData)
+    private native final void native_setup(@NonNull byte[] uuid, @NonNull byte[] initData)
         throws MediaCryptoException;
 
     private native final void native_finalize();
diff --git a/media/java/android/media/MediaCryptoException.java b/media/java/android/media/MediaCryptoException.java
index 703e96f..32ddf47 100644
--- a/media/java/android/media/MediaCryptoException.java
+++ b/media/java/android/media/MediaCryptoException.java
@@ -16,12 +16,14 @@
 
 package android.media;
 
+import android.annotation.Nullable;
+
 /**
  * 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) {
+    public MediaCryptoException(@Nullable String detailMessage) {
         super(detailMessage);
     }
 }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index fc5fc43..acff301 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -16,11 +16,17 @@
 
 package android.media;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.UUID;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.os.Handler;
 import android.os.Looper;
@@ -104,6 +110,8 @@
     private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
 
     private EventHandler mEventHandler;
+    private EventHandler mOnKeysChangeEventHandler;
+    private EventHandler mOnExpirationUpdateEventHandler;
     private OnEventListener mOnEventListener;
     private OnKeysChangeListener mOnKeysChangeListener;
     private OnExpirationUpdateListener mOnExpirationUpdateListener;
@@ -124,12 +132,20 @@
      */
     public static final int CERTIFICATE_TYPE_X509 = 1;
 
+    /** @hide */
+    @IntDef({
+        CERTIFICATE_TYPE_NONE,
+        CERTIFICATE_TYPE_X509,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CertificateType {}
+
     /**
      * Query if the given scheme identified by its UUID is supported on
      * this device.
      * @param uuid The UUID of the crypto scheme.
      */
-    public static final boolean isCryptoSchemeSupported(UUID uuid) {
+    public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) {
         return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null);
     }
 
@@ -141,11 +157,12 @@
      * @param mimeType The MIME type of the media container, e.g. "video/mp4"
      *   or "video/webm"
      */
-    public static final boolean isCryptoSchemeSupported(UUID uuid, String mimeType) {
+    public static final boolean isCryptoSchemeSupported(
+            @NonNull UUID uuid, @NonNull String mimeType) {
         return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType);
     }
 
-    private static final byte[] getByteArrayFromUUID(UUID uuid) {
+    private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
         long msb = uuid.getMostSignificantBits();
         long lsb = uuid.getLeastSignificantBits();
 
@@ -158,8 +175,8 @@
         return uuidBytes;
     }
 
-    private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid,
-            String mimeType);
+    private static final native boolean isCryptoSchemeSupportedNative(
+            @NonNull byte[] uuid, @Nullable String mimeType);
 
     /**
      * Instantiate a MediaDrm object
@@ -169,7 +186,7 @@
      * @throws UnsupportedSchemeException if the device does not support the
      * specified scheme UUID
      */
-    public MediaDrm(UUID uuid) throws UnsupportedSchemeException {
+    public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException {
         Looper looper;
         if ((looper = Looper.myLooper()) != null) {
             mEventHandler = new EventHandler(this, looper);
@@ -198,7 +215,7 @@
         /**
          * @hide
          */
-        public MediaDrmStateException(int errorCode, String detailMessage) {
+        public MediaDrmStateException(int errorCode, @Nullable String detailMessage) {
             super(detailMessage);
             mErrorCode = errorCode;
 
@@ -224,6 +241,7 @@
          * since this string will not be localized or generally comprehensible
          * to end-users.
          */
+        @NonNull
         public String getDiagnosticInfo() {
             return mDiagnosticInfo;
         }
@@ -233,13 +251,13 @@
      * Register a callback to be invoked when a session expiration update
      * occurs.  The app's OnExpirationUpdateListener will be notified
      * when the expiration time of the keys in the session have changed.
-     * @param listener the callback that will be run
+     * @param listener the callback that will be run, or {@code null} to unregister the
+     *     previously registered callback.
      * @param handler the handler on which the listener should be invoked, or
-     *     null if the listener should be invoked on the calling thread's looper.
+     *     {@code null} if the listener should be invoked on the calling thread's looper.
      */
-    public void setOnExpirationUpdateListener(OnExpirationUpdateListener listener,
-            Handler handler)
-    {
+    public void setOnExpirationUpdateListener(
+            @Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) {
         if (listener != null) {
             Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
             if (looper != null) {
@@ -266,20 +284,21 @@
          * @param expirationTime the new expiration time for the keys in the session.
          *     The time is in milliseconds, relative to the Unix epoch.
          */
-        void onExpirationUpdate(MediaDrm md, byte[] sessionId, long expirationTime);
+        void onExpirationUpdate(
+                @NonNull MediaDrm md, @NonNull byte[] sessionId, long expirationTime);
     }
 
     /**
      * Register a callback to be invoked when the state of keys in a session
      * change, e.g. when a license update occurs or when a license expires.
      *
-     * @param listener the callback that will be run when key status changes
+     * @param listener the callback that will be run when key status changes, or
+     *     {@code null} to unregister the previously registered callback.
      * @param handler the handler on which the listener should be invoked, or
      *     null if the listener should be invoked on the calling thread's looper.
      */
-    public void setOnKeysChangeListener(OnKeysChangeListener listener,
-            Handler handler)
-    {
+    public void setOnKeysChangeListener(
+            @Nullable OnKeysChangeListener listener, @Nullable Handler handler) {
         if (listener != null) {
             Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
             if (looper != null) {
@@ -309,7 +328,9 @@
          *     which may trigger an attempt to resume playback on the media stream
          *     if it is currently blocked waiting for a key.
          */
-        void onKeysChange(MediaDrm md, byte[] sessionId, List<KeyStatus> keyInformation,
+        void onKeysChange(
+                @NonNull MediaDrm md, @NonNull byte[] sessionId,
+                @NonNull List<KeyStatus> keyInformation,
                 boolean hasNewUsableKey);
     }
 
@@ -344,6 +365,16 @@
      */
     public static final int KEY_STATUS_INTERNAL_ERROR = 4;
 
+    /** @hide */
+    @IntDef({
+        KEY_STATUS_USABLE,
+        KEY_STATUS_EXPIRED,
+        KEY_STATUS_OUTPUT_NOT_ALLOWED,
+        KEY_STATUS_PENDING,
+        KEY_STATUS_INTERNAL_ERROR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface KeyStatusCode {}
 
     /**
      * Defines the status of a key.
@@ -355,7 +386,7 @@
         private final byte[] mKeyId;
         private final int mStatusCode;
 
-        KeyStatus(byte[] keyId, int statusCode) {
+        KeyStatus(@NonNull byte[] keyId, @KeyStatusCode int statusCode) {
             mKeyId = keyId;
             mStatusCode = statusCode;
         }
@@ -363,20 +394,23 @@
         /**
          * Returns the status code for the key
          */
+        @KeyStatusCode
         public int getStatusCode() { return mStatusCode; }
 
         /**
          * Returns the id for the key
          */
+        @NonNull
         public byte[] getKeyId() { return mKeyId; }
     }
 
     /**
      * Register a callback to be invoked when an event occurs
      *
-     * @param listener the callback that will be run
+     * @param listener the callback that will be run.  Use {@code null} to
+     *        stop receiving event callbacks.
      */
-    public void setOnEventListener(OnEventListener listener)
+    public void setOnEventListener(@Nullable OnEventListener listener)
     {
         mOnEventListener = listener;
     }
@@ -391,12 +425,16 @@
          * Called when an event occurs that requires the app to be notified
          *
          * @param md the MediaDrm object on which the event occurred
-         * @param sessionId the DRM session ID on which the event occurred
+         * @param sessionId the DRM session ID on which the event occurred,
+         *        or {@code null} if there is no session ID associated with the event.
          * @param event indicates the event type
          * @param extra an secondary error code
          * @param data optional byte array of data that may be associated with the event
          */
-        void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data);
+        void onEvent(
+                @NonNull MediaDrm md, @Nullable byte[] sessionId,
+                @DrmEvent int event, int extra,
+                @Nullable byte[] data);
     }
 
     /**
@@ -433,6 +471,17 @@
      */
     public static final int EVENT_SESSION_RECLAIMED = 5;
 
+    /** @hide */
+    @IntDef({
+        EVENT_PROVISION_REQUIRED,
+        EVENT_KEY_REQUIRED,
+        EVENT_KEY_EXPIRED,
+        EVENT_VENDOR_DEFINED,
+        EVENT_SESSION_RECLAIMED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DrmEvent {}
+
     private static final int DRM_EVENT = 200;
     private static final int EXPIRATION_UPDATE = 201;
     private static final int KEYS_CHANGE = 202;
@@ -441,13 +490,13 @@
     {
         private MediaDrm mMediaDrm;
 
-        public EventHandler(MediaDrm md, Looper looper) {
+        public EventHandler(@NonNull MediaDrm md, @NonNull Looper looper) {
             super(looper);
             mMediaDrm = md;
         }
 
         @Override
-        public void handleMessage(Message msg) {
+        public void handleMessage(@NonNull Message msg) {
             if (mMediaDrm.mNativeContext == 0) {
                 Log.w(TAG, "MediaDrm went away with unhandled events");
                 return;
@@ -516,7 +565,8 @@
     /**
      * Parse a list of KeyStatus objects from an event parcel
      */
-    private List<KeyStatus> keyStatusListFromParcel(Parcel parcel) {
+    @NonNull
+    private List<KeyStatus> keyStatusListFromParcel(@NonNull Parcel parcel) {
         int nelems = parcel.readInt();
         List<KeyStatus> keyStatusList = new ArrayList(nelems);
         while (nelems-- > 0) {
@@ -534,8 +584,8 @@
      * code is safe from the object disappearing from underneath it.  (This is
      * the cookie passed to native_setup().)
      */
-    private static void postEventFromNative(Object mediadrm_ref,
-            int what, int eventType, int extra, Object obj)
+    private static void postEventFromNative(@NonNull Object mediadrm_ref,
+            int what, int eventType, int extra, @Nullable Object obj)
     {
         MediaDrm md = (MediaDrm)((WeakReference<MediaDrm>)mediadrm_ref).get();
         if (md == null) {
@@ -553,6 +603,7 @@
      * @throws NotProvisionedException if provisioning is needed
      * @throws ResourceBusyException if required resources are in use
      */
+    @NonNull
     public native byte[] openSession() throws NotProvisionedException,
             ResourceBusyException;
 
@@ -560,7 +611,7 @@
      * Close a session on the MediaDrm object that was previously opened
      * with {@link #openSession}.
      */
-    public native void closeSession(byte[] sessionId);
+    public native void closeSession(@NonNull byte[] sessionId);
 
     /**
      * This key request type species that the keys will be for online use, they will
@@ -580,6 +631,15 @@
      */
     public static final int KEY_TYPE_RELEASE = 3;
 
+    /** @hide */
+    @IntDef({
+        KEY_TYPE_STREAMING,
+        KEY_TYPE_OFFLINE,
+        KEY_TYPE_RELEASE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface KeyType {}
+
     /**
      * Key request type is initial license request
      */
@@ -595,6 +655,15 @@
      */
     public static final int REQUEST_TYPE_RELEASE = 2;
 
+    /** @hide */
+    @IntDef({
+        REQUEST_TYPE_INITIAL,
+        REQUEST_TYPE_RENEWAL,
+        REQUEST_TYPE_RELEASE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RequestType {}
+
     /**
      * Contains the opaque data an app uses to request keys from a license server
      */
@@ -608,18 +677,38 @@
         /**
          * Get the opaque message data
          */
-        public byte[] getData() { return mData; }
+        @NonNull
+        public byte[] getData() {
+            if (mData == null) {
+                // this should never happen as mData is initialized in
+                // JNI after construction of the KeyRequest object. The check
+                // is needed here to guarantee @NonNull annotation.
+                throw new RuntimeException("KeyRequest is not initialized");
+            }
+            return mData;
+        }
 
         /**
          * Get the default URL to use when sending the key request message to a
          * server, if known.  The app may prefer to use a different license
          * server URL from other sources.
+         * This method returns an empty string if the default URL is not known.
          */
-        public String getDefaultUrl() { return mDefaultUrl; }
+        @NonNull
+        public String getDefaultUrl() {
+            if (mDefaultUrl == null) {
+                // this should never happen as mDefaultUrl is initialized in
+                // JNI after construction of the KeyRequest object. The check
+                // is needed here to guarantee @NonNull annotation.
+                throw new RuntimeException("KeyRequest is not initialized");
+            }
+            return mDefaultUrl;
+        }
 
         /**
          * Get the type of the request
          */
+        @RequestType
         public int getRequestType() { return mRequestType; }
     };
 
@@ -652,12 +741,15 @@
      * keys, which are identified by a keySetId.
      * @param optionalParameters are included in the key request message to
      * allow a client application to provide additional message parameters to the server.
-     *
+     * This may be {@code null} if no additional parameters are to be sent.
      * @throws NotProvisionedException if reprovisioning is needed, due to a
      * problem with the certifcate
      */
-    public native KeyRequest getKeyRequest(byte[] scope, byte[] init,
-            String mimeType, int keyType, HashMap<String, String> optionalParameters)
+    @NonNull
+    public native KeyRequest getKeyRequest(
+            @NonNull byte[] scope, @Nullable byte[] init,
+            @Nullable String mimeType, @KeyType int keyType,
+            @Nullable HashMap<String, String> optionalParameters)
             throws NotProvisionedException;
 
 
@@ -680,7 +772,9 @@
      * @throws DeniedByServerException if the response indicates that the
      * server rejected the request
      */
-    public native byte[] provideKeyResponse(byte[] scope, byte[] response)
+    @Nullable
+    public native byte[] provideKeyResponse(
+            @NonNull byte[] scope, @NonNull byte[] response)
             throws NotProvisionedException, DeniedByServerException;
 
 
@@ -691,14 +785,14 @@
      * @param sessionId the session ID for the DRM session
      * @param keySetId identifies the saved key set to restore
      */
-    public native void restoreKeys(byte[] sessionId, byte[] keySetId);
+    public native void restoreKeys(@NonNull byte[] sessionId, @NonNull byte[] keySetId);
 
     /**
      * Remove the current keys from a session.
      *
      * @param sessionId the session ID for the DRM session
      */
-    public native void removeKeys(byte[] sessionId);
+    public native void removeKeys(@NonNull byte[] sessionId);
 
     /**
      * Request an informative description of the key status for the session.  The status is
@@ -709,7 +803,8 @@
      *
      * @param sessionId the session ID for the DRM session
      */
-    public native HashMap<String, String> queryKeyStatus(byte[] sessionId);
+    @NonNull
+    public native HashMap<String, String> queryKeyStatus(@NonNull byte[] sessionId);
 
     /**
      * Contains the opaque data an app uses to request a certificate from a provisioning
@@ -721,14 +816,33 @@
         /**
          * Get the opaque message data
          */
-        public byte[] getData() { return mData; }
+        @NonNull
+        public byte[] getData() {
+            if (mData == null) {
+                // this should never happen as mData is initialized in
+                // JNI after construction of the KeyRequest object. The check
+                // is needed here to guarantee @NonNull annotation.
+                throw new RuntimeException("ProvisionRequest is not initialized");
+            }
+            return mData;
+        }
 
         /**
          * Get the default URL to use when sending the provision request
          * message to a server, if known. The app may prefer to use a different
          * provisioning server URL obtained from other sources.
+         * This method returns an empty string if the default URL is not known.
          */
-        public String getDefaultUrl() { return mDefaultUrl; }
+        @NonNull
+        public String getDefaultUrl() {
+            if (mDefaultUrl == null) {
+                // this should never happen as mDefaultUrl is initialized in
+                // JNI after construction of the ProvisionRequest object. The check
+                // is needed here to guarantee @NonNull annotation.
+                throw new RuntimeException("ProvisionRequest is not initialized");
+            }
+            return mDefaultUrl;
+        }
 
         private byte[] mData;
         private String mDefaultUrl;
@@ -743,12 +857,14 @@
      * is returned in ProvisionRequest.data. The recommended URL to deliver the provision
      * request to is returned in ProvisionRequest.defaultUrl.
      */
+    @NonNull
     public ProvisionRequest getProvisionRequest() {
         return getProvisionRequestNative(CERTIFICATE_TYPE_NONE, "");
     }
 
+    @NonNull
     private native ProvisionRequest getProvisionRequestNative(int certType,
-            String certAuthority);
+           @NonNull String certAuthority);
 
     /**
      * After a provision response is received by the app, it is provided to the DRM
@@ -760,12 +876,14 @@
      * @throws DeniedByServerException if the response indicates that the
      * server rejected the request
      */
-    public void provideProvisionResponse(byte[] response)
+    public void provideProvisionResponse(@NonNull byte[] response)
             throws DeniedByServerException {
         provideProvisionResponseNative(response);
     }
 
-    private native Certificate provideProvisionResponseNative(byte[] response)
+    @NonNull
+    /* could there be a valid response with 0-sized certificate or key? */
+    private native Certificate provideProvisionResponseNative(@NonNull byte[] response)
             throws DeniedByServerException;
 
     /**
@@ -795,6 +913,7 @@
      * record on the client is only removed after positive confirmation that the server
      * received the message using releaseSecureStops().
      */
+    @NonNull
     public native List<byte[]> getSecureStops();
 
     /**
@@ -802,7 +921,8 @@
      *
      * @param ssid - The secure stop ID provided by the license server.
      */
-    public native byte[] getSecureStop(byte[] ssid);
+    @NonNull
+    public native byte[] getSecureStop(@NonNull byte[] ssid);
 
     /**
      * Process the SecureStop server response message ssRelease.  After authenticating
@@ -810,7 +930,7 @@
      *
      * @param ssRelease the server response indicating which secure stops to release
      */
-    public native void releaseSecureStops(byte[] ssRelease);
+    public native void releaseSecureStops(@NonNull byte[] ssRelease);
 
     /**
      * Remove all secure stops without requiring interaction with the server.
@@ -839,6 +959,16 @@
      */
     public static final String PROPERTY_ALGORITHMS = "algorithms";
 
+    /** @hide */
+    @StringDef({
+        PROPERTY_VENDOR,
+        PROPERTY_VERSION,
+        PROPERTY_DESCRIPTION,
+        PROPERTY_ALGORITHMS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StringProperty {}
+
     /**
      * Read a DRM engine plugin String property value, given the property name string.
      * <p>
@@ -846,51 +976,68 @@
      * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION},
      * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS}
      */
-    public native String getPropertyString(String propertyName);
-
+    /* FIXME this throws IllegalStateException for invalid property names */
+    @NonNull
+    public native String getPropertyString(@NonNull @StringProperty String propertyName);
 
     /**
      * Byte array property name: the device unique identifier is established during
      * device provisioning and provides a means of uniquely identifying each device.
      */
+    /* FIXME this throws IllegalStateException for invalid property names */
     public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
 
+    /** @hide */
+    @StringDef({
+        PROPERTY_DEVICE_UNIQUE_ID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ArrayProperty {}
+
     /**
      * Read a DRM engine plugin byte array property value, given the property name string.
      * <p>
      * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID}
      */
-    public native byte[] getPropertyByteArray(String propertyName);
-
+    @NonNull
+    public native byte[] getPropertyByteArray(@ArrayProperty String propertyName);
 
     /**
      * Set a DRM engine plugin String property value.
      */
-    public native void setPropertyString(String propertyName, String value);
+    public native void setPropertyString(
+            @StringProperty String propertyName, @NonNull String value);
 
     /**
      * Set a DRM engine plugin byte array property value.
      */
-    public native void setPropertyByteArray(String propertyName, byte[] value);
+    public native void setPropertyByteArray(
+            @ArrayProperty String propertyName, @NonNull byte[] value);
 
+    private static final native void setCipherAlgorithmNative(
+            @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
 
-    private static final native void setCipherAlgorithmNative(MediaDrm drm, byte[] sessionId,
-            String algorithm);
+    private static final native void setMacAlgorithmNative(
+            @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
 
-    private static final native void setMacAlgorithmNative(MediaDrm drm, byte[] sessionId,
-            String algorithm);
+    @NonNull
+    private static final native byte[] encryptNative(
+            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+            @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv);
 
-    private static final native byte[] encryptNative(MediaDrm drm, byte[] sessionId,
-            byte[] keyId, byte[] input, byte[] iv);
+    @NonNull
+    private static final native byte[] decryptNative(
+            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+            @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv);
 
-    private static final native byte[] decryptNative(MediaDrm drm, byte[] sessionId,
-            byte[] keyId, byte[] input, byte[] iv);
+    @NonNull
+    private static final native byte[] signNative(
+            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+            @NonNull byte[] keyId, @NonNull byte[] message);
 
-    private static final native byte[] signNative(MediaDrm drm, byte[] sessionId,
-            byte[] keyId, byte[] message);
-
-    private static final native boolean verifyNative(MediaDrm drm, byte[] sessionId,
-            byte[] keyId, byte[] message, byte[] signature);
+    private static final native boolean verifyNative(
+            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+            @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature);
 
     /**
      * In addition to supporting decryption of DASH Common Encrypted Media, the
@@ -919,8 +1066,8 @@
         private MediaDrm mDrm;
         private byte[] mSessionId;
 
-        CryptoSession(MediaDrm drm, byte[] sessionId,
-                String cipherAlgorithm, String macAlgorithm)
+        CryptoSession(@NonNull MediaDrm drm, @NonNull byte[] sessionId,
+                @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
         {
             mSessionId = sessionId;
             mDrm = drm;
@@ -935,7 +1082,9 @@
          * @param input the data to encrypt
          * @param iv the initialization vector to use for the cipher
          */
-        public byte[] encrypt(byte[] keyid, byte[] input, byte[] iv) {
+        @NonNull
+        public byte[] encrypt(
+                @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
             return encryptNative(mDrm, mSessionId, keyid, input, iv);
         }
 
@@ -946,7 +1095,9 @@
          * @param input the data to encrypt
          * @param iv the initialization vector to use for the cipher
          */
-        public byte[] decrypt(byte[] keyid, byte[] input, byte[] iv) {
+        @NonNull
+        public byte[] decrypt(
+                @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
             return decryptNative(mDrm, mSessionId, keyid, input, iv);
         }
 
@@ -956,7 +1107,8 @@
          * @param keyid specifies which key to use
          * @param message the data for which a signature is to be computed
          */
-        public byte[] sign(byte[] keyid, byte[] message) {
+        @NonNull
+        public byte[] sign(@NonNull byte[] keyid, @NonNull byte[] message) {
             return signNative(mDrm, mSessionId, keyid, message);
         }
 
@@ -969,7 +1121,8 @@
          * @param signature the reference signature which will be compared with the
          *        computed signature
          */
-        public boolean verify(byte[] keyid, byte[] message, byte[] signature) {
+        public boolean verify(
+                @NonNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature) {
             return verifyNative(mDrm, mSessionId, keyid, message, signature);
         }
     };
@@ -994,8 +1147,9 @@
      * using the method {@link #getPropertyString} with the property name
      * "algorithms".
      */
-    public CryptoSession getCryptoSession(byte[] sessionId,
-            String cipherAlgorithm, String macAlgorithm)
+    public CryptoSession getCryptoSession(
+            @NonNull byte[] sessionId,
+            @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
     {
         return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm);
     }
@@ -1010,7 +1164,7 @@
         private byte[] mData;
         private String mDefaultUrl;
 
-        CertificateRequest(byte[] data, String defaultUrl) {
+        CertificateRequest(@NonNull byte[] data, @NonNull String defaultUrl) {
             mData = data;
             mDefaultUrl = defaultUrl;
         }
@@ -1018,6 +1172,7 @@
         /**
          * Get the opaque message data
          */
+        @NonNull
         public byte[] getData() { return mData; }
 
         /**
@@ -1025,6 +1180,7 @@
          * message to a server, if known. The app may prefer to use a different
          * certificate server URL obtained from other sources.
          */
+        @NonNull
         public String getDefaultUrl() { return mDefaultUrl; }
     }
 
@@ -1040,8 +1196,9 @@
      *
      * @hide - not part of the public API at this time
      */
-    public CertificateRequest getCertificateRequest(int certType,
-            String certAuthority)
+    @NonNull
+    public CertificateRequest getCertificateRequest(
+            @CertificateType int certType, @NonNull String certAuthority)
     {
         ProvisionRequest provisionRequest = getProvisionRequestNative(certType, certAuthority);
         return new CertificateRequest(provisionRequest.getData(),
@@ -1060,12 +1217,30 @@
         /**
          * Get the wrapped private key data
          */
-        public byte[] getWrappedPrivateKey() { return mWrappedKey; }
+        @NonNull
+        public byte[] getWrappedPrivateKey() {
+            if (mWrappedKey == null) {
+                // this should never happen as mWrappedKey is initialized in
+                // JNI after construction of the KeyRequest object. The check
+                // is needed here to guarantee @NonNull annotation.
+                throw new RuntimeException("Cerfificate is not initialized");
+            }
+            return mWrappedKey;
+        }
 
         /**
          * Get the PEM-encoded certificate chain
          */
-        public byte[] getContent() { return mCertificateData; }
+        @NonNull
+        public byte[] getContent() {
+            if (mCertificateData == null) {
+                // this should never happen as mCertificateData is initialized in
+                // JNI after construction of the KeyRequest object. The check
+                // is needed here to guarantee @NonNull annotation.
+                throw new RuntimeException("Cerfificate is not initialized");
+            }
+            return mCertificateData;
+        }
 
         private byte[] mWrappedKey;
         private byte[] mCertificateData;
@@ -1089,13 +1264,16 @@
      *
      * @hide - not part of the public API at this time
      */
-    public Certificate provideCertificateResponse(byte[] response)
+    @NonNull
+    public Certificate provideCertificateResponse(@NonNull byte[] response)
             throws DeniedByServerException {
         return provideProvisionResponseNative(response);
     }
 
-    private static final native byte[] signRSANative(MediaDrm drm, byte[] sessionId,
-            String algorithm, byte[] wrappedKey, byte[] message);
+    @NonNull
+    private static final native byte[] signRSANative(
+            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+            @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message);
 
     /**
      * Sign data using an RSA key
@@ -1108,8 +1286,10 @@
      *
      * @hide - not part of the public API at this time
      */
-    public byte[] signRSA(byte[] sessionId, String algorithm,
-            byte[] wrappedKey, byte[] message) {
+    @NonNull
+    public byte[] signRSA(
+            @NonNull byte[] sessionId, @NonNull String algorithm,
+            @NonNull byte[] wrappedKey, @NonNull byte[] message) {
         return signRSANative(this, sessionId, algorithm, wrappedKey, message);
     }
 
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index b4acbc0..0bf995f 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -16,6 +16,9 @@
 
 package android.media;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -27,6 +30,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.HashMap;
@@ -69,21 +74,26 @@
      * Sets the data source (MediaDataSource) to use.
      *
      * @param dataSource the MediaDataSource for the media you want to extract from
+     *
+     * @throws IllegalArgumentException if dataSource is invalid.
      */
-    public native final void setDataSource(MediaDataSource dataSource) throws IllegalArgumentException, IOException;
+    public native final void setDataSource(@NonNull MediaDataSource dataSource)
+        throws IOException;
 
     /**
      * Sets the data source as a content Uri.
      *
      * @param context the Context to use when resolving the Uri
      * @param uri the Content URI of the data you want to extract from.
-     * @param headers the headers to be sent together with the request for the data
+     * @param headers the headers to be sent together with the request for the data.
+     *        This can be {@code null} if no specific headers are to be sent with the
+     *        request.
      */
     public final void setDataSource(
-            Context context, Uri uri, Map<String, String> headers)
+            @NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers)
         throws IOException {
         String scheme = uri.getScheme();
-        if(scheme == null || scheme.equals("file")) {
+        if (scheme == null || scheme.equals("file")) {
             setDataSource(uri.getPath());
             return;
         }
@@ -122,9 +132,11 @@
      * Sets the data source (file-path or http URL) to use.
      *
      * @param path the path of the file, or the http URL
-     * @param headers the headers associated with the http request for the stream you want to play
+     * @param headers the headers associated with the http request for the stream you want to play.
+     *        This can be {@code null} if no specific headers are to be sent with the
+     *        request.
      */
-    public final void setDataSource(String path, Map<String, String> headers)
+    public final void setDataSource(@NonNull String path, @Nullable Map<String, String> headers)
         throws IOException {
         String[] keys = null;
         String[] values = null;
@@ -149,10 +161,10 @@
     }
 
     private native final void nativeSetDataSource(
-            IBinder httpServiceBinder,
-            String path,
-            String[] keys,
-            String[] values) throws IOException;
+            @NonNull IBinder httpServiceBinder,
+            @NonNull String path,
+            @Nullable String[] keys,
+            @Nullable String[] values) throws IOException;
 
     /**
      * Sets the data source (file-path or http URL) to use.
@@ -166,7 +178,7 @@
      * As an alternative, the application could first open the file for reading,
      * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
      */
-    public final void setDataSource(String path) throws IOException {
+    public final void setDataSource(@NonNull String path) throws IOException {
         nativeSetDataSource(
                 MediaHTTPService.createHttpServiceBinderIfNecessary(path),
                 path,
@@ -180,7 +192,7 @@
      *
      * @param fd the FileDescriptor for the file you want to extract from.
      */
-    public final void setDataSource(FileDescriptor fd) throws IOException {
+    public final void setDataSource(@NonNull FileDescriptor fd) throws IOException {
         setDataSource(fd, 0, 0x7ffffffffffffffL);
     }
 
@@ -194,7 +206,7 @@
      * @param length the length in bytes of the data to be extracted
      */
     public native final void setDataSource(
-            FileDescriptor fd, long offset, long length) throws IOException;
+            @NonNull FileDescriptor fd, long offset, long length) throws IOException;
 
     @Override
     protected void finalize() {
@@ -217,7 +229,9 @@
      * Get the PSSH info if present.
      * @return a map of uuid-to-bytes, with the uuid specifying
      * the crypto scheme, and the bytes being the data specific to that scheme.
+     * This can be {@code null} if the source does not contain PSSH info.
      */
+    @Nullable
     public Map<UUID, byte[]> getPsshInfo() {
         Map<UUID, byte[]> psshMap = null;
         Map<String, Object> formatMap = getFileFormatNative();
@@ -243,16 +257,19 @@
         return psshMap;
     }
 
+    @NonNull
     private native Map<String, Object> getFileFormatNative();
 
     /**
      * Get the track format at the specified index.
      * More detail on the representation can be found at {@link android.media.MediaCodec}
      */
+    @NonNull
     public MediaFormat getTrackFormat(int index) {
         return new MediaFormat(getTrackFormatNative(index));
     }
 
+    @NonNull
     private native Map<String, Object> getTrackFormatNative(int index);
 
     /**
@@ -284,11 +301,20 @@
      */
     public static final int SEEK_TO_CLOSEST_SYNC        = 2;
 
+    /** @hide */
+    @IntDef({
+        SEEK_TO_PREVIOUS_SYNC,
+        SEEK_TO_NEXT_SYNC,
+        SEEK_TO_CLOSEST_SYNC,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SeekMode {}
+
     /**
      * All selected tracks seek near the requested time according to the
      * specified mode.
      */
-    public native void seekTo(long timeUs, int mode);
+    public native void seekTo(long timeUs, @SeekMode int mode);
 
     /**
      * Advance to the next sample. Returns false if no more sample data
@@ -305,7 +331,7 @@
      * @param byteBuf the destination byte buffer
      * @return the sample size (or -1 if no more samples are available).
      */
-    public native int readSampleData(ByteBuffer byteBuf, int offset);
+    public native int readSampleData(@NonNull ByteBuffer byteBuf, int offset);
 
     /**
      * Returns the track index the current sample originates from (or -1
@@ -334,9 +360,20 @@
      */
     public static final int SAMPLE_FLAG_ENCRYPTED = 2;
 
+    /** @hide */
+    @IntDef(
+        flag = true,
+        value = {
+            SAMPLE_FLAG_SYNC,
+            SAMPLE_FLAG_ENCRYPTED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SampleFlag {}
+
     /**
      * Returns the current sample's flags.
      */
+    @SampleFlag
     public native int getSampleFlags();
 
     /**
@@ -347,7 +384,7 @@
      *             to be filled in.
      * @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED}
      */
-    public native boolean getSampleCryptoInfo(MediaCodec.CryptoInfo info);
+    public native boolean getSampleCryptoInfo(@NonNull MediaCodec.CryptoInfo info);
 
     /**
      * Returns an estimate of how much data is presently cached in memory
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index f518ab2..4b6b4fa 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -16,12 +16,18 @@
 
 package android.media;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
 import dalvik.system.CloseGuard;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.util.Map;
 
@@ -80,19 +86,27 @@
         public static final int MUXER_OUTPUT_WEBM   = 1;
     };
 
+    /** @hide */
+    @IntDef({
+        OutputFormat.MUXER_OUTPUT_MPEG_4,
+        OutputFormat.MUXER_OUTPUT_WEBM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Format {}
+
     // All the native functions are listed here.
-    private static native long nativeSetup(FileDescriptor fd, int format);
+    private static native long nativeSetup(@NonNull FileDescriptor fd, int format);
     private static native void nativeRelease(long nativeObject);
     private static native void nativeStart(long nativeObject);
     private static native void nativeStop(long nativeObject);
-    private static native int nativeAddTrack(long nativeObject, String[] keys,
-            Object[] values);
-    private static native void nativeSetOrientationHint(long nativeObject,
-            int degrees);
+    private static native int nativeAddTrack(
+            long nativeObject, @NonNull String[] keys, @NonNull Object[] values);
+    private static native void nativeSetOrientationHint(
+            long nativeObject, int degrees);
     private static native void nativeSetLocation(long nativeObject, int latitude, int longitude);
-    private static native void nativeWriteSampleData(long nativeObject,
-            int trackIndex, ByteBuffer byteBuf,
-            int offset, int size, long presentationTimeUs, int flags);
+    private static native void nativeWriteSampleData(
+            long nativeObject, int trackIndex, @NonNull ByteBuffer byteBuf,
+            int offset, int size, long presentationTimeUs, @MediaCodec.BufferFlag int flags);
 
     // Muxer internal states.
     private static final int MUXER_STATE_UNINITIALIZED  = -1;
@@ -115,7 +129,7 @@
      * @see android.media.MediaMuxer.OutputFormat
      * @throws IOException if failed to open the file for write
      */
-    public MediaMuxer(String path, int format) throws IOException {
+    public MediaMuxer(@NonNull String path, @Format int format) throws IOException {
         if (path == null) {
             throw new IllegalArgumentException("path must not be null");
         }
@@ -246,11 +260,12 @@
 
     /**
      * Adds a track with the specified format.
-     * @param format The media format for the track.
+     * @param format The media format for the track.  This must not be an empty
+     *               MediaFormat.
      * @return The track index for this newly added track, and it should be used
      * in the {@link #writeSampleData}.
      */
-    public int addTrack(MediaFormat format) {
+    public int addTrack(@NonNull MediaFormat format) {
         if (format == null) {
             throw new IllegalArgumentException("format must not be null.");
         }
@@ -302,8 +317,8 @@
      * MediaMuxer uses the flags provided in {@link MediaCodec.BufferInfo},
      * to signal sync frames.
      */
-    public void writeSampleData(int trackIndex, ByteBuffer byteBuf,
-            BufferInfo bufferInfo) {
+    public void writeSampleData(int trackIndex, @NonNull ByteBuffer byteBuf,
+            @NonNull BufferInfo bufferInfo) {
         if (trackIndex < 0 || trackIndex > mLastTrackIndex) {
             throw new IllegalArgumentException("trackIndex is invalid");
         }
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index e6bc10d..74a2fb2 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.media.AudioTrack;
 import android.os.Handler;
 import android.os.Looper;
@@ -120,9 +121,10 @@
          *
          * @param sync The MediaSync object.
          * @param audioBuffer The returned audio buffer.
+         * @param bufferIndex The index associated with the audio buffer
          */
         public abstract void onReturnAudioBuffer(
-                MediaSync sync, ByteBuffer audioBuffer, int bufferIndex);
+                @NonNull MediaSync sync, @NonNull ByteBuffer audioBuffer, int bufferIndex);
     }
 
     private static final String TAG = "MediaSync";
@@ -138,7 +140,7 @@
         public int mSizeInBytes;
         long mPresentationTimeUs;
 
-        public AudioBuffer(ByteBuffer byteBuffer, int bufferIndex,
+        public AudioBuffer(@NonNull ByteBuffer byteBuffer, int bufferIndex,
                            int sizeInBytes, long presentationTimeUs) {
             mByteBuffer = byteBuffer;
             mBufferIndex = bufferIndex;
@@ -200,13 +202,17 @@
 
     /**
      * Sets an asynchronous callback for actionable MediaSync events.
-     * It shouldn't be called inside callback.
+     * <p>
+     * This method can be called multiple times to update a previously set callback. If the
+     * handler is changed, undelivered notifications scheduled for the old handler may be dropped.
+     * <p>
+     * <b>Do not call this inside callback.</b>
      *
-     * @param cb The callback that will run.
-     * @param handler The Handler that will run the callback. Using null means to use MediaSync's
+     * @param cb The callback that will run. Use {@code null} to stop receiving callbacks.
+     * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's
      *     internal handler if it exists.
      */
-    public void setCallback(/* MediaSync. */ Callback cb, Handler handler) {
+    public void setCallback(@Nullable /* MediaSync. */ Callback cb, @Nullable Handler handler) {
         synchronized(mCallbackLock) {
             if (handler != null) {
                 mCallbackHandler = handler;
@@ -235,11 +241,11 @@
      * @throws IllegalStateException if not in the Initialized state, or another surface
      *     has already been configured.
      */
-    public void configureSurface(Surface surface) {
+    public void configureSurface(@Nullable Surface surface) {
         native_configureSurface(surface);
     }
 
-    private native final void native_configureSurface(Surface surface);
+    private native final void native_configureSurface(@Nullable Surface surface);
 
     /**
      * Configures the audio track for MediaSync.
@@ -249,7 +255,7 @@
      * @throws IllegalStateException if not in the Initialized state, or another audio track
      *     has already been configured.
      */
-    public void configureAudioTrack(AudioTrack audioTrack) {
+    public void configureAudioTrack(@Nullable AudioTrack audioTrack) {
         // AudioTrack has sanity check for configured sample rate.
         int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate());
 
@@ -261,7 +267,7 @@
     }
 
     private native final void native_configureAudioTrack(
-            AudioTrack audioTrack, int nativeSampleRateInHz);
+            @Nullable AudioTrack audioTrack, int nativeSampleRateInHz);
 
     /**
      * Requests a Surface to use as the input. This may only be called after
@@ -272,6 +278,7 @@
      * @throws IllegalStateException if not configured, or another input surface has
      *     already been created.
      */
+    @NonNull
     public native final Surface createInputSurface();
 
     /**
@@ -412,7 +419,7 @@
         return native_getTimestamp(timestamp);
     }
 
-    private native final boolean native_getTimestamp(MediaTimestamp timestamp);
+    private native final boolean native_getTimestamp(@NonNull MediaTimestamp timestamp);
 
     /**
      * Queues the audio data asynchronously for playback (AudioTrack must be in streaming mode).
@@ -427,7 +434,8 @@
      *     has not been done correctly.
      */
     public void queueAudio(
-            ByteBuffer audioData, int bufferIndex, int sizeInBytes, long presentationTimeUs) {
+            @NonNull ByteBuffer audioData, int bufferIndex, int sizeInBytes,
+            long presentationTimeUs) {
         if (mAudioTrack == null || mAudioThread == null) {
             throw new IllegalStateException(
                     "AudioTrack is NOT configured or audio thread is not created");
@@ -489,7 +497,7 @@
     private native final void native_updateQueuedAudioData(
             int sizeInBytes, long presentationTimeUs);
 
-    private final void postReturnByteBuffer(final AudioBuffer audioBuffer) {
+    private final void postReturnByteBuffer(@NonNull final AudioBuffer audioBuffer) {
         synchronized(mCallbackLock) {
             if (mCallbackHandler != null) {
                 final MediaSync sync = this;
diff --git a/media/java/android/media/PlaybackSettings.java b/media/java/android/media/PlaybackSettings.java
new file mode 100644
index 0000000..ceb6bb1
--- /dev/null
+++ b/media/java/android/media/PlaybackSettings.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 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.media;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import android.annotation.IntDef;
+
+/**
+ * Structure for common playback settings.
+ *
+ * Used by {@link AudioTrack} {@link AudioTrack#getPlaybackSettings()} and
+ * {@link AudioTrack#setPlaybackSettings(PlaybackSettings)}
+ * to control playback behavior.
+ * <p> <strong>audio fallback mode:</strong>
+ * select out-of-range parameter handling.
+ * <ul>
+ * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_DEFAULT}:
+ *   System will determine best handling. </li>
+ * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_MUTE}:
+ *   Play silence for settings normally out of range.</li>
+ * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_FAIL}:
+ *   Return {@link java.lang.IllegalArgumentException} from
+ *   <code>AudioTrack.setPlaybackSettings(PlaybackSettings)</code>.</li>
+ * </ul>
+ * <p> <strong>audio stretch mode:</strong> select
+ * timestretch handling.
+ * <ul>
+ * <li> {@link PlaybackSettings#AUDIO_STRETCH_MODE_DEFAULT}:
+ *   System will determine best selection. </li>
+ * <li> {@link PlaybackSettings#AUDIO_STRETCH_MODE_VOICE}:
+ *   Content is primarily voice.</li>
+ * </ul>
+ * <p> <strong>pitch:</strong> increases or decreases the tonal frequency of the audio content.
+ * It is expressed as a multiplicative factor, where normal pitch is 1.0f.
+ * <p> <strong>speed:</strong> increases or decreases the time to
+ * play back a set of audio or video frames.
+ * It is expressed as a multiplicative factor, where normal speed is 1.0f.
+ * <p> Different combinations of speed and pitch may be used for audio playback;
+ * some common ones:
+ * <ul>
+ * <li> <em>Pitch equals 1.0f.</em> Speed change will be done with pitch preserved,
+ * often called <em>timestretching</em>.</li>
+ * <li> <em>Pitch equals speed.</em> Speed change will be done by <em>resampling</em>,
+ * similar to {@link AudioTrack#setPlaybackRate(int)}.</li>
+ * </ul>
+ */
+public final class PlaybackSettings {
+    /** @hide */
+    @IntDef(
+        value = {
+                AUDIO_FALLBACK_MODE_DEFAULT,
+                AUDIO_FALLBACK_MODE_MUTE,
+                AUDIO_FALLBACK_MODE_FAIL,
+        }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AudioFallbackMode {}
+    public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0;
+    public static final int AUDIO_FALLBACK_MODE_MUTE = 1;
+    public static final int AUDIO_FALLBACK_MODE_FAIL = 2;
+
+    /** @hide */
+    @IntDef(
+        value = {
+                AUDIO_STRETCH_MODE_DEFAULT,
+                AUDIO_STRETCH_MODE_VOICE,
+        }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AudioStretchMode {}
+    public static final int AUDIO_STRETCH_MODE_DEFAULT = 0;
+    public static final int AUDIO_STRETCH_MODE_VOICE = 1;
+
+    // flags to indicate which settings are actually set
+    private static final int SET_SPEED               = 1 << 0;
+    private static final int SET_PITCH               = 1 << 1;
+    private static final int SET_AUDIO_FALLBACK_MODE = 1 << 2;
+    private static final int SET_AUDIO_STRETCH_MODE  = 1 << 3;
+    private int mSet = 0;
+
+    // settings
+    private int mAudioFallbackMode = AUDIO_FALLBACK_MODE_DEFAULT;
+    private int mAudioStretchMode = AUDIO_STRETCH_MODE_DEFAULT;
+    private float mPitch = 1.0f;
+    private float mSpeed = 1.0f;
+
+    /**
+     * Allows defaults to be returned for properties not set.
+     * Otherwise a {@link java.lang.IllegalArgumentException} exception
+     * is raised when getting those properties
+     * which have defaults but have never been set.
+     * @return this <code>PlaybackSettings</code> instance.
+     */
+    public PlaybackSettings allowDefaults() {
+        mSet |= SET_AUDIO_FALLBACK_MODE | SET_AUDIO_STRETCH_MODE | SET_PITCH | SET_SPEED;
+        return this;
+    }
+
+    /**
+     * Sets the audio fallback mode.
+     * @param audioFallbackMode
+     * @return this <code>PlaybackSettings</code> instance.
+     */
+    public PlaybackSettings setAudioFallbackMode(@AudioFallbackMode int audioFallbackMode) {
+        mAudioFallbackMode = audioFallbackMode;
+        mSet |= SET_AUDIO_FALLBACK_MODE;
+        return this;
+    }
+
+    /**
+     * Retrieves the audio fallback mode.
+     * @return audio fallback mode
+     * @throws IllegalStateException if the audio fallback mode is not set.
+     */
+    public @AudioFallbackMode int getAudioFallbackMode() {
+        if ((mSet & SET_AUDIO_FALLBACK_MODE) == 0) {
+            throw new IllegalStateException("audio fallback mode not set");
+        }
+        return mAudioFallbackMode;
+    }
+
+    /**
+     * Sets the audio stretch mode.
+     * @param audioStretchMode
+     * @return this <code>PlaybackSettings</code> instance.
+     */
+    public PlaybackSettings setAudioStretchMode(@AudioStretchMode int audioStretchMode) {
+        mAudioStretchMode = audioStretchMode;
+        mSet |= SET_AUDIO_STRETCH_MODE;
+        return this;
+    }
+
+    /**
+     * Retrieves the audio stretch mode.
+     * @return audio stretch mode
+     * @throws IllegalStateException if the audio stretch mode is not set.
+     */
+    public @AudioStretchMode int getAudioStretchMode() {
+        if ((mSet & SET_AUDIO_STRETCH_MODE) == 0) {
+            throw new IllegalStateException("audio stretch mode not set");
+        }
+        return mAudioStretchMode;
+    }
+
+    /**
+     * Sets the pitch factor.
+     * @param pitch
+     * @return this <code>PlaybackSettings</code> instance.
+     */
+    public PlaybackSettings setPitch(float pitch) {
+        mPitch = pitch;
+        mSet |= SET_PITCH;
+        return this;
+    }
+
+    /**
+     * Retrieves the pitch factor.
+     * @return pitch
+     * @throws IllegalStateException if pitch is not set.
+     */
+    public float getPitch() {
+        if ((mSet & SET_PITCH) == 0) {
+            throw new IllegalStateException("pitch not set");
+        }
+        return mPitch;
+    }
+
+    /**
+     * Sets the speed factor.
+     * @param speed
+     * @return this <code>PlaybackSettings</code> instance.
+     */
+    public PlaybackSettings setSpeed(float speed) {
+        mSpeed = speed;
+        mSet |= SET_SPEED;
+        return this;
+    }
+
+    /**
+     * Retrieves the speed factor.
+     * @return speed
+     * @throws IllegalStateException if speed is not set.
+     */
+    public float getSpeed() {
+        if ((mSet & SET_SPEED) == 0) {
+            throw new IllegalStateException("speed not set");
+        }
+        return mSpeed;
+    }
+}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 8cf9874..c8464c7 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -43,7 +43,7 @@
     libusbhost \
     libjhead \
     libexif \
-    libstagefright_amrnb_common \
+    libstagefright_amrnb_common
 
 LOCAL_REQUIRED_MODULES := \
     libjhead_jni
@@ -55,6 +55,7 @@
     external/libexif/ \
     external/tremor/Tremor \
     frameworks/base/core/jni \
+    frameworks/base/libs/hwui \
     frameworks/av/media/libmedia \
     frameworks/av/media/libstagefright \
     frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
index 91c9ac6..f89a5af 100644
--- a/native/graphics/jni/Android.mk
+++ b/native/graphics/jni/Android.mk
@@ -24,7 +24,8 @@
 
 LOCAL_C_INCLUDES += \
     frameworks/base/native/include \
-    frameworks/base/core/jni/android/graphics
+    frameworks/base/core/jni/android/graphics \
+    frameworks/base/libs/hwui
 
 LOCAL_MODULE:= libjnigraphics
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 8039b71..efe71d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -16,17 +16,47 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
+import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.Executor;
 
+import libcore.io.IoUtils;
 import android.app.Activity;
 import android.app.Fragment;
-import android.content.pm.ResolveInfo;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Root;
+import android.util.Log;
 import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnActionExpandListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.widget.SearchView.OnQueryTextListener;
+import android.widget.TextView;
 
+import com.android.documentsui.RecentsProvider.ResumeColumns;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
@@ -34,20 +64,125 @@
 import com.google.common.collect.Maps;
 
 abstract class BaseActivity extends Activity {
+
+    static final String EXTRA_STATE = "state";
+
+    private final String mTag;
+    RootsCache mRoots;
+
     public abstract State getDisplayState();
-    public abstract RootInfo getCurrentRoot();
-    public abstract void onStateChanged();
-    public abstract void setRootsDrawerOpen(boolean open);
     public abstract void onDocumentPicked(DocumentInfo doc);
     public abstract void onDocumentsPicked(List<DocumentInfo> docs);
-    public abstract DocumentInfo getCurrentDirectory();
-    public abstract void setPending(boolean pending);
-    public abstract void onStackPicked(DocumentStack stack);
-    public abstract void onPickRequested(DocumentInfo pickTarget);
-    public abstract void onAppPicked(ResolveInfo info);
-    public abstract void onRootPicked(RootInfo root, boolean closeDrawer);
-    public abstract void onSaveRequested(DocumentInfo replaceTarget);
-    public abstract void onSaveRequested(String mimeType, String displayName);
+    abstract void onTaskFinished(Uri... uris);
+    abstract void onDirectoryChanged(int anim);
+    abstract void updateActionBar();
+    abstract void saveStackBlocking();
+
+    public BaseActivity(String tag) {
+        mTag = tag;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mRoots = DocumentsApplication.getRootsCache(this);
+    }
+
+    void onStackRestored(boolean restored, boolean external) {}
+
+    void onRootPicked(RootInfo root) {
+        State state = getDisplayState();
+
+        // Clear entire backstack and start in new root
+        state.stack.root = root;
+        state.stack.clear();
+        state.stackTouched = true;
+
+        if (!mRoots.isRecentsRoot(root)) {
+            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
+        } else {
+            onCurrentDirectoryChanged(ANIM_SIDE);
+        }
+    }
+
+    void expandMenus(Menu menu) {
+        for (int i = 0; i < menu.size(); i++) {
+            final MenuItem item = menu.getItem(i);
+            switch (item.getItemId()) {
+                case R.id.menu_advanced:
+                case R.id.menu_file_size:
+                    break;
+                default:
+                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+            }
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final int id = item.getItemId();
+        if (id == android.R.id.home) {
+            onBackPressed();
+            return true;
+        } else if (id == R.id.menu_create_dir) {
+            CreateDirectoryFragment.show(getFragmentManager());
+            return true;
+        } else if (id == R.id.menu_search) {
+            return false;
+        } else if (id == R.id.menu_sort_name) {
+            setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
+            return true;
+        } else if (id == R.id.menu_sort_date) {
+            setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
+            return true;
+        } else if (id == R.id.menu_sort_size) {
+            setUserSortOrder(State.SORT_ORDER_SIZE);
+            return true;
+        } else if (id == R.id.menu_grid) {
+            setUserMode(State.MODE_GRID);
+            return true;
+        } else if (id == R.id.menu_list) {
+            setUserMode(State.MODE_LIST);
+            return true;
+        } else if (id == R.id.menu_advanced) {
+            setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
+            return true;
+        } else if (id == R.id.menu_file_size) {
+            setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
+            return true;
+        } else if (id == R.id.menu_settings) {
+            final RootInfo root = getCurrentRoot();
+            final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
+            intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
+                    DocumentsContract.Root.MIME_TYPE_ITEM);
+            startActivity(intent);
+            return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * Call this when directory changes. Prior to root fragment update
+     * the (abstract) directoryChanged method will be called.
+     * @param anim
+     */
+    final void onCurrentDirectoryChanged(int anim) {
+        onDirectoryChanged(anim);
+
+        final RootsFragment roots = RootsFragment.get(getFragmentManager());
+        if (roots != null) {
+            roots.onCurrentRootChanged();
+        }
+
+        updateActionBar();
+        invalidateOptionsMenu();
+    }
+
+    final String getCallingPackageMaybeExtra() {
+        final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
+        return (extra != null) ? extra : getCallingPackage();
+    }
 
     public static BaseActivity get(Fragment fragment) {
         return (BaseActivity) fragment.getActivity();
@@ -169,4 +304,382 @@
             }
         };
     }
+
+    void setDisplayAdvancedDevices(boolean display) {
+        State state = getDisplayState();
+        LocalPreferences.setDisplayAdvancedDevices(this, display);
+        state.showAdvanced = state.forceAdvanced | display;
+        RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
+        invalidateOptionsMenu();
+    }
+
+    void setDisplayFileSize(boolean display) {
+        LocalPreferences.setDisplayFileSize(this, display);
+        getDisplayState().showSize = display;
+        DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
+        invalidateOptionsMenu();
+    }
+
+    void onStateChanged() {
+        invalidateOptionsMenu();
+    }
+
+    /**
+     * Set state sort order based on explicit user action.
+     */
+    void setUserSortOrder(int sortOrder) {
+        getDisplayState().userSortOrder = sortOrder;
+        DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
+    }
+
+    /**
+     * Set state mode based on explicit user action.
+     */
+    void setUserMode(int mode) {
+        getDisplayState().userMode = mode;
+        DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
+    }
+
+    void setPending(boolean pending) {
+        final SaveFragment save = SaveFragment.get(getFragmentManager());
+        if (save != null) {
+            save.setPending(pending);
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle state) {
+        super.onSaveInstanceState(state);
+        state.putParcelable(EXTRA_STATE, getDisplayState());
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle state) {
+        super.onRestoreInstanceState(state);
+    }
+
+    RootInfo getCurrentRoot() {
+        State state = getDisplayState();
+        if (state.stack.root != null) {
+            return state.stack.root;
+        } else {
+            return mRoots.getRecentsRoot();
+        }
+    }
+
+    public DocumentInfo getCurrentDirectory() {
+        return getDisplayState().stack.peek();
+    }
+
+    public Executor getCurrentExecutor() {
+        final DocumentInfo cwd = getCurrentDirectory();
+        if (cwd != null && cwd.authority != null) {
+            return ProviderExecutor.forAuthority(cwd.authority);
+        } else {
+            return AsyncTask.THREAD_POOL_EXECUTOR;
+        }
+    }
+
+    public void onStackPicked(DocumentStack stack) {
+        try {
+            // Update the restored stack to ensure we have freshest data
+            stack.updateDocuments(getContentResolver());
+
+            State state = getDisplayState();
+            state.stack = stack;
+            state.stackTouched = true;
+            onCurrentDirectoryChanged(ANIM_SIDE);
+
+        } catch (FileNotFoundException e) {
+            Log.w(mTag, "Failed to restore stack: " + e);
+        }
+    }
+
+    final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+        private RootInfo mRoot;
+
+        public PickRootTask(RootInfo root) {
+            mRoot = root;
+        }
+
+        @Override
+        protected DocumentInfo doInBackground(Void... params) {
+            try {
+                final Uri uri = DocumentsContract.buildDocumentUri(
+                        mRoot.authority, mRoot.documentId);
+                return DocumentInfo.fromUri(getContentResolver(), uri);
+            } catch (FileNotFoundException e) {
+                Log.w(mTag, "Failed to find root", e);
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(DocumentInfo result) {
+            if (result != null) {
+                State state = getDisplayState();
+                state.stack.push(result);
+                state.stackTouched = true;
+                onCurrentDirectoryChanged(ANIM_SIDE);
+            }
+        }
+    }
+
+    final class RestoreStackTask extends AsyncTask<Void, Void, Void> {
+        private volatile boolean mRestoredStack;
+        private volatile boolean mExternal;
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            State state = getDisplayState();
+            RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
+
+            // Restore last stack for calling package
+            final String packageName = getCallingPackageMaybeExtra();
+            final Cursor cursor = getContentResolver()
+                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
+            try {
+                if (cursor.moveToFirst()) {
+                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
+                    final byte[] rawStack = cursor.getBlob(
+                            cursor.getColumnIndex(ResumeColumns.STACK));
+                    DurableUtils.readFromArray(rawStack, state.stack);
+                    mRestoredStack = true;
+                }
+            } catch (IOException e) {
+                Log.w(mTag, "Failed to resume: " + e);
+            } finally {
+                IoUtils.closeQuietly(cursor);
+            }
+
+            if (mRestoredStack) {
+                // Update the restored stack to ensure we have freshest data
+                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state);
+                try {
+                    state.stack.updateRoot(matchingRoots);
+                    state.stack.updateDocuments(getContentResolver());
+                } catch (FileNotFoundException e) {
+                    Log.w(mTag, "Failed to restore stack: " + e);
+                    state.stack.reset();
+                    mRestoredStack = false;
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            if (isDestroyed()) return;
+            getDisplayState().restored = true;
+            onCurrentDirectoryChanged(ANIM_NONE);
+
+            onStackRestored(mRestoredStack, mExternal);
+
+            getDisplayState().restored = true;
+            onCurrentDirectoryChanged(ANIM_NONE);
+        }
+    }
+
+    final class ItemSelectedListener implements OnItemSelectedListener {
+
+        boolean mIgnoreNextNavigation;
+
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            if (mIgnoreNextNavigation) {
+                mIgnoreNextNavigation = false;
+                return;
+            }
+
+            State state = getDisplayState();
+            while (state.stack.size() > position + 1) {
+                state.stackTouched = true;
+                state.stack.pop();
+            }
+            onCurrentDirectoryChanged(ANIM_UP);
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // Ignored
+        }
+    }
+
+    /**
+     * Class providing toolbar with runtime access to useful activity data.
+     */
+    final class StackAdapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            return getDisplayState().stack.size();
+        }
+
+        @Override
+        public DocumentInfo getItem(int position) {
+            State state = getDisplayState();
+            return state.stack.get(state.stack.size() - position - 1);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_subdir_title, parent, false);
+            }
+
+            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+            final DocumentInfo doc = getItem(position);
+
+            if (position == 0) {
+                final RootInfo root = getCurrentRoot();
+                title.setText(root.title);
+            } else {
+                title.setText(doc.displayName);
+            }
+
+            return convertView;
+        }
+
+        @Override
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_subdir, parent, false);
+            }
+
+            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
+            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+            final DocumentInfo doc = getItem(position);
+
+            if (position == 0) {
+                final RootInfo root = getCurrentRoot();
+                title.setText(root.title);
+                subdir.setVisibility(View.GONE);
+            } else {
+                title.setText(doc.displayName);
+                subdir.setVisibility(View.VISIBLE);
+            }
+
+            return convertView;
+        }
+    }
+
+    /**
+     * Facade over the various search parts in the menu.
+     */
+    final class SearchManager implements
+            SearchView.OnCloseListener, OnActionExpandListener, OnQueryTextListener {
+
+        protected boolean mSearchExpanded;
+        protected boolean mIgnoreNextClose;
+        protected boolean mIgnoreNextCollapse;
+
+        private MenuItem mMenu;
+        private SearchView mView;
+
+        public void install(MenuItem menu) {
+            assert(mMenu == null);
+            mMenu = menu;
+            mView = (SearchView) menu.getActionView();
+
+            mMenu.setOnActionExpandListener(this);
+            mView.setOnQueryTextListener(this);
+            mView.setOnCloseListener(this);
+        }
+
+        /**
+         * @param root Info about the current directory.
+         */
+        void update(RootInfo root) {
+            if (mMenu == null) {
+                Log.d(mTag, "showMenu called before Search MenuItem installed.");
+                return;
+            }
+            State state = getDisplayState();
+            if (state.currentSearch != null) {
+                mMenu.expandActionView();
+
+                mView.setIconified(false);
+                mView.clearFocus();
+                mView.setQuery(state.currentSearch, false);
+            } else {
+                mIgnoreNextClose = true;
+                mView.setIconified(true);
+                mView.clearFocus();
+
+                mIgnoreNextCollapse = true;
+                mMenu.collapseActionView();
+            }
+
+            showMenu(root != null
+                    && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
+        }
+
+        void showMenu(boolean visible) {
+            if (mMenu == null) {
+                Log.d(mTag, "showMenu called before Search MenuItem installed.");
+                return;
+            }
+            mMenu.setVisible(visible);
+        }
+
+        boolean isSearching() {
+            return getDisplayState().currentSearch != null;
+        }
+
+        boolean isExpanded() {
+            return mSearchExpanded;
+        }
+
+        @Override
+        public boolean onClose() {
+            mSearchExpanded = false;
+            if (mIgnoreNextClose) {
+                mIgnoreNextClose = false;
+                return false;
+            }
+
+            getDisplayState().currentSearch = null;
+            onCurrentDirectoryChanged(ANIM_NONE);
+            return false;
+        }
+        @Override
+        public boolean onMenuItemActionExpand(MenuItem item) {
+            mSearchExpanded = true;
+            updateActionBar();
+            return true;
+        }
+
+        @Override
+        public boolean onMenuItemActionCollapse(MenuItem item) {
+            mSearchExpanded = false;
+            if (mIgnoreNextCollapse) {
+                mIgnoreNextCollapse = false;
+                return true;
+            }
+
+            getDisplayState().currentSearch = null;
+            onCurrentDirectoryChanged(ANIM_NONE);
+            return true;
+        }
+        @Override
+        public boolean onQueryTextSubmit(String query) {
+            mSearchExpanded = true;
+            getDisplayState().currentSearch = query;
+            mView.clearFocus();
+            onCurrentDirectoryChanged(ANIM_NONE);
+            return true;
+        }
+
+        @Override
+        public boolean onQueryTextChange(String newText) {
+            return false;
+        }
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 37a14c6..7cf58cc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -28,7 +28,6 @@
 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.Fragment;
@@ -311,8 +310,9 @@
                 updateDisplayState();
 
                 // When launched into empty recents, show drawer
-                if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched) {
-                    ((BaseActivity) context).setRootsDrawerOpen(true);
+                if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched &&
+                        context instanceof DocumentsActivity) {
+                    ((DocumentsActivity) context).setRootsDrawerOpen(true);
                 }
 
                 // Restore any previous instance state
@@ -503,7 +503,8 @@
             open.setVisible(!manageOrBrowse);
             share.setVisible(manageOrBrowse);
             delete.setVisible(manageOrBrowse);
-            copy.setVisible(manageOrBrowse);
+            // Disable copying from the Recents view.
+            copy.setVisible(manageOrBrowse && mType != TYPE_RECENT_OPEN);
 
             return true;
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index a2a789f..9d828de 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -21,16 +21,17 @@
 import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
 import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
 import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
 import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_COPY_DESTINATION;
-import static com.android.documentsui.BaseActivity.State.MODE_GRID;
-import static com.android.documentsui.BaseActivity.State.MODE_LIST;
+import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
 import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.DirectoryFragment.ANIM_UP;
 
+import java.util.Arrays;
+import java.util.List;
+
 import android.app.Activity;
+import android.app.Fragment;
 import android.app.FragmentManager;
 import android.content.ActivityNotFoundException;
 import android.content.ClipData;
@@ -42,7 +43,6 @@
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
-import android.database.Cursor;
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -54,51 +54,27 @@
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.DrawerLayout.DrawerListener;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
 import android.widget.Spinner;
-import android.widget.TextView;
 import android.widget.Toast;
 import android.widget.Toolbar;
 
-import libcore.io.IoUtils;
-
 import com.android.documentsui.RecentsProvider.RecentColumns;
 import com.android.documentsui.RecentsProvider.ResumeColumns;
 import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
 
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Executor;
-
 public class DocumentsActivity extends BaseActivity {
+    private static final int CODE_FORWARD = 42;
     public static final String TAG = "Documents";
 
-    private static final String EXTRA_STATE = "state";
-
-    private static final int CODE_FORWARD = 42;
-
     private boolean mShowAsDialog;
 
-    private SearchView mSearchView;
-
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
 
@@ -110,21 +86,20 @@
 
     private DirectoryContainerView mDirectoryContainer;
 
-    private boolean mIgnoreNextNavigation;
-    private boolean mIgnoreNextClose;
-    private boolean mIgnoreNextCollapse;
-
-    private boolean mSearchExpanded;
-
-    private RootsCache mRoots;
     private State mState;
 
+    private SearchManager mSearchManager;
+    private ItemSelectedListener mStackListener;
+    private BaseAdapter mStackAdapter;
+
+    public DocumentsActivity() {
+        super(TAG);
+    }
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mRoots = DocumentsApplication.getRootsCache(this);
-
         setResult(Activity.RESULT_CANCELED);
         setContentView(R.layout.activity);
 
@@ -157,16 +132,16 @@
 
         mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
 
-        if (icicle != null) {
-            mState = icicle.getParcelable(EXTRA_STATE);
-        } else {
-            buildDefaultState();
-        }
+        mState = (icicle != null)
+                ? icicle.<State>getParcelable(EXTRA_STATE)
+                : buildDefaultState();
 
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
         mToolbar.setTitleTextAppearance(context,
                 android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
 
+        mStackAdapter = new StackAdapter();
+        mStackListener = new ItemSelectedListener();
         mToolbarStack = (Spinner) findViewById(R.id.stack);
         mToolbarStack.setOnItemSelectedListener(mStackListener);
 
@@ -176,6 +151,7 @@
                     android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
         }
 
+        mSearchManager = new SearchManager();
         setActionBar(mToolbar);
 
         // Hide roots when we're managing a specific root
@@ -220,55 +196,57 @@
         }
     }
 
-    private void buildDefaultState() {
-        mState = new State();
+    private State buildDefaultState() {
+        State state = new State();
 
         final Intent intent = getIntent();
         final String action = intent.getAction();
         if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
-            mState.action = ACTION_OPEN;
+            state.action = ACTION_OPEN;
         } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
-            mState.action = ACTION_CREATE;
+            state.action = ACTION_CREATE;
         } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
-            mState.action = ACTION_GET_CONTENT;
+            state.action = ACTION_GET_CONTENT;
         } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
-            mState.action = ACTION_OPEN_TREE;
+            state.action = ACTION_OPEN_TREE;
         } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
-            mState.action = ACTION_MANAGE;
+            state.action = ACTION_MANAGE;
         } else if (DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT.equals(action)) {
-            mState.action = ACTION_BROWSE;
+            state.action = ACTION_BROWSE;
         } else if (DocumentsIntent.ACTION_OPEN_COPY_DESTINATION.equals(action)) {
-            mState.action = ACTION_OPEN_COPY_DESTINATION;
+            state.action = ACTION_OPEN_COPY_DESTINATION;
         }
 
-        if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
-            mState.allowMultiple = intent.getBooleanExtra(
+        if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
+            state.allowMultiple = intent.getBooleanExtra(
                     Intent.EXTRA_ALLOW_MULTIPLE, false);
         }
 
-        if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
-            mState.acceptMimes = new String[] { "*/*" };
-            mState.allowMultiple = true;
+        if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+            state.acceptMimes = new String[] { "*/*" };
+            state.allowMultiple = true;
         } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
-            mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
+            state.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
         } else {
-            mState.acceptMimes = new String[] { intent.getType() };
+            state.acceptMimes = new String[] { intent.getType() };
         }
 
-        mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
-        mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
-        mState.showAdvanced = mState.forceAdvanced
+        state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+        state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+        state.showAdvanced = state.forceAdvanced
                 | LocalPreferences.getDisplayAdvancedDevices(this);
 
-        if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
-            mState.showSize = true;
+        if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+            state.showSize = true;
         } else {
-            mState.showSize = LocalPreferences.getDisplayFileSize(this);
+            state.showSize = LocalPreferences.getDisplayFileSize(this);
         }
-        if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
-            mState.directoryCopy = intent.getBooleanExtra(
+        if (state.action == ACTION_OPEN_COPY_DESTINATION) {
+            state.directoryCopy = intent.getBooleanExtra(
                     BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
         }
+
+        return state;
     }
 
     private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
@@ -290,7 +268,7 @@
             mState.restored = true;
 
             if (root != null) {
-                onRootPicked(root, true);
+                onRootPicked(root);
             } else {
                 Log.w(TAG, "Failed to find root: " + mRootUri);
                 finish();
@@ -298,71 +276,55 @@
         }
     }
 
-    private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
-        private volatile boolean mRestoredStack;
-        private volatile boolean mExternal;
+    @Override
+    void onStackRestored(boolean restored, boolean external) {
+        // Show drawer when no stack restored, but only when requesting
+        // non-visual content. However, if we last used an external app,
+        // drawer is always shown.
 
-        @Override
-        protected Void doInBackground(Void... params) {
-            // Restore last stack for calling package
-            final String packageName = getCallingPackageMaybeExtra();
-            final Cursor cursor = getContentResolver()
-                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
-            try {
-                if (cursor.moveToFirst()) {
-                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
-                    final byte[] rawStack = cursor.getBlob(
-                            cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, mState.stack);
-                    mRestoredStack = true;
-                }
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to resume: " + e);
-            } finally {
-                IoUtils.closeQuietly(cursor);
-            }
-
-            if (mRestoredStack) {
-                // Update the restored stack to ensure we have freshest data
-                final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
-                try {
-                    mState.stack.updateRoot(matchingRoots);
-                    mState.stack.updateDocuments(getContentResolver());
-                } catch (FileNotFoundException e) {
-                    Log.w(TAG, "Failed to restore stack: " + e);
-                    mState.stack.reset();
-                    mRestoredStack = false;
-                }
-            }
-
-            return null;
+        boolean showDrawer = false;
+        if (!restored) {
+            showDrawer = true;
+        }
+        if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
+            showDrawer = false;
+        }
+        if (external && mState.action == ACTION_GET_CONTENT) {
+            showDrawer = true;
         }
 
-        @Override
-        protected void onPostExecute(Void result) {
-            if (isDestroyed()) return;
-            mState.restored = true;
+        if (showDrawer) {
+            setRootsDrawerOpen(true);
+        }
+    }
 
-            // Show drawer when no stack restored, but only when requesting
-            // non-visual content. However, if we last used an external app,
-            // drawer is always shown.
+    public void onAppPicked(ResolveInfo info) {
+        final Intent intent = new Intent(getIntent());
+        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+        intent.setComponent(new ComponentName(
+                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
+        startActivityForResult(intent, CODE_FORWARD);
+    }
 
-            boolean showDrawer = false;
-            if (!mRestoredStack) {
-                showDrawer = true;
-            }
-            if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
-                showDrawer = false;
-            }
-            if (mExternal && mState.action == ACTION_GET_CONTENT) {
-                showDrawer = true;
-            }
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Log.d(TAG, "onActivityResult() code=" + resultCode);
 
-            if (showDrawer) {
-                setRootsDrawerOpen(true);
-            }
+        // Only relay back results when not canceled; otherwise stick around to
+        // let the user pick another app/backend.
+        if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
 
-            onCurrentDirectoryChanged(ANIM_NONE);
+            // Remember that we last picked via external app
+            final String packageName = getCallingPackageMaybeExtra();
+            final ContentValues values = new ContentValues();
+            values.put(ResumeColumns.EXTERNAL, 1);
+            getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
+
+            // Pass back result to original caller
+            setResult(resultCode, data);
+            finish();
+        } else {
+            super.onActivityResult(requestCode, resultCode, data);
         }
     }
 
@@ -397,7 +359,6 @@
         updateActionBar();
     }
 
-    @Override
     public void setRootsDrawerOpen(boolean open) {
         if (!mShowAsDialog) {
             if (open) {
@@ -416,6 +377,7 @@
         }
     }
 
+    @Override
     public void updateActionBar() {
         if (mRootsToolbar != null) {
             if (mState.action == ACTION_OPEN ||
@@ -447,7 +409,7 @@
             });
         }
 
-        if (mSearchExpanded) {
+        if (mSearchManager.isExpanded()) {
             mToolbar.setTitle(null);
             mToolbarStack.setVisibility(View.GONE);
             mToolbarStack.setAdapter(null);
@@ -461,7 +423,7 @@
                 mToolbarStack.setVisibility(View.VISIBLE);
                 mToolbarStack.setAdapter(mStackAdapter);
 
-                mIgnoreNextNavigation = true;
+                mStackListener.mIgnoreNextNavigation = true;
                 mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
             }
         }
@@ -469,79 +431,18 @@
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        super.onCreateOptionsMenu(menu);
+        boolean showMenu = super.onCreateOptionsMenu(menu);
+
         getMenuInflater().inflate(R.menu.activity, menu);
 
         // Most actions are visible when showing as dialog
         if (mShowAsDialog) {
-            for (int i = 0; i < menu.size(); i++) {
-                final MenuItem item = menu.getItem(i);
-                switch (item.getItemId()) {
-                    case R.id.menu_advanced:
-                    case R.id.menu_file_size:
-                        break;
-                    default:
-                        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-                }
-            }
+            expandMenus(menu);
         }
 
-        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
-        mSearchView = (SearchView) searchMenu.getActionView();
-        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
-            @Override
-            public boolean onQueryTextSubmit(String query) {
-                mSearchExpanded = true;
-                mState.currentSearch = query;
-                mSearchView.clearFocus();
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return true;
-            }
+        this.mSearchManager.install(menu.findItem(R.id.menu_search));
 
-            @Override
-            public boolean onQueryTextChange(String newText) {
-                return false;
-            }
-        });
-
-        searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
-            @Override
-            public boolean onMenuItemActionExpand(MenuItem item) {
-                mSearchExpanded = true;
-                updateActionBar();
-                return true;
-            }
-
-            @Override
-            public boolean onMenuItemActionCollapse(MenuItem item) {
-                mSearchExpanded = false;
-                if (mIgnoreNextCollapse) {
-                    mIgnoreNextCollapse = false;
-                    return true;
-                }
-
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return true;
-            }
-        });
-
-        mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
-            @Override
-            public boolean onClose() {
-                mSearchExpanded = false;
-                if (mIgnoreNextClose) {
-                    mIgnoreNextClose = false;
-                    return false;
-                }
-
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return false;
-            }
-        });
-
-        return true;
+        return showMenu;
     }
 
     @Override
@@ -554,7 +455,6 @@
         final DocumentInfo cwd = getCurrentDirectory();
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
-        final MenuItem search = menu.findItem(R.id.menu_search);
         final MenuItem sort = menu.findItem(R.id.menu_sort);
         final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
         final MenuItem grid = menu.findItem(R.id.menu_grid);
@@ -564,36 +464,23 @@
         final MenuItem settings = menu.findItem(R.id.menu_settings);
 
         sort.setVisible(cwd != null);
-        grid.setVisible(mState.derivedMode != MODE_GRID);
-        list.setVisible(mState.derivedMode != MODE_LIST);
+        grid.setVisible(mState.derivedMode != State.MODE_GRID);
+        list.setVisible(mState.derivedMode != State.MODE_LIST);
 
-        if (mState.currentSearch != null) {
-            // Search uses backend ranking; no sorting
-            sort.setVisible(false);
 
-            search.expandActionView();
+        mSearchManager.update(root);
 
-            mSearchView.setIconified(false);
-            mSearchView.clearFocus();
-            mSearchView.setQuery(mState.currentSearch, false);
-        } else {
-            mIgnoreNextClose = true;
-            mSearchView.setIconified(true);
-            mSearchView.clearFocus();
-
-            mIgnoreNextCollapse = true;
-            search.collapseActionView();
-        }
+        // Search uses backend ranking; no sorting
+        sort.setVisible(mSearchManager.isSearching());
 
         // Only sort by size when visible
         sortSize.setVisible(mState.showSize);
 
-        boolean searchVisible;
         boolean fileSizeVisible = !(mState.action == ACTION_MANAGE
                 || mState.action == ACTION_BROWSE);
         if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
             createDir.setVisible(cwd != null && cwd.isCreateSupported());
-            searchVisible = false;
+            mSearchManager.showMenu(false);
 
             // No display options in recent directories
             if (cwd == null) {
@@ -607,14 +494,8 @@
             }
         } else {
             createDir.setVisible(false);
-
-            searchVisible = root != null
-                    && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0);
         }
 
-        // TODO: close any search in-progress when hiding
-        search.setVisible(searchVisible);
-
         advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
         fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
@@ -634,90 +515,7 @@
         if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
             return true;
         }
-
-        final int id = item.getItemId();
-        if (id == android.R.id.home) {
-            onBackPressed();
-            return true;
-        } else if (id == R.id.menu_create_dir) {
-            CreateDirectoryFragment.show(getFragmentManager());
-            return true;
-        } else if (id == R.id.menu_search) {
-            return false;
-        } else if (id == R.id.menu_sort_name) {
-            setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
-            return true;
-        } else if (id == R.id.menu_sort_date) {
-            setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
-            return true;
-        } else if (id == R.id.menu_sort_size) {
-            setUserSortOrder(State.SORT_ORDER_SIZE);
-            return true;
-        } else if (id == R.id.menu_grid) {
-            setUserMode(State.MODE_GRID);
-            return true;
-        } else if (id == R.id.menu_list) {
-            setUserMode(State.MODE_LIST);
-            return true;
-        } else if (id == R.id.menu_advanced) {
-            setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
-            return true;
-        } else if (id == R.id.menu_file_size) {
-            setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
-            return true;
-        } else if (id == R.id.menu_settings) {
-            final RootInfo root = getCurrentRoot();
-            final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
-            intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
-                    DocumentsContract.Root.MIME_TYPE_ITEM);
-            startActivity(intent);
-            return true;
-        } else {
-            return super.onOptionsItemSelected(item);
-        }
-    }
-
-    private void setDisplayAdvancedDevices(boolean display) {
-        LocalPreferences.setDisplayAdvancedDevices(this, display);
-        mState.showAdvanced = mState.forceAdvanced | display;
-        RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
-        invalidateOptionsMenu();
-    }
-
-    private void setDisplayFileSize(boolean display) {
-        LocalPreferences.setDisplayFileSize(this, display);
-        mState.showSize = display;
-        DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
-        invalidateOptionsMenu();
-    }
-
-    @Override
-    public void onStateChanged() {
-        invalidateOptionsMenu();
-    }
-
-    /**
-     * Set state sort order based on explicit user action.
-     */
-    private void setUserSortOrder(int sortOrder) {
-        mState.userSortOrder = sortOrder;
-        DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
-    }
-
-    /**
-     * Set state mode based on explicit user action.
-     */
-    private void setUserMode(int mode) {
-        mState.userMode = mode;
-        DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
-    }
-
-    @Override
-    public void setPending(boolean pending) {
-        final SaveFragment save = SaveFragment.get(getFragmentManager());
-        if (save != null) {
-            save.setPending(pending);
-        }
+        return super.onOptionsItemSelected(item);
     }
 
     @Override
@@ -740,131 +538,12 @@
     }
 
     @Override
-    protected void onSaveInstanceState(Bundle state) {
-        super.onSaveInstanceState(state);
-        state.putParcelable(EXTRA_STATE, mState);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle state) {
-        super.onRestoreInstanceState(state);
-    }
-
-    private BaseAdapter mStackAdapter = new BaseAdapter() {
-        @Override
-        public int getCount() {
-            return mState.stack.size();
-        }
-
-        @Override
-        public DocumentInfo getItem(int position) {
-            return mState.stack.get(mState.stack.size() - position - 1);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir_title, parent, false);
-            }
-
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-            } else {
-                title.setText(doc.displayName);
-            }
-
-            return convertView;
-        }
-
-        @Override
-        public View getDropDownView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir, parent, false);
-            }
-
-            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-                subdir.setVisibility(View.GONE);
-            } else {
-                title.setText(doc.displayName);
-                subdir.setVisibility(View.VISIBLE);
-            }
-
-            return convertView;
-        }
-    };
-
-    private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            if (mIgnoreNextNavigation) {
-                mIgnoreNextNavigation = false;
-                return;
-            }
-
-            while (mState.stack.size() > position + 1) {
-                mState.stackTouched = true;
-                mState.stack.pop();
-            }
-            onCurrentDirectoryChanged(ANIM_UP);
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            // Ignored
-        }
-    };
-
-    @Override
-    public RootInfo getCurrentRoot() {
-        if (mState.stack.root != null) {
-            return mState.stack.root;
-        } else {
-            return mRoots.getRecentsRoot();
-        }
-    }
-
-    @Override
-    public DocumentInfo getCurrentDirectory() {
-        return mState.stack.peek();
-    }
-
-    private String getCallingPackageMaybeExtra() {
-        final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
-        return (extra != null) ? extra : getCallingPackage();
-    }
-
-    public Executor getCurrentExecutor() {
-        final DocumentInfo cwd = getCurrentDirectory();
-        if (cwd != null && cwd.authority != null) {
-            return ProviderExecutor.forAuthority(cwd.authority);
-        } else {
-            return AsyncTask.THREAD_POOL_EXECUTOR;
-        }
-    }
-
-    @Override
     public State getDisplayState() {
         return mState;
     }
 
-    private void onCurrentDirectoryChanged(int anim) {
+    @Override
+    void onDirectoryChanged(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
@@ -883,7 +562,7 @@
                 // Start recents in grid when requesting visual things
                 final boolean visualMimes = MimePredicate.mimeMatches(
                         MimePredicate.VISUAL_MIMES, mState.acceptMimes);
-                mState.userMode = visualMimes ? MODE_GRID : MODE_LIST;
+                mState.userMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
                 mState.derivedMode = mState.userMode;
             }
         } else {
@@ -913,108 +592,20 @@
                 pick.setPickTarget(mState.action, cwd, displayName);
             }
         }
+    }
 
-        final RootsFragment roots = RootsFragment.get(fm);
-        if (roots != null) {
-            roots.onCurrentRootChanged();
-        }
+    void onSaveRequested(DocumentInfo replaceTarget) {
+        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
+    }
 
-        updateActionBar();
-        invalidateOptionsMenu();
-        dumpStack();
+    void onSaveRequested(String mimeType, String displayName) {
+        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
     }
 
     @Override
-    public void onStackPicked(DocumentStack stack) {
-        try {
-            // Update the restored stack to ensure we have freshest data
-            stack.updateDocuments(getContentResolver());
-
-            mState.stack = stack;
-            mState.stackTouched = true;
-            onCurrentDirectoryChanged(ANIM_SIDE);
-
-        } catch (FileNotFoundException e) {
-            Log.w(TAG, "Failed to restore stack: " + e);
-        }
-    }
-
-    @Override
-    public void onRootPicked(RootInfo root, boolean closeDrawer) {
-        // Clear entire backstack and start in new root
-        mState.stack.root = root;
-        mState.stack.clear();
-        mState.stackTouched = true;
-
-        if (!mRoots.isRecentsRoot(root)) {
-            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
-        } else {
-            onCurrentDirectoryChanged(ANIM_SIDE);
-        }
-
-        if (closeDrawer) {
-            setRootsDrawerOpen(false);
-        }
-    }
-
-    private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
-        private RootInfo mRoot;
-
-        public PickRootTask(RootInfo root) {
-            mRoot = root;
-        }
-
-        @Override
-        protected DocumentInfo doInBackground(Void... params) {
-            try {
-                final Uri uri = DocumentsContract.buildDocumentUri(
-                        mRoot.authority, mRoot.documentId);
-                return DocumentInfo.fromUri(getContentResolver(), uri);
-            } catch (FileNotFoundException e) {
-                Log.w(TAG, "Failed to find root", e);
-                return null;
-            }
-        }
-
-        @Override
-        protected void onPostExecute(DocumentInfo result) {
-            if (result != null) {
-                mState.stack.push(result);
-                mState.stackTouched = true;
-                onCurrentDirectoryChanged(ANIM_SIDE);
-            }
-        }
-    }
-
-    @Override
-    public void onAppPicked(ResolveInfo info) {
-        final Intent intent = new Intent(getIntent());
-        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
-        intent.setComponent(new ComponentName(
-                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
-        startActivityForResult(intent, CODE_FORWARD);
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        Log.d(TAG, "onActivityResult() code=" + resultCode);
-
-        // Only relay back results when not canceled; otherwise stick around to
-        // let the user pick another app/backend.
-        if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
-
-            // Remember that we last picked via external app
-            final String packageName = getCallingPackageMaybeExtra();
-            final ContentValues values = new ContentValues();
-            values.put(ResumeColumns.EXTERNAL, 1);
-            getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
-
-            // Pass back result to original caller
-            setResult(resultCode, data);
-            finish();
-        } else {
-            super.onActivityResult(requestCode, resultCode, data);
-        }
+    void onRootPicked(RootInfo root) {
+        super.onRootPicked(root);
+        setRootsDrawerOpen(false);
     }
 
     @Override
@@ -1076,17 +667,6 @@
         }
     }
 
-    @Override
-    public void onSaveRequested(DocumentInfo replaceTarget) {
-        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
-    }
-
-    @Override
-    public void onSaveRequested(String mimeType, String displayName) {
-        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
-    }
-
-    @Override
     public void onPickRequested(DocumentInfo pickTarget) {
         Uri result;
         if (mState.action == ACTION_OPEN_TREE) {
@@ -1101,7 +681,8 @@
         new PickFinishTask(result).executeOnExecutor(getCurrentExecutor());
     }
 
-    private void saveStackBlocking() {
+    @Override
+    void saveStackBlocking() {
         final ContentResolver resolver = getContentResolver();
         final ContentValues values = new ContentValues();
 
@@ -1124,7 +705,8 @@
         resolver.insert(RecentsProvider.buildResume(packageName), values);
     }
 
-    private void onFinished(Uri... uris) {
+    @Override
+    void onTaskFinished(Uri... uris) {
         Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
         final Intent intent = new Intent();
@@ -1159,7 +741,52 @@
         finish();
     }
 
-    private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+    public static DocumentsActivity get(Fragment fragment) {
+        return (DocumentsActivity) fragment.getActivity();
+    }
+
+    private final class PickFinishTask extends AsyncTask<Void, Void, Void> {
+        private final Uri mUri;
+
+        public PickFinishTask(Uri uri) {
+            mUri = uri;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            saveStackBlocking();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            onTaskFinished(mUri);
+        }
+    }
+
+    final class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+        private final Uri[] mUris;
+
+        public ExistingFinishTask(Uri... uris) {
+            mUris = uris;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            saveStackBlocking();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            onTaskFinished(mUris);
+        }
+    }
+
+    /**
+     * Task that creates a new document in the background.
+     */
+    final class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
         private final String mMimeType;
         private final String mDisplayName;
 
@@ -1201,7 +828,7 @@
         @Override
         protected void onPostExecute(Uri result) {
             if (result != null) {
-                onFinished(result);
+                onTaskFinished(result);
             } else {
                 Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
                         .show();
@@ -1210,50 +837,4 @@
             setPending(false);
         }
     }
-
-    private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
-        private final Uri[] mUris;
-
-        public ExistingFinishTask(Uri... uris) {
-            mUris = uris;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            onFinished(mUris);
-        }
-    }
-
-    private class PickFinishTask extends AsyncTask<Void, Void, Void> {
-        private final Uri mUri;
-
-        public PickFinishTask(Uri uri) {
-            mUri = uri;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            onFinished(mUri);
-        }
-    }
-
-    private void dumpStack() {
-        Log.d(TAG, "Current stack: ");
-        Log.d(TAG, " * " + mState.stack.root);
-        for (DocumentInfo doc : mState.stack) {
-            Log.d(TAG, " +-- " + doc);
-        }
-    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 7ea51b9..e899379 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -75,7 +75,7 @@
     private View.OnClickListener mPickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            final BaseActivity activity = BaseActivity.get(PickFragment.this);
+            final DocumentsActivity activity = DocumentsActivity.get(PickFragment.this);
             activity.onPickRequested(mPickTarget);
         }
     };
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 26aecc5..e11d7d9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -109,8 +109,9 @@
                 mAdapter.swapStacks(data);
 
                 // When launched into empty recents, show drawer
-                if (mAdapter.isEmpty() && !state.stackTouched) {
-                    ((BaseActivity) context).setRootsDrawerOpen(true);
+                if (mAdapter.isEmpty() && !state.stackTouched &&
+                        context instanceof DocumentsActivity) {
+                    ((DocumentsActivity) context).setRootsDrawerOpen(true);
                 }
             }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index ed5e123..fd67a77 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -174,11 +174,12 @@
     private OnItemClickListener mItemListener = new OnItemClickListener() {
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            final BaseActivity activity = BaseActivity.get(RootsFragment.this);
-            final Item item = mAdapter.getItem(position);
+            Item item = mAdapter.getItem(position);
             if (item instanceof RootItem) {
-                activity.onRootPicked(((RootItem) item).root, true);
+                BaseActivity activity = BaseActivity.get(RootsFragment.this);
+                activity.onRootPicked(((RootItem) item).root);
             } else if (item instanceof AppItem) {
+                DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
                 activity.onAppPicked(((AppItem) item).info);
             } else {
                 throw new IllegalStateException("Unknown root: " + item);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index a13fccc..ce98db2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -113,7 +113,7 @@
     private View.OnClickListener mSaveListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            final BaseActivity activity = BaseActivity.get(SaveFragment.this);
+            final DocumentsActivity activity = DocumentsActivity.get(SaveFragment.this);
             if (mReplaceTarget != null) {
                 activity.onSaveRequested(mReplaceTarget);
             } else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
index 976f21d..f89b182 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
@@ -16,50 +16,29 @@
 
 package com.android.documentsui;
 
-
 import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.DirectoryFragment.ANIM_UP;
 import android.app.Activity;
-import android.app.Fragment;
 import android.app.FragmentManager;
 import android.content.ActivityNotFoundException;
 import android.content.ClipData;
-import android.content.ComponentName;
-import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
 import android.graphics.Point;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Debug;
 import android.provider.DocumentsContract;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v4.widget.DrawerLayout.DrawerListener;
+import android.provider.DocumentsContract.Root;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
 import android.widget.Spinner;
-import android.widget.TextView;
 import android.widget.Toast;
 import android.widget.Toolbar;
 
@@ -70,55 +49,37 @@
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
 
-import libcore.io.IoUtils;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.Executor;
 
+/**
+ * Activity providing a directly launchable file management activity.
+ */
 public class StandaloneActivity extends BaseActivity {
     public static final String TAG = "StandaloneFileManagement";
 
-    private static final String EXTRA_STATE = "state";
-
-    private static final int CODE_FORWARD = 42;
-
-    private SearchView mSearchView;
-
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
-
     private Toolbar mRootsToolbar;
-
-    private ActionBarDrawerToggle mDrawerToggle;
-
     private DirectoryContainerView mDirectoryContainer;
-
-    private boolean mIgnoreNextNavigation;
-    private boolean mIgnoreNextClose;
-    private boolean mIgnoreNextCollapse;
-
-    private boolean mSearchExpanded;
-
-    private RootsCache mRoots;
+    private SearchManager mSearchManager;
     private State mState;
+    private ItemSelectedListener mStackListener;
+    private BaseAdapter mStackAdapter;
+
+    public StandaloneActivity() {
+        super(TAG);
+    }
 
     @Override
     public void onCreate(Bundle icicle) {
-        // Debug.waitForDebugger();
         super.onCreate(icicle);
 
-        mRoots = DocumentsApplication.getRootsCache(this);
-
         setResult(Activity.RESULT_CANCELED);
         setContentView(R.layout.activity);
 
         final Context context = this;
-        final Resources res = getResources();
 
         // Strongly define our horizontal dimension; we leave vertical as
         final WindowManager.LayoutParams a = getWindow().getAttributes();
@@ -131,16 +92,17 @@
 
         mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
 
-        if (icicle != null) {
-            mState = icicle.getParcelable(EXTRA_STATE);
-        } else {
-            buildDefaultState();
-        }
+        mState = (icicle != null)
+            ? icicle.<State>getParcelable(EXTRA_STATE)
+            : buildDefaultState();
 
+        mSearchManager = new SearchManager();
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
         mToolbar.setTitleTextAppearance(context,
                 android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
 
+        mStackAdapter = new StackAdapter();
+        mStackListener = new ItemSelectedListener();
         mToolbarStack = (Spinner) findViewById(R.id.stack);
         mToolbarStack.setOnItemSelectedListener(mStackListener);
 
@@ -167,87 +129,33 @@
         }
     }
 
-    private void buildDefaultState() {
-        mState = new State();
+    private State buildDefaultState() {
+        State state = new State();
 
         final Intent intent = getIntent();
-        mState.action = State.ACTION_BROWSE_ALL;
-        mState.acceptMimes = new String[] { "*/*" };
-        mState.allowMultiple = true;
-        mState.acceptMimes = new String[] { intent.getType() };
-        mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
-        mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
-        mState.showAdvanced = mState.forceAdvanced
+        state.action = State.ACTION_BROWSE_ALL;
+        state.acceptMimes = new String[] { "*/*" };
+        state.allowMultiple = true;
+        state.acceptMimes = new String[] { intent.getType() };
+        state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+        state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+        state.showAdvanced = state.forceAdvanced
                 | LocalPreferences.getDisplayAdvancedDevices(this);
-        mState.showSize = true;
+        state.showSize = true;
         final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
         if (stack != null)
-            mState.stack = stack;
-    }
+            state.stack = stack;
 
-    private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
-        private volatile boolean mRestoredStack;
-        private volatile boolean mExternal;
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            // Restore last stack for calling package
-            final String packageName = getCallingPackageMaybeExtra();
-            final Cursor cursor = getContentResolver()
-                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
-            try {
-                if (cursor.moveToFirst()) {
-                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
-                    final byte[] rawStack = cursor.getBlob(
-                            cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, mState.stack);
-                    mRestoredStack = true;
-                }
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to resume: " + e);
-            } finally {
-                IoUtils.closeQuietly(cursor);
-            }
-
-            if (mRestoredStack) {
-                // Update the restored stack to ensure we have freshest data
-                final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
-                try {
-                    mState.stack.updateRoot(matchingRoots);
-                    mState.stack.updateDocuments(getContentResolver());
-                } catch (FileNotFoundException e) {
-                    Log.w(TAG, "Failed to restore stack: " + e);
-                    mState.stack.reset();
-                    mRestoredStack = false;
-                }
-            }
-
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            if (isDestroyed()) return;
-            mState.restored = true;
-            onCurrentDirectoryChanged(ANIM_NONE);
-        }
+        return state;
     }
 
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
-        if (mDrawerToggle != null) {
-            mDrawerToggle.syncState();
-        }
         updateActionBar();
     }
 
     @Override
-    public void setRootsDrawerOpen(boolean open) {
-        Log.w(TAG, "Trying to change state of roots drawer to > " + (open ? "open" : "closed"));
-      // throw new UnsupportedOperationException();
-    }
-
     public void updateActionBar() {
         final RootInfo root = getCurrentRoot();
         mToolbar.setNavigationIcon(
@@ -255,7 +163,7 @@
         mToolbar.setNavigationContentDescription(R.string.drawer_open);
         mToolbar.setNavigationOnClickListener(null);
 
-        if (mSearchExpanded) {
+        if (mSearchManager.isExpanded()) {
             mToolbar.setTitle(null);
             mToolbarStack.setVisibility(View.GONE);
             mToolbarStack.setAdapter(null);
@@ -269,7 +177,7 @@
                 mToolbarStack.setVisibility(View.VISIBLE);
                 mToolbarStack.setAdapter(mStackAdapter);
 
-                mIgnoreNextNavigation = true;
+                mStackListener.mIgnoreNextNavigation = true;
                 mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
             }
         }
@@ -277,220 +185,60 @@
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        super.onCreateOptionsMenu(menu);
+        boolean showMenu = super.onCreateOptionsMenu(menu);
         getMenuInflater().inflate(R.menu.activity, menu);
 
-        for (int i = 0; i < menu.size(); i++) {
-            final MenuItem item = menu.getItem(i);
-            switch (item.getItemId()) {
-                case R.id.menu_advanced:
-                case R.id.menu_file_size:
-                    break;
-                default:
-                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-            }
-        }
+        expandMenus(menu);
 
-        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
-        mSearchView = (SearchView) searchMenu.getActionView();
-        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
-            @Override
-            public boolean onQueryTextSubmit(String query) {
-                mSearchExpanded = true;
-                mState.currentSearch = query;
-                mSearchView.clearFocus();
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return true;
-            }
+        this.mSearchManager.install(menu.findItem(R.id.menu_search));
 
-            @Override
-            public boolean onQueryTextChange(String newText) {
-                return false;
-            }
-        });
-
-        searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
-            @Override
-            public boolean onMenuItemActionExpand(MenuItem item) {
-                mSearchExpanded = true;
-                updateActionBar();
-                return true;
-            }
-
-            @Override
-            public boolean onMenuItemActionCollapse(MenuItem item) {
-                mSearchExpanded = false;
-                if (mIgnoreNextCollapse) {
-                    mIgnoreNextCollapse = false;
-                    return true;
-                }
-
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return true;
-            }
-        });
-
-        mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
-            @Override
-            public boolean onClose() {
-                mSearchExpanded = false;
-                if (mIgnoreNextClose) {
-                    mIgnoreNextClose = false;
-                    return false;
-                }
-
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return false;
-            }
-        });
-
-        return true;
+        return showMenu;
     }
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
 
-        final FragmentManager fm = getFragmentManager();
-
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
-        final MenuItem search = menu.findItem(R.id.menu_search);
         final MenuItem sort = menu.findItem(R.id.menu_sort);
         final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
         final MenuItem grid = menu.findItem(R.id.menu_grid);
         final MenuItem list = menu.findItem(R.id.menu_list);
         final MenuItem advanced = menu.findItem(R.id.menu_advanced);
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
+        final MenuItem settings = menu.findItem(R.id.menu_settings);
 
-        sort.setVisible(cwd != null);
         grid.setVisible(mState.derivedMode != State.MODE_GRID);
         list.setVisible(mState.derivedMode != State.MODE_LIST);
 
-        if (mState.currentSearch != null) {
-            // Search uses backend ranking; no sorting
-            sort.setVisible(false);
-
-            search.expandActionView();
-
-            mSearchView.setIconified(false);
-            mSearchView.clearFocus();
-            mSearchView.setQuery(mState.currentSearch, false);
-        } else {
-            mIgnoreNextClose = true;
-            mSearchView.setIconified(true);
-            mSearchView.clearFocus();
-
-            mIgnoreNextCollapse = true;
-            search.collapseActionView();
-        }
+        mSearchManager.update(root);
+        sort.setVisible(cwd != null && !mSearchManager.isSearching());
 
         // Only sort by size when visible
         sortSize.setVisible(mState.showSize);
 
-        fileSize.setVisible(true);
-        search.setVisible(true);
-        createDir.setVisible(true);
-        advanced.setVisible(true);
+        createDir.setVisible(cwd != null
+                && cwd.isCreateSupported()
+                && !mSearchManager.isSearching()
+                && !root.isDownloads());
+
+        fileSize.setVisible(cwd != null);
+        advanced.setVisible(cwd != null);
 
         advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
         fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
                 ? R.string.menu_file_size_hide : R.string.menu_file_size_show);
 
+        settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
 
         return true;
     }
 
     @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
-            return true;
-        }
-
-        final int id = item.getItemId();
-        if (id == android.R.id.home) {
-            onBackPressed();
-            return true;
-        } else if (id == R.id.menu_create_dir) {
-            CreateDirectoryFragment.show(getFragmentManager());
-            return true;
-        } else if (id == R.id.menu_search) {
-            return false;
-        } else if (id == R.id.menu_sort_name) {
-            setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
-            return true;
-        } else if (id == R.id.menu_sort_date) {
-            setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
-            return true;
-        } else if (id == R.id.menu_sort_size) {
-            setUserSortOrder(State.SORT_ORDER_SIZE);
-            return true;
-        } else if (id == R.id.menu_grid) {
-            setUserMode(State.MODE_GRID);
-            return true;
-        } else if (id == R.id.menu_list) {
-            setUserMode(State.MODE_LIST);
-            return true;
-        } else if (id == R.id.menu_advanced) {
-            setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
-            return true;
-        } else if (id == R.id.menu_file_size) {
-            setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
-            return true;
-        } else {
-            return super.onOptionsItemSelected(item);
-        }
-    }
-
-    private void setDisplayAdvancedDevices(boolean display) {
-        LocalPreferences.setDisplayAdvancedDevices(this, display);
-        mState.showAdvanced = mState.forceAdvanced | display;
-        RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
-        invalidateOptionsMenu();
-    }
-
-    private void setDisplayFileSize(boolean display) {
-        LocalPreferences.setDisplayFileSize(this, display);
-        mState.showSize = display;
-        DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
-        invalidateOptionsMenu();
-    }
-
-    @Override
-    public void onStateChanged() {
-        invalidateOptionsMenu();
-    }
-
-    /**
-     * Set state sort order based on explicit user action.
-     */
-    private void setUserSortOrder(int sortOrder) {
-        mState.userSortOrder = sortOrder;
-        DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
-    }
-
-    /**
-     * Set state mode based on explicit user action.
-     */
-    private void setUserMode(int mode) {
-        mState.userMode = mode;
-        DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
-    }
-
-    @Override
-    public void setPending(boolean pending) {
-        final SaveFragment save = SaveFragment.get(getFragmentManager());
-        if (save != null) {
-            save.setPending(pending);
-        }
-    }
-
-    @Override
     public void onBackPressed() {
         if (!mState.stackTouched) {
             super.onBackPressed();
@@ -507,130 +255,12 @@
     }
 
     @Override
-    protected void onSaveInstanceState(Bundle state) {
-        super.onSaveInstanceState(state);
-        state.putParcelable(EXTRA_STATE, mState);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle state) {
-        super.onRestoreInstanceState(state);
-    }
-
-    private BaseAdapter mStackAdapter = new BaseAdapter() {
-        @Override
-        public int getCount() {
-            return mState.stack.size();
-        }
-
-        @Override
-        public DocumentInfo getItem(int position) {
-            return mState.stack.get(mState.stack.size() - position - 1);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir_title, parent, false);
-            }
-
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-            } else {
-                title.setText(doc.displayName);
-            }
-
-            return convertView;
-        }
-
-        @Override
-        public View getDropDownView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir, parent, false);
-            }
-
-            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-                subdir.setVisibility(View.GONE);
-            } else {
-                title.setText(doc.displayName);
-                subdir.setVisibility(View.VISIBLE);
-            }
-
-            return convertView;
-        }
-    };
-
-    private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            if (mIgnoreNextNavigation) {
-                mIgnoreNextNavigation = false;
-                return;
-            }
-
-            while (mState.stack.size() > position + 1) {
-                mState.stackTouched = true;
-                mState.stack.pop();
-            }
-            onCurrentDirectoryChanged(ANIM_UP);
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            // Ignored
-        }
-    };
-
-    @Override
-    public RootInfo getCurrentRoot() {
-        if (mState.stack.root != null) {
-            return mState.stack.root;
-        } else {
-            return mRoots.getRecentsRoot();
-        }
-    }
-
-    public DocumentInfo getCurrentDirectory() {
-        return mState.stack.peek();
-    }
-
-    private String getCallingPackageMaybeExtra() {
-        final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
-        return (extra != null) ? extra : getCallingPackage();
-    }
-
-    public Executor getCurrentExecutor() {
-        final DocumentInfo cwd = getCurrentDirectory();
-        if (cwd != null && cwd.authority != null) {
-            return ProviderExecutor.forAuthority(cwd.authority);
-        } else {
-            return AsyncTask.THREAD_POOL_EXECUTOR;
-        }
-    }
-
-    @Override
     public State getDisplayState() {
         return mState;
     }
 
-    private void onCurrentDirectoryChanged(int anim) {
+    @Override
+    void onDirectoryChanged(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
@@ -654,109 +284,10 @@
                 DirectoryFragment.showNormal(fm, root, cwd, anim);
             }
         }
-
-        final RootsFragment roots = RootsFragment.get(fm);
-        if (roots != null) {
-            roots.onCurrentRootChanged();
-        }
-
-        updateActionBar();
-        invalidateOptionsMenu();
-        dumpStack();
-    }
-
-    @Override
-    public void onStackPicked(DocumentStack stack) {
-        try {
-            // Update the restored stack to ensure we have freshest data
-            stack.updateDocuments(getContentResolver());
-
-            mState.stack = stack;
-            mState.stackTouched = true;
-            onCurrentDirectoryChanged(ANIM_SIDE);
-
-        } catch (FileNotFoundException e) {
-            Log.w(TAG, "Failed to restore stack: " + e);
-        }
-    }
-
-    @Override
-    public void onRootPicked(RootInfo root, boolean closeDrawer) {
-        // Clear entire backstack and start in new root
-        mState.stack.root = root;
-        mState.stack.clear();
-        mState.stackTouched = true;
-
-        if (!mRoots.isRecentsRoot(root)) {
-            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
-        } else {
-            onCurrentDirectoryChanged(ANIM_SIDE);
-        }
-    }
-
-    private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
-        private RootInfo mRoot;
-
-        public PickRootTask(RootInfo root) {
-            mRoot = root;
-        }
-
-        @Override
-        protected DocumentInfo doInBackground(Void... params) {
-            try {
-                final Uri uri = DocumentsContract.buildDocumentUri(
-                        mRoot.authority, mRoot.documentId);
-                return DocumentInfo.fromUri(getContentResolver(), uri);
-            } catch (FileNotFoundException e) {
-                Log.w(TAG, "Failed to find root", e);
-                return null;
-            }
-        }
-
-        @Override
-        protected void onPostExecute(DocumentInfo result) {
-            if (result != null) {
-                mState.stack.push(result);
-                mState.stackTouched = true;
-                onCurrentDirectoryChanged(ANIM_SIDE);
-            }
-        }
-    }
-
-    @Override
-    public void onAppPicked(ResolveInfo info) {
-        final Intent intent = new Intent(getIntent());
-        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
-        intent.setComponent(new ComponentName(
-                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
-        startActivityForResult(intent, CODE_FORWARD);
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        Log.d(TAG, "onActivityResult() code=" + resultCode);
-
-        // Only relay back results when not canceled; otherwise stick around to
-        // let the user pick another app/backend.
-        if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
-
-            // Remember that we last picked via external app
-            final String packageName = getCallingPackageMaybeExtra();
-            final ContentValues values = new ContentValues();
-            values.put(ResumeColumns.EXTERNAL, 1);
-            getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
-
-            // Pass back result to original caller
-            setResult(resultCode, data);
-            finish();
-        } else {
-            super.onActivityResult(requestCode, resultCode, data);
-        }
     }
 
     @Override
     public void onDocumentPicked(DocumentInfo doc) {
-        final FragmentManager fm = getFragmentManager();
         if (doc.isDirectory()) {
             mState.stack.push(doc);
             mState.stackTouched = true;
@@ -780,27 +311,12 @@
     }
 
     @Override
-    public void onSaveRequested(DocumentInfo replaceTarget) {
-        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
-    }
-
-    @Override
-    public void onSaveRequested(String mimeType, String displayName) {
-        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
-    }
-
-    @Override
-    public void onPickRequested(DocumentInfo pickTarget) {
-        final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority,
-                pickTarget.documentId);
-        new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor());
-    }
-
-    private void saveStackBlocking() {
+    void saveStackBlocking() {
         final ContentResolver resolver = getContentResolver();
         final ContentValues values = new ContentValues();
 
-        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
+        final byte[] rawStack = DurableUtils.writeToArrayOrNull(
+                getDisplayState().stack);
 
         // Remember location for next app launch
         final String packageName = getCallingPackageMaybeExtra();
@@ -810,7 +326,8 @@
         resolver.insert(RecentsProvider.buildResume(packageName), values);
     }
 
-    private void onFinished(Uri... uris) {
+    @Override
+    void onTaskFinished(Uri... uris) {
         Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
         final Intent intent = new Intent();
@@ -832,106 +349,4 @@
         setResult(Activity.RESULT_OK, intent);
         finish();
     }
-
-    private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
-        private final String mMimeType;
-        private final String mDisplayName;
-
-        public CreateFinishTask(String mimeType, String displayName) {
-            mMimeType = mimeType;
-            mDisplayName = displayName;
-        }
-
-        @Override
-        protected void onPreExecute() {
-            setPending(true);
-        }
-
-        @Override
-        protected Uri doInBackground(Void... params) {
-            final ContentResolver resolver = getContentResolver();
-            final DocumentInfo cwd = getCurrentDirectory();
-
-            ContentProviderClient client = null;
-            Uri childUri = null;
-            try {
-                client = DocumentsApplication.acquireUnstableProviderOrThrow(
-                        resolver, cwd.derivedUri.getAuthority());
-                childUri = DocumentsContract.createDocument(
-                        client, cwd.derivedUri, mMimeType, mDisplayName);
-            } catch (Exception e) {
-                Log.w(TAG, "Failed to create document", e);
-            } finally {
-                ContentProviderClient.releaseQuietly(client);
-            }
-
-            if (childUri != null) {
-                saveStackBlocking();
-            }
-
-            return childUri;
-        }
-
-        @Override
-        protected void onPostExecute(Uri result) {
-            if (result != null) {
-                onFinished(result);
-            } else {
-                Toast.makeText(StandaloneActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
-                        .show();
-            }
-
-            setPending(false);
-        }
-    }
-
-    private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
-        private final Uri[] mUris;
-
-        public ExistingFinishTask(Uri... uris) {
-            mUris = uris;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            onFinished(mUris);
-        }
-    }
-
-    private class PickFinishTask extends AsyncTask<Void, Void, Void> {
-        private final Uri mUri;
-
-        public PickFinishTask(Uri uri) {
-            mUri = uri;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            onFinished(mUri);
-        }
-    }
-
-    private void dumpStack() {
-        Log.d(TAG, "Current stack: ");
-        Log.d(TAG, " * " + mState.stack.root);
-        for (DocumentInfo doc : mState.stack) {
-            Log.d(TAG, " +-- " + doc);
-        }
-    }
-
-    public static BaseActivity get(Fragment fragment) {
-        return (BaseActivity) fragment.getActivity();
-    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c7092b3..0385d1e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -383,8 +383,9 @@
                 Slog.i(LOG_TAG, "[PERSIST END]");
             }
 
-        } catch (IOException e) {
-            Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", e);
+            // Any error while writing is fatal.
+        } catch (Throwable t) {
+            Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
             destination.failWrite(out);
         } finally {
             IoUtils.closeQuietly(out);
@@ -406,9 +407,11 @@
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(in, null);
             parseStateLocked(parser);
-        } catch (XmlPullParserException | IOException ise) {
+
+            // Any error while parsing is fatal.
+        } catch (Throwable t) {
             throw new IllegalStateException("Failed parsing settings file: "
-                    + mStatePersistFile , ise);
+                    + mStatePersistFile , t);
         } finally {
             IoUtils.closeQuietly(in);
         }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a8a4baa..24f6931 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -208,6 +208,10 @@
             </intent-filter>
         </receiver>
 
+        <!-- Callback for dismissing screenshot notification after a share target is picked -->
+        <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
+                  android:exported="false" />
+
         <!-- started from UsbDeviceSettingsManager -->
         <activity android:name=".usb.UsbConfirmActivity"
             android:exported="true"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 105bf0f..715f4e4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -25,6 +25,7 @@
 import android.app.Notification.BigPictureStyle;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -242,7 +243,12 @@
             sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
             sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
 
-            Intent chooserIntent = Intent.createChooser(sharingIntent, null);
+            final PendingIntent callback = PendingIntent.getBroadcast(context, 0,
+                    new Intent(context, GlobalScreenshot.TargetChosenReceiver.class)
+                            .putExtra(GlobalScreenshot.CANCEL_ID, mNotificationId),
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+            Intent chooserIntent = Intent.createChooser(sharingIntent, null,
+                    callback.getIntentSender());
             chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
                     | Intent.FLAG_ACTIVITY_NEW_TASK);
 
@@ -341,6 +347,8 @@
 class GlobalScreenshot {
     private static final String TAG = "GlobalScreenshot";
 
+    static final String CANCEL_ID = "android:cancel_id";
+
     private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
     private static final int SCREENSHOT_DROP_IN_DURATION = 430;
     private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
@@ -732,4 +740,22 @@
                 .build();
         nManager.notify(R.id.notification_screenshot, n);
     }
+
+    /**
+     * Removes the notification for a screenshot after a share target is chosen.
+     */
+    public static class TargetChosenReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!intent.hasExtra(CANCEL_ID)) {
+                return;
+            }
+
+            final NotificationManager nm =
+                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+            final int id = intent.getIntExtra(CANCEL_ID, 0);
+            nm.cancel(id);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 964d75f..1c53655 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -95,7 +95,7 @@
         if (mContractedChild != null) {
             int size = Math.min(maxSize, mSmallHeight);
             mContractedChild.measure(widthMeasureSpec,
-                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
+                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY));
             maxChildHeight = Math.max(maxChildHeight, mContractedChild.getMeasuredHeight());
         }
         if (mExpandedChild != null) {
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index 94f0859..0658620 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -25,6 +25,7 @@
     $(JNI_H_INCLUDE) \
     frameworks/rs \
     frameworks/base/core/jni \
+    frameworks/base/libs/hwui \
     $(rs_generated_include_dir)
 
 LOCAL_CFLAGS += -Wno-unused-parameter -std=c++11
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9f80fd8..5d02576 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -460,6 +460,7 @@
     // set to null if in idle mode; while in this mode, any alarms we don't want
     // to run during this time are placed in mPendingWhileIdleAlarms
     Alarm mPendingIdleUntil = null;
+    Alarm mNextWakeFromIdle = null;
     final ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>();
 
     public AlarmManagerService(Context context) {
@@ -583,7 +584,7 @@
         final int mAlarmType;
 
         InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource,
-                int alarmType, String tag) {
+                int alarmType, String tag, long nowELAPSED) {
             mPendingIntent = pendingIntent;
             mWorkSource = workSource;
             mTag = tag;
@@ -593,6 +594,7 @@
                 fs = new FilterStats(mBroadcastStats, mTag);
                 mBroadcastStats.filterStats.put(mTag, fs);
             }
+            fs.lastTime = nowELAPSED;
             mFilterStats = fs;
             mAlarmType = alarmType;
         }
@@ -602,6 +604,7 @@
         final BroadcastStats mBroadcastStats;
         final String mTag;
 
+        long lastTime;
         long aggregateTime;
         int count;
         int numWakeup;
@@ -806,33 +809,38 @@
         setImplLocked(a, false, doValidate);
     }
 
+    private void updateNextWakeFromIdleFuzzLocked() {
+        if (mNextWakeFromIdle != null) {
+
+        }
+    }
+
     private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
         if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
-            // This is a special alarm that will put the system idle until it goes off.
+            // This is a special alarm that will put the system into idle until it goes off.
             // The caller has given the time they want this to happen at, however we need
             // to pull that earlier if there are existing alarms that have requested to
             // bring us out of idle.
-            final int N = mAlarmBatches.size();
-            for (int i = 0; i < N; i++) {
-                Batch b = mAlarmBatches.get(i);
-                if (a.whenElapsed > b.end) {
-                    // There are no interesting things happening before our idle until,
-                    // so keep the requested time.
-                    break;
-                }
-                if ((b.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
-                    a.when = a.whenElapsed = a.maxWhenElapsed = b.end;
-                    break;
-                }
+            if (mNextWakeFromIdle != null) {
+                a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
             }
             // Add fuzz to make the alarm go off some time before the actual desired time.
             final long nowElapsed = SystemClock.elapsedRealtime();
-            long fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
+            final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
             if (fuzz > 0) {
                 if (mRandom == null) {
                     mRandom = new Random();
                 }
-                a.whenElapsed -= mRandom.nextLong() % fuzz;
+                final int delta = mRandom.nextInt(fuzz);
+                a.whenElapsed -= delta;
+                if (false) {
+                    Slog.d(TAG, "Alarm when: " + a.whenElapsed);
+                    Slog.d(TAG, "Delta until alarm: " + (a.whenElapsed-nowElapsed));
+                    Slog.d(TAG, "Applied fuzz: " + fuzz);
+                    Slog.d(TAG, "Final delta: " + delta);
+                    Slog.d(TAG, "Final when: " + a.whenElapsed);
+                }
+                a.when = a.maxWhenElapsed = a.whenElapsed;
             }
 
         } else if (mPendingIdleUntil != null) {
@@ -869,11 +877,16 @@
         if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
             mPendingIdleUntil = a;
             needRebatch = true;
-        } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0 && mPendingIdleUntil != null) {
-            // If we are adding an alarm that asks to wake from idle, and we are currently
-            // idling, then we need to rebatch alarms in case the idle until time needs to
-            // be updated.
-            needRebatch = true;
+        } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+            if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
+                mNextWakeFromIdle = a;
+                // If this wake from idle is earlier than whatever was previously scheduled,
+                // and we are currently idling, then we need to rebatch alarms in case the idle
+                // until time needs to be updated.
+                if (mPendingIdleUntil != null) {
+                    needRebatch = true;
+                }
+            }
         }
 
         if (!rebatching) {
@@ -960,6 +973,11 @@
         }
 
         @Override
+        public long getNextWakeFromIdleTime() {
+            return getNextWakeFromIdleTimeImpl();
+        }
+
+        @Override
         public AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) {
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, false /* allowAll */, false /* requireFull */,
@@ -1030,10 +1048,15 @@
                 pw.println();
                 pw.println("Idle mode state:");
                 pw.print("  Idling until: "); pw.println(mPendingIdleUntil);
-                mPendingIdleUntil.dump(pw, "    ", nowELAPSED, nowRTC, sdf);
+                mPendingIdleUntil.dump(pw, "    ", nowRTC, nowELAPSED, sdf);
                 pw.println("  Pending alarms:");
                 dumpAlarmList(pw, mPendingWhileIdleAlarms, "    ", nowELAPSED, nowRTC, sdf);
             }
+            if (mNextWakeFromIdle != null) {
+                pw.println();
+                pw.print("  Next wake from idle: "); pw.println(mNextWakeFromIdle);
+                mNextWakeFromIdle.dump(pw, "    ", nowRTC, nowELAPSED, sdf);
+            }
 
             pw.println();
             pw.print("Past-due non-wakeup alarms: ");
@@ -1140,7 +1163,10 @@
                                 TimeUtils.formatDuration(fs.aggregateTime, pw);
                                 pw.print(" "); pw.print(fs.numWakeup);
                                 pw.print(" wakes " ); pw.print(fs.count);
-                                pw.print(" alarms: ");
+                                pw.print(" alarms, last ");
+                                TimeUtils.formatDuration(fs.lastTime, nowELAPSED, pw);
+                                pw.println(":");
+                        pw.print("      ");
                                 pw.print(fs.mTag);
                                 pw.println();
                     }
@@ -1216,7 +1242,13 @@
         return null;
     }
 
-    private AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
+    long getNextWakeFromIdleTimeImpl() {
+        synchronized (mLock) {
+            return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE;
+        }
+    }
+
+    AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
         synchronized (mLock) {
             return mNextAlarmClockForUser.get(userId);
         }
@@ -1398,6 +1430,9 @@
                 mPendingIdleUntil = null;
                 restorePending = true;
             }
+            if (mNextWakeFromIdle != null && mNextWakeFromIdle.operation.equals(operation)) {
+                mNextWakeFromIdle = null;
+            }
             rebatchAllAlarmsLocked(true);
             if (restorePending) {
                 restorePendingWhileIdleAlarmsLocked();
@@ -1585,11 +1620,19 @@
                 Alarm alarm = batch.get(i);
                 alarm.count = 1;
                 triggerList.add(alarm);
+                if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+                    EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
+                            alarm.tag);
+                }
                 if (mPendingIdleUntil == alarm) {
                     mPendingIdleUntil = null;
                     rebatchAllAlarmsLocked(false);
                     restorePendingWhileIdleAlarmsLocked();
                 }
+                if (mNextWakeFromIdle == alarm) {
+                    mNextWakeFromIdle = null;
+                    rebatchAllAlarmsLocked(false);
+                }
 
                 // Recurring alarms may have passed several alarm intervals while the
                 // phone was asleep or off, so pass a trigger count when sending them.
@@ -1655,7 +1698,7 @@
         public final long origWhen;
         public final boolean wakeup;
         public final PendingIntent operation;
-        public final String  tag;
+        public final String tag;
         public final WorkSource workSource;
         public final int flags;
         public int count;
@@ -1725,6 +1768,12 @@
                     pw.print(" repeatInterval="); pw.print(repeatInterval);
                     pw.print(" count="); pw.print(count);
                     pw.print(" flags=0x"); pw.println(Integer.toHexString(flags));
+            if (alarmClock != null) {
+                pw.print(prefix); pw.println("Alarm clock:");
+                pw.print(prefix); pw.print("  triggerTime=");
+                pw.println(sdf.format(new Date(alarmClock.getTriggerTime())));
+                pw.print(prefix); pw.print("  showIntent="); pw.println(alarmClock.getShowIntent());
+            }
             pw.print(prefix); pw.print("operation="); pw.println(operation);
         }
     }
@@ -1762,11 +1811,11 @@
         }
     }
 
-    static long fuzzForDuration(long duration) {
+    static int fuzzForDuration(long duration) {
         if (duration < 15*60*1000) {
             // If the duration until the time is less than 15 minutes, the maximum fuzz
             // is the duration.
-            return duration;
+            return (int)duration;
         } else if (duration < 90*60*1000) {
             // If duration is less than 1 1/2 hours, the maximum fuzz is 15 minutes,
             return 15*60*1000;
@@ -1824,7 +1873,7 @@
                     mWakeLock.acquire();
                 }
                 final InFlight inflight = new InFlight(AlarmManagerService.this,
-                        alarm.operation, alarm.workSource, alarm.type, alarm.tag);
+                        alarm.operation, alarm.workSource, alarm.type, alarm.tag, nowELAPSED);
                 mInFlight.add(inflight);
                 mBroadcastRefCount++;
 
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 66cc29a..9e28b64 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -199,9 +199,6 @@
         private final ArrayList<Bitmap> mBitmaps;
         private final int mPixelCount;
 
-        private long mNativeBitmap;
-
-        // Used for debugging only
         private Bitmap mAtlasBitmap;
 
         Renderer(ArrayList<Bitmap> bitmaps, int pixelCount) {
@@ -299,9 +296,7 @@
                 }
 
                 final long endRender = System.nanoTime();
-                if (mNativeBitmap != 0) {
-                    result = nUploadAtlas(buffer, mNativeBitmap);
-                }
+                result = nUploadAtlas(buffer, mAtlasBitmap);
 
                 final long endUpload = System.nanoTime();
                 if (DEBUG_ATLAS) {
@@ -326,14 +321,8 @@
          * @param height
          */
         private Canvas acquireCanvas(int width, int height) {
-            if (DEBUG_ATLAS_TEXTURE) {
-                mAtlasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-                return new Canvas(mAtlasBitmap);
-            } else {
-                Canvas canvas = new Canvas();
-                mNativeBitmap = nAcquireAtlasCanvas(canvas, width, height);
-                return canvas;
-            }
+            mAtlasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            return new Canvas(mAtlasBitmap);
         }
 
         /**
@@ -343,8 +332,8 @@
          * to disk in /data/system/atlas.png for debugging.
          */
         private void releaseCanvas(Canvas canvas) {
+            canvas.setBitmap(null);
             if (DEBUG_ATLAS_TEXTURE) {
-                canvas.setBitmap(null);
 
                 File systemDirectory = new File(Environment.getDataDirectory(), "system");
                 File dataFile = new File(systemDirectory, "atlas.png");
@@ -358,18 +347,13 @@
                 } catch (IOException e) {
                     // Ignore
                 }
-
-                mAtlasBitmap.recycle();
-                mAtlasBitmap = null;
-            } else {
-                nReleaseAtlasCanvas(canvas, mNativeBitmap);
             }
+            mAtlasBitmap.recycle();
+            mAtlasBitmap = null;
         }
     }
 
-    private static native long nAcquireAtlasCanvas(Canvas canvas, int width, int height);
-    private static native void nReleaseAtlasCanvas(Canvas canvas, long bitmap);
-    private static native boolean nUploadAtlas(GraphicBuffer buffer, long bitmap);
+    private static native boolean nUploadAtlas(GraphicBuffer buffer, Bitmap bitmap);
 
     @Override
     public boolean isCompatible(int ppid) {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index a227341..abd2ca0 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -173,6 +173,13 @@
 # ---------------------------
 33000 wp_wallpaper_crashed (component|3)
 
+# ---------------------------
+# Device idle
+# ---------------------------
+34000 device_idle (state|1|5), (reason|3)
+34001 device_idle_step
+34002 device_idle_wake_from_idle (is_idle|1|5), (reason|3)
+
 
 # ---------------------------
 # ConnectivityService.java
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ac488e3..28597c1 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -111,6 +111,7 @@
     static native int nativeOpenHal();
     static native int nativeCloseHal();
     static native void nativeInit(MessageQueue queue, FingerprintService service);
+    static native long nativeGetAuthenticatorId();
 
     static final class FpHalMsg {
         int type; // Type of the message. One of the constants in fingerprint.h
@@ -595,6 +596,12 @@
             checkPermission(USE_FINGERPRINT);
             return FingerprintService.this.hasEnrolledFingerprints(groupId);
         }
+
+        @Override
+        public long getAuthenticatorId() {
+            checkPermission(USE_FINGERPRINT);
+            return nativeGetAuthenticatorId();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index a72c77e..d3240ec 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -550,14 +550,19 @@
             }
         }
 
-        try {
-            // Convert properties to string contents and send it to HAL.
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
-            properties.store(baos, null);
-            native_configuration_update(baos.toString());
-            Log.d(TAG, "final config = " + baos.toString());
-        } catch (IOException ex) {
-            Log.w(TAG, "failed to dump properties contents");
+        if (native_is_gnss_configuration_supported()) {
+            try {
+                // Convert properties to string contents and send it to HAL.
+                ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
+                properties.store(baos, null);
+                native_configuration_update(baos.toString());
+                Log.d(TAG, "final config = " + baos.toString());
+            } catch (IOException ex) {
+                Log.w(TAG, "failed to dump properties contents");
+            }
+        } else if (DEBUG) {
+            Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
+                    + " supported");
         }
 
         // SUPL_ES configuration.
@@ -732,16 +737,21 @@
         }
 
         if (info != null) {
-            boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
-            boolean networkAvailable = info.isAvailable() && dataEnabled;
-            String defaultApn = getSelectedApn();
-            if (defaultApn == null) {
-                defaultApn = "dummy-apn";
-            }
+            if (native_is_agps_ril_supported()) {
+                boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
+                boolean networkAvailable = info.isAvailable() && dataEnabled;
+                String defaultApn = getSelectedApn();
+                if (defaultApn == null) {
+                    defaultApn = "dummy-apn";
+                }
 
-            native_update_network_state(info.isConnected(), info.getType(),
-                                        info.isRoaming(), networkAvailable,
-                                        info.getExtraInfo(), defaultApn);
+                native_update_network_state(info.isConnected(), info.getType(),
+                        info.isRoaming(), networkAvailable,
+                        info.getExtraInfo(), defaultApn);
+            } else if (DEBUG) {
+                Log.d(TAG, "Skipped network state update because AGPS-RIL in GPS HAL is not"
+                        + " supported");
+            }
         }
 
         if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
@@ -1752,7 +1762,7 @@
     // NI Client support
     //=============================================================
     private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
-        // Sends a response for an NI reqeust to HAL.
+        // Sends a response for an NI request to HAL.
         @Override
         public boolean sendNiResponse(int notificationId, int userResponse)
         {
@@ -1843,7 +1853,7 @@
     private void requestSetID(int flags) {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        int    type = AGPS_SETID_TYPE_NONE;
+        int type = AGPS_SETID_TYPE_NONE;
         String data = "";
 
         if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
@@ -1994,20 +2004,26 @@
                     .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
 
             // listen for events
-            IntentFilter intentFilter = new IntentFilter();
-            intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
-            intentFilter.addDataScheme("sms");
-            intentFilter.addDataAuthority("localhost","7275");
-            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+            IntentFilter intentFilter;
+            if (native_is_agps_ril_supported()) {
+                intentFilter = new IntentFilter();
+                intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
+                intentFilter.addDataScheme("sms");
+                intentFilter.addDataAuthority("localhost", "7275");
+                mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
 
-            intentFilter = new IntentFilter();
-            intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
-            try {
-                intentFilter.addDataType("application/vnd.omaloc-supl-init");
-            } catch (IntentFilter.MalformedMimeTypeException e) {
-                Log.w(TAG, "Malformed SUPL init mime type");
+                intentFilter = new IntentFilter();
+                intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
+                try {
+                    intentFilter.addDataType("application/vnd.omaloc-supl-init");
+                } catch (IntentFilter.MalformedMimeTypeException e) {
+                    Log.w(TAG, "Malformed SUPL init mime type");
+                }
+                mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+            } else if (DEBUG) {
+                Log.d(TAG, "Skipped registration for SMS/WAP-PUSH messages because AGPS Ril in GPS"
+                        + " HAL is not supported");
             }
-            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
 
             intentFilter = new IntentFilter();
             intentFilter.addAction(ALARM_WAKEUP);
@@ -2187,6 +2203,8 @@
     static { class_init_native(); }
     private static native void class_init_native();
     private static native boolean native_is_supported();
+    private static native boolean native_is_agps_ril_supported();
+    private static native boolean native_is_gnss_configuration_supported();
 
     private native boolean native_init();
     private native void native_cleanup();
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f3fdb0d..b3aa966 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4269,9 +4269,11 @@
                 serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
                 serializer.endDocument();
                 destination.finishWrite(out);
-            } catch (IOException e) {
+
+                // Any error while writing is fatal.
+            } catch (Throwable t) {
                 Slog.wtf(PackageManagerService.TAG,
-                        "Failed to write settings, restoring backup", e);
+                        "Failed to write settings, restoring backup", t);
                 destination.failWrite(out);
             } finally {
                 IoUtils.closeQuietly(out);
@@ -4319,9 +4321,11 @@
                 XmlPullParser parser = Xml.newPullParser();
                 parser.setInput(in, null);
                 parseRuntimePermissionsLPr(parser, userId);
-            } catch (XmlPullParserException | IOException ise) {
+
+                // Any error while parsing is fatal.
+            } catch (Throwable t) {
                 throw new IllegalStateException("Failed parsing permissions file: "
-                        + permissionsFile , ise);
+                        + permissionsFile , t);
             } finally {
                 IoUtils.closeQuietly(in);
             }
diff --git a/services/core/java/com/android/server/power/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java
index a23a87b..6b29b9a 100644
--- a/services/core/java/com/android/server/power/DeviceIdleController.java
+++ b/services/core/java/com/android/server/power/DeviceIdleController.java
@@ -41,6 +41,7 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.server.SystemService;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.EventLogTags;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -100,6 +101,11 @@
      * Scaling factor to apply to current idle timeout each time we cycle through that state.
      */
     private static final float DEFAULT_IDLE_FACTOR = 2f;
+    /**
+     * This is the minimum time we will allow until the next upcoming alarm for us to
+     * actually go in to idle mode.
+     */
+    private static final long DEFAULT_MIN_TIME_TO_ALARM = 60*60*1000L;
 
     private AlarmManager mAlarmManager;
     private IBatteryStats mBatteryStats;
@@ -239,7 +245,7 @@
             becomeInactiveIfAppropriateLocked();
         } else if (screenOn) {
             mScreenOn = true;
-            becomeActiveLocked();
+            becomeActiveLocked("screen");
         }
     }
 
@@ -249,12 +255,13 @@
             becomeInactiveIfAppropriateLocked();
         } else if (charging) {
             mCharging = charging;
-            becomeActiveLocked();
+            becomeActiveLocked("charging");
         }
     }
 
-    void becomeActiveLocked() {
+    void becomeActiveLocked(String reason) {
         if (mState != STATE_ACTIVE) {
+            EventLogTags.writeDeviceIdle(STATE_ACTIVE, reason);
             mLocalPowerManager.setDeviceIdleMode(false);
             try {
                 mNetworkPolicyManager.setDeviceIdleMode(false);
@@ -281,10 +288,22 @@
             mNextIdlePendingDelay = 0;
             mNextIdleDelay = 0;
             scheduleAlarmLocked(mInactiveTimeout, false);
+            EventLogTags.writeDeviceIdle(mState, "no activity");
         }
     }
 
     void stepIdleStateLocked() {
+        EventLogTags.writeDeviceIdleStep();
+
+        final long now = SystemClock.elapsedRealtime();
+        if ((now+DEFAULT_MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
+            // Whoops, there is an upcoming alarm.  We don't actually want to go idle.
+            if (mState != STATE_ACTIVE) {
+                becomeActiveLocked("alarm");
+            }
+            return;
+        }
+
         switch (mState) {
             case STATE_INACTIVE:
                 // We have now been inactive long enough, it is time to start looking
@@ -295,6 +314,7 @@
                 mNextIdlePendingDelay = DEFAULT_IDLE_PENDING_TIMEOUT;
                 mNextIdleDelay = DEFAULT_IDLE_TIMEOUT;
                 mState = STATE_IDLE_PENDING;
+                EventLogTags.writeDeviceIdle(mState, "step");
                 break;
             case STATE_IDLE_PENDING:
                 // We have been waiting to become idle, and now it is time!  This is the
@@ -307,6 +327,7 @@
                     mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
                 }
                 mState = STATE_IDLE;
+                EventLogTags.writeDeviceIdle(mState, "step");
                 mLocalPowerManager.setDeviceIdleMode(true);
                 try {
                     mNetworkPolicyManager.setDeviceIdleMode(true);
@@ -323,6 +344,7 @@
                     mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
                 }
                 mState = STATE_IDLE_PENDING;
+                EventLogTags.writeDeviceIdle(mState, "step");
                 mLocalPowerManager.setDeviceIdleMode(false);
                 try {
                     mNetworkPolicyManager.setDeviceIdleMode(false);
@@ -352,6 +374,7 @@
             }
             mState = STATE_ACTIVE;
             mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+            EventLogTags.writeDeviceIdle(mState, "motion");
             becomeInactiveIfAppropriateLocked();
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4f795a6..16d27fa 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -523,8 +523,9 @@
     public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf) {
         mHaveFrame = true;
 
-        TaskStack stack = mAppToken != null ? getStack() : null;
-        if (stack != null && !stack.isFullscreen()) {
+        final TaskStack stack = mAppToken != null ? getStack() : null;
+        final boolean nonFullscreenStack = stack != null && !stack.isFullscreen();
+        if (nonFullscreenStack) {
             stack.getBounds(mContainingFrame);
             final WindowState imeWin = mService.mInputMethodWindow;
             if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this
@@ -607,9 +608,13 @@
             y = mAttrs.y;
         }
 
-        // Make sure window fits in containing frame required by {@link Gravity#apply} call.
-        w = Math.min(w, pw);
-        h = Math.min(h, ph);
+        if (nonFullscreenStack) {
+            // Make sure window fits in containing frame since it is in a non-fullscreen stack as
+            // required by {@link Gravity#apply} call.
+            w = Math.min(w, pw);
+            h = Math.min(h, ph);
+        }
+
         Gravity.apply(mAttrs.gravity, w, h, mContainingFrame,
                 (int) (x + mAttrs.horizontalMargin * pw),
                 (int) (y + mAttrs.verticalMargin * ph), mFrame);
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 19ca2b4..a5546cf 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -33,6 +33,7 @@
     $(JNI_H_INCLUDE) \
     frameworks/base/services \
     frameworks/base/libs \
+    frameworks/base/libs/hwui \
     frameworks/base/core/jni \
     frameworks/native/services \
     libcore/include \
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index e4f242e..ad1d0f5 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -47,40 +47,9 @@
 #define FENCE_TIMEOUT 2000000000
 
 // ----------------------------------------------------------------------------
-// JNI Helpers
-// ----------------------------------------------------------------------------
-
-static struct {
-    jmethodID setNativeBitmap;
-} gCanvasClassInfo;
-
-#define INVOKEV(object, method, ...) \
-    env->CallVoidMethod(object, method, __VA_ARGS__)
-
-// ----------------------------------------------------------------------------
 // Canvas management
 // ----------------------------------------------------------------------------
 
-static jlong com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, jobject,
-        jobject canvas, jint width, jint height) {
-
-    SkBitmap* bitmap = new SkBitmap;
-    bitmap->allocN32Pixels(width, height);
-    bitmap->eraseColor(0);
-    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
-
-    return reinterpret_cast<jlong>(bitmap);
-}
-
-static void com_android_server_AssetAtlasService_releaseCanvas(JNIEnv* env, jobject,
-        jobject canvas, jlong bitmapHandle) {
-
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
-
-    delete bitmap;
-}
-
 #define CLEANUP_GL_AND_RETURN(result) \
     if (fence != EGL_NO_SYNC_KHR) eglDestroySyncKHR(display, fence); \
     if (image) eglDestroyImageKHR(display, image); \
@@ -93,9 +62,11 @@
     return result;
 
 static jboolean com_android_server_AssetAtlasService_upload(JNIEnv* env, jobject,
-        jobject graphicBuffer, jlong bitmapHandle) {
+        jobject graphicBuffer, jobject bitmapHandle) {
 
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkBitmap& bitmap = *GraphicsJNI::getSkBitmap(env, bitmapHandle);
+    SkAutoLockPixels alp(bitmap);
+
     // The goal of this method is to copy the bitmap into the GraphicBuffer
     // using the GPU to swizzle the texture content
     sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
@@ -186,9 +157,9 @@
         }
 
         // Upload the content of the bitmap in the GraphicBuffer
-        glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
-        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap->width(), bitmap->height(),
-                GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+        glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap.bytesPerPixel());
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
+                GL_RGBA, GL_UNSIGNED_BYTE, bitmap.getPixels());
         if (glGetError() != GL_NO_ERROR) {
             ALOGW("Could not upload to texture");
             CLEANUP_GL_AND_RETURN(JNI_FALSE);
@@ -233,20 +204,11 @@
 const char* const kClassPathName = "com/android/server/AssetAtlasService";
 
 static JNINativeMethod gMethods[] = {
-    { "nAcquireAtlasCanvas", "(Landroid/graphics/Canvas;II)J",
-            (void*) com_android_server_AssetAtlasService_acquireCanvas },
-    { "nReleaseAtlasCanvas", "(Landroid/graphics/Canvas;J)V",
-            (void*) com_android_server_AssetAtlasService_releaseCanvas },
-    { "nUploadAtlas", "(Landroid/view/GraphicBuffer;J)Z",
+    { "nUploadAtlas", "(Landroid/view/GraphicBuffer;Landroid/graphics/Bitmap;)Z",
             (void*) com_android_server_AssetAtlasService_upload },
 };
 
 int register_android_server_AssetAtlasService(JNIEnv* env) {
-    jclass clazz;
-
-    FIND_CLASS(clazz, "android/graphics/Canvas");
-    GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
-
     return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
index 5a86923..7dbfaf6 100644
--- a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
+++ b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
@@ -177,6 +177,10 @@
     return reinterpret_cast<jint>(ret);
 }
 
+static jlong nativeGetAuthenticatorId(JNIEnv *, jobject clazz) {
+    return gContext.device->get_authenticator_id(gContext.device);
+}
+
 static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
     ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
     int err;
@@ -229,6 +233,7 @@
     return -ENOSYS; // TODO
 }
 
+
 // ----------------------------------------------------------------------------
 
 
@@ -240,6 +245,7 @@
     { "nativePreEnroll", "()J", (void*)nativePreEnroll },
     { "nativeStopEnrollment", "()I", (void*)nativeStopEnrollment },
     { "nativeRemove", "(II)I", (void*)nativeRemove },
+    { "nativeGetAuthenticatorId", "()J", (void*)nativeGetAuthenticatorId },
     { "nativeOpenHal", "()I", (void*)nativeOpenHal },
     { "nativeCloseHal", "()I", (void*)nativeCloseHal },
     { "nativeInit","(Landroid/os/MessageQueue;"
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 0cd6eb5..3804e1d 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -509,13 +509,22 @@
     }
 }
 
-static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* /* env */,
-                                                                  jclass /* clazz */) {
-    if (sGpsInterface != NULL) {
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
+static jboolean android_location_GpsLocationProvider_is_supported(
+        JNIEnv* /* env */, jclass /* clazz */)
+{
+    return (sGpsInterface != NULL) ?  JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_is_agps_ril_supported(
+        JNIEnv* /* env */, jclass /* clazz */)
+{
+    return (sAGpsRilInterface != NULL) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_location_gpsLocationProvider_is_gnss_configuration_supported(
+        JNIEnv* /* env */, jclass /* jclazz */)
+{
+    return (sGnssConfigurationInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
@@ -715,14 +724,10 @@
         sGpsInterface->inject_location(latitude, longitude, accuracy);
 }
 
-static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* /* env */,
-                                                                   jobject /* obj */)
+static jboolean android_location_GpsLocationProvider_supports_xtra(
+        JNIEnv* /* env */, jobject /* obj */)
 {
-    if (sGpsXtraInterface != NULL) {
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
+    return (sGpsXtraInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
 static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject /* obj */,
@@ -844,13 +849,10 @@
     }
 }
 
-static jboolean android_location_GpsLocationProvider_is_geofence_supported(JNIEnv* /* env */,
-                                                                           jobject /* obj */)
+static jboolean android_location_GpsLocationProvider_is_geofence_supported(
+        JNIEnv* /* env */, jobject /* obj */)
 {
-    if (sGpsGeofencingInterface != NULL) {
-        return JNI_TRUE;
-    }
-    return JNI_FALSE;
+    return (sGpsGeofencingInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* /* env */,
@@ -1436,6 +1438,10 @@
      /* name, signature, funcPtr */
     {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
     {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
+    {"native_is_agps_ril_supported", "()Z",
+            (void*)android_location_GpsLocationProvider_is_agps_ril_supported},
+    {"native_is_gnss_configuration_supported", "()Z",
+            (void*)android_location_gpsLocationProvider_is_gnss_configuration_supported},
     {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},
     {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup},
     {"native_set_position_mode",
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 17db3fb..7b58755 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -470,8 +470,6 @@
 
     /**
      * Get signal level as an int from 0..4
-     *
-     * @hide
      */
     public int getLevel() {
         int level;
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index e7cde74..3dae917 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -54,7 +54,7 @@
           mWantUTF16(false), mValues(false), mIncludeMetaData(false),
           mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL),
           mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
-          mAutoAddOverlay(false), mGenDependencies(false),
+          mAutoAddOverlay(false), mGenDependencies(false), mNoVersionVectors(false),
           mCrunchedOutputDir(NULL), mProguardFile(NULL),
           mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
           mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
@@ -203,6 +203,8 @@
     void setSingleCrunchOutputFile(const char* val) { mSingleCrunchOutputFile = val; }
     bool getBuildSharedLibrary() const { return mBuildSharedLibrary; }
     void setBuildSharedLibrary(bool val) { mBuildSharedLibrary = val; }
+    void setNoVersionVectors(bool val) { mNoVersionVectors = val; }
+    bool getNoVersionVectors() const { return mNoVersionVectors; }
 
     /*
      * Set and get the file specification.
@@ -282,6 +284,7 @@
     const char* mInstrumentationPackageNameOverride;
     bool        mAutoAddOverlay;
     bool        mGenDependencies;
+    bool        mNoVersionVectors;
     const char* mCrunchedOutputDir;
     const char* mProguardFile;
     const char* mAndroidManifestFile;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 8b416aa..7dee585 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -211,7 +211,9 @@
         "       specified folder.\n"
         "   --ignore-assets\n"
         "       Assets to be ignored. Default pattern is:\n"
-        "       %s\n",
+        "       %s\n"
+        "   --no-version-vectors\n"
+        "       Do not automatically generate versioned copies of vector XML resources.\n",
         gDefaultIgnoreAssets);
 }
 
@@ -673,6 +675,8 @@
                     gUserIgnoreAssets = argv[0];
                 } else if (strcmp(cp, "-pseudo-localize") == 0) {
                     bundle.setPseudolocalize(PSEUDO_ACCENTED | PSEUDO_BIDI);
+                } else if (strcmp(cp, "-no-version-vectors") == 0) {
+                    bundle.setNoVersionVectors(true);
                 } else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index c5fccbf..3b146da 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -4611,6 +4611,9 @@
                                         const String16& resourceName,
                                         const sp<AaptFile>& target,
                                         const sp<XMLNode>& root) {
+    const String16 vector16("vector");
+    const String16 animatedVector16("animated-vector");
+
     const int minSdk = getMinSdkVersion(bundle);
     if (minSdk >= SDK_LOLLIPOP_MR1) {
         // Lollipop MR1 and up handles public attributes differently, no
@@ -4620,8 +4623,8 @@
 
     const ConfigDescription config(target->getGroupEntry().toParams());
     if (target->getResourceType() == "" || config.sdkVersion >= SDK_LOLLIPOP_MR1) {
-        // Skip resources that have no type (AndroidManifest.xml) or are already version qualified with v21
-        // or higher.
+        // Skip resources that have no type (AndroidManifest.xml) or are already version qualified
+        // with v21 or higher.
         return NO_ERROR;
     }
 
@@ -4635,6 +4638,12 @@
         sp<XMLNode> node = nodesToVisit.top();
         nodesToVisit.pop();
 
+        if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 ||
+                    node->getElementName() == animatedVector16)) {
+            // We were told not to version vector tags, so skip the children here.
+            continue;
+        }
+
         const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes();
         for (size_t i = 0; i < attrs.size(); i++) {
             const XMLNode::attribute_entry& attr = attrs[i];