Merge "Adds per-user APIs required by the Settings app"
diff --git a/api/current.txt b/api/current.txt
index 9726937..8548e2f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21704,6 +21704,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
     field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
     field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
+    field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
     field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
     field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
     field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
@@ -26579,13 +26580,15 @@
 
   public final class SynthesisRequest {
     ctor public SynthesisRequest(java.lang.String, android.os.Bundle);
+    ctor public SynthesisRequest(java.lang.CharSequence, android.os.Bundle);
     method public int getCallerUid();
+    method public java.lang.CharSequence getCharSequenceText();
     method public java.lang.String getCountry();
     method public java.lang.String getLanguage();
     method public android.os.Bundle getParams();
     method public int getPitch();
     method public int getSpeechRate();
-    method public java.lang.String getText();
+    method public deprecated java.lang.String getText();
     method public java.lang.String getVariant();
   }
 
@@ -26595,7 +26598,9 @@
     method public int addEarcon(java.lang.String, java.lang.String, int);
     method public int addEarcon(java.lang.String, java.lang.String);
     method public int addSpeech(java.lang.String, java.lang.String, int);
+    method public int addSpeech(java.lang.CharSequence, java.lang.String, int);
     method public int addSpeech(java.lang.String, java.lang.String);
+    method public int addSpeech(java.lang.CharSequence, java.lang.String);
     method public boolean areDefaultsEnforced();
     method public java.lang.String getDefaultEngine();
     method public java.util.Locale getDefaultLanguage();
@@ -26605,8 +26610,10 @@
     method public static int getMaxSpeechInputLength();
     method public int isLanguageAvailable(java.util.Locale);
     method public boolean isSpeaking();
-    method public int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
-    method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public deprecated int setEngineByPackageName(java.lang.String);
     method public int setLanguage(java.util.Locale);
     method public deprecated int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
@@ -26614,9 +26621,11 @@
     method public int setPitch(float);
     method public int setSpeechRate(float);
     method public void shutdown();
-    method public int speak(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public int speak(java.lang.CharSequence, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public deprecated int speak(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public int stop();
-    method public int synthesizeToFile(java.lang.String, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public int synthesizeToFile(java.lang.CharSequence, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String, java.lang.String);
+    method public deprecated int synthesizeToFile(java.lang.String, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
     field public static final java.lang.String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_INVALID_REQUEST = -8; // 0xfffffff8
@@ -33883,6 +33892,7 @@
     field public static final int FEATURE_RIGHT_ICON = 4; // 0x4
     field public static final int FEATURE_SWIPE_TO_DISMISS = 11; // 0xb
     field public static final int ID_ANDROID_CONTENT = 16908290; // 0x1020002
+    field public static final java.lang.String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = "android:navigation:background";
     field public static final int PROGRESS_END = 10000; // 0x2710
     field public static final int PROGRESS_INDETERMINATE_OFF = -4; // 0xfffffffc
     field public static final int PROGRESS_INDETERMINATE_ON = -3; // 0xfffffffd
@@ -33891,6 +33901,7 @@
     field public static final int PROGRESS_START = 0; // 0x0
     field public static final int PROGRESS_VISIBILITY_OFF = -2; // 0xfffffffe
     field public static final int PROGRESS_VISIBILITY_ON = -1; // 0xffffffff
+    field public static final java.lang.String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";
   }
 
   public static abstract interface Window.Callback {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index f9c2c8b..fafa948 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -578,9 +578,9 @@
         mResultData = null;
         mResultCode = 0;
         mExitCoordinatorIndex = 0;
+        mAnimationType = otherOptions.mAnimationType;
         switch (otherOptions.mAnimationType) {
             case ANIM_CUSTOM:
-                mAnimationType = otherOptions.mAnimationType;
                 mCustomEnterResId = otherOptions.mCustomEnterResId;
                 mCustomExitResId = otherOptions.mCustomExitResId;
                 mThumbnail = null;
@@ -593,7 +593,6 @@
                 mAnimationStartedListener = otherOptions.mAnimationStartedListener;
                 break;
             case ANIM_SCALE_UP:
-                mAnimationType = otherOptions.mAnimationType;
                 mStartX = otherOptions.mStartX;
                 mStartY = otherOptions.mStartY;
                 mStartWidth = otherOptions.mStartWidth;
@@ -608,7 +607,6 @@
                 break;
             case ANIM_THUMBNAIL_SCALE_UP:
             case ANIM_THUMBNAIL_SCALE_DOWN:
-                mAnimationType = otherOptions.mAnimationType;
                 mThumbnail = otherOptions.mThumbnail;
                 mStartX = otherOptions.mStartX;
                 mStartY = otherOptions.mStartY;
@@ -621,7 +619,6 @@
                 mAnimationStartedListener = otherOptions.mAnimationStartedListener;
                 break;
             case ANIM_SCENE_TRANSITION:
-                mAnimationType = otherOptions.mAnimationType;
                 mTransitionReceiver = otherOptions.mTransitionReceiver;
                 mSharedElementNames = otherOptions.mSharedElementNames;
                 mIsReturning = otherOptions.mIsReturning;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a480219..b7e64a2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -191,7 +191,9 @@
     /** @hide */
     public static final int OP_MUTE_MICROPHONE = 44;
     /** @hide */
-    public static final int _NUM_OP = 45;
+    public static final int OP_TOAST_WINDOW = 45;
+    /** @hide */
+    public static final int _NUM_OP = 46;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION =
@@ -259,7 +261,8 @@
             OP_COARSE_LOCATION,
             OP_COARSE_LOCATION,
             OP_GET_USAGE_STATS,
-            OP_MUTE_MICROPHONE
+            OP_MUTE_MICROPHONE,
+            OP_TOAST_WINDOW,
     };
 
     /**
@@ -312,6 +315,7 @@
             OPSTR_MONITOR_HIGH_POWER_LOCATION,
             null,
             null,
+            null,
     };
 
     /**
@@ -364,6 +368,7 @@
             "MONITOR_HIGH_POWER_LOCATION",
             "GET_USAGE_STATS",
             "OP_MUTE_MICROPHONE",
+            "TOAST_WINDOW",
     };
 
     /**
@@ -416,6 +421,7 @@
             null, // no permission for high power location monitoring
             android.Manifest.permission.PACKAGE_USAGE_STATS,
             null, // no permission for muting/unmuting microphone
+            null, // no permission for displaying toasts
     };
 
     /**
@@ -448,7 +454,7 @@
             null, //READ_ICC_SMS
             null, //WRITE_ICC_SMS
             null, //WRITE_SETTINGS
-            null, //SYSTEM_ALERT_WINDOW
+            UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
             null, //ACCESS_NOTIFICATIONS
             null, //CAMERA
             null, //RECORD_AUDIO
@@ -469,6 +475,60 @@
             null, //MONITOR_HIGH_POWER_LOCATION
             null, //GET_USAGE_STATS
             UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
+            UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW
+    };
+
+    /**
+     * This specifies whether each option should allow the system
+     * (and system ui) to bypass the user restriction when active.
+     */
+    private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] {
+            false, //COARSE_LOCATION
+            false, //FINE_LOCATION
+            false, //GPS
+            false, //VIBRATE
+            false, //READ_CONTACTS
+            false, //WRITE_CONTACTS
+            false, //READ_CALL_LOG
+            false, //WRITE_CALL_LOG
+            false, //READ_CALENDAR
+            false, //WRITE_CALENDAR
+            false, //WIFI_SCAN
+            false, //POST_NOTIFICATION
+            false, //NEIGHBORING_CELLS
+            false, //CALL_PHONE
+            false, //READ_SMS
+            false, //WRITE_SMS
+            false, //RECEIVE_SMS
+            false, //RECEIVE_EMERGECY_SMS
+            false, //RECEIVE_MMS
+            false, //RECEIVE_WAP_PUSH
+            false, //SEND_SMS
+            false, //READ_ICC_SMS
+            false, //WRITE_ICC_SMS
+            false, //WRITE_SETTINGS
+            true, //SYSTEM_ALERT_WINDOW
+            false, //ACCESS_NOTIFICATIONS
+            false, //CAMERA
+            false, //RECORD_AUDIO
+            false, //PLAY_AUDIO
+            false, //READ_CLIPBOARD
+            false, //WRITE_CLIPBOARD
+            false, //TAKE_MEDIA_BUTTONS
+            false, //TAKE_AUDIO_FOCUS
+            false, //AUDIO_MASTER_VOLUME
+            false, //AUDIO_VOICE_VOLUME
+            false, //AUDIO_RING_VOLUME
+            false, //AUDIO_MEDIA_VOLUME
+            false, //AUDIO_ALARM_VOLUME
+            false, //AUDIO_NOTIFICATION_VOLUME
+            false, //AUDIO_BLUETOOTH_VOLUME
+            false, //WAKE_LOCK
+            false, //MONITOR_LOCATION
+            false, //MONITOR_HIGH_POWER_LOCATION
+            false, //GET_USAGE_STATS
+            false, // MUTE_MICROPHONE
+            true, // TOAST_WINDOW
     };
 
     /**
@@ -520,6 +580,7 @@
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
             AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
     };
 
     /**
@@ -575,6 +636,7 @@
             false,
             false,
             false,
+            false,
     };
 
     private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
@@ -608,6 +670,10 @@
             throw new IllegalStateException("sOpRestrictions length " + sOpRestrictions.length
                     + " should be " + _NUM_OP);
         }
+        if (sOpAllowSystemRestrictionBypass.length != _NUM_OP) {
+            throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length "
+                    + sOpRestrictions.length + " should be " + _NUM_OP);
+        }
         for (int i=0; i<_NUM_OP; i++) {
             if (sOpToString[i] != null) {
                 sOpStrToOp.put(sOpToString[i], i);
@@ -649,6 +715,15 @@
     }
 
     /**
+     * Retrieve whether the op allows the system (and system ui) to
+     * bypass the user restriction.
+     * @hide
+     */
+    public static boolean opAllowSystemBypassRestriction(int op) {
+        return sOpAllowSystemRestrictionBypass[op];
+    }
+
+    /**
      * Retrieve the default mode for the operation.
      * @hide
      */
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index a8f6539..5e18d0f 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -57,6 +57,7 @@
     private boolean mIsReadyForTransition;
     private Bundle mSharedElementsBundle;
     private boolean mWasOpaque;
+    private boolean mAreViewsReady;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -81,18 +82,11 @@
     }
 
     public void viewInstancesReady(ArrayList<String> accepted, ArrayList<View> localViews) {
-        if (mIsReadyForTransition) {
-            return;
-        }
-        viewsReady(mapSharedElements(accepted, localViews));
+        triggerViewsReady(mapSharedElements(accepted, localViews));
     }
 
     public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
-        if (mIsReadyForTransition) {
-            return;
-        }
-
-        viewsReady(mapNamedElements(accepted, localNames));
+        triggerViewsReady(mapNamedElements(accepted, localNames));
     }
 
     @Override
@@ -118,6 +112,27 @@
         }
     }
 
+    private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
+        if (mAreViewsReady) {
+            return;
+        }
+        mAreViewsReady = true;
+        // Ensure the views have been laid out before capturing the views -- we need the epicenter.
+        if (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()) {
+            viewsReady(sharedElements);
+        } else {
+            sharedElements.valueAt(0).getViewTreeObserver()
+                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    sharedElements.valueAt(0).getViewTreeObserver().removeOnPreDrawListener(this);
+                    viewsReady(sharedElements);
+                    return true;
+                }
+            });
+        }
+    }
+
     private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
             ArrayList<String> localNames) {
         ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index ba34184..93cba0d 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -123,6 +123,8 @@
                     startSharedElementExit();
                 }
             });
+        } else {
+            sharedElementTransitionComplete();
         }
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d18647a..df51ff5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2586,6 +2586,8 @@
      * @param packages The list of packages allowed to enter lock task mode
      *
      * @see Activity#startLockTask()
+     * @see DeviceAdminReceiver#onLockTaskModeChanged(Context, Intent, boolean, String)
+     * @see UserManager#DISALLOW_CREATE_WINDOWS
      */
     public void setLockTaskPackages(String[] packages) throws SecurityException {
         if (mService != null) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a8b324a..05e179d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2208,15 +2208,15 @@
         if (need == null) throw new IllegalArgumentException("null NetworkCapabilities");
         try {
             incCallbackHandlerRefCount();
-            if (action == LISTEN) {
-                networkCallback.networkRequest = mService.listenForNetwork(need,
-                        new Messenger(sCallbackHandler), new Binder());
-            } else {
-                networkCallback.networkRequest = mService.requestNetwork(need,
-                        new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
-            }
-            if (networkCallback.networkRequest != null) {
-                synchronized(sNetworkCallback) {
+            synchronized(sNetworkCallback) {
+                if (action == LISTEN) {
+                    networkCallback.networkRequest = mService.listenForNetwork(need,
+                            new Messenger(sCallbackHandler), new Binder());
+                } else {
+                    networkCallback.networkRequest = mService.requestNetwork(need,
+                            new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
+                }
+                if (networkCallback.networkRequest != null) {
                     sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
                 }
             }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b980e81..a54320f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -27,6 +27,7 @@
 import android.graphics.drawable.Drawable;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
 
@@ -278,6 +279,24 @@
      */
     public static final String DISALLOW_TELEPHONY = "no_telephony";
 
+    /**
+     * Key for user restrictions. Specifies that windows besides app windows should not be
+     * created. This will block the creation of the following types of windows.
+     * <li>{@link LayoutParams#TYPE_TOAST}</li>
+     * <li>{@link LayoutParams#TYPE_PHONE}</li>
+     * <li>{@link LayoutParams#TYPE_PRIORITY_PHONE}</li>
+     * <li>{@link LayoutParams#TYPE_SYSTEM_ALERT}</li>
+     * <li>{@link LayoutParams#TYPE_SYSTEM_ERROR}</li>
+     * <li>{@link LayoutParams#TYPE_SYSTEM_OVERLAY}</li>
+     *
+     * <p>The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
+
     /** @hide */
     public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
     /** @hide */
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 03ce4e0..f3c26c8 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -154,6 +154,7 @@
         final Rect mWinFrame = new Rect();
         final Rect mOverscanInsets = new Rect();
         final Rect mContentInsets = new Rect();
+        final Rect mStableInsets = new Rect();
         final Configuration mConfiguration = new Configuration();
         
         final WindowManager.LayoutParams mLayout
@@ -253,7 +254,8 @@
         final BaseIWindow mWindow = new BaseIWindow() {
             @Override
             public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
-                    Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+                    Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+                    Configuration newConfig) {
                 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0);
                 mCaller.sendMessage(msg);
@@ -628,7 +630,7 @@
                     final int relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
-                            mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
+                            mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
index 4d322df..694f25a 100644
--- a/core/java/android/speech/tts/ITextToSpeechService.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -36,8 +36,10 @@
      * @param text The text to synthesize.
      * @param queueMode Determines what to do to requests already in the queue.
      * @param param Request parameters.
+     * @param utteranceId Unique identifier of synthesized utterance.
      */
-    int speak(in IBinder callingInstance, in String text, in int queueMode, in Bundle params);
+    int speak(in IBinder callingInstance, in CharSequence text, in int queueMode, in Bundle params,
+            String utteranceId);
 
     /**
      * Tells the engine to synthesize some speech and write it to a file.
@@ -47,10 +49,11 @@
      * @param text The text to synthesize.
      * @param fileDescriptor The file descriptor to write the synthesized audio to. Has to be
               writable.
+     * @param utteranceId Unique identifier of synthesized utterance.
      * @param param Request parameters.
      */
-    int synthesizeToFileDescriptor(in IBinder callingInstance, in String text,
-        in ParcelFileDescriptor fileDescriptor, in Bundle params);
+    int synthesizeToFileDescriptor(in IBinder callingInstance, in CharSequence text,
+        in ParcelFileDescriptor fileDescriptor, in Bundle params, String utteranceId);
 
     /**
      * Plays an existing audio resource.
@@ -59,9 +62,11 @@
      *        TextToSpeech object.
      * @param audioUri URI for the audio resource (a file or android.resource URI)
      * @param queueMode Determines what to do to requests already in the queue.
+     * @param utteranceId Unique identifier of synthesized utterance.
      * @param param Request parameters.
      */
-    int playAudio(in IBinder callingInstance, in Uri audioUri, in int queueMode, in Bundle params);
+    int playAudio(in IBinder callingInstance, in Uri audioUri, in int queueMode, in Bundle params,
+            String utteranceId);
 
     /**
      * Plays silence.
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
index 12a026b..eaacc06 100644
--- a/core/java/android/speech/tts/SynthesisRequest.java
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -34,7 +34,7 @@
  * and {@link TextToSpeech#synthesizeToFile}.
  */
 public final class SynthesisRequest {
-    private final String mText;
+    private final CharSequence mText;
     private final Bundle mParams;
     private String mLanguage;
     private String mCountry;
@@ -49,10 +49,25 @@
         mParams = new Bundle(params);
     }
 
+    public SynthesisRequest(CharSequence text, Bundle params) {
+        mText = text;
+        // Makes a copy of params.
+        mParams = new Bundle(params);
+    }
+
+    /**
+     * Gets the text which should be synthesized.
+     * @deprecated As of API level 20, replaced by {@link #getCharSequenceText}.
+     */
+    @Deprecated
+    public String getText() {
+        return mText.toString();
+    }
+
     /**
      * Gets the text which should be synthesized.
      */
-    public String getText() {
+    public CharSequence getCharSequenceText() {
         return mText;
     }
 
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 0d2b69b..e1c1767b 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -593,7 +593,7 @@
     // too.
     private final boolean mUseFallback;
     private final Map<String, Uri> mEarcons;
-    private final Map<String, Uri> mUtterances;
+    private final Map<CharSequence, Uri> mUtterances;
     private final Bundle mParams = new Bundle();
     private final TtsEngines mEnginesHelper;
     private final String mPackageName;
@@ -644,7 +644,7 @@
         mUseFallback = useFallback;
 
         mEarcons = new HashMap<String, Uri>();
-        mUtterances = new HashMap<String, Uri>();
+        mUtterances = new HashMap<CharSequence, Uri>();
         mUtteranceProgressListener = null;
 
         mEnginesHelper = new TtsEngines(mContext);
@@ -825,6 +825,40 @@
     }
 
     /**
+     * Adds a mapping between a CharSequence (may be spanned with TtsSpans) of text
+     * and a sound resource in a package. After a call to this method, subsequent calls to
+     * {@link #speak(String, int, HashMap)} will play the specified sound resource
+     * if it is available, or synthesize the text it is missing.
+     *
+     * @param text
+     *            The string of text. Example: <code>"south_south_east"</code>
+     *
+     * @param packagename
+     *            Pass the packagename of the application that contains the
+     *            resource. If the resource is in your own application (this is
+     *            the most common case), then put the packagename of your
+     *            application here.<br/>
+     *            Example: <b>"com.google.marvin.compass"</b><br/>
+     *            The packagename can be found in the AndroidManifest.xml of
+     *            your application.
+     *            <p>
+     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
+     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
+     *            </p>
+     *
+     * @param resourceId
+     *            Example: <code>R.raw.south_south_east</code>
+     *
+     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     */
+    public int addSpeech(CharSequence text, String packagename, int resourceId) {
+        synchronized (mStartLock) {
+            mUtterances.put(text, makeResourceUri(packagename, resourceId));
+            return SUCCESS;
+        }
+    }
+
+    /**
      * Adds a mapping between a string of text and a sound file. Using this, it
      * is possible to add custom pronounciations for a string of text.
      * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
@@ -846,6 +880,28 @@
         }
     }
 
+    /**
+     * Adds a mapping between a CharSequence (may be spanned with TtsSpans and a sound file.
+     * Using this, it is possible to add custom pronounciations for a string of text.
+     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
+     * will play the specified sound resource if it is available, or synthesize the text it is
+     * missing.
+     *
+     * @param text
+     *            The string of text. Example: <code>"south_south_east"</code>
+     * @param filename
+     *            The full path to the sound file (for example:
+     *            "/sdcard/mysounds/hello.wav")
+     *
+     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     */
+    public int addSpeech(CharSequence text, String filename) {
+        synchronized (mStartLock) {
+            mUtterances.put(text, Uri.parse(filename));
+            return SUCCESS;
+        }
+    }
+
 
     /**
      * Adds a mapping between a string of text and a sound resource in a
@@ -910,6 +966,51 @@
     }
 
     /**
+     * Speaks the text using the specified queuing strategy and speech parameters, the text may
+     * be spanned with TtsSpans.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}) and using the
+     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     *
+     * @param text The string of text to be spoken. No longer than
+     *            {@link #getMaxSpeechInputLength()} characters.
+     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_STREAM},
+     *            {@link Engine#KEY_PARAM_VOLUME},
+     *            {@link Engine#KEY_PARAM_PAN}.
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+     *            engine named "com.svox.pico" if it is being used.
+     * @param utteranceId An unique identifier for this request.
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
+     */
+    public int speak(final CharSequence text,
+                     final int queueMode,
+                     final HashMap<String, String> params,
+                     final String utteranceId) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                Uri utteranceUri = mUtterances.get(text);
+                if (utteranceUri != null) {
+                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
+                            getParams(params), utteranceId);
+                } else {
+                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
+                            utteranceId);
+                }
+            }
+        }, ERROR, "speak");
+    }
+
+    /**
      * Speaks the string using the specified queuing strategy and speech parameters.
      * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
      * requests and then returns. The synthesis might not have finished (or even started!) at the
@@ -933,20 +1034,51 @@
      *            engine named "com.svox.pico" if it is being used.
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
+     * @deprecated As of API level 20, replaced by
+     *         {@link #speak(CharSequence, int, HashMap, String)}.
      */
+    @Deprecated
     public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
+        return speak(text, queueMode, params,
+                     params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+    }
+
+    /**
+     * Plays the earcon using the specified queueing mode and parameters.
+     * The earcon must already have been added with {@link #addEarcon(String, String)} or
+     * {@link #addEarcon(String, String, int)}.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}) and using the
+     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     *
+     * @param earcon The earcon that should be played
+     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_STREAM},
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+     *            engine named "com.svox.pico" if it is being used.
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
+     */
+    public int playEarcon(final String earcon, final int queueMode,
+            final HashMap<String, String> params, final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
-                Uri utteranceUri = mUtterances.get(text);
-                if (utteranceUri != null) {
-                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
-                            getParams(params));
-                } else {
-                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params));
+                Uri earconUri = mEarcons.get(earcon);
+                if (earconUri == null) {
+                    return ERROR;
                 }
+                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
+                        getParams(params), utteranceId);
             }
-        }, ERROR, "speak");
+        }, ERROR, "playEarcon");
     }
 
     /**
@@ -972,20 +1104,46 @@
      *            engine named "com.svox.pico" if it is being used.
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
+     * @deprecated As of API level 20, replaced by
+     *         {@link #playEarcon(String, int, HashMap, String)}.
      */
+    @Deprecated
     public int playEarcon(final String earcon, final int queueMode,
             final HashMap<String, String> params) {
+        return playEarcon(earcon, queueMode, params,
+                          params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+    }
+
+    /**
+     * Plays silence for the specified amount of time using the specified
+     * queue mode.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}) and using the
+     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     *
+     * @param durationInMs The duration of the silence.
+     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+     *            engine named "com.svox.pico" if it is being used.
+     * @param utteranceId An unique identifier for this request.
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
+     */
+    public int playSilence(final long durationInMs, final int queueMode,
+            final HashMap<String, String> params, final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
-                Uri earconUri = mEarcons.get(earcon);
-                if (earconUri == null) {
-                    return ERROR;
-                }
-                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
-                        getParams(params));
+                return service.playSilence(getCallerIdentity(), durationInMs,
+                                           queueMode, utteranceId);
             }
-        }, ERROR, "playEarcon");
+        }, ERROR, "playSilence");
     }
 
     /**
@@ -1009,16 +1167,14 @@
      *            engine named "com.svox.pico" if it is being used.
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
+     * @deprecated As of API level 20, replaced by
+     *         {@link #playSilence(long, int, HashMap, String)}.
      */
+    @Deprecated
     public int playSilence(final long durationInMs, final int queueMode,
             final HashMap<String, String> params) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                return service.playSilence(getCallerIdentity(), durationInMs, queueMode,
-                        params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
-            }
-        }, ERROR, "playSilence");
+        return playSilence(durationInMs, queueMode, params,
+                           params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
     }
 
     /**
@@ -1294,25 +1450,22 @@
      * requests and then returns. The synthesis might not have finished (or even started!) at the
      * time when this method returns. In order to reliably detect errors during synthesis,
      * we recommend setting an utterance progress listener (see
-     * {@link #setOnUtteranceProgressListener}) and using the
-     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     * {@link #setOnUtteranceProgressListener}).
      *
      * @param text The text that should be synthesized. No longer than
      *            {@link #getMaxSpeechInputLength()} characters.
      * @param params Parameters for the request. Can be null.
-     *            Supported parameter names:
-     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
      *            Engine specific parameters may be passed in but the parameter keys
      *            must be prefixed by the name of the engine they are intended for. For example
      *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
      *            engine named "com.svox.pico" if it is being used.
      * @param filename Absolute file filename to write the generated audio data to.It should be
      *            something like "/sdcard/myappsounds/mysound.wav".
-     *
+     * @param utteranceId An unique identifier for this request.
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
      */
-    public int synthesizeToFile(final String text, final HashMap<String, String> params,
-            final String filename) {
+    public int synthesizeToFile(final CharSequence text, final HashMap<String, String> params,
+            final String filename, final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
@@ -1329,7 +1482,7 @@
                             ParcelFileDescriptor.MODE_CREATE |
                             ParcelFileDescriptor.MODE_TRUNCATE);
                     returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
-                            fileDescriptor, getParams(params));
+                            fileDescriptor, getParams(params), utteranceId);
                     fileDescriptor.close();
                     return returnValue;
                 } catch (FileNotFoundException e) {
@@ -1343,6 +1496,36 @@
         }, ERROR, "synthesizeToFile");
     }
 
+    /**
+     * Synthesizes the given text to a file using the specified parameters.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}) and using the
+     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     *
+     * @param text The text that should be synthesized. No longer than
+     *            {@link #getMaxSpeechInputLength()} characters.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+     *            engine named "com.svox.pico" if it is being used.
+     * @param filename Absolute file filename to write the generated audio data to.It should be
+     *            something like "/sdcard/myappsounds/mysound.wav".
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
+     * @deprecated As of API level 20, replaced by
+     *         {@link #synthesizeToFile(CharSequence, HashMap, String, String)}.
+     */
+    public int synthesizeToFile(final String text, final HashMap<String, String> params,
+            final String filename) {
+        return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+    }
+
     private Bundle getParams(HashMap<String, String> params) {
         if (params != null && !params.isEmpty()) {
             Bundle bundle = new Bundle(mParams);
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 5a3c5f7..017be93 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -637,11 +637,13 @@
      */
     private abstract class SpeechItemV1 extends UtteranceSpeechItem {
         protected final Bundle mParams;
+        protected final String mUtteranceId;
 
         SpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params) {
+                Bundle params, String utteranceId) {
             super(callerIdentity, callerUid, callerPid);
             mParams = params;
+            mUtteranceId = utteranceId;
         }
 
         boolean hasLanguage() {
@@ -658,7 +660,7 @@
 
         @Override
         public String getUtteranceId() {
-            return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null);
+            return mUtteranceId;
         }
 
         AudioOutputParams getAudioParams() {
@@ -668,7 +670,7 @@
 
     class SynthesisSpeechItemV1 extends SpeechItemV1 {
         // Never null.
-        private final String mText;
+        private final CharSequence mText;
         private final SynthesisRequest mSynthesisRequest;
         private final String[] mDefaultLocale;
         // Non null after synthesis has started, and all accesses
@@ -678,8 +680,8 @@
         private final int mCallerUid;
 
         public SynthesisSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params, String text) {
-            super(callerIdentity, callerUid, callerPid, params);
+                Bundle params, String utteranceId, CharSequence text) {
+            super(callerIdentity, callerUid, callerPid, params, utteranceId);
             mText = text;
             mCallerUid = callerUid;
             mSynthesisRequest = new SynthesisRequest(mText, mParams);
@@ -689,7 +691,7 @@
                     mPackageName);
         }
 
-        public String getText() {
+        public CharSequence getText() {
             return mText;
         }
 
@@ -774,8 +776,9 @@
         private final FileOutputStream mFileOutputStream;
 
         public SynthesisToFileOutputStreamSpeechItemV1(Object callerIdentity, int callerUid,
-                int callerPid, Bundle params, String text, FileOutputStream fileOutputStream) {
-            super(callerIdentity, callerUid, callerPid, params, text);
+                int callerPid, Bundle params, String utteranceId, CharSequence text,
+                FileOutputStream fileOutputStream) {
+            super(callerIdentity, callerUid, callerPid, params, utteranceId, text);
             mFileOutputStream = fileOutputStream;
         }
 
@@ -801,8 +804,8 @@
         private final AudioPlaybackQueueItem mItem;
 
         public AudioSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params, Uri uri) {
-            super(callerIdentity, callerUid, callerPid, params);
+                Bundle params, String utteranceId, Uri uri) {
+            super(callerIdentity, callerUid, callerPid, params, utteranceId);
             mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(),
                     TextToSpeechService.this, uri, getAudioParams());
         }
@@ -909,19 +912,20 @@
     // they can be used as message objects (which are tested for equality using ==).
     private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
         @Override
-        public int speak(IBinder caller, String text, int queueMode, Bundle params) {
+        public int speak(IBinder caller, CharSequence text, int queueMode, Bundle params,
+                String utteranceId) {
             if (!checkNonNull(caller, text, params)) {
                 return TextToSpeech.ERROR;
             }
 
             SpeechItem item = new SynthesisSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, text);
+                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text);
             return mSynthHandler.enqueueSpeechItem(queueMode, item);
         }
 
         @Override
-        public int synthesizeToFileDescriptor(IBinder caller, String text, ParcelFileDescriptor
-                fileDescriptor, Bundle params) {
+        public int synthesizeToFileDescriptor(IBinder caller, CharSequence text, ParcelFileDescriptor
+                fileDescriptor, Bundle params, String utteranceId) {
             if (!checkNonNull(caller, text, fileDescriptor, params)) {
                 return TextToSpeech.ERROR;
             }
@@ -933,19 +937,20 @@
                     fileDescriptor.detachFd());
 
             SpeechItem item = new SynthesisToFileOutputStreamSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, text,
+                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text,
                     new ParcelFileDescriptor.AutoCloseOutputStream(sameFileDescriptor));
             return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
         }
 
         @Override
-        public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params) {
+        public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params,
+                String utteranceId) {
             if (!checkNonNull(caller, audioUri, params)) {
                 return TextToSpeech.ERROR;
             }
 
             SpeechItem item = new AudioSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, audioUri);
+                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, audioUri);
             return mSynthHandler.enqueueSpeechItem(queueMode, item);
         }
 
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 1066430..f41afcf 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -431,7 +431,7 @@
     /**
      * Gets the time when the current frame started.
      * <p>
-     * This method provides the time in nanoseconds when the frame started being rendered.
+     * This method provides the time in milliseconds when the frame started being rendered.
      * The frame time provides a stable time base for synchronizing animations
      * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
      * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 3670eed..3e7aae0 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -46,7 +46,8 @@
     void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
 
     void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
-            in Rect visibleInsets, boolean reportDraw, in Configuration newConfig);
+            in Rect visibleInsets, in Rect stableInsets, boolean reportDraw,
+            in Configuration newConfig);
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index fa5bd88..0f3f182 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -89,7 +89,7 @@
     int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility,
             int flags, out Rect outFrame, out Rect outOverscanInsets,
-            out Rect outContentInsets, out Rect outVisibleInsets,
+            out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Configuration outConfig, out Surface outSurface);
 
     /**
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 1363a5c..4b53c8e 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -18,6 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
@@ -34,7 +35,7 @@
 /**
  * @hide
  */
-public final class RenderNodeAnimator extends Animator {
+public class RenderNodeAnimator extends Animator {
     // Keep in sync with enum RenderProperty in Animator.h
     public static final int TRANSLATION_X = 0;
     public static final int TRANSLATION_Y = 1;
@@ -83,16 +84,23 @@
 
     private RenderNode mTarget;
     private View mViewTarget;
+    private int mRenderProperty = -1;
+    private float mFinalValue;
     private TimeInterpolator mInterpolator;
 
     private boolean mStarted = false;
     private boolean mFinished = false;
 
+    private long mUnscaledDuration = 300;
+    private long mUnscaledStartDelay = 0;
+
     public static int mapViewPropertyToRenderProperty(int viewProperty) {
         return sViewPropertyAnimatorMap.get(viewProperty);
     }
 
     public RenderNodeAnimator(int property, float finalValue) {
+        mRenderProperty = property;
+        mFinalValue = finalValue;
         init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
                 property, finalValue));
     }
@@ -156,7 +164,16 @@
 
         mStarted = true;
         applyInterpolator();
-        mTarget.addAnimator(this);
+        nStart(mNativePtr.get());
+
+        // Alpha is a special snowflake that has the canonical value stored
+        // in mTransformationInfo instead of in RenderNode, so we need to update
+        // it with the final value here.
+        if (mRenderProperty == RenderNodeAnimator.ALPHA) {
+            // Don't need null check because ViewPropertyAnimator's
+            // ctor calls ensureTransformationInfo()
+            mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
+        }
 
         final ArrayList<AnimatorListener> listeners = getListeners();
         final int numListeners = listeners == null ? 0 : listeners.size();
@@ -201,6 +218,7 @@
     public void setTarget(View view) {
         mViewTarget = view;
         mTarget = view.mRenderNode;
+        mTarget.addAnimator(this);
     }
 
     public void setTarget(Canvas canvas) {
@@ -213,12 +231,12 @@
     }
 
     public void setTarget(RenderNode node) {
+        if (mTarget != null) {
+            throw new IllegalStateException("Target already set!");
+        }
         mViewTarget = null;
         mTarget = node;
-    }
-
-    public RenderNode getTarget() {
-        return mTarget;
+        mTarget.addAnimator(this);
     }
 
     public void setStartValue(float startValue) {
@@ -232,12 +250,13 @@
         if (startDelay < 0) {
             throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
         }
-        nSetStartDelay(mNativePtr.get(), startDelay);
+        mUnscaledStartDelay = startDelay;
+        nSetStartDelay(mNativePtr.get(), (long) (startDelay * ValueAnimator.getDurationScale()));
     }
 
     @Override
     public long getStartDelay() {
-        return nGetStartDelay(mNativePtr.get());
+        return mUnscaledStartDelay;
     }
 
     @Override
@@ -246,13 +265,14 @@
         if (duration < 0) {
             throw new IllegalArgumentException("duration must be positive; " + duration);
         }
-        nSetDuration(mNativePtr.get(), duration);
+        mUnscaledDuration = duration;
+        nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
         return this;
     }
 
     @Override
     public long getDuration() {
-        return nGetDuration(mNativePtr.get());
+        return mUnscaledDuration;
     }
 
     @Override
@@ -307,5 +327,6 @@
     private static native long nGetStartDelay(long nativePtr);
     private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
 
+    private static native void nStart(long animPtr);
     private static native void nCancel(long animPtr);
 }
diff --git a/core/java/android/view/RenderNodeAnimatorCompat.java b/core/java/android/view/RenderNodeAnimatorCompat.java
new file mode 100644
index 0000000..151277a
--- /dev/null
+++ b/core/java/android/view/RenderNodeAnimatorCompat.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.animation.ValueAnimator;
+
+import java.util.ArrayList;
+
+/**
+ * This class provides compatibility for things like start listeners &
+ * start delays for use by ViewPropertyAnimator and ObjectAnimator
+ * @hide
+ */
+public class RenderNodeAnimatorCompat extends RenderNodeAnimator {
+
+    private long mUnscaledStartDelay = 0;
+    private long mStartDelay = 0;
+    private long mStartTime;
+    private boolean mCanceled;
+
+    public RenderNodeAnimatorCompat(int property, float finalValue) {
+        super(property, finalValue);
+    }
+
+    @Override
+    public void setStartDelay(long startDelay) {
+        mUnscaledStartDelay = startDelay;
+        mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
+    }
+
+    @Override
+    public long getStartDelay() {
+        return mUnscaledStartDelay;
+    }
+
+    @Override
+    public void start() {
+        if (mStartDelay <= 0) {
+            doStart();
+        } else {
+            getHelper().addDelayedAnimation(this);
+        }
+    }
+
+    private void doStart() {
+        if (!mCanceled) {
+            super.start();
+        }
+    }
+
+    @Override
+    public void cancel() {
+        mCanceled = true;
+        super.cancel();
+    }
+
+    /**
+     * @return true if the animator was started, false if still delayed
+     */
+    private boolean processDelayed(long frameTimeMs) {
+        if (mCanceled) return true;
+
+        if (mStartTime == 0) {
+            mStartTime = frameTimeMs;
+        } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+            doStart();
+            return true;
+        }
+        return false;
+    }
+
+    private static AnimationHelper getHelper() {
+        AnimationHelper helper = sAnimationHelper.get();
+        if (helper == null) {
+            helper = new AnimationHelper();
+            sAnimationHelper.set(helper);
+        }
+        return helper;
+    }
+
+    private static ThreadLocal<AnimationHelper> sAnimationHelper =
+            new ThreadLocal<AnimationHelper>();
+
+    private static class AnimationHelper implements Runnable {
+
+        private ArrayList<RenderNodeAnimatorCompat> mDelayedAnims = new ArrayList<RenderNodeAnimatorCompat>();
+        private final Choreographer mChoreographer;
+        private boolean mCallbackScheduled;
+
+        public AnimationHelper() {
+            mChoreographer = Choreographer.getInstance();
+        }
+
+        public void addDelayedAnimation(RenderNodeAnimatorCompat animator) {
+            mDelayedAnims.add(animator);
+            scheduleCallback();
+        }
+
+        private void scheduleCallback() {
+            if (!mCallbackScheduled) {
+                mCallbackScheduled = true;
+                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+            }
+        }
+
+        @Override
+        public void run() {
+            long frameTimeMs = mChoreographer.getFrameTime();
+            mCallbackScheduled = false;
+
+            int end = 0;
+            for (int i = 0; i < mDelayedAnims.size(); i++) {
+                RenderNodeAnimatorCompat animator = mDelayedAnims.get(i);
+                if (!animator.processDelayed(frameTimeMs)) {
+                    if (end != i) {
+                        mDelayedAnims.set(end, animator);
+                    }
+                    end++;
+                }
+            }
+            while (mDelayedAnims.size() > end) {
+                mDelayedAnims.remove(mDelayedAnims.size() - 1);
+            }
+
+            if (mDelayedAnims.size() > 0) {
+                scheduleCallback();
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 4a2cc1a..a2a4540 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -105,6 +105,7 @@
     final Rect mWinFrame = new Rect();
     final Rect mOverscanInsets = new Rect();
     final Rect mContentInsets = new Rect();
+    final Rect mStableInsets = new Rect();
     final Configuration mConfiguration = new Configuration();
 
     static final int KEEP_SCREEN_ON_MSG = 1;
@@ -518,7 +519,7 @@
                             visible ? VISIBLE : GONE,
                             WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
                             mWinFrame, mOverscanInsets, mContentInsets,
-                            mVisibleInsets, mConfiguration, mNewSurface);
+                            mVisibleInsets, mStableInsets, mConfiguration, mNewSurface);
                     if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                         mReportDrawNeeded = true;
                     }
@@ -653,7 +654,8 @@
 
         @Override
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
-                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+                Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+                Configuration newConfig) {
             SurfaceView surfaceView = mSurfaceView.get();
             if (surfaceView != null) {
                 if (DEBUG) Log.v(
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 33b6d6c..3b2e1d1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19805,6 +19805,13 @@
         final Rect mVisibleInsets = new Rect();
 
         /**
+         * For windows that are full-screen but using insets to layout inside
+         * of the screen decorations, these are the current insets for the
+         * stable system windows.
+         */
+        final Rect mStableInsets = new Rect();
+
+        /**
          * The internal insets given by this window.  This value is
          * supplied by the client (through
          * {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
index 31b360c..20f5182 100644
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ b/core/java/android/view/ViewPropertyAnimatorRT.java
@@ -81,21 +81,14 @@
             int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
 
             final float finalValue = holder.mFromValue + holder.mDeltaValue;
-            RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
+            RenderNodeAnimator animator = new RenderNodeAnimatorCompat(property, finalValue);
             animator.setStartDelay(startDelay);
             animator.setDuration(duration);
             animator.setInterpolator(interpolator);
             animator.setTarget(mView);
             animator.start();
 
-            // Alpha is a special snowflake that has the canonical value stored
-            // in mTransformationInfo instead of in RenderNode, so we need to update
-            // it with the final value here.
-            if (property == RenderNodeAnimator.ALPHA) {
-                // Don't need null check because ViewPropertyAnimator's
-                // ctor calls ensureTransformationInfo()
-                parent.mView.mTransformationInfo.mAlpha = finalValue;
-            }
+            mAnimators[property] = animator;
         }
 
         parent.mPendingAnimations.clear();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3219330..5def940 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,7 +21,6 @@
 import android.app.ActivityManagerNative;
 import android.content.ClipDescription;
 import android.content.ComponentCallbacks;
-import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
@@ -205,7 +204,7 @@
     /** Set to true while in performTraversals for detecting when die(true) is called from internal
      * callbacks such as onMeasure, onPreDraw, onDraw and deferring doDie() until later. */
     boolean mIsInTraversal;
-    boolean mFitSystemWindowsRequested;
+    boolean mApplyInsetsRequested;
     boolean mLayoutRequested;
     boolean mFirst;
     boolean mReportNextDraw;
@@ -257,11 +256,13 @@
 
     final Rect mPendingOverscanInsets = new Rect();
     final Rect mPendingVisibleInsets = new Rect();
+    final Rect mPendingStableInsets = new Rect();
     final Rect mPendingContentInsets = new Rect();
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
-    final Rect mFitSystemWindowsInsets = new Rect();
+    final Rect mDispatchContentInsets = new Rect();
+    final Rect mDispatchStableInsets = new Rect();
 
     final Configuration mLastConfiguration = new Configuration();
     final Configuration mPendingConfiguration = new Configuration();
@@ -532,6 +533,7 @@
                 }
                 mPendingOverscanInsets.set(0, 0, 0, 0);
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
+                mPendingStableInsets.set(mAttachInfo.mStableInsets);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
                 if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
@@ -816,7 +818,7 @@
     @Override
     public void requestFitSystemWindows() {
         checkThread();
-        mFitSystemWindowsRequested = true;
+        mApplyInsetsRequested = true;
         scheduleTraversals();
     }
 
@@ -1161,7 +1163,8 @@
     }
 
     void dispatchApplyInsets(View host) {
-        mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
+        mDispatchContentInsets.set(mAttachInfo.mContentInsets);
+        mDispatchStableInsets.set(mAttachInfo.mStableInsets);
         boolean isRound = false;
         if ((mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0
                 && mDisplay.getDisplayId() == 0) {
@@ -1171,7 +1174,8 @@
                             com.android.internal.R.bool.config_windowIsRound);
         }
         host.dispatchApplyWindowInsets(new WindowInsets(
-                mFitSystemWindowsInsets, isRound));
+                mDispatchContentInsets, null /* windowDecorInsets */,
+                mDispatchStableInsets, isRound));
     }
 
     private void performTraversals() {
@@ -1310,6 +1314,9 @@
                 if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
                     insetsChanged = true;
                 }
+                if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
+                    insetsChanged = true;
+                }
                 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
@@ -1383,8 +1390,8 @@
                     & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
         }
 
-        if (mFitSystemWindowsRequested) {
-            mFitSystemWindowsRequested = false;
+        if (mApplyInsetsRequested) {
+            mApplyInsetsRequested = false;
             mLastOverscanRequested = mAttachInfo.mOverscanRequested;
             dispatchApplyInsets(host);
             if (mLayoutRequested) {
@@ -1469,6 +1476,7 @@
                         + " overscan=" + mPendingOverscanInsets.toShortString()
                         + " content=" + mPendingContentInsets.toShortString()
                         + " visible=" + mPendingVisibleInsets.toShortString()
+                        + " visible=" + mPendingStableInsets.toShortString()
                         + " surface=" + mSurface);
 
                 if (mPendingConfiguration.seq != 0) {
@@ -1484,6 +1492,8 @@
                         mAttachInfo.mContentInsets);
                 final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
                         mAttachInfo.mVisibleInsets);
+                final boolean stableInsetsChanged = !mPendingStableInsets.equals(
+                        mAttachInfo.mStableInsets);
                 if (contentInsetsChanged) {
                     if (mWidth > 0 && mHeight > 0 && lp != null &&
                             ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility)
@@ -1558,12 +1568,19 @@
                     // Need to relayout with content insets.
                     contentInsetsChanged = true;
                 }
+                if (stableInsetsChanged) {
+                    mAttachInfo.mStableInsets.set(mPendingStableInsets);
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Decor insets changing to: "
+                            + mAttachInfo.mStableInsets);
+                    // Need to relayout with content insets.
+                    contentInsetsChanged = true;
+                }
                 if (contentInsetsChanged || mLastSystemUiVisibility !=
-                        mAttachInfo.mSystemUiVisibility || mFitSystemWindowsRequested
+                        mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
                         || mLastOverscanRequested != mAttachInfo.mOverscanRequested) {
                     mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
                     mLastOverscanRequested = mAttachInfo.mOverscanRequested;
-                    mFitSystemWindowsRequested = false;
+                    mApplyInsetsRequested = false;
                     dispatchApplyInsets(host);
                 }
                 if (visibleInsetsChanged) {
@@ -3065,6 +3082,7 @@
                 if (mWinFrame.equals(args.arg1)
                         && mPendingOverscanInsets.equals(args.arg5)
                         && mPendingContentInsets.equals(args.arg2)
+                        && mPendingStableInsets.equals(args.arg6)
                         && mPendingVisibleInsets.equals(args.arg3)
                         && args.arg4 == null) {
                     break;
@@ -3082,6 +3100,7 @@
                     mWinFrame.set((Rect) args.arg1);
                     mPendingOverscanInsets.set((Rect) args.arg5);
                     mPendingContentInsets.set((Rect) args.arg2);
+                    mPendingStableInsets.set((Rect) args.arg6);
                     mPendingVisibleInsets.set((Rect) args.arg3);
 
                     args.recycle();
@@ -5167,7 +5186,7 @@
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
-                mPendingConfiguration, mSurface);
+                mPendingStableInsets, mPendingConfiguration, mSurface);
         //Log.d(TAG, "<<<<<< BACK FROM relayout");
         if (restore) {
             params.restore();
@@ -5178,6 +5197,7 @@
             mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
+            mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
         }
         return relayoutResult;
     }
@@ -5446,7 +5466,7 @@
     }
 
     public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
-            Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+            Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig) {
         if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
@@ -5465,6 +5485,7 @@
         args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets;
         args.arg4 = sameProcessCall && newConfig != null ? new Configuration(newConfig) : newConfig;
         args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
+        args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
         msg.obj = args;
         mHandler.sendMessage(msg);
     }
@@ -6292,11 +6313,12 @@
 
         @Override
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
-                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+                Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+                Configuration newConfig) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
-                        visibleInsets, reportDraw, newConfig);
+                        visibleInsets, stableInsets, reportDraw, newConfig);
             }
         }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0a44d23..aa71ed8 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -130,7 +130,20 @@
     public static final int PROGRESS_SECONDARY_START = 20000;
     /** Highest possible value for the secondary progress */
     public static final int PROGRESS_SECONDARY_END = 30000;
-    
+
+    /**
+     * The transitionName for the status bar background View when a custom background is used.
+     * @see android.view.Window#setStatusBarColor(int)
+     */
+    public static final String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";
+
+    /**
+     * The transitionName for the navigation bar background View when a custom background is used.
+     * @see android.view.Window#setNavigationBarColor(int)
+     */
+    public static final String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME =
+            "android:navigation:background";
+
     /** The default features enabled */
     @SuppressWarnings({"PointlessBitwiseExpression"})
     protected static final int DEFAULT_FEATURES = (1 << FEATURE_OPTIONS_PANEL) |
@@ -1554,6 +1567,9 @@
      * If {@param color} is not opaque, consider setting
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
+     * <p>
+     * The transitionName for the view background will be "android:status:background".
+     * </p>
      */
     public abstract void setStatusBarColor(int color);
 
@@ -1573,6 +1589,9 @@
      * If {@param color} is not opaque, consider setting
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
+     * <p>
+     * The transitionName for the view background will be "android:navigation:background".
+     * </p>
      */
     public abstract void setNavigationBarColor(int color);
 
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 1d2f1bf..1832cc3 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -30,13 +30,16 @@
  * @see View#onApplyWindowInsets(WindowInsets)
  */
 public final class WindowInsets {
+
     private Rect mSystemWindowInsets;
     private Rect mWindowDecorInsets;
+    private Rect mStableInsets;
     private Rect mTempRect;
     private boolean mIsRound;
 
     private boolean mSystemWindowInsetsConsumed = false;
     private boolean mWindowDecorInsetsConsumed = false;
+    private boolean mStableInsetsConsumed = false;
 
     private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
 
@@ -49,29 +52,21 @@
     public static final WindowInsets CONSUMED;
 
     static {
-        CONSUMED = new WindowInsets(EMPTY_RECT, EMPTY_RECT);
-        CONSUMED.mSystemWindowInsetsConsumed = true;
-        CONSUMED.mWindowDecorInsetsConsumed = true;
+        CONSUMED = new WindowInsets(null, null, null, false);
     }
 
     /** @hide */
-    public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) {
-        this(systemWindowInsets, windowDecorInsets, false);
-    }
-
-    /** @hide */
-    public WindowInsets(Rect systemWindowInsets, boolean isRound) {
-        this(systemWindowInsets, null, isRound);
-    }
-
-    /** @hide */
-    public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) {
+    public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
+            boolean isRound) {
         mSystemWindowInsetsConsumed = systemWindowInsets == null;
         mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets;
 
         mWindowDecorInsetsConsumed = windowDecorInsets == null;
         mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets;
 
+        mStableInsetsConsumed = stableInsets == null;
+        mStableInsets = mStableInsetsConsumed ? EMPTY_RECT : stableInsets;
+
         mIsRound = isRound;
     }
 
@@ -83,14 +78,16 @@
     public WindowInsets(WindowInsets src) {
         mSystemWindowInsets = src.mSystemWindowInsets;
         mWindowDecorInsets = src.mWindowDecorInsets;
+        mStableInsets = src.mStableInsets;
         mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed;
         mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed;
+        mStableInsetsConsumed = src.mStableInsetsConsumed;
         mIsRound = src.mIsRound;
     }
 
     /** @hide */
     public WindowInsets(Rect systemWindowInsets) {
-        this(systemWindowInsets, null);
+        this(systemWindowInsets, null, null, false);
     }
 
     /**
@@ -272,7 +269,7 @@
      * @hide Pending API
      */
     public boolean isConsumed() {
-        return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed;
+        return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed;
     }
 
     /**
@@ -381,9 +378,57 @@
         return result;
     }
 
+    /**
+     * @hide
+     */
+    public int getStableInsetTop() {
+        return mStableInsets.top;
+    }
+
+    /**
+     * @hide
+     */
+    public int getStableInsetLeft() {
+        return mStableInsets.left;
+    }
+
+    /**
+     * @hide
+     */
+    public int getStableInsetRight() {
+        return mStableInsets.right;
+    }
+
+    /**
+     * @hide
+     */
+    public int getStableInsetBottom() {
+        return mStableInsets.bottom;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean hasStableInsets() {
+        return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0
+                || mStableInsets.bottom != 0;
+    }
+
+    /**
+     * @hide
+     */
+    public WindowInsets consumeStableInsets() {
+        final WindowInsets result = new WindowInsets(this);
+        result.mStableInsets = EMPTY_RECT;
+        result.mStableInsetsConsumed = true;
+        return result;
+    }
+
     @Override
     public String toString() {
-        return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets + " windowDecorInsets=" +
-                mWindowDecorInsets + (isRound() ? "round}" : "}");
+        return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
+                + " windowDecorInsets=" + mWindowDecorInsets
+                + " stableInsets=" + mStableInsets +
+                (isRound() ? " round}" : "}");
     }
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 024600d..ee542a1 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -149,9 +149,11 @@
          * are visible.
          * @param decorFrame The decor frame specified by policy specific to this window,
          * to use for proper cropping during animation.
+         * @param stableFrame The frame around which stable system decoration is positioned.
          */
         public void computeFrameLw(Rect parentFrame, Rect displayFrame,
-                Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame);
+                Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
+                Rect stableFrame);
 
         /**
          * Retrieve the current frame of the window that has been assigned by
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 86c9fe3..50a7a5e 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -35,7 +35,7 @@
 
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
-            Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+            Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this);
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index a54eba1..c8812fc 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -150,7 +150,7 @@
         ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
         return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
     }
-    uint32_t channelCount = popcount(channelMask);
+    uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
 
     // compare the format against the Java constants
     audio_format_t format = audioFormatToNative(audioFormat);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 677c230..7e2448e 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -205,7 +205,7 @@
         return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
     }
 
-    uint32_t channelCount = popcount(nativeChannelMask);
+    uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
 
     // check the format.
     // This function was called from Java, so we compare the format against the Java constants
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 6ba22bf..28473e0 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -456,7 +456,6 @@
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
     renderNode->addAnimator(animator);
-    animator->start();
 }
 
 #endif // USE_OPENGL_RENDERER
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index de3dd16..ed57979 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -149,6 +149,11 @@
     animator->setInterpolator(interpolator);
 }
 
+static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->start();
+}
+
 static void cancel(JNIEnv* env, jobject clazz, jlong animatorPtr) {
     BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
     animator->cancel();
@@ -173,6 +178,7 @@
     { "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
     { "nGetStartDelay", "(J)J", (void*) getStartDelay },
     { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
+    { "nStart", "(J)V", (void*) start },
     { "nCancel", "(J)V", (void*) cancel },
 #endif
 };
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 4a8c122..f3ef48b 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -32,7 +32,8 @@
  ************************************************************/
 
 BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
-        : mFinalValue(finalValue)
+        : mTarget(NULL)
+        , mFinalValue(finalValue)
         , mDeltaValue(0)
         , mFromValue(0)
         , mInterpolator(0)
@@ -81,9 +82,14 @@
     mStartDelay = startDelay;
 }
 
-void BaseRenderNodeAnimator::pushStaging(RenderNode* target, TreeInfo& info) {
+void BaseRenderNodeAnimator::attach(RenderNode* target) {
+    mTarget = target;
+    onAttached();
+}
+
+void BaseRenderNodeAnimator::pushStaging(TreeInfo& info) {
     if (!mHasStartValue) {
-        doSetStartValue(getValue(target));
+        doSetStartValue(getValue(mTarget));
     }
     if (mStagingPlayState > mPlayState) {
         mPlayState = mStagingPlayState;
@@ -109,20 +115,25 @@
     }
     // No interpolator was set, use the default
     if (!mInterpolator) {
-        setInterpolator(Interpolator::createDefaultInterpolator());
+        mInterpolator = Interpolator::createDefaultInterpolator();
     }
     if (mDuration < 0 || mDuration > 50000) {
         ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
     }
 }
 
-bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) {
+bool BaseRenderNodeAnimator::animate(TreeInfo& info) {
     if (mPlayState < RUNNING) {
         return false;
     }
 
+    // If BaseRenderNodeAnimator is handling the delay (not typical), then
+    // because the staging properties reflect the final value, we always need
+    // to call setValue even if the animation isn't yet running or is still
+    // being delayed as we need to override the staging value
     if (mStartTime > info.frameTimeMs) {
         info.out.hasAnimations |= true;
+        setValue(mTarget, mFromValue);
         return false;
     }
 
@@ -136,7 +147,7 @@
     }
 
     fraction = mInterpolator->interpolate(fraction);
-    setValue(target, mFromValue + (mDeltaValue * fraction));
+    setValue(mTarget, mFromValue + (mDeltaValue * fraction));
 
     if (mPlayState == FINISHED) {
         callOnFinishedListener(info);
@@ -188,12 +199,17 @@
         , mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) {
 }
 
-void RenderPropertyAnimator::onAttached(RenderNode* target) {
+void RenderPropertyAnimator::onAttached() {
     if (!mHasStartValue
-            && target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
-        setStartValue((target->stagingProperties().*mPropertyAccess->getter)());
+            && mTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
+        setStartValue((mTarget->stagingProperties().*mPropertyAccess->getter)());
     }
-    (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+}
+
+void RenderPropertyAnimator::onStagingPlayStateChanged() {
+    if (mStagingPlayState == RUNNING) {
+        (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+    }
 }
 
 uint32_t RenderPropertyAnimator::dirtyMask() {
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index a981b5a..0dda23f 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -50,12 +50,14 @@
     ANDROID_API void setListener(AnimationListener* listener) {
         mListener = listener;
     }
-    ANDROID_API void start() { mStagingPlayState = RUNNING; }
-    ANDROID_API void cancel() { mStagingPlayState = FINISHED; }
+    ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
+    ANDROID_API void cancel() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
 
-    virtual void onAttached(RenderNode* target) {}
-    virtual void pushStaging(RenderNode* target, TreeInfo& info);
-    bool animate(RenderNode* target, TreeInfo& info);
+    void attach(RenderNode* target);
+    virtual void onAttached() {}
+    void detach() { mTarget = 0; }
+    void pushStaging(TreeInfo& info);
+    bool animate(TreeInfo& info);
 
     bool isFinished() { return mPlayState == FINISHED; }
     float finalValue() { return mFinalValue; }
@@ -68,15 +70,20 @@
 
     virtual float getValue(RenderNode* target) const = 0;
     virtual void setValue(RenderNode* target, float value) = 0;
+    RenderNode* target() { return mTarget; }
 
     void callOnFinishedListener(TreeInfo& info);
 
+    virtual void onStagingPlayStateChanged() {}
+
     enum PlayState {
         NOT_STARTED,
         RUNNING,
         FINISHED,
     };
 
+    RenderNode* mTarget;
+
     float mFinalValue;
     float mDeltaValue;
     float mFromValue;
@@ -92,9 +99,9 @@
     sp<AnimationListener> mListener;
 
 private:
-    void doSetStartValue(float value);
     inline void checkMutable();
-    void transitionToRunning(TreeInfo& info);
+    virtual void transitionToRunning(TreeInfo& info);
+    void doSetStartValue(float value);
 };
 
 class RenderPropertyAnimator : public BaseRenderNodeAnimator {
@@ -116,13 +123,13 @@
 
     ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue);
 
-    virtual void onAttached(RenderNode* target);
-
     ANDROID_API virtual uint32_t dirtyMask();
 
 protected:
     virtual float getValue(RenderNode* target) const;
     virtual void setValue(RenderNode* target, float value);
+    virtual void onAttached();
+    virtual void onStagingPlayStateChanged();
 
 private:
     typedef bool (RenderProperties::*SetFloatProperty)(float value);
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 6a10cf8..27b0893 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -25,6 +25,7 @@
 using namespace std;
 
 static void unref(BaseRenderNodeAnimator* animator) {
+    animator->detach();
     animator->decStrong(0);
 }
 
@@ -39,7 +40,7 @@
 
 void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
     animator->incStrong(0);
-    animator->onAttached(&mParent);
+    animator->attach(&mParent);
     mNewAnimators.push_back(animator.get());
 }
 
@@ -58,7 +59,7 @@
         move_all(mNewAnimators, mAnimators);
     }
     for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) {
-        (*it)->pushStaging(&mParent, info);
+        (*it)->pushStaging(info);
     }
 }
 
@@ -68,7 +69,7 @@
             : mTarget(target), mInfo(info) {}
 
     bool operator() (BaseRenderNodeAnimator* animator) {
-        bool remove = animator->animate(&mTarget, mInfo);
+        bool remove = animator->animate(mInfo);
         if (remove) {
             animator->decStrong(0);
         }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e12549a..ad27e41 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -106,6 +106,8 @@
     <!-- Wifi Display -->
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
 
+    <uses-permission android:name="android.permission.CAMERA" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml b/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml
new file mode 100644
index 0000000..49eba22
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64.0dp"
+        android:height="64.0dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#4DFFFFFF"
+        android:pathData="M3.3,3.0L2.0,4.3l5.0,5.0L7.0,13.0l3.0,0.0l0.0,9.0l3.6,-6.1l4.1,4.1l1.3,-1.3L3.3,3.0zM17.0,10.0l-4.0,0.0l4.0,-8.0L7.0,2.0l0.0,2.2l8.5,8.5L17.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml b/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml
new file mode 100644
index 0000000..101ca84
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64.0dp"
+        android:height="64.0dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M7.0,2.0l0.0,11.0 3.0,0.0 0.0,9.0 7.0,-12.0 -4.0,0.0 4.0,-8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2b7793d..2188a8e 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -62,6 +62,14 @@
             android:layout_height="match_parent"
             android:layout_marginBottom="@dimen/close_handle_underlap"/>
 
+        <ViewStub
+                android:id="@+id/keyguard_user_switcher"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
+                android:layout_gravity="end"
+                android:layout="@layout/keyguard_user_switcher" />
+
         <com.android.systemui.statusbar.phone.ObservableScrollView
             android:id="@+id/scroll_view"
             android:layout_width="match_parent"
@@ -92,14 +100,6 @@
 
     <include layout="@layout/status_bar_expanded_header" />
 
-    <ViewStub
-        android:id="@+id/keyguard_user_switcher"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
-        android:layout_gravity="end"
-        android:layout="@layout/keyguard_user_switcher" />
-
     <include
         layout="@layout/keyguard_bottom_area"
         android:visibility="gone" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f021253..a8799f7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -540,6 +540,8 @@
     <string name="quick_settings_hotspot_label">Hotspot</string>
     <!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
     <string name="quick_settings_notifications_label">Notifications</string>
+    <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_flashlight_label">Flashlight</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
     <string name="recents_empty_message">No recent apps</string>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 322174a..85f58881 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -881,8 +881,7 @@
             return;
         }
 
-        if (!mUserManager.isUserSwitcherEnabled()
-                && mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
+        if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 786cd9e..ba350e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -30,6 +30,7 @@
 import com.android.systemui.qs.QSTile.State;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.Listenable;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -221,6 +222,7 @@
         TetheringController getTetheringController();
         CastController getCastController();
         VolumeComponent getVolumeComponent();
+        FlashlightController getFlashlightController();
     }
 
     public static class State {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
new file mode 100644
index 0000000..b610cf3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.qs.tiles;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.statusbar.policy.FlashlightController;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings.Secure;
+
+/** Quick settings tile: Control flashlight **/
+public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
+        FlashlightController.FlashlightListener {
+
+    private final FlashlightController mFlashlightController;
+
+    public FlashlightTile(Host host) {
+        super(host);
+        mFlashlightController = host.getFlashlightController();
+        mFlashlightController.addListener(this);
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+    }
+
+    @Override
+    protected void handleUserSwitch(int newUserId) {
+    }
+
+    @Override
+    protected void handleClick() {
+        boolean newState = !mState.value;
+        mFlashlightController.setFlashlight(newState);
+        refreshState(newState);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        if (arg instanceof Boolean) {
+            state.value = (Boolean) arg;
+        }
+        state.visible = mFlashlightController.isAvailable();
+        state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
+        state.iconId = state.value
+                ? R.drawable.ic_qs_flashlight_on : R.drawable.ic_qs_flashlight_off;
+    }
+
+    @Override
+    public void onFlashlightOff() {
+        refreshState(false);
+    }
+
+    @Override
+    public void onFlashlightError() {
+        refreshState(false);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index fd2db8e..b2e79c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -78,6 +78,12 @@
     private boolean mQsTracking;
 
     /**
+     * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
+     * the expansion for quick settings.
+     */
+    private boolean mConflictingQsExpansionGesture;
+
+    /**
      * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
      * intercepted yet.
      */
@@ -95,6 +101,7 @@
     private int mQsMaxExpansionHeight;
     private int mQsPeekHeight;
     private boolean mStackScrollerOverscrolling;
+    private boolean mQsExpansionFromOverscroll;
     private boolean mQsExpansionEnabled = true;
     private ValueAnimator mQsExpansionAnimator;
     private FlingAnimationUtils mFlingAnimationUtils;
@@ -423,11 +430,21 @@
 
     private void flingQsWithCurrentVelocity() {
         float vel = getCurrentVelocity();
-
-        // TODO: Better logic whether we should expand or not.
-        flingSettings(vel, vel > 0);
+        flingSettings(vel, flingExpandsQs(vel));
     }
 
+    private boolean flingExpandsQs(float vel) {
+        if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+            return getQsExpansionFraction() > 0.5f;
+        } else {
+            return vel > 0;
+        }
+    }
+
+    private float getQsExpansionFraction() {
+        return (mQsExpansionHeight - mQsMinExpansionHeight)
+                / (mQsMaxExpansionHeight - mQsMinExpansionHeight);
+    }
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (mBlockTouches) {
@@ -443,20 +460,48 @@
                 return true;
             }
         }
-        if (mQsTracking || mQsExpanded) {
-            return onQsTouch(event);
-        }
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
+                && mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
 
+            // Down in the empty area while fully expanded - go to QS.
+            mQsTracking = true;
+            mConflictingQsExpansionGesture = true;
+            onQsExpansionStarted();
+            mInitialHeightOnTouch = mQsExpansionHeight;
+            mInitialTouchY = event.getX();
+            mInitialTouchX = event.getY();
+        }
+        if (mQsTracking || mQsExpanded) {
+            onQsTouch(event);
+            if (!mConflictingQsExpansionGesture) {
+                return true;
+            }
+        }
+        if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
+                || event.getActionMasked() == MotionEvent.ACTION_UP) {
+            mConflictingQsExpansionGesture = false;
+        }
         super.onTouchEvent(event);
         return true;
     }
 
     @Override
+    protected boolean flingExpands(float vel, float vectorVel) {
+        boolean expands = super.flingExpands(vel, vectorVel);
+
+        // If we are already running a QS expansion, make sure that we keep the panel open.
+        if (mQsExpansionAnimator != null) {
+            expands = true;
+        }
+        return expands;
+    }
+
+    @Override
     protected boolean hasConflictingGestures() {
         return mStatusBar.getBarState() != StatusBarState.SHADE;
     }
 
-    private boolean onQsTouch(MotionEvent event) {
+    private void onQsTouch(MotionEvent event) {
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -501,14 +546,17 @@
                 mQsTracking = false;
                 mTrackingPointer = -1;
                 trackMovement(event);
-                flingQsWithCurrentVelocity();
+                float fraction = getQsExpansionFraction();
+                if ((fraction != 0f || y >= mInitialTouchY)
+                        && (fraction != 1f || y <= mInitialTouchY)) {
+                    flingQsWithCurrentVelocity();
+                }
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
                 }
                 break;
         }
-        return true;
     }
 
     @Override
@@ -524,19 +572,25 @@
 
 
     @Override
-    public void onOverscrollTopChanged(float amount) {
+    public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
         cancelAnimation();
         float rounded = amount >= 1f ? amount : 0f;
-        mStackScrollerOverscrolling = rounded != 0f;
+        mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
+        mQsExpansionFromOverscroll = rounded != 0f;
         setQsExpansion(mQsMinExpansionHeight + rounded);
         updateQsState();
     }
 
     @Override
     public void flingTopOverscroll(float velocity, boolean open) {
-        mStackScrollerOverscrolling = false;
         setQsExpansion(mQsExpansionHeight);
-        flingSettings(velocity, open);
+        flingSettings(velocity, open, new Runnable() {
+            @Override
+            public void run() {
+                mStackScrollerOverscrolling = false;
+                mQsExpansionFromOverscroll = false;
+            }
+        });
     }
 
     private void onQsExpansionStarted() {
@@ -572,13 +626,12 @@
     private void updateQsState() {
         boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
         mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling);
-        mNotificationStackScroller.setEnabled(!mQsExpanded);
+        mNotificationStackScroller.setEnabled(!mQsExpanded || mQsExpansionFromOverscroll);
         mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
-        mQsContainer.setVisibility(mKeyguardShowing && !expandVisually
-                ? View.INVISIBLE
-                : View.VISIBLE);
+        mQsContainer.setVisibility(
+                mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
         mScrollView.setTouchEnabled(mQsExpanded);
-        mNotificationStackScroller.setTouchEnabled(!mQsExpanded);
+        mNotificationStackScroller.setTouchEnabled(!mQsExpanded || mQsExpansionFromOverscroll);
     }
 
     private void setQsExpansion(float height) {
@@ -641,9 +694,17 @@
             mQsExpansionAnimator.cancel();
         }
     }
+
     private void flingSettings(float vel, boolean expand) {
+        flingSettings(vel, expand, null);
+    }
+
+    private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable) {
         float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
         if (target == mQsExpansionHeight) {
+            if (onFinishRunnable != null) {
+                onFinishRunnable.run();
+            }
             return;
         }
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
@@ -658,6 +719,9 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mQsExpansionAnimator = null;
+                if (onFinishRunnable != null) {
+                    onFinishRunnable.run();
+                }
             }
         });
         animator.start();
@@ -892,6 +956,9 @@
 
     @Override
     protected void setOverExpansion(float overExpansion, boolean isPixels) {
+        if (mConflictingQsExpansionGesture) {
+            return;
+        }
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
             mNotificationStackScroller.setOnHeightChangedListener(null);
             if (isPixels) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 5b47749..7d5d99d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -301,6 +301,8 @@
                 mTrackingPointer = -1;
                 trackMovement(event);
                 if ((mTracking && mTouchSlopExceeded)
+                        || Math.abs(x - mInitialTouchX) > mTouchSlop
+                        || Math.abs(y - mInitialTouchY) > mTouchSlop
                         || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
                     float vel = 0f;
                     float vectorVel = 0f;
@@ -317,6 +319,7 @@
                     boolean expands = onEmptySpaceClick(mInitialTouchX);
                     onTrackingStopped(expands);
                 }
+
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
@@ -447,7 +450,7 @@
      * @param vectorVel the length of the vectorial velocity
      * @return whether a fling should expands the panel; contracts otherwise
      */
-    private boolean flingExpands(float vel, float vectorVel) {
+    protected boolean flingExpands(float vel, float vectorVel) {
         if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
             return getExpandedFraction() > 0.5f;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 5c6d279..387f5a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -122,6 +122,7 @@
 import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
 import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.DateView;
+import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -193,6 +194,7 @@
     CastControllerImpl mCastController;
     VolumeComponent mVolumeComponent;
     KeyguardUserSwitcher mKeyguardUserSwitcher;
+    FlashlightController mFlashlightController;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -710,6 +712,7 @@
         }
 
         mBatteryController.setStatusBarHeaderView(mHeader);
+        mFlashlightController = new FlashlightController(mContext);
 
         // Set up the quick settings tile panel
         mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
@@ -725,7 +728,7 @@
             final QSTileHost qsh = new QSTileHost(mContext, this,
                     mBluetoothController, mLocationController, mRotationLockController,
                     mNetworkController, mZenModeController, null /*tethering*/,
-                    mCastController, mVolumeComponent);
+                    mCastController, mVolumeComponent, mFlashlightController);
             for (QSTile<?> tile : qsh.getTiles()) {
                 mQSPanel.addTile(tile);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 7c87580..60f38b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -29,6 +29,7 @@
 import com.android.systemui.qs.tiles.CastTile;
 import com.android.systemui.qs.tiles.CellularTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
+import com.android.systemui.qs.tiles.FlashlightTile;
 import com.android.systemui.qs.tiles.LocationTile;
 import com.android.systemui.qs.tiles.NotificationsTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
@@ -37,6 +38,7 @@
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.RotationLockController;
@@ -64,12 +66,13 @@
     private final VolumeComponent mVolume;
     private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>();
     private final int mFeedbackStartDelay;
+    private final FlashlightController mFlashlight;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
             BluetoothController bluetooth, LocationController location,
             RotationLockController rotation, NetworkController network,
             ZenModeController zen, TetheringController tethering,
-            CastController cast, VolumeComponent volume) {
+            CastController cast, VolumeComponent volume, FlashlightController flashlight) {
         mContext = context;
         mStatusBar = statusBar;
         mBluetooth = bluetooth;
@@ -80,6 +83,7 @@
         mTethering = tethering;
         mCast = cast;
         mVolume = volume;
+        mFlashlight = flashlight;
 
         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
         ht.start();
@@ -95,6 +99,7 @@
         mTiles.add(new LocationTile(this));
         mTiles.add(new CastTile(this));
         mTiles.add(new HotspotTile(this));
+        mTiles.add(new FlashlightTile(this));
 
         mUserTracker = new CurrentUserTracker(mContext) {
             @Override
@@ -177,4 +182,9 @@
     public VolumeComponent getVolumeComponent() {
         return mVolume;
     }
+
+    @Override
+    public FlashlightController getFlashlightController() {
+        return mFlashlight;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
new file mode 100644
index 0000000..b059043
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Manages the flashlight.
+ */
+public class FlashlightController {
+
+    private static final String TAG = "FlashlightController";
+
+    private final CameraManager mCameraManager;
+    /** Call {@link #ensureHandler()} before using */
+    private Handler mHandler;
+
+    /** Lock on mListeners when accessing */
+    private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
+
+    /** Lock on {@code this} when accessing */
+    private boolean mFlashlightEnabled;
+
+    private CameraDevice mCameraDevice;
+    private CaptureRequest mFlashlightRequest;
+    private CameraCaptureSession mSession;
+    private SurfaceTexture mSurfaceTexture;
+    private Surface mSurface;
+
+    public FlashlightController(Context mContext) {
+        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+    }
+
+    public synchronized void setFlashlight(boolean enabled) {
+        if (mFlashlightEnabled != enabled) {
+            mFlashlightEnabled = enabled;
+            postUpdateFlashlight();
+        }
+    }
+
+    public boolean isAvailable() {
+        try {
+            return getCameraId() != null;
+        } catch (CameraAccessException e) {
+            return false;
+        }
+    }
+
+    public void addListener(FlashlightListener l) {
+        synchronized (mListeners) {
+            cleanUpListenersLocked(l);
+            mListeners.add(new WeakReference<>(l));
+        }
+    }
+
+    public void removeListener(FlashlightListener l) {
+        synchronized (mListeners) {
+            cleanUpListenersLocked(l);
+        }
+    }
+
+    private synchronized void ensureHandler() {
+        if (mHandler == null) {
+            HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+            thread.start();
+            mHandler = new Handler(thread.getLooper());
+        }
+    }
+
+    private void startDevice() throws CameraAccessException {
+        mCameraManager.openCamera(getCameraId(), mCameraListener, mHandler);
+    }
+
+    private void startSession() throws CameraAccessException {
+        mSurfaceTexture = new SurfaceTexture(false);
+        Size size = getSmallestSize(mCameraDevice.getId());
+        mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
+        mSurface = new Surface(mSurfaceTexture);
+        ArrayList<Surface> outputs = new ArrayList<>(1);
+        outputs.add(mSurface);
+        mCameraDevice.createCaptureSession(outputs, mSessionListener, mHandler);
+    }
+
+    private Size getSmallestSize(String cameraId) throws CameraAccessException {
+        Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
+                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
+                .getOutputSizes(SurfaceTexture.class);
+        if (outputSizes == null || outputSizes.length == 0) {
+            throw new IllegalStateException(
+                    "Camera " + cameraId + "doesn't support any outputSize.");
+        }
+        Size chosen = outputSizes[0];
+        for (Size s : outputSizes) {
+            if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
+                chosen = s;
+            }
+        }
+        return chosen;
+    }
+
+    private void postUpdateFlashlight() {
+        ensureHandler();
+        mHandler.post(mUpdateFlashlightRunnable);
+    }
+
+    private String getCameraId() throws CameraAccessException {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (String id : ids) {
+            CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
+            Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+            Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
+            if (flashAvailable != null && flashAvailable
+                    && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
+                return id;
+            }
+        }
+        return null;
+    }
+
+    private void updateFlashlight(boolean forceDisable) {
+        try {
+            boolean enabled;
+            synchronized (this) {
+                enabled = mFlashlightEnabled && !forceDisable;
+            }
+            if (enabled) {
+                if (mCameraDevice == null) {
+                    startDevice();
+                    return;
+                }
+                if (mSession == null) {
+                    startSession();
+                    return;
+                }
+                if (mFlashlightRequest == null) {
+                    CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
+                            CameraDevice.TEMPLATE_PREVIEW);
+                    builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
+                    builder.addTarget(mSurface);
+                    CaptureRequest request = builder.build();
+                    mSession.capture(request, null, mHandler);
+                    mFlashlightRequest = request;
+                }
+            } else {
+                if (mCameraDevice != null) {
+                    mCameraDevice.close();
+                    teardown();
+                }
+            }
+
+        } catch (CameraAccessException|IllegalStateException|UnsupportedOperationException e) {
+            Log.e(TAG, "Error in updateFlashlight", e);
+            handleError();
+        }
+    }
+
+    private void teardown() {
+        mCameraDevice = null;
+        mSession = null;
+        mFlashlightRequest = null;
+        if (mSurface != null) {
+            mSurface.release();
+            mSurfaceTexture.release();
+        }
+        mSurface = null;
+        mSurfaceTexture = null;
+    }
+
+    private void handleError() {
+        synchronized (this) {
+            mFlashlightEnabled = false;
+        }
+        dispatchError();
+        dispatchOff();
+        updateFlashlight(true /* forceDisable */);
+    }
+
+    private void dispatchOff() {
+        dispatchListeners(false, true /* off */);
+    }
+
+    private void dispatchError() {
+        dispatchListeners(true /* error */, false);
+    }
+
+    private void dispatchListeners(boolean error, boolean off) {
+        synchronized (mListeners) {
+            final int N = mListeners.size();
+            boolean cleanup = false;
+            for (int i = 0; i < N; i++) {
+                FlashlightListener l = mListeners.get(i).get();
+                if (l != null) {
+                    if (error) {
+                        l.onFlashlightError();
+                    } else if (off) {
+                        l.onFlashlightOff();
+                    }
+                } else {
+                    cleanup = true;
+                }
+            }
+            if (cleanup) {
+                cleanUpListenersLocked(null);
+            }
+        }
+    }
+
+    private void cleanUpListenersLocked(FlashlightListener listener) {
+        for (int i = mListeners.size() - 1; i >= 0; i--) {
+            FlashlightListener found = mListeners.get(i).get();
+            if (found == null || found == listener) {
+                mListeners.remove(i);
+            }
+        }
+    }
+
+    private final CameraDevice.StateListener mCameraListener = new CameraDevice.StateListener() {
+        @Override
+        public void onOpened(CameraDevice camera) {
+            mCameraDevice = camera;
+            postUpdateFlashlight();
+        }
+
+        @Override
+        public void onDisconnected(CameraDevice camera) {
+            if (mCameraDevice == camera) {
+                dispatchOff();
+                teardown();
+            }
+        }
+
+        @Override
+        public void onError(CameraDevice camera, int error) {
+            Log.e(TAG, "Camera error: camera=" + camera + " error=" + error);
+            if (camera == mCameraDevice || mCameraDevice == null) {
+                handleError();
+            }
+        }
+    };
+
+    private final CameraCaptureSession.StateListener mSessionListener =
+            new CameraCaptureSession.StateListener() {
+        @Override
+        public void onConfigured(CameraCaptureSession session) {
+            mSession = session;
+            postUpdateFlashlight();
+        }
+
+        @Override
+        public void onConfigureFailed(CameraCaptureSession session) {
+            Log.e(TAG, "Configure failed.");
+            if (mSession == null || mSession == session) {
+                handleError();
+            }
+        }
+    };
+
+    private final Runnable mUpdateFlashlightRunnable = new Runnable() {
+        @Override
+        public void run() {
+            updateFlashlight(false /* forceDisable */);
+        }
+    };
+
+    public interface FlashlightListener {
+
+        /**
+         * Called when the flashlight turns off unexpectedly.
+         */
+        void onFlashlightOff();
+
+        /**
+         * Called when there is an error that turns the flashlight off.
+         */
+        void onFlashlightError();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index c9a1b28..94fdd1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -988,34 +988,50 @@
      */
     public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
             boolean cancelAnimators) {
+        setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     * @param cancelAnimators Should running animations be cancelled.
+     * @param isRubberbanded The value which will be passed to
+     *                     {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+            boolean cancelAnimators, boolean isRubberbanded) {
         if (cancelAnimators) {
             mStateAnimator.cancelOverScrollAnimators(onTop);
         }
-        setOverScrollAmountInternal(amount, onTop, animate);
+        setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
     }
 
-    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate) {
+    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
+            boolean isRubberbanded) {
         amount = Math.max(0, amount);
         if (animate) {
-            mStateAnimator.animateOverScrollToAmount(amount, onTop);
+            mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
         } else {
             setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
             mAmbientState.setOverScrollAmount(amount, onTop);
             if (onTop) {
-                notifyOverscrollTopListener(amount);
+                notifyOverscrollTopListener(amount, isRubberbanded);
             }
             requestChildrenUpdate();
         }
     }
 
-    private void notifyOverscrollTopListener(float amount) {
+    private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
         mExpandHelper.onlyObserveMovements(amount > 1.0f);
         if (mDontReportNextOverScroll) {
             mDontReportNextOverScroll = false;
             return;
         }
         if (mOverscrollTopChangedListener != null) {
-            mOverscrollTopChangedListener.onOverscrollTopChanged(amount);
+            mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
         }
     }
 
@@ -1061,9 +1077,9 @@
                 updateChildren();
                 float overScrollTop = getCurrentOverScrollAmount(true);
                 if (mOwnScrollY < 0) {
-                    notifyOverscrollTopListener(-mOwnScrollY);
+                    notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
                 } else {
-                    notifyOverscrollTopListener(overScrollTop);
+                    notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
                 }
             }
         } else {
@@ -1288,6 +1304,16 @@
         return RUBBER_BAND_FACTOR_NORMAL;
     }
 
+    /**
+     * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
+     * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
+     * overscroll view (e.g. expand QS).
+     */
+    private boolean isRubberbanded(boolean onTop) {
+        return !onTop || mExpandedInThisMotion || mIsExpansionChanging
+                || !mScrolledToTopOnFirstDown;
+    }
+
     private void endDrag() {
         setIsBeingDragged(false);
 
@@ -1880,7 +1906,16 @@
      * A listener that gets notified when the overscroll at the top has changed.
      */
     public interface OnOverscrollTopChangedListener {
-        public void onOverscrollTopChanged(float amount);
+
+        /**
+         * Notifies a listener that the overscroll has changed.
+         *
+         * @param amount the amount of overscroll, in pixels
+         * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
+         *                     unrubberbanded motion to directly expand overscroll view (e.g expand
+         *                     QS)
+         */
+        public void onOverscrollTopChanged(float amount, boolean isRubberbanded);
 
         /**
          * Notify a listener that the scroller wants to escape from the scrolling motion and
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 494195e..f732cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -719,7 +719,8 @@
         }
     }
 
-    public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
+    public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
+            final boolean isRubberbanded) {
         final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
         if (targetAmount == startOverScrollAmount) {
             return;
@@ -733,7 +734,8 @@
             public void onAnimationUpdate(ValueAnimator animation) {
                 float currentOverScroll = (float) animation.getAnimatedValue();
                 mHostLayout.setOverScrollAmount(
-                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */);
+                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */,
+                        isRubberbanded);
             }
         });
         overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index abe9076..adfa1f2 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -32,20 +32,15 @@
 import com.android.internal.view.menu.MenuDialogHelper;
 import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.view.menu.MenuView;
-import com.android.internal.widget.ActionBarContainer;
 import com.android.internal.widget.ActionBarContextView;
-import com.android.internal.widget.ActionBarOverlayLayout;
-import com.android.internal.widget.ActionBarView;
 import com.android.internal.widget.DecorContentParent;
 import com.android.internal.widget.SwipeDismissLayout;
 
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -397,8 +392,7 @@
         }
         if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
             // TODO Augment the scenes/transitions API to support this.
-            throw new UnsupportedOperationException(
-                    "addContentView does not support content transitions");
+            Log.v(TAG, "addContentView does not support content transitions");
         }
         mContentParent.addView(view, params);
         final Callback cb = getCallback();
@@ -2650,25 +2644,29 @@
             return false;
         }
 
-        private void updateColorViews(WindowInsets insets) {
-            if (mIsFloating || !ActivityManager.isHighEndGfx()) {
-                // No colors on floating windows or low end devices :(
-                return;
+        private WindowInsets updateColorViews(WindowInsets insets) {
+            if (!mIsFloating && ActivityManager.isHighEndGfx()) {
+                if (insets != null) {
+                    mLastTopInset = insets.getStableInsetTop();
+                    mLastBottomInset = insets.getStableInsetBottom();
+                }
+                mStatusColorView = updateColorViewInt(mStatusColorView,
+                        SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
+                        mStatusBarColor, mLastTopInset, Gravity.TOP,
+                        STATUS_BAR_BACKGROUND_TRANSITION_NAME);
+                mNavigationColorView = updateColorViewInt(mNavigationColorView,
+                        SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
+                        mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM,
+                        NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
             }
             if (insets != null) {
-                mLastTopInset = insets.getSystemWindowInsetTop();
-                mLastBottomInset = insets.getSystemWindowInsetBottom();
+                insets = insets.consumeStableInsets();
             }
-            mStatusColorView = updateColorViewInt(mStatusColorView,
-                    SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
-                    mStatusBarColor, mLastTopInset, Gravity.TOP);
-            mNavigationColorView = updateColorViewInt(mNavigationColorView,
-                    SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
-                    mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM);
+            return insets;
         }
 
         private View updateColorViewInt(View view, int systemUiHideFlag, int translucentFlag,
-                int color, int height, int verticalGravity) {
+                int color, int height, int verticalGravity, String transitionName) {
             boolean show = height > 0 && (mLastSystemUiVisibility & systemUiHideFlag) == 0
                     && (getAttributes().flags & translucentFlag) == 0
                     && (color & Color.BLACK) != 0
@@ -2678,6 +2676,7 @@
                 if (show) {
                     view = new View(mContext);
                     view.setBackgroundColor(color);
+                    view.setTransitionName(transitionName);
                     addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height,
                             Gravity.START | verticalGravity));
                 }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 728e781..4458a8d 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -409,6 +409,7 @@
     static final Rect mTmpContentFrame = new Rect();
     static final Rect mTmpVisibleFrame = new Rect();
     static final Rect mTmpDecorFrame = new Rect();
+    static final Rect mTmpStableFrame = new Rect();
     static final Rect mTmpNavigationFrame = new Rect();
 
     WindowState mTopFullscreenOpaqueWindowState;
@@ -1364,6 +1365,7 @@
                 // XXX right now the app process has complete control over
                 // this...  should introduce a token to let the system
                 // monitor/control what they are doing.
+                outAppOp[0] = AppOpsManager.OP_TOAST_WINDOW;
                 break;
             case TYPE_DREAM:
             case TYPE_INPUT_METHOD:
@@ -2973,7 +2975,8 @@
                 mStatusBarLayer = mNavigationBar.getSurfaceLayer();
                 // And compute the final frame.
                 mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
-                        mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf);
+                        mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
+                        mTmpNavigationFrame);
                 if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
                 if (mNavigationBarController.checkHiddenLw()) {
                     updateSysUiVisibility = true;
@@ -2998,7 +3001,7 @@
                 mStatusBarLayer = mStatusBar.getSurfaceLayer();
 
                 // Let the status bar determine its size.
-                mStatusBar.computeFrameLw(pf, df, vf, vf, vf, dcf);
+                mStatusBar.computeFrameLw(pf, df, vf, vf, vf, dcf, vf);
 
                 // For layout, the status bar is always at the top with our fixed height.
                 mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
@@ -3158,6 +3161,7 @@
         final Rect cf = mTmpContentFrame;
         final Rect vf = mTmpVisibleFrame;
         final Rect dcf = mTmpDecorFrame;
+        final Rect sf = mTmpStableFrame;
         dcf.setEmpty();
 
         final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
@@ -3165,6 +3169,12 @@
 
         final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
 
+        if (isDefaultDisplay) {
+            sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom);
+        } else {
+            sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom);
+        }
+
         if (!isDefaultDisplay) {
             if (attached != null) {
                 // If this window is attached to another, our display
@@ -3527,9 +3537,10 @@
                 + " pf=" + pf.toShortString() + " df=" + df.toShortString()
                 + " of=" + of.toShortString()
                 + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
-                + " dcf=" + dcf.toShortString());
+                + " dcf=" + dcf.toShortString()
+                + " sf=" + sf.toShortString());
 
-        win.computeFrameLw(pf, df, of, cf, vf, dcf);
+        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf);
 
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 176ba5d..14a462e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -29,8 +29,11 @@
 import java.util.List;
 import java.util.Map;
 
+import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.media.AudioService;
@@ -102,10 +105,12 @@
     public final static class Ops extends SparseArray<Op> {
         public final String packageName;
         public final int uid;
+        public final boolean isPrivileged;
 
-        public Ops(String _packageName, int _uid) {
+        public Ops(String _packageName, int _uid, boolean _isPrivileged) {
             packageName = _packageName;
             uid = _uid;
+            isPrivileged = _isPrivileged;
         }
     }
 
@@ -560,7 +565,7 @@
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         synchronized (this) {
-            if (isOpRestricted(uid, code)) {
+            if (isOpRestricted(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
@@ -646,7 +651,7 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
-            if (isOpRestricted(uid, code)) {
+            if (isOpRestricted(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             if (op.duration == -1) {
@@ -683,7 +688,7 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
-            if (isOpRestricted(uid, code)) {
+            if (isOpRestricted(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             final int switchCode = AppOpsManager.opToSwitch(code);
@@ -782,6 +787,7 @@
             if (!edit) {
                 return null;
             }
+            boolean isPrivileged = false;
             // This is the first time we have seen this package name under this uid,
             // so let's make sure it is valid.
             if (uid != 0) {
@@ -789,12 +795,19 @@
                 try {
                     int pkgUid = -1;
                     try {
-                        pkgUid = mContext.getPackageManager().getPackageUid(packageName,
-                                UserHandle.getUserId(uid));
-                    } catch (NameNotFoundException e) {
-                        if ("media".equals(packageName)) {
-                            pkgUid = Process.MEDIA_UID;
+                        ApplicationInfo appInfo = ActivityThread.getPackageManager()
+                                .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid));
+                        if (appInfo != null) {
+                            pkgUid = appInfo.uid;
+                            isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0;
+                        } else {
+                            if ("media".equals(packageName)) {
+                                pkgUid = Process.MEDIA_UID;
+                                isPrivileged = false;
+                            }
                         }
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Could not contact PackageManager", e);
                     }
                     if (pkgUid != uid) {
                         // Oops!  The package name is not valid for the uid they are calling
@@ -807,7 +820,7 @@
                     Binder.restoreCallingIdentity(ident);
                 }
             }
-            ops = new Ops(packageName, uid);
+            ops = new Ops(packageName, uid, isPrivileged);
             pkgOps.put(packageName, ops);
         }
         return ops;
@@ -851,10 +864,18 @@
         return op;
     }
 
-    private boolean isOpRestricted(int uid, int code) {
+    private boolean isOpRestricted(int uid, int code, String packageName) {
         int userHandle = UserHandle.getUserId(uid);
         boolean[] opRestrictions = mOpRestrictions.get(userHandle);
         if ((opRestrictions != null) && opRestrictions[code]) {
+            if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+                synchronized (this) {
+                    Ops ops = getOpsLocked(uid, packageName, true);
+                    if ((ops != null) && ops.isPrivileged) {
+                        return false;
+                    }
+                }
+            }
             if (userHandle == UserHandle.USER_OWNER) {
                 if (uid != mDeviceOwnerUid) {
                     return true;
@@ -959,6 +980,27 @@
     void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
             XmlPullParserException, IOException {
         int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+        String isPrivilegedString = parser.getAttributeValue(null, "p");
+        boolean isPrivileged = false;
+        if (isPrivilegedString == null) {
+            try {
+                IPackageManager packageManager = ActivityThread.getPackageManager();
+                if (packageManager != null) {
+                    ApplicationInfo appInfo = ActivityThread.getPackageManager()
+                            .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid));
+                    if (appInfo != null) {
+                        isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0;
+                    }
+                } else {
+                    // Could not load data, don't add to cache so it will be loaded later.
+                    return;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not contact PackageManager", e);
+            }
+        } else {
+            isPrivileged = Boolean.parseBoolean(isPrivilegedString);
+        }
         int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -993,7 +1035,7 @@
                 }
                 Ops ops = pkgOps.get(pkgName);
                 if (ops == null) {
-                    ops = new Ops(pkgName, uid);
+                    ops = new Ops(pkgName, uid, isPrivileged);
                     pkgOps.put(pkgName, ops);
                 }
                 ops.put(op.op, op);
@@ -1037,6 +1079,16 @@
                         }
                         out.startTag(null, "uid");
                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
+                        synchronized (this) {
+                            Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
+                            // Should always be present as the list of PackageOps is generated
+                            // from Ops.
+                            if (ops != null) {
+                                out.attribute(null, "p", Boolean.toString(ops.isPrivileged));
+                            } else {
+                                out.attribute(null, "p", Boolean.toString(false));
+                            }
+                        }
                         List<AppOpsManager.OpEntry> ops = pkg.getOps();
                         for (int j=0; j<ops.size(); j++) {
                             AppOpsManager.OpEntry op = ops.get(j);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7fe1e82..64db8cf 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -331,7 +331,9 @@
 
     /**
      * used internally to clear a wakelock when transitioning
-     * from one net to another
+     * from one net to another.  Clear happens when we get a new
+     * network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
+     * after a timeout if no network is found (typically 1 min).
      */
     private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
 
@@ -420,6 +422,15 @@
      */
     private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23;
 
+    /**
+     * used internally to expire a wakelock when transitioning
+     * from one net to another.  Expire happens when we fail to find
+     * a new network (typically after 1 minute) -
+     * EVENT_CLEAR_NET_TRANSITION_WAKELOCK happens if we had found
+     * a replacement network.
+     */
+    private static final int EVENT_EXPIRE_NET_TRANSITION_WAKELOCK = 24;
+
 
     /** Handler used for internal events. */
     final private InternalHandler mHandler;
@@ -3290,6 +3301,7 @@
             if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
                 removeDataActivityTracking(nai);
                 mActiveDefaultNetwork = ConnectivityManager.TYPE_NONE;
+                requestNetworkTransitionWakelock(nai.name());
             }
             for (NetworkAgentInfo networkToActivate : toActivate) {
                 networkToActivate.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
@@ -3391,6 +3403,7 @@
         public void handleMessage(Message msg) {
             NetworkInfo info;
             switch (msg.what) {
+                case EVENT_EXPIRE_NET_TRANSITION_WAKELOCK:
                 case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: {
                     String causedBy = null;
                     synchronized (ConnectivityService.this) {
@@ -3398,10 +3411,15 @@
                                 mNetTransitionWakeLock.isHeld()) {
                             mNetTransitionWakeLock.release();
                             causedBy = mNetTransitionWakeLockCausedBy;
+                        } else {
+                            break;
                         }
                     }
-                    if (causedBy != null) {
-                        log("NetTransition Wakelock for " + causedBy + " released by timeout");
+                    if (msg.what == EVENT_EXPIRE_NET_TRANSITION_WAKELOCK) {
+                        log("Failed to find a new network - expiring NetTransition Wakelock");
+                    } else {
+                        log("NetTransition Wakelock (" + (causedBy == null ? "unknown" : causedBy) +
+                                " cleared because we found a replacement network");
                     }
                     break;
                 }
@@ -3602,15 +3620,15 @@
     // restarted by subsequent callers.
     public void requestNetworkTransitionWakelock(String forWhom) {
         enforceConnectivityInternalPermission();
+        int serialNum = 0;
         synchronized (this) {
             if (mNetTransitionWakeLock.isHeld()) return;
-            mNetTransitionWakeLockSerialNumber++;
+            serialNum = ++mNetTransitionWakeLockSerialNumber;
             mNetTransitionWakeLock.acquire();
             mNetTransitionWakeLockCausedBy = forWhom;
         }
         mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
-                mNetTransitionWakeLockSerialNumber, 0),
+                EVENT_EXPIRE_NET_TRANSITION_WAKELOCK, serialNum, 0),
                 mNetTransitionWakeLockTimeout);
         return;
     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e1c7da9..1ecb43c 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1936,6 +1936,9 @@
                     resetTaskIfNeededLocked(r, r);
                     doShow = topRunningNonDelayedActivityLocked(null) == r;
                 }
+            } else if (options != null && new ActivityOptions(options).getAnimationType()
+                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
+                doShow = false;
             }
             if (SHOW_APP_STARTING_PREVIEW && doShow) {
                 // Figure out if we are transitioning from another activity that is
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 99535b6..9471cff 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8051,17 +8051,22 @@
      *
      * @return true if verification should be performed
      */
-    private boolean isVerificationEnabled(int flags) {
+    private boolean isVerificationEnabled(int userId, int flags) {
         if (!DEFAULT_VERIFY_ENABLE) {
             return false;
         }
 
+        boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
+
         // Check if installing from ADB
         if ((flags & PackageManager.INSTALL_FROM_ADB) != 0) {
             // Do not run verification in a test harness environment
             if (ActivityManager.isRunningInTestHarness()) {
                 return false;
             }
+            if (ensureVerifyAppsEnabled) {
+                return true;
+            }
             // Check if the developer does not want package verification for ADB installs
             if (android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                     android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 0) {
@@ -8069,6 +8074,10 @@
             }
         }
 
+        if (ensureVerifyAppsEnabled) {
+            return true;
+        }
+
         return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                 android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
     }
@@ -8639,7 +8648,7 @@
                  */
                 final int requiredUid = mRequiredVerifierPackage == null ? -1
                         : getPackageUid(mRequiredVerifierPackage, userIdentifier);
-                if (requiredUid != -1 && isVerificationEnabled(flags)) {
+                if (requiredUid != -1 && isVerificationEnabled(userIdentifier, flags)) {
                     final Intent verification = new Intent(
                             Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
                     verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 544e93b..194e85d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -838,6 +838,7 @@
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_TELEPHONY);
+        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
         serializer.endTag(null, TAG_RESTRICTIONS);
     }
 
@@ -980,6 +981,7 @@
         readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
         readBoolean(parser, restrictions, UserManager.DISALLOW_TELEPHONY);
+        readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
     }
 
     private void readBoolean(XmlPullParser parser, Bundle restrictions,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b4cf2ae..f2703ad 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -189,13 +189,14 @@
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
-            Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
+            Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
+            Surface outSurface) {
         if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
-                outConfig, outSurface);
+                outStableInsets, outConfig, outSurface);
         if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
         return res;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1d857231..771b53b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2835,7 +2835,8 @@
             WindowManager.LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
-            Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
+            Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
+            Surface outSurface) {
         boolean toBeDisplayed = false;
         boolean inTouchMode;
         boolean configChanged;
@@ -3112,6 +3113,7 @@
             outOverscanInsets.set(win.mOverscanInsets);
             outContentInsets.set(win.mContentInsets);
             outVisibleInsets.set(win.mVisibleInsets);
+            outStableInsets.set(win.mStableInsets);
             if (localLOGV) Slog.v(
                 TAG, "Relayout given client " + client.asBinder()
                 + ", requestedWidth=" + requestedWidth
@@ -8878,6 +8880,8 @@
                             + " " + w.mContentInsets.toShortString()
                             + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
                             + " " + w.mVisibleInsets.toShortString()
+                            + " stableInsetsChanged=" + w.mStableInsetsChanged
+                            + " " + w.mStableInsets.toShortString()
                             + " surfaceResized=" + winAnimator.mSurfaceResized
                             + " configChanged=" + configChanged);
                 }
@@ -8885,6 +8889,7 @@
                 w.mLastOverscanInsets.set(w.mOverscanInsets);
                 w.mLastContentInsets.set(w.mContentInsets);
                 w.mLastVisibleInsets.set(w.mVisibleInsets);
+                w.mLastStableInsets.set(w.mStableInsets);
                 makeWindowFreezingScreenIfNeededLocked(w);
                 // If the orientation is changing, then we need to
                 // hold off on unfreezing the display until this
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7003c8c..23ab73c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -167,6 +167,14 @@
     boolean mOverscanInsetsChanged;
 
     /**
+     * Insets that determine the area covered by the stable system windows.  These are in the
+     * application's coordinate space (without compatibility scale applied).
+     */
+    final Rect mStableInsets = new Rect();
+    final Rect mLastStableInsets = new Rect();
+    boolean mStableInsetsChanged;
+
+    /**
      * Set to true if we are waiting for this window to receive its
      * given internal insets before laying out other windows based on it.
      */
@@ -225,6 +233,7 @@
     final Rect mParentFrame = new Rect();
     final Rect mVisibleFrame = new Rect();
     final Rect mDecorFrame = new Rect();
+    final Rect mStableFrame = new Rect();
 
     boolean mContentChanged;
 
@@ -470,7 +479,7 @@
     }
 
     @Override
-    public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf) {
+    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;
@@ -537,6 +546,7 @@
         mContentFrame.set(cf);
         mVisibleFrame.set(vf);
         mDecorFrame.set(dcf);
+        mStableFrame.set(sf);
 
         final int fw = mFrame.width();
         final int fh = mFrame.height();
@@ -574,6 +584,11 @@
                 Math.min(mVisibleFrame.right, mFrame.right),
                 Math.min(mVisibleFrame.bottom, mFrame.bottom));
 
+        mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
+                Math.max(mStableFrame.top, mFrame.top),
+                Math.min(mStableFrame.right, mFrame.right),
+                Math.min(mStableFrame.bottom, mFrame.bottom));
+
         mOverscanInsets.set(Math.max(mOverscanFrame.left - mFrame.left, 0),
                 Math.max(mOverscanFrame.top - mFrame.top, 0),
                 Math.max(mFrame.right - mOverscanFrame.right, 0),
@@ -589,6 +604,11 @@
                 mFrame.right - mVisibleFrame.right,
                 mFrame.bottom - mVisibleFrame.bottom);
 
+        mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
+                Math.max(mStableFrame.top - mFrame.top, 0),
+                Math.max(mFrame.right - mStableFrame.right, 0),
+                Math.max(mFrame.bottom - mStableFrame.bottom, 0));
+
         mCompatFrame.set(mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
@@ -597,6 +617,7 @@
             mOverscanInsets.scale(mInvGlobalScale);
             mContentInsets.scale(mInvGlobalScale);
             mVisibleInsets.scale(mInvGlobalScale);
+            mStableInsets.scale(mInvGlobalScale);
 
             // Also the scaled frame that we report to the app needs to be
             // adjusted to be in its coordinate space.
@@ -618,7 +639,8 @@
                 + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
                 + "): frame=" + mFrame.toShortString()
                 + " ci=" + mContentInsets.toShortString()
-                + " vi=" + mVisibleInsets.toShortString());
+                + " vi=" + mVisibleInsets.toShortString()
+                + " vi=" + mStableInsets.toShortString());
     }
 
     @Override
@@ -724,6 +746,7 @@
         mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
         mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
         mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
+        mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
         return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged;
     }
 
@@ -1344,6 +1367,7 @@
             final Rect overscanInsets = mLastOverscanInsets;
             final Rect contentInsets = mLastContentInsets;
             final Rect visibleInsets = mLastVisibleInsets;
+            final Rect stableInsets = mLastStableInsets;
             final boolean reportDraw = mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING;
             final Configuration newConfig = configChanged ? mConfiguration : null;
             if (mClient instanceof IWindow.Stub) {
@@ -1353,15 +1377,15 @@
                     public void run() {
                         try {
                             mClient.resized(frame, overscanInsets, contentInsets,
-                                    visibleInsets, reportDraw, newConfig);
+                                    visibleInsets, stableInsets,  reportDraw, newConfig);
                         } catch (RemoteException e) {
                             // Not a remote call, RemoteException won't be raised.
                         }
                     }
                 });
             } else {
-                mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, reportDraw,
-                        newConfig);
+                mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
+                        reportDraw, newConfig);
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
@@ -1373,6 +1397,7 @@
             mOverscanInsetsChanged = false;
             mContentInsetsChanged = false;
             mVisibleInsetsChanged = false;
+            mStableInsetsChanged = false;
             mWinAnimator.mSurfaceResized = false;
         } catch (RemoteException e) {
             mOrientationChanging = false;
@@ -1522,11 +1547,13 @@
                     mOverscanInsets.printShortString(pw);
                     pw.print(" content="); mContentInsets.printShortString(pw);
                     pw.print(" visible="); mVisibleInsets.printShortString(pw);
+                    pw.print(" stable="); mStableInsets.printShortString(pw);
                     pw.println();
             pw.print(prefix); pw.print("Lst insets: overscan=");
                     mLastOverscanInsets.printShortString(pw);
                     pw.print(" content="); mLastContentInsets.printShortString(pw);
                     pw.print(" visible="); mLastVisibleInsets.printShortString(pw);
+                    pw.print(" stable="); mLastStableInsets.printShortString(pw);
                     pw.println();
         }
         pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index d32f6ee..997b199 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -47,8 +47,8 @@
     }
 
     @Override
-    public void resized(Rect arg1, Rect arg1p5, Rect arg2, Rect arg3,
-            boolean arg4, Configuration arg5) throws RemoteException {
+    public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, boolean b,
+            Configuration configuration) throws RemoteException {
         // pass for now.
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index e1064b1..130500a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -85,10 +85,11 @@
         // pass for now.
         return false;
     }
+
     @Override
-    public int relayout(IWindow arg0, int seq, LayoutParams arg1, int arg2, int arg3, int arg4,
-            int arg4_5, Rect arg5Z, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b,
-            Surface arg8) throws RemoteException {
+    public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2,
+            int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5,
+            Configuration configuration, Surface surface) throws RemoteException {
         // pass for now.
         return 0;
     }