Merge "Relax access on deprecated fields for compatibility." into lmp-mr1-dev
diff --git a/Android.mk b/Android.mk
index ba45e6f..1416472 100644
--- a/Android.mk
+++ b/Android.mk
@@ -100,6 +100,7 @@
 	core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
 	core/java/android/bluetooth/IBluetoothAvrcpController.aidl \
 	core/java/android/bluetooth/IBluetoothCallback.aidl \
+	core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl \
 	core/java/android/bluetooth/IBluetoothHeadset.aidl \
 	core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl \
 	core/java/android/bluetooth/IBluetoothHealth.aidl \
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4753099..ead89b3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -504,6 +504,22 @@
         }
         return false;
     }
+    /**
+     * Return true if the given administrator component is currently being removed
+     * for the user.
+     * @hide
+     */
+    public boolean isRemovingAdmin(ComponentName who, int userId) {
+        if (mService != null) {
+            try {
+                return mService.isRemovingAdmin(who, userId);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
+
 
     /**
      * Return a list of all currently active device administrator's component
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d144ae8..0ca60c0 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -196,4 +196,6 @@
 
     void setAutoTimeRequired(in ComponentName who, int userHandle, boolean required);
     boolean getAutoTimeRequired();
+
+    boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle);
 }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 353f0fb..546a50e 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -20,9 +20,10 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -221,11 +222,14 @@
      */
     public static final int STATE_AUDIO_CONNECTED = 12;
 
+    private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
+    private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
 
     private Context mContext;
     private ServiceListener mServiceListener;
     private IBluetoothHeadset mService;
     private BluetoothAdapter mAdapter;
+    private boolean mIsClosed;
 
     final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
             new IBluetoothStateChangeCallback.Stub() {
@@ -233,14 +237,7 @@
                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                     if (!up) {
                         if (VDBG) Log.d(TAG,"Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG,"",re);
-                            }
-                        }
+                        doUnbind();
                     } else {
                         synchronized (mConnection) {
                             try {
@@ -263,6 +260,7 @@
         mContext = context;
         mServiceListener = l;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mIsClosed = false;
 
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
@@ -277,15 +275,26 @@
     }
 
     boolean doBind() {
-        Intent intent = new Intent(IBluetoothHeadset.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
-            Log.e(TAG, "Could not bind to Bluetooth Headset Service with " + intent);
-            return false;
+        try {
+            return mAdapter.getBluetoothManager().bindBluetoothProfileService(
+                    BluetoothProfile.HEADSET, mConnection);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to bind HeadsetService", e);
         }
-        return true;
+        return false;
+    }
+
+    void doUnbind() {
+        synchronized (mConnection) {
+            if (mService != null) {
+                try {
+                    mAdapter.getBluetoothManager().unbindBluetoothProfileService(
+                            BluetoothProfile.HEADSET, mConnection);
+                } catch (RemoteException e) {
+                    Log.e(TAG,"Unable to unbind HeadsetService", e);
+                }
+            }
+        }
     }
 
     /**
@@ -305,18 +314,8 @@
                 Log.e(TAG,"",e);
             }
         }
-
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG,"",re);
-                }
-            }
-        }
-        mServiceListener = null;
+        mIsClosed = true;
+        doUnbind();
     }
 
     /**
@@ -930,21 +929,21 @@
         return false;
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
+    private final IBluetoothProfileServiceConnection mConnection
+            = new IBluetoothProfileServiceConnection.Stub()  {
+        @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
             if (DBG) Log.d(TAG, "Proxy object connected");
             mService = IBluetoothHeadset.Stub.asInterface(service);
-
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this);
-            }
+            mHandler.sendMessage(mHandler.obtainMessage(
+                    MESSAGE_HEADSET_SERVICE_CONNECTED));
         }
+        @Override
         public void onServiceDisconnected(ComponentName className) {
             if (DBG) Log.d(TAG, "Proxy object disconnected");
             mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
-            }
+            mHandler.sendMessage(mHandler.obtainMessage(
+                    MESSAGE_HEADSET_SERVICE_DISCONNECTED));
         }
     };
 
@@ -968,4 +967,28 @@
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_HEADSET_SERVICE_CONNECTED: {
+                    if (mServiceListener != null) {
+                        mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
+                                BluetoothHeadset.this);
+                    }
+                    break;
+                }
+                case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
+                    if (mServiceListener != null) {
+                        mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
+                    }
+                    if (mIsClosed){
+                        mServiceListener = null;
+                    }
+                    break;
+                }
+            }
+        }
+    };
 }
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 493d2f8..7411d3f 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -19,6 +19,7 @@
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothManagerCallback;
+import android.bluetooth.IBluetoothProfileServiceConnection;
 import android.bluetooth.IBluetoothStateChangeCallback;
 
 /**
@@ -38,6 +39,9 @@
     boolean disable(boolean persist);
     IBluetoothGatt getBluetoothGatt();
 
+    boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);
+    void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);
+
     String getAddress();
     String getName();
 }
diff --git a/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl b/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl
new file mode 100755
index 0000000..96c59e2
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.bluetooth;
+
+import android.content.ComponentName;
+import android.os.IBinder;
+
+/**
+ * Callback for bluetooth profile connections.
+ *
+ * {@hide}
+ */
+interface IBluetoothProfileServiceConnection {
+    void onServiceConnected(in ComponentName comp, in IBinder service);
+    void onServiceDisconnected(in ComponentName comp);
+}
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index c1341e1..14be269 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -402,7 +402,7 @@
         }
 
         boolean hasPrefix = false;
-        
+
         for (int i = 0; i < prefixes.length; i++) {
             if (url.regionMatches(true, 0, prefixes[i], 0,
                                   prefixes[i].length())) {
@@ -450,7 +450,7 @@
     private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) {
         PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
         Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
-                Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE);
+                Locale.getDefault().getCountry(), Leniency.VALID, Long.MAX_VALUE);
         for (PhoneNumberMatch match : matches) {
             LinkSpec spec = new LinkSpec();
             spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 2e5c1e0..6944c53 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -364,7 +364,7 @@
         /**
          * This hook is called whenever the window focus changes.  See
          * {@link View#onWindowFocusChanged(boolean)
-         * View.onWindowFocusChanged(boolean)} for more information.
+         * View.onWindowFocusChangedNotLocked(boolean)} for more information.
          *
          * @param hasFocus Whether the window now has focus.
          */
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f4f047e..094a8a1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1102,6 +1102,13 @@
         public static final int PRIVATE_FLAG_KEYGUARD = 0x00000400;
 
         /**
+         * Flag that prevents the wallpaper behind the current window from receiving touch events.
+         *
+         * {@hide}
+         */
+        public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 0x00000800;
+
+        /**
          * Control flags that are private to the platform.
          * @hide
          */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3dd9770..3fdaaa6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2914,7 +2914,8 @@
          Any service that filters for this intent must be a carrier privileged app. -->
     <permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"
         android:label="@string/permlab_bindCarrierMessagingService"
-        android:description="@string/permdesc_bindCarrierMessagingService" />
+        android:description="@string/permdesc_bindCarrierMessagingService"
+        android:protectionLevel="signature|system" />
 
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
diff --git a/core/res/res/drawable/btn_default_mtrl_shape.xml b/core/res/res/drawable/btn_default_mtrl_shape.xml
index 6d0f7f8..8a31d5e 100644
--- a/core/res/res/drawable/btn_default_mtrl_shape.xml
+++ b/core/res/res/drawable/btn_default_mtrl_shape.xml
@@ -21,9 +21,10 @@
        android:insetTop="@dimen/button_inset_vertical_material"
        android:insetRight="@dimen/button_inset_horizontal_material"
        android:insetBottom="@dimen/button_inset_vertical_material">
-    <shape android:shape="rectangle">
+    <shape android:shape="rectangle"
+           android:tint="?attr/colorButtonNormal">
         <corners android:radius="@dimen/control_corner_material" />
-        <solid android:color="?attr/colorButtonNormal" />
+        <solid android:color="@color/white" />
         <padding android:left="@dimen/button_padding_horizontal_material"
                  android:top="@dimen/button_padding_vertical_material"
                  android:right="@dimen/button_padding_horizontal_material"
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 0244891..f94fe66 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -229,6 +229,12 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansCham-Regular.ttf</file>
+            <file>NotoSansCham-Bold.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansBalinese-Regular.ttf</file>
         </fileset>
     </family>
@@ -259,6 +265,16 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansCoptic-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansGlagolitic-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansHanunoo-Regular.ttf</file>
         </fileset>
     </family>
@@ -269,6 +285,11 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansKayahLi-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansLepcha-Regular.ttf</file>
         </fileset>
     </family>
@@ -314,6 +335,26 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansTaiLe-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansTaiTham-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansTaiViet-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansTifinagh-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansYi-Regular.ttf</file>
         </fileset>
     </family>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index a62e972..02bf877 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -224,6 +224,10 @@
         <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
     </family>
     <family>
@@ -242,12 +246,21 @@
         <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
     </family>
     <family>
@@ -275,6 +288,18 @@
         <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
     </family>
     <family>
diff --git a/docs/html/guide/topics/ui/settings.jd b/docs/html/guide/topics/ui/settings.jd
index f454c4e..02f1255 100644
--- a/docs/html/guide/topics/ui/settings.jd
+++ b/docs/html/guide/topics/ui/settings.jd
@@ -801,7 +801,7 @@
 
 <h3 id="Listening">Listening for preference changes</h3>
 
-<p>There are several reasons you might want to be notified as soon as the use changes one of the
+<p>There are several reasons you might want to be notified as soon as the user changes one of the
 preferences. In order to receive a callback when a change happens to any one of the preferences,
 implement the {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener
 SharedPreference.OnSharedPreferenceChangeListener} interface and register the listener for the
diff --git a/docs/html/samples/images/ActivitySceneTransitionBasic.png b/docs/html/samples/images/ActivitySceneTransitionBasic.png
index ea58641..a126cf8 100644
--- a/docs/html/samples/images/ActivitySceneTransitionBasic.png
+++ b/docs/html/samples/images/ActivitySceneTransitionBasic.png
Binary files differ
diff --git a/docs/html/samples/images/ActivitySceneTransitionBasic@2x.png b/docs/html/samples/images/ActivitySceneTransitionBasic@2x.png
index cd28ade..44378bb 100644
--- a/docs/html/samples/images/ActivitySceneTransitionBasic@2x.png
+++ b/docs/html/samples/images/ActivitySceneTransitionBasic@2x.png
Binary files differ
diff --git a/docs/html/samples/images/BasicManagedProfile.png b/docs/html/samples/images/BasicManagedProfile.png
index 7354842..5749db3 100644
--- a/docs/html/samples/images/BasicManagedProfile.png
+++ b/docs/html/samples/images/BasicManagedProfile.png
Binary files differ
diff --git a/docs/html/samples/images/BasicManagedProfile@2x.png b/docs/html/samples/images/BasicManagedProfile@2x.png
index c232809..860c954 100644
--- a/docs/html/samples/images/BasicManagedProfile@2x.png
+++ b/docs/html/samples/images/BasicManagedProfile@2x.png
Binary files differ
diff --git a/docs/html/samples/images/JobSchedulerSample.png b/docs/html/samples/images/JobSchedulerSample.png
index ee57bdb..a043f3e 100644
--- a/docs/html/samples/images/JobSchedulerSample.png
+++ b/docs/html/samples/images/JobSchedulerSample.png
Binary files differ
diff --git a/docs/html/samples/images/JobSchedulerSample@2x.png b/docs/html/samples/images/JobSchedulerSample@2x.png
index 3d543db..1ed2cf9 100644
--- a/docs/html/samples/images/JobSchedulerSample@2x.png
+++ b/docs/html/samples/images/JobSchedulerSample@2x.png
Binary files differ
diff --git a/docs/html/training/wearables/ui/exit.jd b/docs/html/training/wearables/ui/exit.jd
index 6b205a57..84e1e45 100644
--- a/docs/html/training/wearables/ui/exit.jd
+++ b/docs/html/training/wearables/ui/exit.jd
@@ -67,7 +67,7 @@
         android:id="@+id/dismiss_overlay"
         android:layout_height="match_parent"
         android:layout_width="match_parent"/>
-&lt;/FrameLayout>
+&lt;FrameLayout>
 </pre>
 
 <p>In your activity, obtain the <code>DismissOverlayView</code> element and set some introductory
@@ -100,8 +100,8 @@
 
     // Capture long presses
     &#64;Override
-    public boolean dispatchTouchEvent(MotionEvent e) {
-        return mDetector.onTouchEvent(e) || super.dispatchTouchEvent(e);
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
     }
 }
 </pre>
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index e9c8c2a..2a17a60 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -146,7 +146,8 @@
     @Override
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
-            mAnimatedVectorState.mVectorDrawable.mutate();
+            mAnimatedVectorState = new AnimatedVectorDrawableState(
+                    mAnimatedVectorState, mCallback, null);
             mMutated = true;
         }
         return this;
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index b88d9e6..acfd427 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -30,6 +30,7 @@
 import android.graphics.ColorFilter;
 import android.graphics.Insets;
 import android.graphics.Outline;
+import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -317,7 +318,13 @@
 
     @Override
     public int getOpacity() {
-        return mState.mDrawable.getOpacity();
+        final InsetState state = mState;
+        final int opacity = state.mDrawable.getOpacity();
+        if (opacity == PixelFormat.OPAQUE && (state.mInsetLeft > 0 || state.mInsetTop > 0
+                || state.mInsetRight > 0 || state.mInsetBottom > 0)) {
+            return PixelFormat.TRANSLUCENT;
+        }
+        return opacity;
     }
 
     @Override
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 6731366..a3a220c 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -22,11 +22,8 @@
 import android.animation.TimeInterpolator;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
-import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.Paint.Style;
 import android.graphics.Rect;
-import android.graphics.Xfermode;
 import android.util.MathUtils;
 import android.view.HardwareCanvas;
 import android.view.RenderNodeAnimator;
@@ -51,19 +48,12 @@
     // Hardware animators.
     private final ArrayList<RenderNodeAnimator> mRunningAnimations =
             new ArrayList<RenderNodeAnimator>();
-    private final ArrayList<RenderNodeAnimator> mPendingAnimations =
-            new ArrayList<RenderNodeAnimator>();
 
     private final RippleDrawable mOwner;
 
     /** Bounds used for computing max radius. */
     private final Rect mBounds;
 
-    /** ARGB color for drawing this ripple. */
-    private int mColor;
-
-    private Xfermode mXfermode;
-
     /** Maximum ripple radius. */
     private float mOuterRadius;
 
@@ -112,6 +102,10 @@
     /** Whether we were canceled externally and should avoid self-removal. */
     private boolean mCanceled;
 
+    private boolean mHasPendingHardwareExit;
+    private int mPendingRadiusDuration;
+    private int mPendingOpacityDuration;
+
     /**
      * Creates a new ripple.
      */
@@ -217,10 +211,6 @@
      * Draws the ripple centered at (0,0) using the specified paint.
      */
     public boolean draw(Canvas c, Paint p) {
-        // Store the color and xfermode, we might need them later.
-        mColor = p.getColor();
-        mXfermode = p.getXfermode();
-
         final boolean canUseHardware = c.isHardwareAccelerated();
         if (mCanUseHardware != canUseHardware && mCanUseHardware) {
             // We've switched from hardware to non-hardware mode. Panic.
@@ -229,8 +219,8 @@
         mCanUseHardware = canUseHardware;
 
         final boolean hasContent;
-        if (canUseHardware && mHardwareAnimating) {
-            hasContent = drawHardware((HardwareCanvas) c);
+        if (canUseHardware && (mHardwareAnimating || mHasPendingHardwareExit)) {
+            hasContent = drawHardware((HardwareCanvas) c, p);
         } else {
             hasContent = drawSoftware(c, p);
         }
@@ -238,24 +228,10 @@
         return hasContent;
     }
 
-    private boolean drawHardware(HardwareCanvas c) {
-        // If we have any pending hardware animations, cancel any running
-        // animations and start those now.
-        final ArrayList<RenderNodeAnimator> pendingAnimations = mPendingAnimations;
-        final int N = pendingAnimations.size();
-        if (N > 0) {
+    private boolean drawHardware(HardwareCanvas c, Paint p) {
+        if (mHasPendingHardwareExit) {
             cancelHardwareAnimations(false);
-
-            // We canceled old animations, but we're about to run new ones.
-            mHardwareAnimating = true;
-
-            for (int i = 0; i < N; i++) {
-                pendingAnimations.get(i).setTarget(c);
-                pendingAnimations.get(i).start();
-            }
-
-            mRunningAnimations.addAll(pendingAnimations);
-            pendingAnimations.clear();
+            startPendingHardwareExit(c, p);
         }
 
         c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
@@ -347,8 +323,6 @@
      * Starts the exit animation.
      */
     public void exit() {
-        cancel();
-
         final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
         final float remaining;
         if (mAnimRadius != null && mAnimRadius.isRunning()) {
@@ -357,19 +331,33 @@
             remaining = mOuterRadius;
         }
 
+        cancel();
+
         final int radiusDuration = (int) (1000 * Math.sqrt(remaining / (WAVE_TOUCH_UP_ACCELERATION
                 + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
         final int opacityDuration = (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
 
         if (mCanUseHardware) {
-            exitHardware(radiusDuration, opacityDuration);
+            createPendingHardwareExit(radiusDuration, opacityDuration);
         } else {
             exitSoftware(radiusDuration, opacityDuration);
         }
     }
 
-    private void exitHardware(int radiusDuration, int opacityDuration) {
-        mPendingAnimations.clear();
+    private void createPendingHardwareExit(int radiusDuration, int opacityDuration) {
+        mHasPendingHardwareExit = true;
+        mPendingRadiusDuration = radiusDuration;
+        mPendingOpacityDuration = opacityDuration;
+
+        // The animation will start on the next draw().
+        invalidateSelf();
+    }
+
+    private void startPendingHardwareExit(HardwareCanvas c, Paint p) {
+        mHasPendingHardwareExit = false;
+
+        final int radiusDuration = mPendingRadiusDuration;
+        final int opacityDuration = mPendingOpacityDuration;
 
         final float startX = MathUtils.lerp(
                 mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
@@ -377,12 +365,8 @@
                 mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
 
         final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
-        final Paint paint = getTempPaint();
-        paint.setAntiAlias(true);
-        paint.setColor(mColor);
-        paint.setXfermode(mXfermode);
-        paint.setAlpha((int) (Color.alpha(mColor) * mOpacity + 0.5f));
-        paint.setStyle(Style.FILL);
+        final Paint paint = getTempPaint(p);
+        paint.setAlpha((int) (paint.getAlpha() * mOpacity + 0.5f));
         mPropPaint = CanvasProperty.createPaint(paint);
         mPropRadius = CanvasProperty.createFloat(startRadius);
         mPropX = CanvasProperty.createFloat(startX);
@@ -391,25 +375,33 @@
         final RenderNodeAnimator radiusAnim = new RenderNodeAnimator(mPropRadius, mOuterRadius);
         radiusAnim.setDuration(radiusDuration);
         radiusAnim.setInterpolator(DECEL_INTERPOLATOR);
+        radiusAnim.setTarget(c);
+        radiusAnim.start();
 
         final RenderNodeAnimator xAnim = new RenderNodeAnimator(mPropX, mOuterX);
         xAnim.setDuration(radiusDuration);
         xAnim.setInterpolator(DECEL_INTERPOLATOR);
+        xAnim.setTarget(c);
+        xAnim.start();
 
         final RenderNodeAnimator yAnim = new RenderNodeAnimator(mPropY, mOuterY);
         yAnim.setDuration(radiusDuration);
         yAnim.setInterpolator(DECEL_INTERPOLATOR);
+        yAnim.setTarget(c);
+        yAnim.start();
 
         final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPropPaint,
                 RenderNodeAnimator.PAINT_ALPHA, 0);
         opacityAnim.setDuration(opacityDuration);
         opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
         opacityAnim.addListener(mAnimationListener);
+        opacityAnim.setTarget(c);
+        opacityAnim.start();
 
-        mPendingAnimations.add(radiusAnim);
-        mPendingAnimations.add(opacityAnim);
-        mPendingAnimations.add(xAnim);
-        mPendingAnimations.add(yAnim);
+        mRunningAnimations.add(radiusAnim);
+        mRunningAnimations.add(opacityAnim);
+        mRunningAnimations.add(xAnim);
+        mRunningAnimations.add(yAnim);
 
         mHardwareAnimating = true;
 
@@ -418,8 +410,6 @@
         mTweenX = 1;
         mTweenY = 1;
         mTweenRadius = 1;
-
-        invalidateSelf();
     }
 
     /**
@@ -455,10 +445,11 @@
         }
     }
 
-    private Paint getTempPaint() {
+    private Paint getTempPaint(Paint original) {
         if (mTempPaint == null) {
             mTempPaint = new Paint();
         }
+        mTempPaint.set(original);
         return mTempPaint;
     }
 
@@ -539,10 +530,7 @@
         }
         runningAnimations.clear();
 
-        if (cancelPending && !mPendingAnimations.isEmpty()) {
-            mPendingAnimations.clear();
-        }
-
+        mHasPendingHardwareExit = false;
         mHardwareAnimating = false;
     }
 
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 69847b5..665d736 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -24,9 +24,7 @@
 import android.graphics.CanvasProperty;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.Paint.Style;
 import android.graphics.Rect;
-import android.graphics.Xfermode;
 import android.util.MathUtils;
 import android.view.HardwareCanvas;
 import android.view.RenderNodeAnimator;
@@ -53,8 +51,6 @@
     // Hardware animators.
     private final ArrayList<RenderNodeAnimator> mRunningAnimations =
             new ArrayList<RenderNodeAnimator>();
-    private final ArrayList<RenderNodeAnimator> mPendingAnimations =
-            new ArrayList<RenderNodeAnimator>();
 
     private final RippleDrawable mOwner;
 
@@ -64,8 +60,6 @@
     /** ARGB color for drawing this ripple. */
     private int mColor;
 
-    private Xfermode mXfermode;
-
     /** Maximum ripple radius. */
     private float mOuterRadius;
 
@@ -98,6 +92,11 @@
     /** Whether we have an explicit maximum radius. */
     private boolean mHasMaxRadius;
 
+    private boolean mHasPendingHardwareExit;
+    private int mPendingOpacityDuration;
+    private int mPendingInflectionDuration;
+    private int mPendingInflectionOpacity;
+
     /**
      * Creates a new ripple.
      */
@@ -144,9 +143,7 @@
      * Draws the ripple centered at (0,0) using the specified paint.
      */
     public boolean draw(Canvas c, Paint p) {
-        // Store the color and xfermode, we might need them later.
         mColor = p.getColor();
-        mXfermode = p.getXfermode();
 
         final boolean canUseHardware = c.isHardwareAccelerated();
         if (mCanUseHardware != canUseHardware && mCanUseHardware) {
@@ -156,8 +153,8 @@
         mCanUseHardware = canUseHardware;
 
         final boolean hasContent;
-        if (canUseHardware && mHardwareAnimating) {
-            hasContent = drawHardware((HardwareCanvas) c);
+        if (canUseHardware && (mHardwareAnimating || mHasPendingHardwareExit)) {
+            hasContent = drawHardware((HardwareCanvas) c, p);
         } else {
             hasContent = drawSoftware(c, p);
         }
@@ -169,24 +166,10 @@
         return (mCanUseHardware && mHardwareAnimating) || (mOuterOpacity > 0 && mOuterRadius > 0);
     }
 
-    private boolean drawHardware(HardwareCanvas c) {
-        // If we have any pending hardware animations, cancel any running
-        // animations and start those now.
-        final ArrayList<RenderNodeAnimator> pendingAnimations = mPendingAnimations;
-        final int N = pendingAnimations.size();
-        if (N > 0) {
+    private boolean drawHardware(HardwareCanvas c, Paint p) {
+        if (mHasPendingHardwareExit) {
             cancelHardwareAnimations(false);
-
-            // We canceled old animations, but we're about to run new ones.
-            mHardwareAnimating = true;
-
-            for (int i = 0; i < N; i++) {
-                pendingAnimations.get(i).setTarget(c);
-                pendingAnimations.get(i).start();
-            }
-
-            mRunningAnimations.addAll(pendingAnimations);
-            pendingAnimations.clear();
+            startPendingHardwareExit(c, p);
         }
 
         c.drawCircle(mPropOuterX, mPropOuterY, mPropOuterRadius, mPropOuterPaint);
@@ -263,21 +246,32 @@
                 + inflectionDuration * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
 
         if (mCanUseHardware) {
-            exitHardware(opacityDuration, inflectionDuration, inflectionOpacity);
+            createPendingHardwareExit(opacityDuration, inflectionDuration, inflectionOpacity);
         } else {
             exitSoftware(opacityDuration, inflectionDuration, inflectionOpacity);
         }
     }
 
-    private void exitHardware(int opacityDuration, int inflectionDuration, int inflectionOpacity) {
-        mPendingAnimations.clear();
+    private void createPendingHardwareExit(
+            int opacityDuration, int inflectionDuration, int inflectionOpacity) {
+        mHasPendingHardwareExit = true;
+        mPendingOpacityDuration = opacityDuration;
+        mPendingInflectionDuration = inflectionDuration;
+        mPendingInflectionOpacity = inflectionOpacity;
 
-        final Paint outerPaint = getTempPaint();
-        outerPaint.setAntiAlias(true);
-        outerPaint.setXfermode(mXfermode);
-        outerPaint.setColor(mColor);
-        outerPaint.setAlpha((int) (Color.alpha(mColor) * mOuterOpacity + 0.5f));
-        outerPaint.setStyle(Style.FILL);
+        // The animation will start on the next draw().
+        invalidateSelf();
+    }
+
+    private void startPendingHardwareExit(HardwareCanvas c, Paint p) {
+        mHasPendingHardwareExit = false;
+
+        final int opacityDuration = mPendingOpacityDuration;
+        final int inflectionDuration = mPendingInflectionDuration;
+        final int inflectionOpacity = mPendingInflectionOpacity;
+
+        final Paint outerPaint = getTempPaint(p);
+        outerPaint.setAlpha((int) (outerPaint.getAlpha() * mOuterOpacity + 0.5f));
         mPropOuterPaint = CanvasProperty.createPaint(outerPaint);
         mPropOuterRadius = CanvasProperty.createFloat(mOuterRadius);
         mPropOuterX = CanvasProperty.createFloat(mOuterX);
@@ -301,8 +295,10 @@
                 outerFadeOutAnim.setStartDelay(inflectionDuration);
                 outerFadeOutAnim.setStartValue(inflectionOpacity);
                 outerFadeOutAnim.addListener(mAnimationListener);
+                outerFadeOutAnim.setTarget(c);
+                outerFadeOutAnim.start();
 
-                mPendingAnimations.add(outerFadeOutAnim);
+                mRunningAnimations.add(outerFadeOutAnim);
             } else {
                 outerOpacityAnim.addListener(mAnimationListener);
             }
@@ -314,14 +310,15 @@
             outerOpacityAnim.addListener(mAnimationListener);
         }
 
-        mPendingAnimations.add(outerOpacityAnim);
+        outerOpacityAnim.setTarget(c);
+        outerOpacityAnim.start();
+
+        mRunningAnimations.add(outerOpacityAnim);
 
         mHardwareAnimating = true;
 
         // Set up the software values to match the hardware end values.
         mOuterOpacity = 0;
-
-        invalidateSelf();
     }
 
     /**
@@ -340,10 +337,11 @@
         }
     }
 
-    private Paint getTempPaint() {
+    private Paint getTempPaint(Paint original) {
         if (mTempPaint == null) {
             mTempPaint = new Paint();
         }
+        mTempPaint.set(original);
         return mTempPaint;
     }
 
@@ -422,10 +420,7 @@
         }
         runningAnimations.clear();
 
-        if (cancelPending && !mPendingAnimations.isEmpty()) {
-            mPendingAnimations.clear();
-        }
-
+        mHasPendingHardwareExit = false;
         mHardwareAnimating = false;
     }
 
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index d5d5d51..13e3d54 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -27,15 +27,19 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Matrix;
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
+import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 
@@ -56,7 +60,7 @@
  * &ltripple android:color="#ffff0000">
  *   &ltitem android:id="@android:id/mask"
  *         android:drawable="@android:color/white" />
- * &ltripple /></code>
+ * &lt/ripple></code>
  * </pre>
  * <p>
  * If a mask layer is set, the ripple effect will be masked against that layer
@@ -65,15 +69,15 @@
  * If no mask layer is set, the ripple effect is masked against the composite
  * of the child layers.
  * <pre>
- * <code>&lt!-- A blue ripple drawn atop a black rectangle. --/>
+ * <code>&lt!-- A green ripple drawn atop a black rectangle. --/>
  * &ltripple android:color="#ff00ff00">
  *   &ltitem android:drawable="@android:color/black" />
- * &ltripple />
+ * &lt/ripple>
  *
- * &lt!-- A red ripple drawn atop a drawable resource. --/>
- * &ltripple android:color="#ff00ff00">
+ * &lt!-- A blue ripple drawn atop a drawable resource. --/>
+ * &ltripple android:color="#ff0000ff">
  *   &ltitem android:drawable="@drawable/my_drawable" />
- * &ltripple /></code>
+ * &lt/ripple></code>
  * </pre>
  * <p>
  * If no child layers or mask is specified and the ripple is set as a View
@@ -81,16 +85,17 @@
  * background within the View's hierarchy. In this case, the drawing region
  * may extend outside of the Drawable bounds.
  * <pre>
- * <code>&lt!-- An unbounded green ripple. --/>
- * &ltripple android:color="#ff0000ff" /></code>
+ * <code>&lt!-- An unbounded red ripple. --/>
+ * &ltripple android:color="#ffff0000" /></code>
  * </pre>
  *
  * @attr ref android.R.styleable#RippleDrawable_color
  */
 public class RippleDrawable extends LayerDrawable {
-    private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
-    private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
-    private static final PorterDuffXfermode SRC_OVER = new PorterDuffXfermode(Mode.SRC_OVER);
+    private static final int MASK_UNKNOWN = -1;
+    private static final int MASK_NONE = 0;
+    private static final int MASK_CONTENT = 1;
+    private static final int MASK_EXPLICIT = 2;
 
     /**
      * Constant for automatically determining the maximum ripple radius.
@@ -123,6 +128,13 @@
     /** The current background. May be actively animating or pending entry. */
     private RippleBackground mBackground;
 
+    private Bitmap mMaskBuffer;
+    private BitmapShader mMaskShader;
+    private Canvas mMaskCanvas;
+    private Matrix mMaskMatrix;
+    private PorterDuffColorFilter mMaskColorFilter;
+    private boolean mHasValidMask;
+
     /** Whether we expect to draw a background when visible. */
     private boolean mBackgroundActive;
 
@@ -147,9 +159,6 @@
     /** Paint used to control appearance of ripples. */
     private Paint mRipplePaint;
 
-    /** Paint used to control reveal layer masking. */
-    private Paint mMaskingPaint;
-
     /** Target density of the display into which ripples are drawn. */
     private float mDensity = 1.0f;
 
@@ -615,37 +624,116 @@
      */
     @Override
     public void draw(@NonNull Canvas canvas) {
-        final boolean hasMask = mMask != null;
-        final boolean hasRipples = mRipple != null || mExitingRipplesCount > 0
-                || (mBackground != null && mBackground.shouldDraw());
-
         // Clip to the dirty bounds, which will be the drawable bounds if we
         // have a mask or content and the ripple bounds if we're projecting.
         final Rect bounds = getDirtyBounds();
         final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
         canvas.clipRect(bounds);
 
-        // If we have content, draw it first. If we have ripples and no mask,
-        // we'll draw it into a SRC_OVER layer so that we can mask ripples
-        // against it using SRC_IN.
-        final boolean hasContentLayer = drawContent(canvas, bounds, hasRipples, hasMask);
+        drawContent(canvas);
+        drawBackgroundAndRipples(canvas);
 
-        // Next, try to draw the ripples. If we have a non-opaque mask, we'll
-        // draw the ripples into a SRC_OVER layer, draw the mask into a DST_IN
-        // layer, and blend.
-        if (hasRipples) {
-            final boolean hasNonOpaqueMask = hasMask && mMask.getOpacity() != PixelFormat.OPAQUE;
-            final boolean hasRippleLayer = drawBackgroundAndRipples(canvas, bounds,
-                    hasNonOpaqueMask, hasContentLayer);
+        canvas.restoreToCount(saveCount);
+    }
 
-            // If drawing ripples created a layer, we have a non-opaque mask
-            // that needs to be blended on top of the ripples with DST_IN.
-            if (hasRippleLayer) {
-                drawMaskingLayer(canvas, bounds, DST_IN);
+    @Override
+    public void invalidateSelf() {
+        super.invalidateSelf();
+
+        // Force the mask to update on the next draw().
+        mHasValidMask = false;
+    }
+
+    /**
+     * @return whether we need to use a mask
+     */
+    private void updateMaskShaderIfNeeded() {
+        if (mHasValidMask) {
+            return;
+        }
+
+        final int maskType = getMaskType();
+        if (maskType == MASK_UNKNOWN) {
+            return;
+        }
+
+        mHasValidMask = true;
+
+        if (maskType == MASK_NONE) {
+            if (mMaskBuffer != null) {
+                mMaskBuffer.recycle();
+                mMaskBuffer = null;
+                mMaskShader = null;
+                mMaskCanvas = null;
+            }
+            mMaskMatrix = null;
+            mMaskColorFilter = null;
+            return;
+        }
+
+        // Ensure we have a correctly-sized buffer.
+        final Rect bounds = getBounds();
+        if (mMaskBuffer == null
+                || mMaskBuffer.getWidth() != bounds.width()
+                || mMaskBuffer.getHeight() != bounds.height()) {
+            if (mMaskBuffer != null) {
+                mMaskBuffer.recycle();
+            }
+
+            mMaskBuffer = Bitmap.createBitmap(
+                    bounds.width(), bounds.height(), Bitmap.Config.ALPHA_8);
+            mMaskShader = new BitmapShader(mMaskBuffer,
+                    Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+            mMaskCanvas = new Canvas(mMaskBuffer);
+        } else {
+            mMaskBuffer.eraseColor(Color.TRANSPARENT);
+        }
+
+        if (mMaskMatrix == null) {
+            mMaskMatrix = new Matrix();
+        } else {
+            mMaskMatrix.reset();
+        }
+
+        if (mMaskColorFilter == null) {
+            mMaskColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_IN);
+        }
+
+        // Draw the appropriate mask.
+        if (maskType == MASK_EXPLICIT) {
+            drawMask(mMaskCanvas);
+        } else if (maskType == MASK_CONTENT) {
+            drawContent(mMaskCanvas);
+        }
+    }
+
+    private int getMaskType() {
+        if (mRipple == null && mExitingRipplesCount <= 0
+                && (mBackground == null || !mBackground.shouldDraw())) {
+            // We might need a mask later.
+            return MASK_UNKNOWN;
+        }
+
+        if (mMask != null) {
+            if (mMask.getOpacity() == PixelFormat.OPAQUE) {
+                // Clipping handles opaque explicit masks.
+                return MASK_NONE;
+            } else {
+                return MASK_EXPLICIT;
             }
         }
 
-        canvas.restoreToCount(saveCount);
+        // Check for non-opaque, non-mask content.
+        final ChildDrawable[] array = mLayerState.mChildren;
+        final int count = mLayerState.mNum;
+        for (int i = 0; i < count; i++) {
+            if (array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
+                return MASK_CONTENT;
+            }
+        }
+
+        // Clipping handles opaque content.
+        return MASK_NONE;
     }
 
     /**
@@ -678,65 +766,65 @@
         return -1;
     }
 
-    private boolean drawContent(Canvas canvas, Rect bounds, boolean hasRipples, boolean hasMask) {
+    private void drawContent(Canvas canvas) {
+        // Draw everything except the mask.
         final ChildDrawable[] array = mLayerState.mChildren;
         final int count = mLayerState.mNum;
-
-        boolean needsLayer = false;
-
-        if (hasRipples && !hasMask) {
-            // If we only have opaque content, we don't really need a layer
-            // because the ripples will be clipped to the drawable bounds.
-            for (int i = 0; i < count; i++) {
-                if (array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
-                    needsLayer = true;
-                    break;
-                }
-            }
-        }
-
-        if (needsLayer) {
-            canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
-                    getMaskingPaint(SRC_OVER));
-        }
-
-        // Draw everything except the mask.
         for (int i = 0; i < count; i++) {
             if (array[i].mId != R.id.mask) {
                 array[i].mDrawable.draw(canvas);
             }
         }
-
-        return needsLayer;
     }
 
-    private boolean drawBackgroundAndRipples(
-            Canvas canvas, Rect bounds, boolean hasNonOpaqueMask, boolean hasContentLayer) {
-        if (hasNonOpaqueMask) {
-            final Paint p = getMaskingPaint(SRC_OVER);
-            canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, p);
+    private void drawBackgroundAndRipples(Canvas canvas) {
+        final Ripple active = mRipple;
+        final RippleBackground background = mBackground;
+        final int count = mExitingRipplesCount;
+        if (active == null && count <= 0 && (background == null || !background.shouldDraw())) {
+            // Move along, nothing to draw here.
+            return;
         }
 
-        final PorterDuffXfermode mode = hasContentLayer ? SRC_ATOP : SRC_OVER;
         final float x = mHotspotBounds.exactCenterX();
         final float y = mHotspotBounds.exactCenterY();
         canvas.translate(x, y);
 
-        final Paint p = getRipplePaint();
-        p.setXfermode(mode);
+        updateMaskShaderIfNeeded();
+
+        // Position the shader to account for canvas translation.
+        if (mMaskShader != null) {
+            mMaskMatrix.setTranslate(-x, -y);
+            mMaskShader.setLocalMatrix(mMaskMatrix);
+        }
 
         // Grab the color for the current state and cut the alpha channel in
         // half so that the ripple and background together yield full alpha.
         final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
-        final int alpha = (Color.alpha(color) / 2) << 24;
-        p.setColor(color & 0xFFFFFF | alpha);
+        final int halfAlpha = (Color.alpha(color) / 2) << 24;
+        final Paint p = getRipplePaint();
 
-        final RippleBackground background = mBackground;
+        if (mMaskColorFilter != null) {
+            // The ripple timing depends on the paint's alpha value, so we need
+            // to push just the alpha channel into the paint and let the filter
+            // handle the full-alpha color.
+            final int fullAlphaColor = color | (0xFF << 24);
+            mMaskColorFilter.setColor(fullAlphaColor);
+
+            p.setColor(halfAlpha);
+            p.setColorFilter(mMaskColorFilter);
+            p.setShader(mMaskShader);
+        } else {
+            final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha;
+            p.setColor(halfAlphaColor);
+            p.setColorFilter(null);
+            p.setShader(null);
+        }
+
         if (background != null && background.shouldDraw()) {
             background.draw(canvas, p);
         }
 
-        final int count = mExitingRipplesCount;
         if (count > 0) {
             final Ripple[] ripples = mExitingRipples;
             for (int i = 0; i < count; i++) {
@@ -744,27 +832,15 @@
             }
         }
 
-        final Ripple active = mRipple;
         if (active != null) {
             active.draw(canvas, p);
         }
 
         canvas.translate(-x, -y);
-
-        // Returns true if a layer was created.
-        return hasNonOpaqueMask;
     }
 
-    private int drawMaskingLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
-        final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
-                bounds.right, bounds.bottom, getMaskingPaint(mode));
-
-        // Ensure that DST_IN blends using the entire layer.
-        canvas.drawColor(Color.TRANSPARENT);
-
+    private void drawMask(Canvas canvas) {
         mMask.draw(canvas);
-
-        return restoreToCount;
     }
 
     private Paint getRipplePaint() {
@@ -776,15 +852,6 @@
         return mRipplePaint;
     }
 
-    private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
-        if (mMaskingPaint == null) {
-            mMaskingPaint = new Paint();
-        }
-        mMaskingPaint.setXfermode(xfermode);
-        mMaskingPaint.setAlpha(0xFF);
-        return mMaskingPaint;
-    }
-
     @Override
     public Rect getDirtyBounds() {
         if (isProjected()) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 1029fcc..4513643 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -648,7 +648,7 @@
                 maxChannels = 48;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS)) {
                 bitRates = Range.create(32000, 500000);
-                sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000, 192000 };
+                sampleRateRange = Range.create(8000, 192000);
                 maxChannels = 255;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS)) {
                 bitRates = Range.create(6000, 510000);
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index da6f3fc..94c9690 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -42,7 +42,7 @@
     private static final String TAG = "TvInputSessionWrapper";
 
     private static final int MESSAGE_HANDLING_DURATION_THRESHOLD_MILLIS = 50;
-    private static final int MESSAGE_TUNE_DURATION_THRESHOLD_MILLIS = 1000;
+    private static final int MESSAGE_TUNE_DURATION_THRESHOLD_MILLIS = 2000;
 
     private static final int DO_RELEASE = 1;
     private static final int DO_SET_MAIN = 2;
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 779d486..26aedbd 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -323,24 +323,22 @@
      * <p>
      * This should be called as soon as possible during the service's startup.
      * It may only be called once.
-     *
-     * @return The media session token, must not be null.
      */
     public void setSessionToken(final MediaSession.Token token) {
         if (token == null) {
             throw new IllegalArgumentException("Session token may not be null.");
         }
+        if (mSession != null) {
+            throw new IllegalStateException("The session token has already been set.");
+        }
+        mSession = token;
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (mSession != null) {
-                    throw new IllegalStateException("The session token has already been set.");
-                }
-                mSession = token;
                 for (IBinder key : mConnections.keySet()) {
                     ConnectionRecord connection = mConnections.get(key);
                     try {
-                        connection.callbacks.onConnect(connection.root.getRootId(), mSession,
+                        connection.callbacks.onConnect(connection.root.getRootId(), token,
                                 connection.root.getExtras());
                     } catch (RemoteException e) {
                         Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
diff --git a/packages/SystemUI/res/layout/lland.xml b/packages/SystemUI/res/layout/lland.xml
index 053225d..71a16c9 100644
--- a/packages/SystemUI/res/layout/lland.xml
+++ b/packages/SystemUI/res/layout/lland.xml
@@ -31,10 +31,10 @@
             android:textSize="32sp"
             android:textColor="#FFAAAAAA"
             android:layout_marginTop="32dp"
-            android:layout_marginLeft="16dp"
-            android:layout_gravity="top|left"
-            android:paddingLeft="16dp"
-            android:paddingRight="16dp"
+            android:layout_marginStart="16dp"
+            android:layout_gravity="top|start"
+            android:paddingStart="16dp"
+            android:paddingEnd="16dp"
             android:paddingTop="8dp"
             android:paddingBottom="8dp"
             android:background="@drawable/scorecard"
diff --git a/packages/SystemUI/src/com/android/systemui/egg/LLand.java b/packages/SystemUI/src/com/android/systemui/egg/LLand.java
index 5de09a3..fa257b1 100644
--- a/packages/SystemUI/src/com/android/systemui/egg/LLand.java
+++ b/packages/SystemUI/src/com/android/systemui/egg/LLand.java
@@ -178,6 +178,9 @@
         setFocusable(true);
         PARAMS = new Params(getResources());
         mTimeOfDay = irand(0, SKIES.length);
+
+        // we assume everything will be laid out left|top
+        setLayoutDirection(LAYOUT_DIRECTION_LTR);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 206fc43..7ac0daf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -72,6 +72,7 @@
 import com.android.systemui.statusbar.phone.StatusBarWindowManager;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
 
@@ -1217,7 +1218,12 @@
         synchronized (this) {
             if (mBootCompleted) {
                 final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser());
-                mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, currentUser);
+                final UserManager um = (UserManager) mContext.getSystemService(
+                        Context.USER_SERVICE);
+                List <UserInfo> userHandles = um.getProfiles(currentUser.getIdentifier());
+                for (UserInfo ui : userHandles) {
+                    mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, ui.getUserHandle());
+                }
             } else {
                 mBootSendUserPresent = true;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 57ac4b0..2b6ac26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -683,15 +683,12 @@
             Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
                     n, thisUserId, notificationUserId));
         }
-        synchronized (mCurrentProfiles) {
-            return notificationUserId == UserHandle.USER_ALL
-                    || mCurrentProfiles.get(notificationUserId) != null;
-        }
+        return isCurrentProfile(notificationUserId);
     }
 
     protected boolean isCurrentProfile(int userId) {
         synchronized (mCurrentProfiles) {
-            return mCurrentProfiles.get(userId) != null;
+            return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
         }
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2781890..89aebe8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3314,8 +3314,6 @@
         public int mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
         public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
 
-        public AccessibilityEvent mShowingFocusedWindowEvent;
-
         private boolean mTouchInteractionInProgress;
 
         private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) {
@@ -3324,19 +3322,6 @@
                 // All events that are for changes in a global window
                 // state should *always* be dispatched.
                 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
-                    if (mWindowsForAccessibilityCallback != null) {
-                        // OK, this is fun. Sometimes the focused window is notified
-                        // it has focus before being shown. Historically this event
-                        // means that the window is focused and can be introspected.
-                        // But we still have not gotten the window state from the
-                        // window manager, so delay the notification until then.
-                        AccessibilityWindowInfo window = findWindowById(event.getWindowId());
-                        if (window == null) {
-                            mShowingFocusedWindowEvent = AccessibilityEvent.obtain(event);
-                            return false;
-                        }
-                    }
-                // $fall-through$
                 case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
                 case AccessibilityEvent.TYPE_ANNOUNCEMENT:
                 // All events generated by the user touching the
@@ -3428,18 +3413,6 @@
             }
 
             notifyWindowsChanged();
-
-            // If we are delaying a window state change event as the window
-            // source was showing when it was fired, now is the time to send.
-            if (mShowingFocusedWindowEvent != null) {
-                final int windowId = mShowingFocusedWindowEvent.getWindowId();
-                AccessibilityWindowInfo window = findWindowById(windowId);
-                if (window != null) {
-                    // Sending does the recycle.
-                    sendAccessibilityEvent(mShowingFocusedWindowEvent, mCurrentUserId);
-                }
-                mShowingFocusedWindowEvent = null;
-            }
         }
 
         public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index ebdd386..3117a17 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -18,11 +18,14 @@
 
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothCallback;
+import android.bluetooth.IBluetoothHeadset;
 import android.bluetooth.IBluetoothManager;
 import android.bluetooth.IBluetoothManagerCallback;
+import android.bluetooth.IBluetoothProfileServiceConnection;
 import android.bluetooth.IBluetoothStateChangeCallback;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -32,6 +35,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -42,12 +46,18 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.Vector;
+
 class BluetoothManagerService extends IBluetoothManager.Stub {
     private static final String TAG = "BluetoothManagerService";
     private static final boolean DBG = true;
@@ -67,6 +77,8 @@
     private static final int ERROR_RESTART_TIME_MS = 3000;
     //Maximum msec to delay MESSAGE_USER_SWITCHED
     private static final int USER_SWITCHED_TIME_MS = 200;
+    // Delay for the addProxy function in msec
+    private static final int ADD_PROXY_DELAY_MS = 100;
 
     private static final int MESSAGE_ENABLE = 1;
     private static final int MESSAGE_DISABLE = 2;
@@ -83,6 +95,8 @@
     private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
     private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
     private static final int MESSAGE_USER_SWITCHED = 300;
+    private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
+    private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
     private static final int MAX_SAVE_RETRIES=3;
     private static final int MAX_ERROR_RESTART_RETRIES=6;
 
@@ -127,6 +141,11 @@
     private int mErrorRecoveryRetryCounter;
     private final int mSystemUiUid;
 
+    // Save a ProfileServiceConnections object for each of the bound
+    // bluetooth profile services
+    private final Map <Integer, ProfileServiceConnections> mProfileServices =
+            new HashMap <Integer, ProfileServiceConnections>();
+
     private void registerForAirplaneMode(IntentFilter filter) {
         final ContentResolver resolver = mContext.getContentResolver();
         final String airplaneModeRadios = Settings.Global.getString(resolver,
@@ -499,6 +518,187 @@
         return mBluetoothGatt;
     }
 
+    @Override
+    public boolean bindBluetoothProfileService(int bluetoothProfile,
+            IBluetoothProfileServiceConnection proxy) {
+        if (!mEnable) {
+            if (DBG) {
+                Log.d(TAG, "Trying to bind to profile: " + bluetoothProfile +
+                        ", while Bluetooth was disabled");
+            }
+            return false;
+        }
+        synchronized (mProfileServices) {
+            ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
+            if (psc == null) {
+                if (DBG) {
+                    Log.d(TAG, "Creating new ProfileServiceConnections object for"
+                            + " profile: " + bluetoothProfile);
+                }
+                Intent intent = null;
+                if (bluetoothProfile == BluetoothProfile.HEADSET) {
+                    intent = new Intent(IBluetoothHeadset.class.getName());
+                } else {
+                    return false;
+                }
+                psc = new ProfileServiceConnections(intent);
+                mProfileServices.put(new Integer(bluetoothProfile), psc);
+                psc.bindService();
+            }
+        }
+
+        // Introducing a delay to give the client app time to prepare
+        Message addProxyMsg = mHandler.obtainMessage(MESSAGE_ADD_PROXY_DELAYED);
+        addProxyMsg.arg1 = bluetoothProfile;
+        addProxyMsg.obj = proxy;
+        mHandler.sendMessageDelayed(addProxyMsg, ADD_PROXY_DELAY_MS);
+        return true;
+    }
+
+    @Override
+    public void unbindBluetoothProfileService(int bluetoothProfile,
+            IBluetoothProfileServiceConnection proxy) {
+        synchronized (mProfileServices) {
+            ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
+            if (psc == null) {
+                return;
+            }
+            psc.removeProxy(proxy);
+        }
+    }
+
+    private void unbindAllBluetoothProfileServices() {
+        synchronized (mProfileServices) {
+            for (Integer i : mProfileServices.keySet()) {
+                ProfileServiceConnections psc = mProfileServices.get(i);
+                mContext.unbindService(psc);
+                psc.removeAllProxies();
+            }
+            mProfileServices.clear();
+        }
+    }
+
+    /**
+     * This class manages the clients connected to a given ProfileService
+     * and maintains the connection with that service.
+     */
+    final private class ProfileServiceConnections implements ServiceConnection,
+            IBinder.DeathRecipient {
+        final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies =
+                new RemoteCallbackList <IBluetoothProfileServiceConnection>();
+        IBinder mService;
+        ComponentName mClassName;
+        Intent mIntent;
+
+        ProfileServiceConnections(Intent intent) {
+            mService = null;
+            mClassName = null;
+            mIntent = intent;
+        }
+
+        private void bindService() {
+            if (mIntent != null && mService == null) {
+                if (!doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) {
+                    Log.w(TAG, "Unable to bind with intent: " + mIntent
+                            + ". Triggering retry.");
+                }
+                Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
+                msg.obj = this;
+                mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
+            }
+        }
+
+        private void addProxy(IBluetoothProfileServiceConnection proxy) {
+            mProxies.register(proxy);
+            if (mService != null) {
+                try{
+                    proxy.onServiceConnected(mClassName, mService);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to connect to proxy", e);
+                }
+            } else {
+                if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) {
+                    Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
+                    msg.obj = this;
+                    mHandler.sendMessage(msg);
+                }
+            }
+        }
+
+        private void removeProxy(IBluetoothProfileServiceConnection proxy) {
+            if (proxy != null) {
+                if (mProxies.unregister(proxy)) {
+                    try {
+                        proxy.onServiceDisconnected(mClassName);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Unable to disconnect proxy", e);
+                    }
+                }
+            } else {
+                Log.w(TAG, "Trying to remove a null proxy");
+            }
+        }
+
+        private void removeAllProxies() {
+            onServiceDisconnected(mClassName);
+            mProxies.kill();
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            // remove timeout message
+            mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE, this);
+            mService = service;
+            mClassName = className;
+            try {
+                mService.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to linkToDeath", e);
+            }
+            int n = mProxies.beginBroadcast();
+            for (int i = 0; i < n; i++) {
+                try {
+                    mProxies.getBroadcastItem(i).onServiceConnected(className, service);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to connect to proxy", e);
+                }
+            }
+            mProxies.finishBroadcast();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            if (mService == null) {
+                return;
+            }
+            mService.unlinkToDeath(this, 0);
+            mService = null;
+            mClassName = null;
+            int n = mProxies.beginBroadcast();
+            for (int i = 0; i < n; i++) {
+                try {
+                    mProxies.getBroadcastItem(i).onServiceDisconnected(className);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to disconnect from proxy", e);
+                }
+            }
+            mProxies.finishBroadcast();
+        }
+
+        @Override
+        public void binderDied() {
+            if (DBG) {
+                Log.w(TAG, "Profile service for profile: " + mClassName
+                        + " died.");
+            }
+            onServiceDisconnected(mClassName);
+            // Trigger rebind
+            Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
+            msg.obj = this;
+            mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
+        }
+    }
+
     private void sendBluetoothStateCallback(boolean isUp) {
         int n = mStateChangeCallbacks.beginBroadcast();
         if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
@@ -803,6 +1003,28 @@
                     }
                     break;
                 }
+                case MESSAGE_ADD_PROXY_DELAYED:
+                {
+                    ProfileServiceConnections psc = mProfileServices.get(
+                            new Integer(msg.arg1));
+                    if (psc == null) {
+                        break;
+                    }
+                    IBluetoothProfileServiceConnection proxy =
+                            (IBluetoothProfileServiceConnection) msg.obj;
+                    psc.addProxy(proxy);
+                    break;
+                }
+                case MESSAGE_BIND_PROFILE_SERVICE:
+                {
+                    ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj;
+                    removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj);
+                    if (psc == null) {
+                        break;
+                    }
+                    psc.bindService();
+                    break;
+                }
                 case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
                 {
                     if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
@@ -1005,6 +1227,7 @@
                             bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
                         }
 
+                        unbindAllBluetoothProfileServices();
                         // disable
                         handleDisable();
                         // Pbap service need receive STATE_TURNING_OFF intent to close
@@ -1129,16 +1352,21 @@
         int callingUser = UserHandle.getCallingUserId();
         int callingUid = Binder.getCallingUid();
         long callingIdentity = Binder.clearCallingIdentity();
+        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        UserInfo ui = um.getProfileParent(callingUser);
+        int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL;
         int callingAppId = UserHandle.getAppId(callingUid);
         boolean valid = false;
         try {
             foregroundUser = ActivityManager.getCurrentUser();
             valid = (callingUser == foregroundUser) ||
+                    parentUser == foregroundUser    ||
                     callingAppId == Process.NFC_UID ||
                     callingAppId == mSystemUiUid;
             if (DBG) {
                 Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
                     + " callingUser=" + callingUser
+                    + " parentUser=" + parentUser
                     + " foregroundUser=" + foregroundUser);
             }
         } finally {
@@ -1165,6 +1393,7 @@
                 } else {
                     //If Bluetooth is off, send service down event to proxy objects, and unbind
                     if (!isUp && canUnbindBluetoothService()) {
+                        unbindAllBluetoothProfileServices();
                         sendBluetoothServiceDownCallback();
                         unbindAndFinish();
                     }
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index bc851a3..e0b2307 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -18,12 +18,14 @@
 
 import java.util.Calendar;
 
+import android.app.ActivityManagerNative;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.RemoteException;
 import android.util.Slog;
 
 public class MountServiceIdler extends JobService {
@@ -53,6 +55,13 @@
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        // First have the activity manager do its idle maintenance.  (Yes this job
+        // is really more than just mount, some day it should be renamed to be system
+        // idleer).
+        try {
+            ActivityManagerNative.getDefault().performIdleMaintenance();
+        } catch (RemoteException e) {
+        }
         // The mount service will run an fstrim operation asynchronously
         // on a designated separate thread, so we provide it with a callback
         // that lets us cleanly end our idle timeslice.  It's safe to call
@@ -98,7 +107,7 @@
     private static Calendar tomorrowMidnight() {
         Calendar calendar = Calendar.getInstance();
         calendar.setTimeInMillis(System.currentTimeMillis());
-        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.HOUR_OF_DAY, 3);
         calendar.set(Calendar.MINUTE, 0);
         calendar.set(Calendar.SECOND, 0);
         calendar.set(Calendar.MILLISECOND, 0);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8cf1020..221a0a6 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9293,9 +9293,9 @@
                             "Attempt to launch content provider before system ready");
                 }
 
-                // Make sure that the user who owns this provider is started.  If not,
+                // Make sure that the user who owns this provider is running.  If not,
                 // we don't want to allow it to run.
-                if (mStartedUsers.get(userId) == null) {
+                if (!isUserRunningLocked(userId, false)) {
                     Slog.w(TAG, "Unable to launch app "
                             + cpi.applicationInfo.packageName + "/"
                             + cpi.applicationInfo.uid + " for provider "
@@ -12900,7 +12900,7 @@
                     + PowerManagerInternal.wakefulnessToString(mWakefulness));
             pw.println("  mSleeping=" + mSleeping + " mLockScreenShown="
                     + lockScreenShownToString());
-            pw.print("  mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
+            pw.println("  mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
         }
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
@@ -15660,10 +15660,10 @@
         userId = handleIncomingUser(callingPid, callingUid, userId,
                 true, ALLOW_NON_FULL, "broadcast", callerPackage);
 
-        // Make sure that the user who is receiving this broadcast is started.
+        // Make sure that the user who is receiving this broadcast is running.
         // If not, we will just skip it.
 
-        if (userId != UserHandle.USER_ALL && mStartedUsers.get(userId) == null) {
+        if (userId != UserHandle.USER_ALL && !isUserRunningLocked(userId, false)) {
             if (callingUid != Process.SYSTEM_UID || (intent.getFlags()
                     & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
                 Slog.w(TAG, "Skipping broadcast of " + intent
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index da404c4..97a6e85 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -38,6 +38,7 @@
  *   <li>Gather "OSD (display) name" of all acknowledge devices
  *   <li>Gather "Vendor id" of all acknowledge devices
  * </ol>
+ * We attempt to get OSD name/vendor ID up to 5 times in case the communication fails.
  */
 final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
     private static final String TAG = "DeviceDiscoveryAction";
@@ -87,6 +88,7 @@
     private final ArrayList<DeviceInfo> mDevices = new ArrayList<>();
     private final DeviceDiscoveryCallback mCallback;
     private int mProcessedDeviceCount = 0;
+    private int mTimeoutRetry = 0;
 
     /**
      * Constructor.
@@ -309,6 +311,7 @@
 
     private void increaseProcessedDeviceCount() {
         mProcessedDeviceCount++;
+        mTimeoutRetry = 0;
     }
 
     private void removeDevice(int index) {
@@ -353,19 +356,23 @@
                     return;
             }
         } else {
-            int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
-            switch (mState) {
-                case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
-                    queryPhysicalAddress(address);
-                    return;
-                case STATE_WAITING_FOR_OSD_NAME:
-                    queryOsdName(address);
-                    return;
-                case STATE_WAITING_FOR_VENDOR_ID:
-                    queryVendorId(address);
-                default:
-                    return;
-            }
+            sendQueryCommand();
+        }
+    }
+
+    private void sendQueryCommand() {
+        int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
+        switch (mState) {
+            case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
+                queryPhysicalAddress(address);
+                return;
+            case STATE_WAITING_FOR_OSD_NAME:
+                queryOsdName(address);
+                return;
+            case STATE_WAITING_FOR_VENDOR_ID:
+                queryVendorId(address);
+            default:
+                return;
         }
     }
 
@@ -375,6 +382,11 @@
             return;
         }
 
+        if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
+            sendQueryCommand();
+            return;
+        }
+        mTimeoutRetry = 0;
         Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount);
         removeDevice(mProcessedDeviceCount);
         checkAndProceedStage();
diff --git a/services/core/java/com/android/server/hdmi/HdmiConfig.java b/services/core/java/com/android/server/hdmi/HdmiConfig.java
index 046f393..a787c12 100644
--- a/services/core/java/com/android/server/hdmi/HdmiConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiConfig.java
@@ -39,6 +39,10 @@
     // Number of retries for polling each device in address allocation mechanism.
     static final int ADDRESS_ALLOCATION_RETRY = 3;
 
+    // Number of retries for sendCommand in actions related to new device discovery.
+    // Number 5 comes from 10 seconds for Chromecast preparation time.
+    static final int TIMEOUT_RETRY = 5;
+
     // CEC spec said that it should try retransmission at least once.
     // The actual number of send request for a single command will be at most
     // RETRANSMISSION_COUNT + 1. Note that it affects only to normal commands
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 64f0703..3d64cc5 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -51,6 +51,7 @@
 
     private int mVendorId;
     private String mDisplayName;
+    private int mTimeoutRetry;
 
     /**
      * Constructor.
@@ -71,15 +72,22 @@
 
     @Override
     public boolean start() {
+        requestOsdName(true);
+        return true;
+    }
+
+    private void requestOsdName(boolean firstTry) {
+        if (firstTry) {
+            mTimeoutRetry = 0;
+        }
         mState = STATE_WAITING_FOR_SET_OSD_NAME;
         if (mayProcessCommandIfCached(mDeviceLogicalAddress, Constants.MESSAGE_SET_OSD_NAME)) {
-            return true;
+            return;
         }
 
         sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(),
                 mDeviceLogicalAddress));
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
-        return true;
     }
 
     @Override
@@ -103,12 +111,12 @@
                 } catch (UnsupportedEncodingException e) {
                     Slog.e(TAG, "Failed to get OSD name: " + e.getMessage());
                 }
-                requestVendorId();
+                requestVendorId(true);
                 return true;
             } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) {
                 int requestOpcode = params[0] & 0xFF;
                 if (requestOpcode == Constants.MESSAGE_GIVE_OSD_NAME) {
-                    requestVendorId();
+                    requestVendorId(true);
                     return true;
                 }
             }
@@ -138,7 +146,10 @@
         return false;
     }
 
-    private void requestVendorId() {
+    private void requestVendorId(boolean firstTry) {
+        if (firstTry) {
+            mTimeoutRetry = 0;
+        }
         // At first, transit to waiting status for <Device Vendor Id>.
         mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
         // If the message is already in cache, process it.
@@ -176,9 +187,17 @@
             return;
         }
         if (state == STATE_WAITING_FOR_SET_OSD_NAME) {
+            if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
+                requestOsdName(false);
+                return;
+            }
             // Osd name request timed out. Try vendor id
-            requestVendorId();
+            requestVendorId(true);
         } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
+            if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
+                requestVendorId(false);
+                return;
+            }
             // vendor id timed out. Go ahead creating the device info what we've got so far.
             addDeviceInfo();
             finish();
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5ab3fa1..5375bfc 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -461,7 +461,7 @@
         UserState userState = getUserStateLocked(userId);
         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (sessionState == null) {
-            throw new IllegalArgumentException("Session state not found for token " + sessionToken);
+            throw new SessionNotFoundException("Session state not found for token " + sessionToken);
         }
         // Only the application that requested this session or the system can access it.
         if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
@@ -589,18 +589,22 @@
     }
 
     private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
-        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
-        if (sessionState.session != null) {
-            UserState userState = getUserStateLocked(userId);
-            if (sessionToken == userState.mainSessionToken) {
-                setMainLocked(sessionToken, false, callingUid, userId);
-            }
-            try {
+        SessionState sessionState = null;
+        try {
+            sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+            if (sessionState.session != null) {
+                UserState userState = getUserStateLocked(userId);
+                if (sessionToken == userState.mainSessionToken) {
+                    setMainLocked(sessionToken, false, callingUid, userId);
+                }
                 sessionState.session.release();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "session process has already died", e);
             }
-            sessionState.session = null;
+        } catch (RemoteException | SessionNotFoundException e) {
+            Slog.e(TAG, "error in releaseSession", e);
+        } finally {
+            if (sessionState != null) {
+                sessionState.session = null;
+            }
         }
         removeSessionStateLocked(sessionToken, userId);
     }
@@ -648,19 +652,19 @@
     }
 
     private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
-        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
-        if (sessionState.hardwareSessionToken != null) {
-            sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
-                    Process.SYSTEM_UID, userId);
-        }
-        ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId);
-        if (!serviceState.isHardware) {
-            return;
-        }
-        ITvInputSession session = getSessionLocked(sessionState);
         try {
+            SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+            if (sessionState.hardwareSessionToken != null) {
+                sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
+                        Process.SYSTEM_UID, userId);
+            }
+            ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId);
+            if (!serviceState.isHardware) {
+                return;
+            }
+            ITvInputSession session = getSessionLocked(sessionState);
             session.setMain(isMain);
-        } catch (RemoteException e) {
+        } catch (RemoteException | SessionNotFoundException e) {
             Slog.e(TAG, "error in setMain", e);
         }
     }
@@ -1085,7 +1089,7 @@
                             getSessionLocked(sessionState.hardwareSessionToken,
                                     Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
                         }
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in setSurface", e);
                     }
                 }
@@ -1116,7 +1120,7 @@
                             getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
                                     resolvedUserId).dispatchSurfaceChanged(format, width, height);
                         }
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in dispatchSurfaceChanged", e);
                     }
                 }
@@ -1146,7 +1150,7 @@
                                     Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
                                             ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
                         }
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in setVolume", e);
                     }
                 }
@@ -1183,7 +1187,7 @@
                         args.arg5 = sessionToken;
                         mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args)
                                 .sendToTarget();
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in tune", e);
                         return;
                     }
@@ -1205,7 +1209,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .requestUnblockContent(unblockedRating);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in requestUnblockContent", e);
                     }
                 }
@@ -1225,7 +1229,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .setCaptionEnabled(enabled);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in setCaptionEnabled", e);
                     }
                 }
@@ -1245,7 +1249,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
                                 type, trackId);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in selectTrack", e);
                     }
                 }
@@ -1266,7 +1270,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .appPrivateCommand(command, data);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in appPrivateCommand", e);
                     }
                 }
@@ -1287,7 +1291,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .createOverlayView(windowToken, frame);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in createOverlayView", e);
                     }
                 }
@@ -1307,7 +1311,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .relayoutOverlayView(frame);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in relayoutOverlayView", e);
                     }
                 }
@@ -1327,7 +1331,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .removeOverlayView();
-                    } catch (RemoteException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in removeOverlayView", e);
                     }
                 }
@@ -2340,4 +2344,13 @@
             }
         }
     }
+
+    private static class SessionNotFoundException extends IllegalArgumentException {
+        public SessionNotFoundException() {
+        }
+
+        public SessionNotFoundException(String name) {
+            super(name);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 0cbf03a..08754f9 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -159,11 +159,15 @@
         }
     }
 
-    public void onWindowFocusChangedLocked() {
+    public void onWindowFocusChangedNotLocked() {
         // Not relevant for the display magnifier.
 
-        if (mWindowsForAccessibilityObserver != null) {
-            mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
+        WindowsForAccessibilityObserver observer = null;
+        synchronized (mWindowManagerService) {
+            observer = mWindowsForAccessibilityObserver;
+        }
+        if (observer != null) {
+            observer.performComputeChangedWindowsNotLocked();
         }
     }
 
@@ -937,14 +941,13 @@
             computeChangedWindows();
         }
 
+        public void performComputeChangedWindowsNotLocked() {
+            mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
+            computeChangedWindows();
+        }
+
         public void scheduleComputeChangedWindowsLocked() {
-            // If focus changed, compute changed windows immediately as the focused window
-            // is used by the accessibility manager service to determine the active window.
-            if (mWindowManagerService.mCurrentFocus != null
-                    && mWindowManagerService.mCurrentFocus != mWindowManagerService.mLastFocus) {
-                mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
-                computeChangedWindows();
-            } else if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
+            if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
                 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
                         mRecurringAccessibilityEventsIntervalMillis);
             }
@@ -955,6 +958,9 @@
                 Slog.i(LOG_TAG, "computeChangedWindows()");
             }
 
+            boolean windowsChanged = false;
+            List<WindowInfo> windows = new ArrayList<WindowInfo>();
+
             synchronized (mWindowManagerService.mWindowMap) {
                 // Do not send the windows if there is no current focus as
                 // the window manager is still looking for where to put it.
@@ -975,8 +981,6 @@
                 SparseArray<WindowState> visibleWindows = mTempWindowStates;
                 populateVisibleWindowsOnScreenLocked(visibleWindows);
 
-                List<WindowInfo> windows = new ArrayList<WindowInfo>();
-
                 Set<IBinder> addedWindows = mTempBinderSet;
                 addedWindows.clear();
 
@@ -1074,7 +1078,6 @@
                 addedWindows.clear();
 
                 // We computed the windows and if they changed notify the client.
-                boolean windowsChanged = false;
                 if (mOldWindows.size() != windows.size()) {
                     // Different size means something changed.
                     windowsChanged = true;
@@ -1096,22 +1099,24 @@
                 }
 
                 if (windowsChanged) {
-                    if (DEBUG) {
-                        Log.i(LOG_TAG, "Windows changed:" + windows);
-                    }
-                    // Remember the old windows to detect changes.
                     cacheWindows(windows);
-                    // Announce the change.
-                    mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
-                            windows).sendToTarget();
-                } else {
-                    if (DEBUG) {
-                        Log.i(LOG_TAG, "No windows changed.");
-                    }
-                    // Recycle the nodes as we do not need them.
-                    clearAndRecycleWindows(windows);
                 }
             }
+
+            // Now we do not hold the lock, so send the windows over.
+            if (windowsChanged) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Windows changed:" + windows);
+                }
+                mCallback.onWindowsForAccessibilityChanged(windows);
+            } else {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "No windows changed.");
+                }
+            }
+
+            // Recycle the windows as we do not need them.
+            clearAndRecycleWindows(windows);
         }
 
         private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
@@ -1217,7 +1222,7 @@
             return false;
         }
 
-        private void clearAndRecycleWindows(List<WindowInfo> windows) {
+        private static void clearAndRecycleWindows(List<WindowInfo> windows) {
             final int windowCount = windows.size();
             for (int i = windowCount - 1; i >= 0; i--) {
                 windows.remove(i).recycle();
@@ -1254,7 +1259,6 @@
 
         private class MyHandler extends Handler {
             public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
-            public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 2;
 
             public MyHandler(Looper looper) {
                 super(looper, null, false);
@@ -1267,12 +1271,6 @@
                     case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
                         computeChangedWindows();
                     } break;
-
-                    case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
-                        List<WindowInfo> windows = (List<WindowInfo>) message.obj;
-                        mCallback.onWindowsForAccessibilityChanged(windows);
-                        clearAndRecycleWindows(windows);
-                    } break;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0327cb3..27ac32a 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -242,6 +242,7 @@
         final WindowStateAnimator universeBackground = mService.mAnimator.mUniverseBackground;
         final int aboveUniverseLayer = mService.mAnimator.mAboveUniverseLayer;
         boolean addedUniverse = false;
+        boolean disableWallpaperTouchEvents = false;
 
         // If there's a drag in flight, provide a pseudowindow to catch drag input
         final boolean inDrag = (mService.mDragState != null);
@@ -282,8 +283,14 @@
 
                 final boolean hasFocus = (child == mInputFocus);
                 final boolean isVisible = child.isVisibleLw();
+                if ((privateFlags
+                        & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS)
+                            != 0) {
+                    disableWallpaperTouchEvents = true;
+                }
                 final boolean hasWallpaper = (child == mService.mWallpaperTarget)
-                        && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0;
+                        && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0
+                        && !disableWallpaperTouchEvents;
                 final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
 
                 // If there's a drag in progress and 'child' is a potential drop target,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b7857e1..2750941 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7610,7 +7610,15 @@
                     WindowState lastFocus;
                     WindowState newFocus;
 
+                    AccessibilityController accessibilityController = null;
+
                     synchronized(mWindowMap) {
+                        // TODO(multidisplay): Accessibility supported only of default desiplay.
+                        if (mAccessibilityController != null && getDefaultDisplayContentLocked()
+                                .getDisplayId() == Display.DEFAULT_DISPLAY) {
+                            accessibilityController = mAccessibilityController;
+                        }
+
                         lastFocus = mLastFocus;
                         newFocus = mCurrentFocus;
                         if (lastFocus == newFocus) {
@@ -7628,6 +7636,12 @@
                         }
                     }
 
+                    // First notify the accessibility manager for the change so it has
+                    // the windows before the newly focused one starts firing eventgs.
+                    if (accessibilityController != null) {
+                        accessibilityController.onWindowFocusChangedNotLocked();
+                    }
+
                     //System.out.println("Changing focus from " + lastFocus
                     //                   + " to " + newFocus);
                     if (newFocus != null) {
@@ -10402,12 +10416,6 @@
             mCurrentFocus = newFocus;
             mLosingFocus.remove(newFocus);
 
-            // TODO(multidisplay): Accessibilty supported only of default desiplay.
-            if (mAccessibilityController != null
-                    && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                mAccessibilityController.onWindowFocusChangedLocked();
-            }
-
             int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
 
             if (imWindowChanged && oldFocus != mInputMethodWindow) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2201d2b..72a3337 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -120,7 +120,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
@@ -265,6 +264,8 @@
                 = new HashMap<ComponentName, ActiveAdmin>();
         final ArrayList<ActiveAdmin> mAdminList
                 = new ArrayList<ActiveAdmin>();
+        final ArrayList<ComponentName> mRemovingAdmins
+                = new ArrayList<ComponentName>();
 
         // This is the list of component allowed to start lock task mode.
         final List<String> mLockTaskPackages = new ArrayList<String>();
@@ -303,8 +304,6 @@
             if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 removeUserData(userHandle);
             } else if (Intent.ACTION_USER_STARTED.equals(action)
-                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)
-                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
                     || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
 
                 if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -313,8 +312,14 @@
                         mUserData.remove(userHandle);
                     }
                 }
-
-                handlePackagesChanged(userHandle);
+                handlePackagesChanged(null /* check all admins */, userHandle);
+            } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
+                    || (Intent.ACTION_PACKAGE_ADDED.equals(action)
+                            && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) {
+                handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
+                    && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
             }
         }
     };
@@ -899,7 +904,7 @@
         }
     }
 
-    private void handlePackagesChanged(int userHandle) {
+    private void handlePackagesChanged(String packageName, int userHandle) {
         boolean removed = false;
         if (DBG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
         DevicePolicyData policy = getUserData(userHandle);
@@ -908,11 +913,17 @@
             for (int i = policy.mAdminList.size() - 1; i >= 0; i--) {
                 ActiveAdmin aa = policy.mAdminList.get(i);
                 try {
-                    if (pm.getPackageInfo(aa.info.getPackageName(), 0, userHandle) == null
-                            || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle) == null) {
-                        removed = true;
-                        policy.mAdminList.remove(i);
-                        policy.mAdminMap.remove(aa.info.getComponent());
+                    // If we're checking all packages or if the specific one we're checking matches,
+                    // then check if the package and receiver still exist.
+                    final String adminPackage = aa.info.getPackageName();
+                    if (packageName == null || packageName.equals(adminPackage)) {
+                        if (pm.getPackageInfo(adminPackage, 0, userHandle) == null
+                                || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle)
+                                    == null) {
+                            removed = true;
+                            policy.mAdminList.remove(i);
+                            policy.mAdminMap.remove(aa.info.getComponent());
+                        }
                     }
                 } catch (RemoteException re) {
                     // Shouldn't happen
@@ -1202,6 +1213,9 @@
     void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) {
         final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
         if (admin != null) {
+            synchronized (this) {
+                getUserData(userHandle).mRemovingAdmins.add(adminReceiver);
+            }
             sendAdminCommandLocked(admin,
                     DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED,
                     new BroadcastReceiver() {
@@ -1221,9 +1235,10 @@
                                 }
                                 saveSettingsLocked(userHandle);
                                 updateMaximumTimeToLockLocked(policy);
+                                policy.mRemovingAdmins.remove(adminReceiver);
                             }
                         }
-            });
+                    });
         }
     }
 
@@ -1788,6 +1803,18 @@
         }
     }
 
+    @Override
+    public boolean isRemovingAdmin(ComponentName adminReceiver, int userHandle) {
+        if (!mHasFeature) {
+            return false;
+        }
+        enforceCrossUserPermission(userHandle);
+        synchronized (this) {
+            DevicePolicyData policyData = getUserData(userHandle);
+            return policyData.mRemovingAdmins.contains(adminReceiver);
+        }
+    }
+
     public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId, int userHandle) {
         if (!mHasFeature) {
             return false;
@@ -4091,6 +4118,10 @@
                         ap.dump("    ", pw);
                     }
                 }
+                if (!policy.mRemovingAdmins.isEmpty()) {
+                    p.println("  Removing Device Admins (User " + policy.mUserHandle + "): "
+                            + policy.mRemovingAdmins);
+                }
 
                 pw.println(" ");
                 pw.print("  mPasswordOwner="); pw.println(policy.mPasswordOwner);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 2a3d7ab8..6621726 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -732,6 +732,26 @@
     }
 
     /**
+     * Return whether a given phone account has a voicemail number configured.
+     *
+     * @param accountHandle The handle for the account to check for a voicemail number.
+     * @return {@code true} If the given phone account has a voicemail number.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean hasVoiceMailNumber(PhoneAccountHandle accountHandle) {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().hasVoiceMailNumber(accountHandle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling isInCall().", e);
+        }
+        return false;
+    }
+
+    /**
      * Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding
      * states).
      * <p>
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index cbd9d69..f8d7539 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -121,6 +121,11 @@
     boolean isVoiceMailNumber(in PhoneAccountHandle accountHandle, String number);
 
     /**
+     * @see TelecomServiceImpl#hasVoiceMailNumber
+     */
+    boolean hasVoiceMailNumber(in PhoneAccountHandle accountHandle);
+
+    /**
      * @see TelecomServiceImpl#getDefaultPhoneApp
      */
     ComponentName getDefaultPhoneApp();
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index 513f622..33d40ad 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -57,6 +57,8 @@
         </service>
         <receiver android:name="UserTarget">
         </receiver>
+        <service android:name="IsolatedService" android:isolatedProcess="true">
+        </service>
         <receiver android:name="StartEmpty" android:exported="true">
             <intent-filter>
                 <action android:name="com.example.START_EMPTY" />
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index b065b88c..4281c68 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -62,6 +62,8 @@
 
     ArrayList<ServiceConnection> mConnections = new ArrayList<ServiceConnection>();
 
+    ServiceConnection mIsolatedConnection;
+
     static final int MSG_SPAM = 1;
 
     final Handler mHandler = new Handler() {
@@ -207,6 +209,34 @@
                 return true;
             }
         });
+        menu.add("Rebind Isolated!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+            @Override public boolean onMenuItemClick(MenuItem item) {
+                Intent intent = new Intent(ActivityTestMain.this, IsolatedService.class);
+                ServiceConnection conn = new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        Log.i(TAG, "Isolated service connected " + name + " " + service);
+                    }
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        Log.i(TAG, "Isolated service disconnected " + name);
+                    }
+                };
+                if (mIsolatedConnection != null) {
+                    Log.i(TAG, "Unbinding existing service: " + mIsolatedConnection);
+                    unbindService(mIsolatedConnection);
+                    mIsolatedConnection = null;
+                }
+                Log.i(TAG, "Binding new service: " + conn);
+                if (bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
+                    mIsolatedConnection = conn;
+                } else {
+                    Toast.makeText(ActivityTestMain.this, "Failed to bind",
+                            Toast.LENGTH_LONG).show();
+                }
+                return true;
+            }
+        });
         menu.add("Send!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override public boolean onMenuItemClick(MenuItem item) {
                 Intent intent = new Intent(ActivityTestMain.this, SingleUserReceiver.class);
@@ -417,6 +447,10 @@
             unbindService(conn);
         }
         mConnections.clear();
+        if (mIsolatedConnection != null) {
+            unbindService(mIsolatedConnection);
+            mIsolatedConnection = null;
+        }
     }
 
     @Override
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/IsolatedService.java b/tests/ActivityTests/src/com/google/android/test/activity/IsolatedService.java
new file mode 100644
index 0000000..f3bf42e
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/IsolatedService.java
@@ -0,0 +1,44 @@
+/*
+ * 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.google.android.test.activity;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Log;
+
+public class IsolatedService extends Service {
+    Binder mBinder = new Binder();
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i("IsolatedService", "Service created in pid " + android.os.Process.myPid());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i("IsolatedService", "Service destroyed in pid " + android.os.Process.myPid());
+    }
+}