Merge "sql statement with syntax errors can leave database lock in bad state"
diff --git a/api/current.xml b/api/current.xml
index ed51bbf..a9abb141 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -93977,6 +93977,8 @@
 >
 <parameter name="uid" type="int">
 </parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
 </method>
 <method name="onDisable"
  return="void"
@@ -94106,6 +94108,8 @@
 >
 <parameter name="uid" type="int">
 </parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
 </method>
 <method name="onRequiresCell"
  return="boolean"
@@ -94167,6 +94171,8 @@
 >
 <parameter name="minTime" type="long">
 </parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
 </method>
 <method name="onSupportsAltitude"
  return="boolean"
@@ -111503,6 +111509,19 @@
 <parameter name="refCounted" type="boolean">
 </parameter>
 </method>
+<method name="setWorkSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
+</method>
 </class>
 </package>
 <package name="android.opengl"
@@ -138355,6 +138374,19 @@
 <parameter name="value" type="boolean">
 </parameter>
 </method>
+<method name="setWorkSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
+</method>
 </class>
 <class name="Process"
  extends="java.lang.Object"
@@ -139503,6 +139535,134 @@
 </parameter>
 </method>
 </class>
+<class name="WorkSource"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="WorkSource"
+ type="android.os.WorkSource"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="WorkSource"
+ type="android.os.WorkSource"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="orig" type="android.os.WorkSource">
+</parameter>
+</constructor>
+<method name="add"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="clear"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="diff"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="remove"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="set"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 </package>
 <package name="android.os.storage"
 >
@@ -142069,6 +142229,19 @@
 <parameter name="args" type="android.os.Bundle">
 </parameter>
 </method>
+<method name="switchToHeader"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="android.preference.PreferenceActivity.Header">
+</parameter>
+</method>
 <field name="EXTRA_NO_HEADERS"
  type="java.lang.String"
  transient="false"
@@ -142102,15 +142275,28 @@
  visibility="public"
 >
 </field>
+<field name="HEADER_ID_UNDEFINED"
+ type="long"
+ transient="false"
+ volatile="false"
+ value="-1L"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="PreferenceActivity.Header"
  extends="java.lang.Object"
  abstract="false"
  static="true"
- final="false"
+ final="true"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.os.Parcelable">
+</implements>
 <constructor name="PreferenceActivity.Header"
  type="android.preference.PreferenceActivity.Header"
  static="false"
@@ -142119,6 +142305,65 @@
  visibility="public"
 >
 </constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="readFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="in" type="android.os.Parcel">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="extras"
+ type="android.os.Bundle"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="fragment"
  type="java.lang.String"
  transient="false"
@@ -142139,8 +142384,8 @@
  visibility="public"
 >
 </field>
-<field name="icon"
- type="android.graphics.drawable.Drawable"
+<field name="iconRes"
+ type="int"
  transient="false"
  volatile="false"
  static="false"
@@ -142149,8 +142394,8 @@
  visibility="public"
 >
 </field>
-<field name="iconRes"
- type="int"
+<field name="id"
+ type="long"
  transient="false"
  volatile="false"
  static="false"
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index d3ec3d9..e1d6619 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -64,7 +64,7 @@
                             = IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
                     try {
                         IBinder lock = new Binder();
-                        pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power");
+                        pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power", null);
                         pm.setStayOnSetting(val);
                         pm.releaseWakeLock(lock, 0);
                     }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 97aa23e..2e8d682 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4204,6 +4204,7 @@
     }
     
     final void performStart() {
+        mFragments.mStateSaved = false;
         mCalled = false;
         mFragments.execPendingActions();
         mInstrumentation.callActivityOnStart(this);
@@ -4221,6 +4222,8 @@
     }
     
     final void performRestart() {
+        mFragments.mStateSaved = false;
+
         synchronized (mManagedCursors) {
             final int N = mManagedCursors.size();
             for (int i=0; i<N; i++) {
@@ -4359,6 +4362,7 @@
         if (Config.LOGV) Log.v(
             TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
             + ", resCode=" + resultCode + ", data=" + data);
+        mFragments.mStateSaved = false;
         if (who == null) {
             onActivityResult(requestCode, resultCode, data);
         } else {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 03bcadc..16a8c57 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -26,8 +26,10 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
+import android.util.Pair;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -863,6 +865,37 @@
         return socket;
     }
 
+    /**
+     * Read the local Out of Band Pairing Data
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @return Pair<byte[], byte[]> of Hash and Randomizer
+     *
+     * @hide
+     */
+    public Pair<byte[], byte[]> readOutOfBandData() {
+        if (getState() != STATE_ON) return null;
+        try {
+            byte[] hash = new byte[16];
+            byte[] randomizer = new byte[16];
+
+            byte[] ret = mService.readOutOfBandData();
+
+            if (ret  == null || ret.length != 32) return null;
+
+            hash = Arrays.copyOfRange(ret, 0, 16);
+            randomizer = Arrays.copyOfRange(ret, 16, 32);
+
+            if (DBG) {
+                Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) +
+                  ":" + Arrays.toString(randomizer));
+            }
+            return new Pair<byte[], byte[]>(hash, randomizer);
+
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return null;
+    }
+
     private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
         Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
         for (int i = 0; i < addresses.length; i++) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index e77e76f..e577ec4 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -325,7 +325,9 @@
     /** The user will be prompted to enter the passkey displayed on remote device
      * @hide */
     public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
-
+    /** The user will be prompted to accept or deny the OOB pairing request
+     * @hide */
+    public static final int PAIRING_VARIANT_OOB_CONSENT = 5;
     /**
      * Used as an extra field in {@link #ACTION_UUID} intents,
      * Contains the {@link android.os.ParcelUuid}s of the remote device which
@@ -464,6 +466,52 @@
     }
 
     /**
+     * Start the bonding (pairing) process with the remote device using the
+     * Out Of Band mechanism.
+     *
+     * <p>This is an asynchronous call, it will return immediately. Register
+     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
+     * the bonding process completes, and its result.
+     *
+     * <p>Android system services will handle the necessary user interactions
+     * to confirm and complete the bonding process.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+     *
+     * @param hash - Simple Secure pairing hash
+     * @param randomizer - The random key obtained using OOB
+     * @return false on immediate error, true if bonding will begin
+     *
+     * @hide
+     */
+    public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) {
+        try {
+            return sService.createBondOutOfBand(mAddress, hash, randomizer);
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return false;
+    }
+
+    /**
+     * Set the Out Of Band data for a remote device to be used later
+     * in the pairing mechanism. Users can obtain this data through other
+     * trusted channels
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+     *
+     * @param hash Simple Secure pairing hash
+     * @param randomizer The random key obtained using OOB
+     * @return false on error; true otherwise
+     *
+     * @hide
+     */
+    public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) {
+      try {
+        return sService.setDeviceOutOfBandData(mAddress, hash, randomizer);
+      } catch (RemoteException e) {Log.e(TAG, "", e);}
+      return false;
+    }
+
+    /**
      * Cancel an in-progress bonding request started with {@link #createBond}.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
      *
@@ -617,6 +665,14 @@
     }
 
     /** @hide */
+    public boolean setRemoteOutOfBandData() {
+        try {
+          return sService.setRemoteOutOfBandData(mAddress);
+      } catch (RemoteException e) {Log.e(TAG, "", e);}
+      return false;
+    }
+
+    /** @hide */
     public boolean cancelPairingUserInput() {
         try {
             return sService.cancelPairingUserInput(mAddress);
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index c4a40cd..cc23146 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -45,12 +45,15 @@
     boolean startDiscovery();
     boolean cancelDiscovery();
     boolean isDiscovering();
+    byte[] readOutOfBandData();
 
     boolean createBond(in String address);
+    boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer);
     boolean cancelBondProcess(in String address);
     boolean removeBond(in String address);
     String[] listBonds();
     int getBondState(in String address);
+    boolean setDeviceOutOfBandData(in String address, in byte[] hash, in byte[] randomizer);
 
     String getRemoteName(in String address);
     int getRemoteClass(in String address);
@@ -61,6 +64,7 @@
     boolean setPin(in String address, in byte[] pin);
     boolean setPasskey(in String address, int passkey);
     boolean setPairingConfirmation(in String address, boolean confirm);
+    boolean setRemoteOutOfBandData(in String addres);
     boolean cancelPairingUserInput(in String address);
 
     boolean setTrust(in String address, in boolean value);
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 7f749bb..950d339 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -47,6 +47,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.format.DateUtils;
 import android.text.format.Time;
@@ -129,8 +130,8 @@
 
     private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
 
-    private static final String SYNC_WAKE_LOCK_PREFIX = "SyncWakeLock";
-    private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
+    private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
+    private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
 
     private Context mContext;
 
@@ -1735,6 +1736,7 @@
             PowerManager.WakeLock oldWakeLock = mSyncWakeLock;
             try {
                 mSyncWakeLock = getSyncWakeLock(op.account.type, op.authority);
+				mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterInfo.uid));
                 mSyncWakeLock.acquire();
             } finally {
                 if (oldWakeLock != null && oldWakeLock != mSyncWakeLock) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f5b1e57..f182a7a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -404,6 +404,7 @@
         
         public static final byte CMD_UPDATE = 0;
         public static final byte CMD_START = 1;
+        public static final byte CMD_OVERFLOW = 2;
         
         public byte cmd;
         
@@ -1703,6 +1704,8 @@
                 pw.print(" ");
                 if (rec.cmd == HistoryItem.CMD_START) {
                     pw.println(" START");
+                } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
+                    pw.println(" *OVERFLOW*");
                 } else {
                     if (rec.batteryLevel < 10) pw.print("00");
                     else if (rec.batteryLevel < 100) pw.print("0");
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 01cc408..0067e940 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -17,10 +17,13 @@
 
 package android.os;
 
+import android.os.WorkSource;
+
 /** @hide */
 interface IPowerManager
 {
-    void acquireWakeLock(int flags, IBinder lock, String tag);
+    void acquireWakeLock(int flags, IBinder lock, String tag, in WorkSource ws);
+    void updateWakeLockWorkSource(IBinder lock, in WorkSource ws);
     void goToSleep(long time);
     void goToSleepWithReason(long time, int reason);
     void releaseWakeLock(IBinder lock, int flags);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index f4ca8bc..3876a3e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -209,6 +209,7 @@
         int mCount = 0;
         boolean mRefCounted = true;
         boolean mHeld = false;
+        WorkSource mWorkSource;
 
         WakeLock(int flags, String tag)
         {
@@ -247,7 +248,7 @@
             synchronized (mToken) {
                 if (!mRefCounted || mCount++ == 0) {
                     try {
-                        mService.acquireWakeLock(mFlags, mToken, mTag);
+                        mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);
                     } catch (RemoteException e) {
                     }
                     mHeld = true;
@@ -313,6 +314,32 @@
             }
         }
 
+        public void setWorkSource(WorkSource ws) {
+            synchronized (mToken) {
+                if (ws != null && ws.size() == 0) {
+                    ws = null;
+                }
+                boolean changed = true;
+                if (ws == null) {
+                    mWorkSource = null;
+                } else if (mWorkSource == null) {
+                    changed = mWorkSource != null;
+                    mWorkSource = new WorkSource(ws);
+                } else {
+                    changed = mWorkSource.diff(ws);
+                    if (changed) {
+                        mWorkSource.set(ws);
+                    }
+                }
+                if (changed && mHeld) {
+                    try {
+                        mService.updateWakeLockWorkSource(mToken, mWorkSource);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+        }
+
         public String toString() {
             synchronized (mToken) {
                 return "WakeLock{"
diff --git a/core/java/android/os/WorkSource.aidl b/core/java/android/os/WorkSource.aidl
new file mode 100644
index 0000000..1e7fabc
--- /dev/null
+++ b/core/java/android/os/WorkSource.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2010, 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.os;
+
+parcelable WorkSource;
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
new file mode 100644
index 0000000..bba1984
--- /dev/null
+++ b/core/java/android/os/WorkSource.java
@@ -0,0 +1,311 @@
+package android.os;
+
+/**
+ * Describes the source of some work that may be done by someone else.
+ * Currently the public representation of what a work source is is not
+ * defined; this is an opaque container.
+ */
+public class WorkSource implements Parcelable {
+    int mNum;
+    int[] mUids;
+
+    /**
+     * Internal statics to avoid object allocations in some operations.
+     * The WorkSource object itself is not thread safe, but we need to
+     * hold sTmpWorkSource lock while working with these statics.
+     */
+    static final WorkSource sTmpWorkSource = new WorkSource(0);
+    /**
+     * For returning newbie work from a modification operation.
+     */
+    static WorkSource sNewbWork;
+    /**
+     * For returning gone work form a modification operation.
+     */
+    static WorkSource sGoneWork;
+
+    /**
+     * Create an empty work source.
+     */
+    public WorkSource() {
+        mNum = 0;
+    }
+
+    /**
+     * Create a new WorkSource that is a copy of an existing one.
+     * If <var>orig</var> is null, an empty WorkSource is created.
+     */
+    public WorkSource(WorkSource orig) {
+        if (orig == null) {
+            mNum = 0;
+            return;
+        }
+        mNum = orig.mNum;
+        if (orig.mUids != null) {
+            mUids = orig.mUids.clone();
+        } else {
+            mUids = null;
+        }
+    }
+
+    /** @hide */
+    public WorkSource(int uid) {
+        mNum = 1;
+        mUids = new int[] { uid, 0 };
+    }
+
+    WorkSource(Parcel in) {
+        mNum = in.readInt();
+        mUids = in.createIntArray();
+    }
+
+    /** @hide */
+    public int size() {
+        return mNum;
+    }
+
+    /** @hide */
+    public int get(int index) {
+        return mUids[index];
+    }
+
+    /**
+     * Clear this WorkSource to be empty.
+     */
+    public void clear() {
+        mNum = 0;
+    }
+
+    /**
+     * Compare this WorkSource with another.
+     * @param other The WorkSource to compare against.
+     * @return If there is a difference, true is returned.
+     */
+    public boolean diff(WorkSource other) {
+        int N = mNum;
+        if (N != other.mNum) {
+            return true;
+        }
+        final int[] uids1 = mUids;
+        final int[] uids2 = other.mUids;
+        for (int i=0; i<N; i++) {
+            if (uids1[i] != uids2[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Replace the current contents of this work source with the given
+     * work source.  If <var>other</var> is null, the current work source
+     * will be made empty.
+     */
+    public void set(WorkSource other) {
+        if (other == null) {
+            mNum = 0;
+            return;
+        }
+        mNum = other.mNum;
+        if (other.mUids != null) {
+            if (mUids != null && mUids.length >= mNum) {
+                System.arraycopy(other.mUids, 0, mUids, 0, mNum);
+            } else {
+                mUids = other.mUids.clone();
+            }
+        } else {
+            mUids = null;
+        }
+    }
+
+    /** @hide */
+    public void set(int uid) {
+        mNum = 1;
+        if (mUids == null) mUids = new int[2];
+        mUids[0] = uid;
+    }
+
+    /** @hide */
+    public WorkSource[] setReturningDiffs(WorkSource other) {
+        synchronized (sTmpWorkSource) {
+            sNewbWork = null;
+            sGoneWork = null;
+            updateLocked(other, true, true);
+            if (sNewbWork != null || sGoneWork != null) {
+                WorkSource[] diffs = new WorkSource[2];
+                diffs[0] = sNewbWork;
+                diffs[1] = sGoneWork;
+                return diffs;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Merge the contents of <var>other</var> WorkSource in to this one.
+     *
+     * @param other The other WorkSource whose contents are to be merged.
+     * @return Returns true if any new sources were added.
+     */
+    public boolean add(WorkSource other) {
+        synchronized (sTmpWorkSource) {
+            return updateLocked(other, false, false);
+        }
+    }
+
+    /** @hide */
+    public WorkSource addReturningNewbs(WorkSource other) {
+        synchronized (sTmpWorkSource) {
+            sNewbWork = null;
+            updateLocked(other, false, true);
+            return sNewbWork;
+        }
+    }
+
+    /** @hide */
+    public boolean add(int uid) {
+        synchronized (sTmpWorkSource) {
+            sTmpWorkSource.mUids[0] = uid;
+            return updateLocked(sTmpWorkSource, false, false);
+        }
+    }
+
+    /** @hide */
+    public WorkSource addReturningNewbs(int uid) {
+        synchronized (sTmpWorkSource) {
+            sNewbWork = null;
+            sTmpWorkSource.mUids[0] = uid;
+            updateLocked(sTmpWorkSource, false, true);
+            return sNewbWork;
+        }
+    }
+
+    public boolean remove(WorkSource other) {
+        int N1 = mNum;
+        final int[] uids1 = mUids;
+        final int N2 = other.mNum;
+        final int[] uids2 = other.mUids;
+        boolean changed = false;
+        int i1 = 0;
+        for (int i2=0; i2<N2 && i1<N1; i2++) {
+            if (uids2[i2] == uids1[i1]) {
+                N1--;
+                if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1-1, N1-i1);
+            }
+            while (i1 < N1 && uids2[i2] > uids1[i1]) {
+                i1++;
+            }
+        }
+
+        mNum = N1;
+
+        return changed;
+    }
+
+    private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
+        int N1 = mNum;
+        int[] uids1 = mUids;
+        final int N2 = other.mNum;
+        final int[] uids2 = other.mUids;
+        boolean changed = false;
+        int i1 = 0;
+        for (int i2=0; i2<N2; i2++) {
+            if (i1 >= N1 || uids2[i2] < uids1[i1]) {
+                // Need to insert a new uid.
+                changed = true;
+                if (uids1 == null) {
+                    uids1 = new int[4];
+                    uids1[0] = uids2[i2];
+                } else if (i1 >= uids1.length) {
+                    int[] newuids = new int[(uids1.length*3)/2];
+                    if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
+                    if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
+                    uids1 = newuids;
+                    uids1[i1] = uids2[i2];
+                } else {
+                    if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
+                    uids1[i1] = uids2[i2];
+                }
+                if (returnNewbs) {
+                    if (sNewbWork == null) {
+                        sNewbWork = new WorkSource(uids2[i2]);
+                    } else {
+                        sNewbWork.addLocked(uids2[i2]);
+                    }
+                }
+                N1++;
+                i1++;
+            } else {
+                if (!set) {
+                    // Skip uids that already exist or are not in 'other'.
+                    do {
+                        i1++;
+                    } while (i1 < N1 && uids2[i2] >= uids1[i1]);
+                } else {
+                    // Remove any uids that don't exist in 'other'.
+                    int start = i1;
+                    while (i1 < N1 && uids2[i2] > uids1[i1]) {
+                        if (sGoneWork == null) {
+                            sGoneWork = new WorkSource(uids1[i1]);
+                        } else {
+                            sGoneWork.addLocked(uids1[i1]);
+                        }
+                        i1++;
+                    }
+                    if (start < i1) {
+                        System.arraycopy(uids1, i1, uids1, start, i1-start);
+                        N1 -= i1-start;
+                        i1 = start;
+                    }
+                    // If there is a matching uid, skip it.
+                    if (i1 < N1 && uids2[i1] == uids1[i1]) {
+                        i1++;
+                    }
+                }
+            }
+        }
+
+        mNum = N1;
+        mUids = uids1;
+
+        return changed;
+    }
+
+    private void addLocked(int uid) {
+        if (mUids == null) {
+            mUids = new int[4];
+            mUids[0] = uid;
+            mNum = 1;
+            return;
+        }
+        if (mNum >= mUids.length) {
+            int[] newuids = new int[(mNum*3)/2];
+            System.arraycopy(mUids, 0, newuids, 0, mNum);
+            mUids = newuids;
+        }
+
+        mUids[mNum] = uid;
+        mNum++;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mNum);
+        dest.writeIntArray(mUids);
+    }
+
+    public static final Parcelable.Creator<WorkSource> CREATOR
+            = new Parcelable.Creator<WorkSource>() {
+        public WorkSource createFromParcel(Parcel in) {
+            return new WorkSource(in);
+        }
+        public WorkSource[] newArray(int size) {
+            return new WorkSource[size];
+        }
+    };
+}
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 2efb09d..60d810e 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -29,10 +29,11 @@
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Xml;
@@ -40,6 +41,7 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.widget.AbsListView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.FrameLayout;
@@ -112,7 +114,11 @@
         PreferenceFragment.OnPreferenceStartFragmentCallback {
     private static final String TAG = "PreferenceActivity";
 
-    private static final String PREFERENCES_TAG = "android:preferences";
+    // Constants for state save/restore
+    private static final String HEADERS_TAG = ":android:headers";
+    private static final String CUR_HEADER_TAG = ":android:cur_header";
+    private static final String SINGLE_PANE_TAG = ":android:single_pane";
+    private static final String PREFERENCES_TAG = ":android:preferences";
 
     /**
      * When starting this activity, the invoking Intent can contain this extra
@@ -165,6 +171,8 @@
 
     private boolean mSinglePane;
 
+    private Header mCurHeader;
+
     // --- State for old mode when showing a single preference list
 
     private PreferenceManager mPreferenceManager;
@@ -186,17 +194,29 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_BIND_PREFERENCES:
+                case MSG_BIND_PREFERENCES: {
                     bindPreferences();
-                    break;
-                case MSG_BUILD_HEADERS:
+                } break;
+                case MSG_BUILD_HEADERS: {
+                    ArrayList<Header> oldHeaders = new ArrayList<Header>(mHeaders);
+                    mHeaders.clear();
                     onBuildHeaders(mHeaders);
                     mAdapter.notifyDataSetChanged();
                     Header header = onGetNewHeader();
                     if (header != null && header.fragment != null) {
-                        switchToHeader(header.fragment, header.fragmentArguments);
+                        Header mappedHeader = findBestMatchingHeader(header, oldHeaders);
+                        if (mappedHeader == null || mCurHeader != mappedHeader) {
+                            switchToHeader(header);
+                        }
+                    } else if (mCurHeader != null) {
+                        Header mappedHeader = findBestMatchingHeader(mCurHeader, mHeaders);
+                        if (mappedHeader != null) {
+                            setSelectedHeader(mappedHeader);
+                        } else {
+                            switchToHeader(null);
+                        }
                     }
-                    break;
+                } break;
             }
         }
     };
@@ -235,13 +255,7 @@
 
             // All view fields must be updated every time, because the view may be recycled 
             Header header = getItem(position);
-            if (header.icon == null) {
-                holder.icon.setImageDrawable(null);
-                holder.icon.setImageResource(header.iconRes);
-            } else {
-                holder.icon.setImageResource(0);
-                holder.icon.setImageDrawable(header.icon);
-            }
+            holder.icon.setImageResource(header.iconRes);
             holder.title.setText(header.title);
             if (TextUtils.isEmpty(header.summary)) {
                 holder.summary.setVisibility(View.GONE);
@@ -255,9 +269,24 @@
     }
 
     /**
+     * Default value for {@link Header#id Header.id} indicating that no
+     * identifier value is set.  All other values (including those below -1)
+     * are valid.
+     */
+    public static final long HEADER_ID_UNDEFINED = -1;
+    
+    /**
      * Description of a single Header item that the user can select.
      */
-    public static class Header {
+    public static final class Header implements Parcelable {
+        /**
+         * Identifier for this header, to correlate with a new list when
+         * it is updated.  The default value is
+         * {@link PreferenceActivity#HEADER_ID_UNDEFINED}, meaning no id.
+         * @attr ref android.R.styleable#PreferenceHeader_id
+         */
+        public long id = HEADER_ID_UNDEFINED;
+
         /**
          * Title of the header that is shown to the user.
          * @attr ref android.R.styleable#PreferenceHeader_title
@@ -277,12 +306,6 @@
         public int iconRes;
 
         /**
-         * Optional icon drawable to show for this header.  (If this is non-null,
-         * the iconRes will be ignored.)
-         */
-        public Drawable icon;
-
-        /**
          * Full class name of the fragment to display when this header is
          * selected.
          * @attr ref android.R.styleable#PreferenceHeader_fragment
@@ -299,6 +322,62 @@
          * Intent to launch when the preference is selected.
          */
         public Intent intent;
+
+        /**
+         * Optional additional data for use by subclasses of PreferenceActivity.
+         */
+        public Bundle extras;
+
+        public Header() {
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeLong(id);
+            TextUtils.writeToParcel(title, dest, flags);
+            TextUtils.writeToParcel(summary, dest, flags);
+            dest.writeInt(iconRes);
+            dest.writeString(fragment);
+            dest.writeBundle(fragmentArguments);
+            if (intent != null) {
+                dest.writeInt(1);
+                intent.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
+            dest.writeBundle(extras);
+        }
+
+        public void readFromParcel(Parcel in) {
+            id = in.readLong();
+            title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            iconRes = in.readInt();
+            fragment = in.readString();
+            fragmentArguments = in.readBundle();
+            if (in.readInt() != 0) {
+                intent = Intent.CREATOR.createFromParcel(in);
+            }
+            extras = in.readBundle();
+        }
+
+        Header(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public static final Creator<Header> CREATOR = new Creator<Header>() {
+            public Header createFromParcel(Parcel source) {
+                return new Header(source);
+            }
+            public Header[] newArray(int size) {
+                return new Header[size];
+            }
+        };
     }
 
     @Override
@@ -314,40 +393,66 @@
         String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
         Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
 
-        if (initialFragment != null && mSinglePane) {
-            // If we are just showing a fragment, we want to run in
-            // new fragment mode, but don't need to compute and show
-            // the headers.
-            getListView().setVisibility(View.GONE);
-            mPrefsContainer.setVisibility(View.VISIBLE);
-            switchToHeader(initialFragment, initialArguments);
+        if (savedInstanceState != null) {
+            // We are restarting from a previous saved state; used that to
+            // initialize, instead of starting fresh.
+            ArrayList<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG);
+            if (headers != null) {
+                mHeaders.addAll(headers);
+                int curHeader = savedInstanceState.getInt(CUR_HEADER_TAG,
+                        (int)HEADER_ID_UNDEFINED);
+                if (curHeader >= 0 && curHeader < mHeaders.size()) {
+                    setSelectedHeader(mHeaders.get(curHeader));
+                }
+            }
+            mSinglePane = savedInstanceState.getBoolean(SINGLE_PANE_TAG);
 
         } else {
-            // We need to try to build the headers.
-            onBuildHeaders(mHeaders);
+            if (initialFragment != null && mSinglePane) {
+                // If we are just showing a fragment, we want to run in
+                // new fragment mode, but don't need to compute and show
+                // the headers.
+                switchToHeader(initialFragment, initialArguments);
 
-            // If there are headers, then at this point we need to show
-            // them and, depending on the screen, we may also show in-line
-            // the currently selected preference fragment.
-            if (mHeaders.size() > 0) {
-                mAdapter = new HeaderAdapter(this, mHeaders);
-                setListAdapter(mAdapter);
-                if (!mSinglePane) {
-                    mPrefsContainer.setVisibility(View.VISIBLE);
-                    if (initialFragment == null) {
-                        Header h = onGetInitialHeader();
-                        initialFragment = h.fragment;
-                        initialArguments = h.fragmentArguments;
+            } else {
+                // We need to try to build the headers.
+                onBuildHeaders(mHeaders);
+
+                // If there are headers, then at this point we need to show
+                // them and, depending on the screen, we may also show in-line
+                // the currently selected preference fragment.
+                if (mHeaders.size() > 0) {
+                    if (!mSinglePane) {
+                        if (initialFragment == null) {
+                            Header h = onGetInitialHeader();
+                            switchToHeader(h);
+                        } else {
+                            switchToHeader(initialFragment, initialArguments);
+                        }
                     }
-                    switchToHeader(initialFragment, initialArguments);
                 }
+            }
+        }
 
+        // The default configuration is to only show the list view.  Adjust
+        // visibility for other configurations.
+        if (initialFragment != null && mSinglePane) {
+            // Single pane, showing just a prefs fragment.
+            getListView().setVisibility(View.GONE);
+            mPrefsContainer.setVisibility(View.VISIBLE);
+        } else if (mHeaders.size() > 0) {
+            mAdapter = new HeaderAdapter(this, mHeaders);
+            setListAdapter(mAdapter);
+            if (!mSinglePane) {
+                // Multi-pane.
+                getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+                mPrefsContainer.setVisibility(View.VISIBLE);
+            }
+        } else {
             // If there are no headers, we are in the old "just show a screen
             // of preferences" mode.
-            } else {
-                mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
-                mPreferenceManager.setOnPreferenceTreeClickListener(this);
-            }
+            mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
+            mPreferenceManager.setOnPreferenceTreeClickListener(this);
         }
 
         getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
@@ -534,6 +639,9 @@
 
                     TypedArray sa = getResources().obtainAttributes(attrs,
                             com.android.internal.R.styleable.PreferenceHeader);
+                    header.id = sa.getInt(
+                            com.android.internal.R.styleable.PreferenceHeader_id,
+                            (int)HEADER_ID_UNDEFINED);
                     header.title = sa.getText(
                             com.android.internal.R.styleable.PreferenceHeader_title);
                     header.summary = sa.getText(
@@ -621,6 +729,17 @@
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
+        if (mHeaders.size() > 0) {
+            outState.putParcelableArrayList(HEADERS_TAG, mHeaders);
+            if (mCurHeader != null) {
+                int index = mHeaders.indexOf(mCurHeader);
+                if (index >= 0) {
+                    outState.putInt(CUR_HEADER_TAG, index);
+                }
+            }
+        }
+        outState.putBoolean(SINGLE_PANE_TAG, mSinglePane);
+
         if (mPreferenceManager != null) {
             final PreferenceScreen preferenceScreen = getPreferenceScreen();
             if (preferenceScreen != null) {
@@ -680,7 +799,7 @@
     /**
      * Called when the user selects an item in the header list.  The default
      * implementation will call either {@link #startWithFragment(String, Bundle)}
-     * or {@link #switchToHeader(String, Bundle)} as appropriate.
+     * or {@link #switchToHeader(Header)} as appropriate.
      *
      * @param header The header that was selected.
      * @param position The header's position in the list.
@@ -690,7 +809,7 @@
             if (mSinglePane) {
                 startWithFragment(header.fragment, header.fragmentArguments);
             } else {
-                switchToHeader(header.fragment, header.fragmentArguments);
+                switchToHeader(header);
             }
         } else if (header.intent != null) {
             startActivity(header.intent);
@@ -715,6 +834,16 @@
         startActivity(intent);
     }
 
+    void setSelectedHeader(Header header) {
+        mCurHeader = header;
+        int index = mHeaders.indexOf(header);
+        if (index >= 0) {
+            getListView().setItemChecked(index, true);
+        } else {
+            getListView().clearChoices();
+        }
+    }
+
     /**
      * When in two-pane mode, switch the fragment pane to show the given
      * preference fragment.
@@ -723,6 +852,8 @@
      * @param args Optional arguments to supply to the fragment.
      */
     public void switchToHeader(String fragmentName, Bundle args) {
+        setSelectedHeader(null);
+
         getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
 
         Fragment f = Fragment.instantiate(this, fragmentName, args);
@@ -731,6 +862,63 @@
     }
 
     /**
+     * When in two-pane mode, switch to the fragment pane to show the given
+     * preference fragment.
+     *
+     * @param header The new header to display.
+     */
+    public void switchToHeader(Header header) {
+        switchToHeader(header.fragment, header.fragmentArguments);
+        mCurHeader = header;
+        setSelectedHeader(header);
+    }
+
+    Header findBestMatchingHeader(Header cur, ArrayList<Header> from) {
+        ArrayList<Header> matches = new ArrayList<Header>();
+        for (int j=0; j<from.size(); j++) {
+            Header oh = from.get(j);
+            if (cur == oh || (cur.id != HEADER_ID_UNDEFINED && cur.id == oh.id)) {
+                // Must be this one.
+                matches.clear();
+                matches.add(oh);
+                break;
+            }
+            if (cur.fragment != null) {
+                if (cur.fragment.equals(oh.fragment)) {
+                    matches.add(oh);
+                }
+            } else if (cur.intent != null) {
+                if (cur.intent.equals(oh.intent)) {
+                    matches.add(oh);
+                }
+            } else if (cur.title != null) {
+                if (cur.title.equals(oh.title)) {
+                    matches.add(oh);
+                }
+            }
+        }
+        final int NM = matches.size();
+        if (NM == 1) {
+            return matches.get(0);
+        } else if (NM > 1) {
+            for (int j=0; j<NM; j++) {
+                Header oh = matches.get(j);
+                if (cur.fragmentArguments != null &&
+                        cur.fragmentArguments.equals(oh.fragmentArguments)) {
+                    return oh;
+                }
+                if (cur.extras != null && cur.extras.equals(oh.extras)) {
+                    return oh;
+                }
+                if (cur.title != null && cur.title.equals(oh.title)) {
+                    return oh;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Start a new fragment.
      *
      * @param fragment The fragment to start
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index fde3769..7ba11bb 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -614,6 +614,17 @@
         mWakeLock.release();
     }
 
+    private void onRequestOobData(String objectPath , int nativeData) {
+        String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+        if (address == null) return;
+
+        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
+        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
+                BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT);
+        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+    }
+
     private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
         if (!mBluetoothService.isEnabled()) return false;
 
@@ -672,6 +683,19 @@
         return false;
     }
 
+    private boolean onAgentOutOfBandDataAvailable(String objectPath) {
+        if (!mBluetoothService.isEnabled()) return false;
+
+        String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+        if (address == null) return false;
+
+        if (mBluetoothService.getDeviceOutOfBandData(
+            mAdapter.getRemoteDevice(address)) != null) {
+            return true;
+        }
+        return false;
+    }
+
     private boolean isOtherSinkInNonDisconnectingState(String address) {
         BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
         Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 7252736..71f2477 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -55,6 +55,7 @@
 import android.os.SystemService;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.app.IBatteryStats;
 
@@ -144,6 +145,7 @@
     private BluetoothA2dpService mA2dpService;
     private final HashMap<BluetoothDevice, Integer> mInputDevices;
     private final HashMap<BluetoothDevice, Integer> mPanDevices;
+    private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
 
     private static String mDockAddress;
     private String mDockPin;
@@ -200,6 +202,7 @@
         mDeviceProperties = new HashMap<String, Map<String,String>>();
 
         mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
+        mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
         mUuidIntentTracker = new ArrayList<String>();
         mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
         mServiceRecordToPid = new HashMap<Integer, Integer>();
@@ -1155,7 +1158,7 @@
         mIsDiscovering = isDiscovering;
     }
 
-    public synchronized boolean createBond(String address) {
+    private boolean isBondingFeasible(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
         if (!isEnabledInternal()) return false;
@@ -1185,8 +1188,13 @@
                 return false;
             }
         }
+        return true;
+    }
 
-        if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
+    public synchronized boolean createBond(String address) {
+        if (!isBondingFeasible(address)) return false;
+
+        if (!createPairedDeviceNative(address, 60000  /*1 minute*/ )) {
             return false;
         }
 
@@ -1196,6 +1204,51 @@
         return true;
     }
 
+    public synchronized boolean createBondOutOfBand(String address, byte[] hash,
+                                                    byte[] randomizer) {
+        if (!isBondingFeasible(address)) return false;
+
+        if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
+            return false;
+        }
+
+        setDeviceOutOfBandData(address, hash, randomizer);
+        mBondState.setPendingOutgoingBonding(address);
+        mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
+
+        return true;
+    }
+
+    public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
+            byte[] randomizer) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        if (!isEnabledInternal()) return false;
+
+        Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
+
+        if (DBG) {
+            log("Setting out of band data for:" + address + ":" +
+              Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
+        }
+
+        mDeviceOobData.put(address, value);
+        return true;
+    }
+
+    Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
+        return mDeviceOobData.get(device.getAddress());
+    }
+
+
+    public synchronized byte[] readOutOfBandData() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
+                                                "Need BLUETOOTH permission");
+        if (!isEnabledInternal()) return null;
+
+        return readAdapterOutOfBandDataNative();
+    }
+
     public synchronized boolean cancelBondProcess(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
@@ -1954,6 +2007,32 @@
         return setPairingConfirmationNative(address, confirm, data.intValue());
     }
 
+    public synchronized boolean setRemoteOutOfBandData(String address) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        if (!isEnabledInternal()) return false;
+        address = address.toUpperCase();
+        Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+        if (data == null) {
+            Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
+                  "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
+                  " or by bluez.\n");
+            return false;
+        }
+
+        Pair<byte[], byte[]> val = mDeviceOobData.get(address);
+        byte[] hash, randomizer;
+        if (val == null) {
+            // TODO: check what should be passed in this case.
+            hash = new byte[16];
+            randomizer = new byte[16];
+        } else {
+            hash = val.first;
+            randomizer = val.second;
+        }
+        return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
+    }
+
     public synchronized boolean cancelPairingUserInput(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
@@ -2487,6 +2566,9 @@
     private native boolean stopDiscoveryNative();
 
     private native boolean createPairedDeviceNative(String address, int timeout_ms);
+    private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
+    private native byte[] readAdapterOutOfBandDataNative();
+
     private native boolean cancelDeviceCreationNative(String address);
     private native boolean removeDeviceNative(String objectPath);
     private native int getDeviceServiceChannelNative(String objectPath, String uuid,
@@ -2497,6 +2579,9 @@
     private native boolean setPasskeyNative(String address, int passkey, int nativeData);
     private native boolean setPairingConfirmationNative(String address, boolean confirm,
             int nativeData);
+    private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
+                                                        byte[] randomizer, int nativeData);
+
     private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
             int value);
     private native boolean createDeviceNative(String address);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3db05b6..012f3cc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -877,7 +877,7 @@
                 mSplitMotionTargets = new SplitMotionTargets();
             }
             return dispatchSplitTouchEvent(ev);
-	}
+        }
 
         final int action = ev.getAction();
         final float xf = ev.getX();
@@ -1222,13 +1222,7 @@
                     uniqueTargetCount--;
                 }
 
-                final boolean childHandled = target.dispatchTouchEvent(targetEvent);
-                handled |= childHandled;
-                if (!childHandled) {
-                    // Child doesn't want these events anymore, but we're still dispatching
-                    // other split events to children.
-                    targets.removeView(target);
-                }
+                handled |= target.dispatchTouchEvent(targetEvent);
             } finally {
                 targetEvent.recycle();
             }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1620778..bd87a0d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -18,6 +18,7 @@
 
 import com.android.internal.os.BatteryStatsImpl;
 
+import android.os.WorkSource;
 import android.telephony.SignalStrength;
 
 interface IBatteryStats {
@@ -33,6 +34,9 @@
        SensorService.cpp */
     void noteStopSensor(int uid, int sensor);
 
+    void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, int type);
+    void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type);
+
     void noteStartGps(int uid);
     void noteStopGps(int uid);
     void noteScreenOn();
@@ -57,6 +61,12 @@
     void noteScanWifiLockReleased(int uid);
     void noteWifiMulticastEnabled(int uid);
     void noteWifiMulticastDisabled(int uid);
+    void noteFullWifiLockAcquiredFromSource(in WorkSource ws);
+    void noteFullWifiLockReleasedFromSource(in WorkSource ws);
+    void noteScanWifiLockAcquiredFromSource(in WorkSource ws);
+    void noteScanWifiLockReleasedFromSource(in WorkSource ws);
+    void noteWifiMulticastEnabledFromSource(in WorkSource ws);
+    void noteWifiMulticastDisabledFromSource(in WorkSource ws);
     void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
     long getAwakeTimeBattery();
     long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b9f0c61..4943531 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -29,6 +29,7 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.WorkSource;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
@@ -68,12 +69,12 @@
     private static final int VERSION = 50;
 
     // Maximum number of items we will record in the history.
-    private static final int MAX_HISTORY_ITEMS = 1000;
+    private static final int MAX_HISTORY_ITEMS = 2000;
     
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
     // in to one common name.
-    private static final int MAX_WAKELOCKS_PER_UID = 20;
+    private static final int MAX_WAKELOCKS_PER_UID = 30;
     
     private static final String BATCHED_WAKELOCK_NAME = "*overflow*";
     
@@ -1173,7 +1174,7 @@
         // If the current time is basically the same as the last time,
         // just collapse into one record.
         if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
-                && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) {
+                && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)) {
             // If the current is the same as the one before, then we no
             // longer need the entry.
             if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
@@ -1189,6 +1190,10 @@
             return;
         }
 
+        if (mNumHistoryItems == MAX_HISTORY_ITEMS) {
+            addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
+        }
+
         if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
             // Once we've reached the maximum number of items, we only
             // record changes to the battery level.
@@ -1329,6 +1334,20 @@
         }
     }
 
+    public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
+        int N = ws.size();
+        for (int i=0; i<N; i++) {
+            noteStartWakeLocked(ws.get(i), pid, name, type);
+        }
+    }
+
+    public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
+        int N = ws.size();
+        for (int i=0; i<N; i++) {
+            noteStopWakeLocked(ws.get(i), pid, name, type);
+        }
+    }
+
     public int startAddingCpuLocked() {
         mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
 
@@ -1949,6 +1968,48 @@
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked();
     }
 
+    public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
+        int N = ws.size();
+        for (int i=0; i<N; i++) {
+            noteFullWifiLockAcquiredLocked(ws.get(i));
+        }
+    }
+
+    public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
+        int N = ws.size();
+        for (int i=0; i<N; i++) {
+            noteFullWifiLockReleasedLocked(ws.get(i));
+        }
+    }
+
+    public void noteScanWifiLockAcquiredFromSourceLocked(WorkSource ws) {
+        int N = ws.size();
+        for (int i=0; i<N; i++) {
+            noteScanWifiLockAcquiredLocked(ws.get(i));
+        }
+    }
+
+    public void noteScanWifiLockReleasedFromSourceLocked(WorkSource ws) {
+        int N = ws.size();
+        for (int i=0; i<N; i++) {
+            noteScanWifiLockReleasedLocked(ws.get(i));
+        }
+    }
+
+    public void noteWifiMulticastEnabledFromSourceLocked(WorkSource ws) {
+        int N = ws.size();
+        for (int i=0; i<N; i++) {
+            noteWifiMulticastEnabledLocked(ws.get(i));
+        }
+    }
+
+    public void noteWifiMulticastDisabledFromSourceLocked(WorkSource ws) {
+        int N = ws.size();
+        for (int i=0; i<N; i++) {
+            noteWifiMulticastDisabledLocked(ws.get(i));
+        }
+    }
+
     @Override public long getScreenOnTime(long batteryRealtime, int which) {
         return mScreenOnTimer.getTotalTimeLocked(batteryRealtime, which);
     }
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 1307ec3..459458b 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -63,6 +63,8 @@
 static jmethodID method_onRequestPasskeyConfirmation;
 static jmethodID method_onRequestPairingConsent;
 static jmethodID method_onDisplayPasskey;
+static jmethodID method_onRequestOobData;
+static jmethodID method_onAgentOutOfBandDataAvailable;
 static jmethodID method_onAgentAuthorize;
 static jmethodID method_onAgentCancel;
 
@@ -116,6 +118,8 @@
 
     method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
                                                "(Ljava/lang/String;Ljava/lang/String;)Z");
+    method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
+                                               "(Ljava/lang/String;)Z");
     method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
     method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
                                                "(Ljava/lang/String;I)V");
@@ -135,6 +139,8 @@
                                                "(Ljava/lang/String;[Ljava/lang/String;)V");
     method_onPanDeviceConnectionResult = env->GetMethodID(clazz, "onPanDeviceConnectionResult",
                                                "(Ljava/lang/String;Z)V");
+    method_onRequestOobData = env->GetMethodID(clazz, "onRequestOobData",
+                                               "(Ljava/lang/String;I)V");
 
     field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
 #endif
@@ -345,6 +351,7 @@
 {
     DBusMessage *msg, *reply;
     DBusError err;
+    bool oob = TRUE;
 
     if (!dbus_connection_register_object_path(nat->conn, agent_path,
             &agent_vtable, nat)) {
@@ -366,6 +373,7 @@
     }
     dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
                              DBUS_TYPE_STRING, &capabilities,
+                             DBUS_TYPE_BOOLEAN, &oob,
                              DBUS_TYPE_INVALID);
 
     dbus_error_init(&err);
@@ -1056,6 +1064,43 @@
         }
         goto success;
     } else if (dbus_message_is_method_call(msg,
+            "org.bluez.Agent", "OutOfBandAvailable")) {
+        char *object_path;
+        if (!dbus_message_get_args(msg, NULL,
+                                   DBUS_TYPE_OBJECT_PATH, &object_path,
+                                   DBUS_TYPE_INVALID)) {
+            LOGE("%s: Invalid arguments for OutOfBandData available() method", __FUNCTION__);
+            goto failure;
+        }
+
+        LOGV("... object_path = %s", object_path);
+
+        bool available =
+            env->CallBooleanMethod(nat->me, method_onAgentOutOfBandDataAvailable,
+                env->NewStringUTF(object_path));
+
+
+        // reply
+        if (available) {
+            DBusMessage *reply = dbus_message_new_method_return(msg);
+            if (!reply) {
+                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
+                goto failure;
+            }
+            dbus_connection_send(nat->conn, reply, NULL);
+            dbus_message_unref(reply);
+        } else {
+            DBusMessage *reply = dbus_message_new_error(msg,
+                    "org.bluez.Error.DoesNotExist", "OutofBand data not available");
+            if (!reply) {
+                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
+                goto failure;
+            }
+            dbus_connection_send(nat->conn, reply, NULL);
+            dbus_message_unref(reply);
+        }
+        goto success;
+    } else if (dbus_message_is_method_call(msg,
             "org.bluez.Agent", "RequestPinCode")) {
         char *object_path;
         if (!dbus_message_get_args(msg, NULL,
@@ -1086,6 +1131,21 @@
                                        int(msg));
         goto success;
     } else if (dbus_message_is_method_call(msg,
+            "org.bluez.Agent", "RequestOobData")) {
+        char *object_path;
+        if (!dbus_message_get_args(msg, NULL,
+                                   DBUS_TYPE_OBJECT_PATH, &object_path,
+                                   DBUS_TYPE_INVALID)) {
+            LOGE("%s: Invalid arguments for RequestOobData() method", __FUNCTION__);
+            goto failure;
+        }
+
+        dbus_message_ref(msg);  // increment refcount because we pass to java
+        env->CallVoidMethod(nat->me, method_onRequestOobData,
+                                       env->NewStringUTF(object_path),
+                                       int(msg));
+        goto success;
+    } else if (dbus_message_is_method_call(msg,
             "org.bluez.Agent", "DisplayPasskey")) {
         char *object_path;
         uint32_t passkey;
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 74127cf..337dccc 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -296,6 +296,46 @@
 #endif
 }
 
+static jbyteArray readAdapterOutOfBandDataNative(JNIEnv *env, jobject object) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    DBusError err;
+    jbyte *hash, *randomizer;
+    jbyteArray byteArray = NULL;
+    int hash_len, r_len;
+    if (nat) {
+       DBusMessage *reply = dbus_func_args(env, nat->conn,
+                           get_adapter_path(env, object),
+                           DBUS_ADAPTER_IFACE, "ReadLocalOutOfBandData",
+                           DBUS_TYPE_INVALID);
+       if (!reply) return NULL;
+
+       dbus_error_init(&err);
+       if (dbus_message_get_args(reply, &err,
+                                DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hash_len,
+                                DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &r_len,
+                                DBUS_TYPE_INVALID)) {
+          if (hash_len == 16 && r_len == 16) {
+               byteArray = env->NewByteArray(32);
+               if (byteArray) {
+                   env->SetByteArrayRegion(byteArray, 0, 16, hash);
+                   env->SetByteArrayRegion(byteArray, 16, 16, randomizer);
+               }
+           } else {
+               LOGE("readAdapterOutOfBandDataNative: Hash len = %d, R len = %d",
+                                                                  hash_len, r_len);
+           }
+       } else {
+          LOG_AND_FREE_DBUS_ERROR(&err);
+       }
+       dbus_message_unref(reply);
+       return byteArray;
+    }
+#endif
+    return NULL;
+}
+
 static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
                                          jstring address, jint timeout_ms) {
     LOGV(__FUNCTION__);
@@ -332,6 +372,41 @@
     return JNI_FALSE;
 }
 
+static jboolean createPairedDeviceOutOfBandNative(JNIEnv *env, jobject object,
+                                                jstring address, jint timeout_ms) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+
+    if (nat && eventLoopNat) {
+        const char *c_address = env->GetStringUTFChars(address, NULL);
+        LOGV("... address = %s", c_address);
+        char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
+        const char *capabilities = "DisplayYesNo";
+        const char *agent_path = "/android/bluetooth/remote_device_agent";
+
+        strlcpy(context_address, c_address, BTADDR_SIZE);  // for callback
+        bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
+                                        onCreatePairedDeviceResult, // callback
+                                        context_address,
+                                        eventLoopNat,
+                                        get_adapter_path(env, object),
+                                        DBUS_ADAPTER_IFACE,
+                                        "CreatePairedDeviceOutOfBand",
+                                        DBUS_TYPE_STRING, &c_address,
+                                        DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                        DBUS_TYPE_STRING, &capabilities,
+                                        DBUS_TYPE_INVALID);
+        env->ReleaseStringUTFChars(address, c_address);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
 static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object,
                                           jstring path,
                                           jstring pattern, jint attr_id) {
@@ -498,6 +573,40 @@
     return JNI_FALSE;
 }
 
+static jboolean setRemoteOutOfBandDataNative(JNIEnv *env, jobject object, jstring address,
+                         jbyteArray hash, jbyteArray randomizer, int nativeData) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    native_data_t *nat = get_native_data(env, object);
+    if (nat) {
+        DBusMessage *msg = (DBusMessage *)nativeData;
+        DBusMessage *reply = dbus_message_new_method_return(msg);
+        jbyte *h_ptr = env->GetByteArrayElements(hash, NULL);
+        jbyte *r_ptr = env->GetByteArrayElements(randomizer, NULL);
+        if (!reply) {
+            LOGE("%s: Cannot create message reply to return remote OOB data to "
+                 "D-Bus\n", __FUNCTION__);
+            dbus_message_unref(msg);
+            return JNI_FALSE;
+        }
+
+        dbus_message_append_args(reply,
+                                DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &h_ptr, 16,
+                                DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &r_ptr, 16,
+                                DBUS_TYPE_INVALID);
+
+        env->ReleaseByteArrayElements(hash, h_ptr, 0);
+        env->ReleaseByteArrayElements(randomizer, r_ptr, 0);
+
+        dbus_connection_send(nat->conn, reply, NULL);
+        dbus_message_unref(msg);
+        dbus_message_unref(reply);
+        return JNI_TRUE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
 static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
                          jstring pin, int nativeData) {
 #ifdef HAVE_BLUETOOTH
@@ -1069,7 +1178,10 @@
     {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
     {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
 
+    {"readAdapterOutOfBandDataNative", "()[B", (void *)readAdapterOutOfBandDataNative},
     {"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative},
+    {"createPairedDeviceOutOfBandNative", "(Ljava/lang/String;I)Z",
+                                    (void *)createPairedDeviceOutOfBandNative},
     {"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative},
     {"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative},
     {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
@@ -1078,6 +1190,7 @@
     {"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
             (void *)setPairingConfirmationNative},
     {"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
+    {"setRemoteOutOfBandDataNative", "(Ljava/lang/String;[B[BI)Z", (void *)setRemoteOutOfBandDataNative},
     {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
     {"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
             (void *)cancelPairingUserInputNative},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 89298ea..4d02b60 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -939,7 +939,7 @@
     <permission android:name="android.permission.UPDATE_DEVICE_STATS"
         android:label="@string/permlab_batteryStats"
         android:description="@string/permdesc_batteryStats"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signatureOrSystem" />
 
     <!-- Allows an application to open windows that are for use by parts
          of the system user interface.  Not for use by third party apps. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9363db5..cda997a 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3530,6 +3530,8 @@
     <!-- Attribute for a header describing the item shown in the top-level list
          from which the selects the set of preference to dig in to. -->
     <declare-styleable name="PreferenceHeader">
+        <!-- Identifier value for the header. -->
+        <attr name="id" />
         <!-- The title of the item that is shown to the user. -->
         <attr name="title" />
         <!-- The summary for the item. -->
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7cc4880..b1f5f6b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -80,6 +80,9 @@
         { SkXfermode::kXor_Mode,     GL_ONE_MINUS_DST_ALPHA,  GL_ONE_MINUS_SRC_ALPHA }
 };
 
+// This array contains the swapped version of each SkXfermode. For instance
+// this array's SrcOver blending mode is actually DstOver. You can refer to
+// createLayer() for more information on the purpose of this array.
 static const Blender gBlendsSwap[] = {
         { SkXfermode::kClear_Mode,   GL_ZERO,                 GL_ZERO },
         { SkXfermode::kSrc_Mode,     GL_ZERO,                 GL_ONE },
@@ -280,6 +283,54 @@
     }
 }
 
+/**
+ * Layers are viewed by Skia are slightly different than layers in image editing
+ * programs (for instance.) When a layer is created, previously created layers
+ * and the frame buffer still receive every drawing command. For instance, if a
+ * layer is created and a shape intersecting the bounds of the layers and the
+ * framebuffer is draw, the shape will be drawn on both (unless the layer was
+ * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
+ *
+ * A way to implement layers is to create an FBO for each layer, backed by an RGBA
+ * texture. Unfortunately, this is inefficient as it requires every primitive to
+ * be drawn n + 1 times, where n is the number of active layers. In practice this
+ * means, for every primitive:
+ *   - Switch active frame buffer
+ *   - Change viewport, clip and projection matrix
+ *   - Issue the drawing
+ *
+ * Switching rendering target n + 1 times per drawn primitive is extremely costly.
+ * To avoid this, layers are implemented in a different way here.
+ *
+ * This implementation relies on the frame buffer being at least RGBA 8888. When
+ * a layer is created, only a texture is created, not an FBO. The content of the
+ * frame buffer contained within the layer's bounds is copied into this texture
+ * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
+ * buffer and drawing continues as normal. This technique therefore treats the
+ * frame buffer as a scratch buffer for the layers.
+ *
+ * To compose the layers back onto the frame buffer, each layer texture
+ * (containing the original frame buffer data) is drawn as a simple quad over
+ * the frame buffer. The trick is that the quad is set as the composition
+ * destination in the blending equation, and the frame buffer becomes the source
+ * of the composition.
+ *
+ * Drawing layers with an alpha value requires an extra step before composition.
+ * An empty quad is drawn over the layer's region in the frame buffer. This quad
+ * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
+ * quad is used to multiply the colors in the frame buffer. This is achieved by
+ * changing the GL blend functions for the GL_FUNC_ADD blend equation to
+ * GL_ZERO, GL_SRC_ALPHA.
+ *
+ * Because glCopyTexImage2D() can be slow, an alternative implementation might
+ * be use to draw a single clipped layer. The implementation described above
+ * is correct in every case.
+ *
+ * (1) The frame buffer is actually not cleared right away. To allow the GPU
+ *     to potentially optimize series of calls to glCopyTexImage2D, the frame
+ *     buffer is left untouched until the first drawing operation. Only when
+ *     something actually gets drawn are the layers regions cleared.
+ */
 bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
         float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
     LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
@@ -289,8 +340,11 @@
     Rect bounds(left, top, right, bottom);
     mSnapshot->transform->mapRect(bounds);
 
-    LayerSize size(bounds.getWidth(), bounds.getHeight());
+    // Layers only make sense if they are in the framebuffer's bounds
+    bounds.intersect(*mSnapshot->clipRect);
+    if (bounds.isEmpty()) return false;
 
+    LayerSize size(bounds.getWidth(), bounds.getHeight());
     Layer* layer = mCaches.layerCache.get(size);
     if (!layer) {
         return false;
@@ -319,6 +373,9 @@
     return true;
 }
 
+/**
+ * Read the documentation of createLayer() before doing anything in this method.
+ */
 void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
     if (!current->layer) {
         LOGE("Attempting to compose a layer that does not exist");
@@ -333,16 +390,8 @@
     const Rect& rect = layer->layer;
 
     if (layer->alpha < 255) {
-        glEnable(GL_BLEND);
-        glBlendFuncSeparate(GL_ZERO, GL_SRC_ALPHA, GL_DST_ALPHA, GL_ZERO);
-
         drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
-                layer->alpha << 24, SkXfermode::kSrcOver_Mode, true, true);
-
-        glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
-        if (!mCaches.blend) {
-            glDisable(GL_BLEND);
-        }
+                layer->alpha << 24, SkXfermode::kDstIn_Mode, true);
     }
 
     // Layers are already drawn with a top-left origin, don't flip the texture
@@ -525,6 +574,7 @@
     Patch* mesh = mCaches.patchCache.get(patch);
     mesh->updateVertices(bitmap, left, top, right, bottom,
             &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
+    mesh->dump();
 
     // Specify right and bottom as +1.0f from left/top to prevent scaling since the
     // patch mesh already defines the final size
@@ -841,7 +891,7 @@
 }
 
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
-        int color, SkXfermode::Mode mode, bool ignoreTransform, bool ignoreBlending) {
+        int color, SkXfermode::Mode mode, bool ignoreTransform) {
     clearLayerRegions();
 
     // If a shader is set, preserve only the alpha
@@ -867,10 +917,8 @@
         mColorFilter->describe(description, mExtensions);
     }
 
-    if (!ignoreBlending) {
-        // Setup the blending mode
-        chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
-    }
+    // Setup the blending mode
+    chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
 
     // Build and use the appropriate shader
     useProgram(mCaches.programCache.get(description));
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 12ec276..3126754 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -176,8 +176,7 @@
      * @paran ignoreBlending True if the blending is set by the caller
      */
     void drawColorRect(float left, float top, float right, float bottom,
-    		int color, SkXfermode::Mode mode, bool ignoreTransform = false,
-    		bool ignoreBlending = false);
+            int color, SkXfermode::Mode mode, bool ignoreTransform = false);
 
     /**
      * Draws a textured rectangle with the specified texture. The specified coordinates
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index ebedf53..062c986 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -116,6 +116,11 @@
     bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) {
         bool clipped = false;
 
+        // NOTE: The unimplemented operations require support for regions
+        // Supporting regions would require using a stencil buffer instead
+        // of the scissor. The stencil buffer itself is not too expensive
+        // (memory cost excluded) but on fillrate limited devices, managing
+        // the stencil might have a negative impact on the framerate.
         switch (op) {
             case SkRegion::kDifference_Op:
                 break;
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 2b9782a..ecf6789 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -20,6 +20,7 @@
 import android.location.Location;
 import android.net.NetworkInfo;
 import android.os.Bundle;
+import android.os.WorkSource;
 
 /**
  * Binder interface for services that implement location providers.
@@ -43,7 +44,7 @@
     long getStatusUpdateTime();
     String getInternalState();
     void enableLocationTracking(boolean enable);
-    void setMinTime(long minTime);
+    void setMinTime(long minTime, in WorkSource ws);
     void updateNetworkState(int state, in NetworkInfo info);
     void updateLocation(in Location location);
     boolean sendExtraCommand(String command, inout Bundle extras);
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java
index cf939de..95b4425 100644
--- a/location/java/android/location/provider/LocationProvider.java
+++ b/location/java/android/location/provider/LocationProvider.java
@@ -26,6 +26,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.WorkSource;
 import android.util.Log;
 
 /**
@@ -106,8 +107,8 @@
             LocationProvider.this.onEnableLocationTracking(enable);
         }
 
-        public void setMinTime(long minTime) {
-            LocationProvider.this.onSetMinTime(minTime);
+        public void setMinTime(long minTime, WorkSource ws) {
+            LocationProvider.this.onSetMinTime(minTime, ws);
         }
 
         public void updateNetworkState(int state, NetworkInfo info) {
@@ -123,11 +124,11 @@
         }
 
         public void addListener(int uid) {
-            LocationProvider.this.onAddListener(uid);
+            LocationProvider.this.onAddListener(uid, new WorkSource(uid));
         }
 
         public void removeListener(int uid) {
-            LocationProvider.this.onRemoveListener(uid);
+            LocationProvider.this.onRemoveListener(uid, new WorkSource(uid));
         }
     };
 
@@ -303,8 +304,9 @@
      * the frequency of updates to match the requested frequency.
      *
      * @param minTime the smallest minTime value over all listeners for this provider.
+     * @param ws the source this work is coming from.
      */
-    public abstract void onSetMinTime(long minTime);
+    public abstract void onSetMinTime(long minTime, WorkSource ws);
 
     /**
      * Updates the network state for the given provider. This function must
@@ -340,14 +342,16 @@
      * Notifies the location provider when a new client is listening for locations.
      *
      * @param uid user ID of the new client.
+     * @param ws a WorkSource representation of the client.
      */
-    public abstract void onAddListener(int uid);
+    public abstract void onAddListener(int uid, WorkSource ws);
 
     /**
      * Notifies the location provider when a client is no longer listening for locations.
      *
      * @param uid user ID of the client no longer listening.
+     * @param ws a WorkSource representation of the client.
      */
-    public abstract void onRemoveListener(int uid);
+    public abstract void onRemoveListener(int uid, WorkSource ws);
 }
 
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9f2a49c..b2a0a46 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -110,15 +110,28 @@
 
     private final static String TAG = "MediaScanner";
 
-    private static final String[] PRESCAN_PROJECTION = new String[] {
+    private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
             Files.FileColumns._ID, // 0
             Files.FileColumns.DATA, // 1
-            Files.FileColumns.DATE_MODIFIED, // 2
+            Files.FileColumns.FORMAT, // 2
+            Files.FileColumns.DATE_MODIFIED, // 3
     };
 
-    private static final int PRESCAN_ID_COLUMN_INDEX = 0;
-    private static final int PRESCAN_PATH_COLUMN_INDEX = 1;
-    private static final int PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;
+    private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
+    private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
+    private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
+    private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
+
+    private static final String[] MEDIA_PRESCAN_PROJECTION = new String[] {
+            MediaStore.MediaColumns._ID, // 0
+            MediaStore.MediaColumns.DATA, // 1
+            MediaStore.MediaColumns.DATE_MODIFIED, // 2
+    };
+
+    private static final int MEDIA_PRESCAN_ID_COLUMN_INDEX = 0;
+    private static final int MEDIA_PRESCAN_PATH_COLUMN_INDEX = 1;
+    private static final int MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;
+
 
     private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
             Audio.Playlists.Members.PLAYLIST_ID, // 0
@@ -316,14 +329,16 @@
         long mRowId;
         String mPath;
         long mLastModified;
+        int mFormat;
         boolean mSeenInFileSystem;
         boolean mLastModifiedChanged;
 
-        FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified) {
+        FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified, int format) {
             mTableUri = tableUri;
             mRowId = rowId;
             mPath = path;
             mLastModified = lastModified;
+            mFormat = format;
             mSeenInFileSystem = false;
             mLastModifiedChanged = false;
         }
@@ -444,7 +459,7 @@
                 } else {
                     tableUri = mFilesUri;
                 }
-                entry = new FileCacheEntry(tableUri, 0, path, 0);
+                entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
                 mFileCache.put(key, entry);
             }
             entry.mSeenInFileSystem = true;
@@ -884,13 +899,15 @@
             if (prescanFiles) {
                 // First read existing files from the files table
 
-                c = mMediaProvider.query(mFilesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+                c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+                        where, selectionArgs, null);
 
                 if (c != null) {
                     while (c.moveToNext()) {
-                        long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
-                        String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
-                        long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
+                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
+                        int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
+                        long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
 
                         // Only consider entries with absolute path names.
                         // This allows storing URIs in the database without the
@@ -902,7 +919,7 @@
                             }
 
                             FileCacheEntry entry = new FileCacheEntry(mFilesUri, rowId, path,
-                                    lastModified);
+                                    lastModified, format);
                             mFileCache.put(key, entry);
                         }
                     }
@@ -912,12 +929,13 @@
             }
 
             // Read existing files from the audio table and update FileCacheEntry
-            c = mMediaProvider.query(mAudioUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+            c = mMediaProvider.query(mAudioUri, MEDIA_PRESCAN_PROJECTION,
+                    where, selectionArgs, null);
             if (c != null) {
                 while (c.moveToNext()) {
-                    long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
-                    String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
-                    long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
 
                     // Only consider entries with absolute path names.
                     // This allows storing URIs in the database without the
@@ -930,7 +948,7 @@
                         FileCacheEntry entry = mFileCache.get(path);
                         if (entry == null) {
                             mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
-                                    lastModified));
+                                    lastModified, 0));
                         } else {
                             // update the entry
                             entry.mTableUri = mAudioUri;
@@ -943,12 +961,13 @@
             }
 
             // Read existing files from the video table and update FileCacheEntry
-            c = mMediaProvider.query(mVideoUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+            c = mMediaProvider.query(mVideoUri, MEDIA_PRESCAN_PROJECTION,
+                    where, selectionArgs, null);
             if (c != null) {
                 while (c.moveToNext()) {
-                    long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
-                    String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
-                    long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
 
                     // Only consider entries with absolute path names.
                     // This allows storing URIs in the database without the
@@ -961,7 +980,7 @@
                         FileCacheEntry entry = mFileCache.get(path);
                         if (entry == null) {
                             mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
-                                    lastModified));
+                                    lastModified, 0));
                         } else {
                             // update the entry
                             entry.mTableUri = mVideoUri;
@@ -974,12 +993,13 @@
             }
 
             // Read existing files from the video table and update FileCacheEntry
-            c = mMediaProvider.query(mImagesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+            c = mMediaProvider.query(mImagesUri, MEDIA_PRESCAN_PROJECTION,
+                    where, selectionArgs, null);
             if (c != null) {
                 while (c.moveToNext()) {
-                    long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
-                    String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
-                    long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
 
                     // Only consider entries with absolute path names.
                     // This allows storing URIs in the database without the
@@ -992,7 +1012,7 @@
                         FileCacheEntry entry = mFileCache.get(path);
                         if (entry == null) {
                             mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
-                                    lastModified));
+                                    lastModified, 0));
                         } else {
                             // update the entry
                             entry.mTableUri = mImagesUri;
@@ -1006,13 +1026,13 @@
 
             if (mProcessPlaylists) {
                 // Read existing files from the playlists table and update FileCacheEntry
-                c = mMediaProvider.query(mPlaylistsUri, PRESCAN_PROJECTION, where,
-                                            selectionArgs, null);
+                c = mMediaProvider.query(mPlaylistsUri, MEDIA_PRESCAN_PROJECTION,
+                        where, selectionArgs, null);
                 if (c != null) {
                     while (c.moveToNext()) {
-                        long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
-                        String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
-                        long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+                        long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+                        String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+                        long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
 
                         // Only consider entries with absolute path names.
                         // This allows storing URIs in the database without the
@@ -1025,7 +1045,7 @@
                             FileCacheEntry entry = mFileCache.get(path);
                             if (entry == null) {
                                 mFileCache.put(key, new FileCacheEntry(mPlaylistsUri, rowId, path,
-                                        lastModified));
+                                        lastModified, 0));
                             } else {
                                 // update the entry
                                 entry.mTableUri = mPlaylistsUri;
@@ -1109,12 +1129,14 @@
             // remove database entries for files that no longer exist.
             boolean fileMissing = false;
 
-            if (!entry.mSeenInFileSystem) {
-                if (inScanDirectory(path, directories)) {
+            if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
+                if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
+                        inScanDirectory(path, directories)) {
                     // we didn't see this file in the scan directory.
                     fileMissing = true;
                 } else {
-                    // the file is outside of our scan directory,
+                    // the file actually a directory or other abstract object
+                    // or is outside of our scan directory,
                     // so we need to check for file existence here.
                     File testFile = new File(path);
                     if (!testFile.exists()) {
@@ -1134,9 +1156,11 @@
                     ContentValues values = new ContentValues();
                     values.put(MediaStore.Audio.Playlists.DATA, "");
                     values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, 0);
-                    mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId), values, null, null);
+                    mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId),
+                            values, null, null);
                 } else {
-                    mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId), null, null);
+                    mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId),
+                            null, null);
                     iterator.remove();
                 }
             }
diff --git a/media/java/android/media/MtpConstants.java b/media/java/android/media/MtpConstants.java
index 153f64f..a7d33ce 100644
--- a/media/java/android/media/MtpConstants.java
+++ b/media/java/android/media/MtpConstants.java
@@ -138,6 +138,28 @@
     public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81;
     public static final int FORMAT_VCARD_2 = 0xBB82;
 
+    public static boolean isAbstractObject(int format) {
+        switch (format) {
+            case FORMAT_ABSTRACT_MULTIMEDIA_ALBUM:
+            case FORMAT_ABSTRACT_IMAGE_ALBUM:
+            case FORMAT_ABSTRACT_AUDIO_ALBUM:
+            case FORMAT_ABSTRACT_VIDEO_ALBUM:
+            case FORMAT_ABSTRACT_AV_PLAYLIST:
+            case FORMAT_ABSTRACT_CONTACT_GROUP:
+            case FORMAT_ABSTRACT_MESSAGE_FOLDER:
+            case FORMAT_ABSTRACT_CHAPTERED_PRODUCTION:
+            case FORMAT_ABSTRACT_AUDIO_PLAYLIST:
+            case FORMAT_ABSTRACT_VIDEO_PLAYLIST:
+            case FORMAT_ABSTRACT_MEDIACAST:
+            case FORMAT_ABSTRACT_DOCUMENT:
+            case FORMAT_ABSTRACT_MESSSAGE:
+            case FORMAT_ABSTRACT_CONTACT:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     // MTP object properties
     public static final int PROPERTY_STORAGE_ID = 0xDC01;
     public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 6b0b761..b64299a 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -478,12 +478,26 @@
         }
     }
 
+    private int deleteRecursive(int handle) throws RemoteException {
+        int[] children = getObjectList(0 /* storageID */, 0 /* format */, handle);
+        Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
+        // delete parent first, to avoid potential infinite recursion
+        int count = mMediaProvider.delete(uri, null, null);
+        if (count == 1) {
+            if (children != null) {
+                for (int i = 0; i < children.length; i++) {
+                    count += deleteRecursive(children[i]);
+                }
+            }
+        }
+        return count;
+    }
+
     private int deleteFile(int handle) {
         Log.d(TAG, "deleteFile: " + handle);
         mDatabaseModified = true;
-        Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
         try {
-            if (mMediaProvider.delete(uri, null, null) == 1) {
+            if (deleteRecursive(handle) > 0) {
                 return MtpConstants.RESPONSE_OK;
             } else {
                 return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index e86ed99..90756d0 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -65,6 +65,7 @@
 int LvmInitFlag = LVM_FALSE;
 int LvmSessionsActive = 0;
 SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
+
 int SessionIndex[LVM_MAX_SESSIONS];
 
 // NXP SW BassBoost UUID
@@ -199,11 +200,6 @@
         return -EINVAL;
     }
 
-    if(sessionId < 0){
-        LOGV("\tLVM_ERROR : EffectCreate sessionId is less than 0");
-        return -EINVAL;
-    }
-
     if(LvmInitFlag == LVM_FALSE){
         LvmInitFlag = LVM_TRUE;
         LOGV("\tEffectCreate - Initializing all global memory");
@@ -214,7 +210,7 @@
 
     // Find next available sessionNo
     for(i=0; i<LVM_MAX_SESSIONS; i++){
-        if((SessionIndex[i] == -1)||(SessionIndex[i] == sessionId)){
+        if((SessionIndex[i] == LVM_UNUSED_SESSION)||(SessionIndex[i] == sessionId)){
             sessionNo       = i;
             SessionIndex[i] = sessionId;
             LOGV("\tEffectCreate: Allocating SessionNo %d for SessionId %d\n", sessionNo,sessionId);
@@ -398,7 +394,7 @@
         // Clear the SessionIndex
         for(int i=0; i<LVM_MAX_SESSIONS; i++){
             if(SessionIndex[i] == pContext->pBundledContext->SessionId){
-                SessionIndex[i] = -1;
+                SessionIndex[i] = LVM_UNUSED_SESSION;
                 LOGV("\tEffectRelease: Clearing SessionIndex SessionNo %d for SessionId %d\n",
                         i, pContext->pBundledContext->SessionId);
                 break;
@@ -432,7 +428,7 @@
         GlobalSessionMemory[i].bVirtualizerInstantiated = LVM_FALSE;
         GlobalSessionMemory[i].pBundledContext          = LVM_NULL;
 
-        SessionIndex[i] = -1;
+        SessionIndex[i] = LVM_UNUSED_SESSION;
     }
     return;
 }
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 35e1114..91963af 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -21,6 +21,7 @@
 #include <media/EffectBassBoostApi.h>
 #include <media/EffectVirtualizerApi.h>
 #include <LVM.h>
+#include <limits.h>
 
 #if __cplusplus
 extern "C" {
@@ -30,6 +31,7 @@
 #define MAX_NUM_BANDS              5
 #define MAX_CALL_SIZE              256
 #define LVM_MAX_SESSIONS           32
+#define LVM_UNUSED_SESSION         INT_MAX
 #define BASS_BOOST_CUP_LOAD_ARM9E  150    // Expressed in 0.1 MIPS
 #define VIRTUALIZER_CUP_LOAD_ARM9E 120    // Expressed in 0.1 MIPS
 #define EQUALIZER_CUP_LOAD_ARM9E   220    // Expressed in 0.1 MIPS
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 60f4288..26c5aca 100755
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -59,17 +59,17 @@
         // REVERB_PRESET_NONE: values are unused
         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
         // REVERB_PRESET_SMALLROOM
-        {-1000, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000},
+        {-400, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000},
         // REVERB_PRESET_MEDIUMROOM
-        {-1000, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000},
+        {-400, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000},
         // REVERB_PRESET_LARGEROOM
-        {-1000, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000},
+        {-400, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000},
         // REVERB_PRESET_MEDIUMHALL
-        {-1000, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000},
+        {-400, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000},
         // REVERB_PRESET_LARGEHALL
-        {-1000, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000},
+        {-400, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000},
         // REVERB_PRESET_PLATE
-        {-1000, -200, 1300, 900, 0, 2, 0, 10, 1000, 750},
+        {-400, -200, 1300, 900, 0, 2, 0, 10, 1000, 750},
 };
 
 
@@ -90,7 +90,7 @@
         {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
         {0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
         EFFECT_API_VERSION,
-        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_VOLUME_CTRL,
         LVREV_CUP_LOAD_ARM9E,
         LVREV_MEM_USAGE,
         "Insert Environmental Reverb",
@@ -114,7 +114,7 @@
         {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
         {0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
         EFFECT_API_VERSION,
-        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_VOLUME_CTRL,
         LVREV_CUP_LOAD_ARM9E,
         LVREV_MEM_USAGE,
         "Insert Preset Reverb",
@@ -153,10 +153,25 @@
     uint16_t                        curPreset;
     uint16_t                        nextPreset;
     int                             SamplesToExitCount;
+    LVM_INT16                       leftVolume;
+    LVM_INT16                       rightVolume;
+    LVM_INT16                       prevLeftVolume;
+    LVM_INT16                       prevRightVolume;
+    int                             volumeMode;
+};
+
+enum {
+    REVERB_VOLUME_OFF,
+    REVERB_VOLUME_FLAT,
+    REVERB_VOLUME_RAMP,
 };
 
 #define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM
 
+
+#define REVERB_SEND_LEVEL   (0x0C00) // 0.75 in 4.12 format
+#define REVERB_UNIT_VOLUME  (0x1000) // 1.0 in 4.12 format
+
 //--- local function prototypes
 int  Reverb_init            (ReverbContext *pContext);
 void Reverb_free            (ReverbContext *pContext);
@@ -426,9 +441,20 @@
     if (pContext->preset && pContext->nextPreset != pContext->curPreset) {
         Reverb_LoadPreset(pContext);
     }
+
+
+
     // Convert to Input 32 bits
-    for(int i=0; i<frameCount*samplesPerFrame; i++){
-        pContext->InFrames32[i] = (LVM_INT32)pIn[i]<<8;
+    if (pContext->auxiliary) {
+        for(int i=0; i<frameCount*samplesPerFrame; i++){
+            pContext->InFrames32[i] = (LVM_INT32)pIn[i]<<8;
+        }
+    } else {
+        // insert reverb input is always stereo
+        for (int i = 0; i < frameCount; i++) {
+            pContext->InFrames32[2*i] = (pIn[2*i] * REVERB_SEND_LEVEL) >> 4; // <<8 + >>12
+            pContext->InFrames32[2*i+1] = (pIn[2*i+1] * REVERB_SEND_LEVEL) >> 4; // <<8 + >>12
+        }
     }
 
     if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) {
@@ -458,6 +484,42 @@
         for (int i=0; i < frameCount*2; i++) { //always stereo here
             OutFrames16[i] = clamp16((pContext->OutFrames32[i]>>8) + (LVM_INT32)pIn[i]);
         }
+
+        // apply volume with ramp if needed
+        if ((pContext->leftVolume != pContext->prevLeftVolume ||
+                pContext->rightVolume != pContext->prevRightVolume) &&
+                pContext->volumeMode == REVERB_VOLUME_RAMP) {
+            LVM_INT32 vl = (LVM_INT32)pContext->prevLeftVolume << 16;
+            LVM_INT32 incl = (((LVM_INT32)pContext->leftVolume << 16) - vl) / frameCount;
+            LVM_INT32 vr = (LVM_INT32)pContext->prevRightVolume << 16;
+            LVM_INT32 incr = (((LVM_INT32)pContext->rightVolume << 16) - vr) / frameCount;
+
+            for (int i = 0; i < frameCount; i++) {
+                OutFrames16[2*i] =
+                        clamp16((LVM_INT32)((vl >> 16) * OutFrames16[2*i]) >> 12);
+                OutFrames16[2*i+1] =
+                        clamp16((LVM_INT32)((vr >> 16) * OutFrames16[2*i+1]) >> 12);
+
+                vl += incl;
+                vr += incr;
+            }
+
+            pContext->prevLeftVolume = pContext->leftVolume;
+            pContext->prevRightVolume = pContext->rightVolume;
+        } else if (pContext->volumeMode != REVERB_VOLUME_OFF) {
+            if (pContext->leftVolume != REVERB_UNIT_VOLUME ||
+                pContext->rightVolume != REVERB_UNIT_VOLUME) {
+                for (int i = 0; i < frameCount; i++) {
+                    OutFrames16[2*i] =
+                            clamp16((LVM_INT32)(pContext->leftVolume * OutFrames16[2*i]) >> 12);
+                    OutFrames16[2*i+1] =
+                            clamp16((LVM_INT32)(pContext->rightVolume * OutFrames16[2*i+1]) >> 12);
+                }
+            }
+            pContext->prevLeftVolume = pContext->leftVolume;
+            pContext->prevRightVolume = pContext->rightVolume;
+            pContext->volumeMode = REVERB_VOLUME_RAMP;
+        }
     }
 
     #ifdef LVM_PCM
@@ -658,6 +720,12 @@
     pContext->config.outputCfg.bufferProvider.cookie        = NULL;
     pContext->config.outputCfg.mask                         = EFFECT_CONFIG_ALL;
 
+    pContext->leftVolume = REVERB_UNIT_VOLUME;
+    pContext->rightVolume = REVERB_UNIT_VOLUME;
+    pContext->prevLeftVolume = REVERB_UNIT_VOLUME;
+    pContext->prevRightVolume = REVERB_UNIT_VOLUME;
+    pContext->volumeMode = REVERB_VOLUME_FLAT;
+
     LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;        /* Function call status */
     LVREV_ControlParams_st    params;                         /* Control Parameters */
     LVREV_InstanceParams_st   InstParams;                     /* Instance parameters */
@@ -1781,15 +1849,6 @@
         LOGV("\tLVM_ERROR : Reverb_process() ERROR NULL INPUT POINTER OR FRAME COUNT IS WRONG");
         return -EINVAL;
     }
-    if (pContext->bEnabled == LVM_FALSE){
-        if( pContext->SamplesToExitCount > 0){
-            pContext->SamplesToExitCount -= outBuffer->frameCount;
-            LOGV("\tReverb_process() Effect is being stopped %d", pContext->SamplesToExitCount);
-        }else{
-            LOGV("\tReverb_process() Effect is being stopped");
-            return -ENODATA;
-        }
-    }
     //LOGV("\tReverb_process() Calling process with %d frames", outBuffer->frameCount);
     /* Process all the available frames, block processing is handled internalLY by the LVM bundle */
     status = process(    (LVM_INT16 *)inBuffer->raw,
@@ -1797,6 +1856,14 @@
                                       outBuffer->frameCount,
                                       pContext);
 
+    if (pContext->bEnabled == LVM_FALSE) {
+        if (pContext->SamplesToExitCount > 0) {
+            pContext->SamplesToExitCount -= outBuffer->frameCount;
+        } else {
+            status = -ENODATA;
+        }
+    }
+
     return status;
 }   /* end Reverb_process */
 
@@ -1943,6 +2010,8 @@
             LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "EFFECT_CMD_ENABLE")
             pContext->SamplesToExitCount =
                     (ActiveParams.T60 * pContext->config.inputCfg.samplingRate)/1000;
+            // force no volume ramp for first buffer processed after enabling the effect
+            pContext->volumeMode = android::REVERB_VOLUME_FLAT;
             //LOGV("\tEFFECT_CMD_ENABLE SamplesToExitCount = %d", pContext->SamplesToExitCount);
             break;
         case EFFECT_CMD_DISABLE:
@@ -1963,8 +2032,34 @@
             pContext->bEnabled = LVM_FALSE;
             break;
 
-        case EFFECT_CMD_SET_DEVICE:
         case EFFECT_CMD_SET_VOLUME:
+            if (pCmdData == NULL ||
+                cmdSize != 2 * sizeof(uint32_t)) {
+                LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                        "EFFECT_CMD_SET_VOLUME: ERROR");
+                return -EINVAL;
+            }
+
+
+            if (pReplyData != NULL) { // we have volume control
+                pContext->leftVolume = (LVM_INT16)((*(uint32_t *)pCmdData + (1 << 11)) >> 12);
+                pContext->rightVolume = (LVM_INT16)((*((uint32_t *)pCmdData + 1) + (1 << 11)) >> 12);
+                *(uint32_t *)pReplyData = (1 << 24);
+                *((uint32_t *)pReplyData + 1) = (1 << 24);
+                if (pContext->volumeMode == android::REVERB_VOLUME_OFF) {
+                    // force no volume ramp for first buffer processed after getting volume control
+                    pContext->volumeMode = android::REVERB_VOLUME_FLAT;
+                }
+            } else { // we don't have volume control
+                pContext->leftVolume = REVERB_UNIT_VOLUME;
+                pContext->rightVolume = REVERB_UNIT_VOLUME;
+                pContext->volumeMode = android::REVERB_VOLUME_OFF;
+            }
+            LOGV("EFFECT_CMD_SET_VOLUME left %d, right %d mode %d",
+                    pContext->leftVolume, pContext->rightVolume,  pContext->volumeMode);
+            break;
+
+        case EFFECT_CMD_SET_DEVICE:
         case EFFECT_CMD_SET_AUDIO_MODE:
         //LOGV("\tReverb_command cmdCode Case: "
         //        "EFFECT_CMD_SET_DEVICE/EFFECT_CMD_SET_VOLUME/EFFECT_CMD_SET_AUDIO_MODE start");
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 3d3bd62..6332b4e 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -21,6 +21,8 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
 
 #include <cutils/properties.h>
 
@@ -686,21 +688,77 @@
     return result;
 }
 
+static void deleteRecursive(const char* path) {
+    char pathbuf[PATH_MAX];
+    int pathLength = strlen(path);
+    if (pathLength >= sizeof(pathbuf) - 1) {
+        LOGE("path too long: %s\n", path);
+    }
+    strcpy(pathbuf, path);
+    if (pathbuf[pathLength - 1] != '/') {
+        pathbuf[pathLength++] = '/';
+    }
+    char* fileSpot = pathbuf + pathLength;
+    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
+
+    DIR* dir = opendir(path);
+    if (!dir) {
+        LOGE("opendir %s failed: %s", path, strerror(errno));
+        return;
+    }
+
+    struct dirent* entry;
+    while ((entry = readdir(dir))) {
+        const char* name = entry->d_name;
+
+        // ignore "." and ".."
+        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+            continue;
+        }
+
+        int nameLength = strlen(name);
+        if (nameLength > pathRemaining) {
+            LOGE("path %s/%s too long\n", path, name);
+            continue;
+        }
+        strcpy(fileSpot, name);
+
+        int type = entry->d_type;
+        if (entry->d_type == DT_DIR) {
+            deleteRecursive(pathbuf);
+            rmdir(pathbuf);
+        } else {
+            unlink(pathbuf);
+        }
+    }
+}
+
+static void deletePath(const char* path) {
+    struct stat statbuf;
+    if (stat(path, &statbuf) == 0) {
+        if (S_ISDIR(statbuf.st_mode)) {
+            deleteRecursive(path);
+            rmdir(path);
+        } else {
+            unlink(path);
+        }
+    } else {
+        LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
+    }
+}
+
 MtpResponseCode MtpServer::doDeleteObject() {
     MtpObjectHandle handle = mRequest.getParameter(1);
-    MtpObjectFormat format = mRequest.getParameter(1);
+    MtpObjectFormat format = mRequest.getParameter(2);
     // FIXME - support deleting all objects if handle is 0xFFFFFFFF
     // FIXME - implement deleting objects by format
-    // FIXME - handle non-empty directories
 
     MtpString filePath;
     int64_t fileLength;
     int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
     if (result == MTP_RESPONSE_OK) {
         LOGV("deleting %s", (const char *)filePath);
-        // one of these should work
-        rmdir((const char *)filePath);
-        unlink((const char *)filePath);
+        deletePath((const char *)filePath);
         return mDatabase->deleteFile(handle);
     } else {
         return result;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index b4e0d3a..5dedcc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -718,7 +718,6 @@
                     iconId = sWifiSignalImages[mLastWifiInetConnectivityState]
                             [mLastWifiSignalLevel];
                 }
-
                 mService.setIcon("wifi", iconId, 0);
                 // Show the icon since wi-fi is connected
                 mService.setIconVisibility("wifi", true);
@@ -1101,7 +1100,7 @@
             int iconId;
             final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
             int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
-                                                                  sWifiSignalImages.length);
+                                                                  sWifiSignalImages[0].length);
             if (newSignalLevel != mLastWifiSignalLevel) {
                 mLastWifiSignalLevel = newSignalLevel;
                 if (mIsWifiConnected) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 3770b55..886c25b 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1752,14 +1752,15 @@
             }
 
             // compute volume for this track
-            int16_t left, right, aux;
+            uint32_t vl, vr, va;
             if (track->isMuted() || track->isPausing() ||
                 mStreamTypes[track->type()].mute) {
-                left = right = aux = 0;
+                vl = vr = va = 0;
                 if (track->isPausing()) {
                     track->setPaused();
                 }
             } else {
+
                 // read original volumes with volume control
                 float typeVolume = mStreamTypes[track->type()].volume;
 #ifdef LVMX
@@ -1774,35 +1775,36 @@
                 }
 #endif
                 float v = masterVolume * typeVolume;
-                uint32_t vl = (uint32_t)(v * cblk->volume[0]) << 12;
-                uint32_t vr = (uint32_t)(v * cblk->volume[1]) << 12;
+                vl = (uint32_t)(v * cblk->volume[0]) << 12;
+                vr = (uint32_t)(v * cblk->volume[1]) << 12;
 
-                // Delegate volume control to effect in track effect chain if needed
-                if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
-                    // Do not ramp volume is volume is controlled by effect
-                    param = AudioMixer::VOLUME;
-                    track->mHasVolumeController = true;
-                } else {
-                    // force no volume ramp when volume controller was just disabled or removed
-                    // from effect chain to avoid volume spike
-                    if (track->mHasVolumeController) {
-                        param = AudioMixer::VOLUME;
-                    }
-                    track->mHasVolumeController = false;
-                }
-
-                // Convert volumes from 8.24 to 4.12 format
-                uint32_t v_clamped = (vl + (1 << 11)) >> 12;
-                if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
-                left = int16_t(v_clamped);
-                v_clamped = (vr + (1 << 11)) >> 12;
-                if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
-                right = int16_t(v_clamped);
-
-                v_clamped = (uint32_t)(v * cblk->sendLevel);
-                if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
-                aux = int16_t(v_clamped);
+                va = (uint32_t)(v * cblk->sendLevel);
             }
+            // Delegate volume control to effect in track effect chain if needed
+            if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
+                // Do not ramp volume if volume is controlled by effect
+                param = AudioMixer::VOLUME;
+                track->mHasVolumeController = true;
+            } else {
+                // force no volume ramp when volume controller was just disabled or removed
+                // from effect chain to avoid volume spike
+                if (track->mHasVolumeController) {
+                    param = AudioMixer::VOLUME;
+                }
+                track->mHasVolumeController = false;
+            }
+
+            // Convert volumes from 8.24 to 4.12 format
+            int16_t left, right, aux;
+            uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+            if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+            left = int16_t(v_clamped);
+            v_clamped = (vr + (1 << 11)) >> 12;
+            if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+            right = int16_t(v_clamped);
+
+            if (va > MAX_GAIN_INT) va = MAX_GAIN_INT;
+            aux = int16_t(va);
 
 #ifdef LVMX
             if ( tracksConnectedChanged || stateChanged )
@@ -2283,7 +2285,7 @@
                         // only one effect chain can be present on DirectOutputThread, so if
                         // there is one, the track is connected to it
                         if (!effectChains.isEmpty()) {
-                            // Do not ramp volume is volume is controlled by effect
+                            // Do not ramp volume if volume is controlled by effect
                             if(effectChains[0]->setVolume_l(&vl, &vr)) {
                                 rampVolume = false;
                             }
@@ -4728,7 +4730,7 @@
         // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
         // that can only be created by audio policy manager (running in same process)
         if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE &&
-                getpid() != IPCThreadState::self()->getCallingPid()) {
+                getpid() != pid) {
             lStatus = INVALID_OPERATION;
             goto Exit;
         }
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index e454c08..aa87f29 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -54,6 +54,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.WorkSource;
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Slog;
@@ -504,7 +505,7 @@
         parseLeftoverJournals();
 
         // Power management
-        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
+        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
 
         // Start the backup passes going
         setBackupEnabled(areEnabled);
@@ -1363,6 +1364,7 @@
                         ? IApplicationThread.BACKUP_MODE_FULL
                         : IApplicationThread.BACKUP_MODE_INCREMENTAL;
                 try {
+                    mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
                     agent = bindToAgentSynchronous(request.appInfo, mode);
                     if (agent != null) {
                         int result = processOneBackup(request, agent, transport);
@@ -1378,6 +1380,8 @@
                 }
             }
 
+            mWakelock.setWorkSource(null);
+
             return BackupConstants.TRANSPORT_OK;
         }
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index a343c59..8452a9f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -52,6 +52,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.WorkSource;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
@@ -157,6 +158,12 @@
     private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
         new HashMap<String,ArrayList<UpdateRecord>>();
 
+    /**
+     * Temporary filled in when computing min time for a provider.  Access is
+     * protected by global lock mLock.
+     */
+    private final WorkSource mTmpWorkSource = new WorkSource();
+
     // Proximity listeners
     private Receiver mProximityReceiver = null;
     private ILocationListener mProximityListener = null;
@@ -913,7 +920,7 @@
         if (enabled) {
             p.enable();
             if (listeners > 0) {
-                p.setMinTime(getMinTimeLocked(provider));
+                p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
                 p.enableLocationTracking(true);
             }
         } else {
@@ -925,9 +932,21 @@
     private long getMinTimeLocked(String provider) {
         long minTime = Long.MAX_VALUE;
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+        mTmpWorkSource.clear();
         if (records != null) {
             for (int i=records.size()-1; i>=0; i--) {
-                minTime = Math.min(minTime, records.get(i).mMinTime);
+                UpdateRecord ur = records.get(i);
+                long curTime = ur.mMinTime;
+                if (curTime < minTime) {
+                    minTime = curTime;
+                }
+            }
+            long inclTime = (minTime*3)/2;
+            for (int i=records.size()-1; i>=0; i--) {
+                UpdateRecord ur = records.get(i);
+                if (ur.mMinTime <= inclTime) {
+                    mTmpWorkSource.add(ur.mUid);
+                }
             }
         }
         return minTime;
@@ -1125,7 +1144,7 @@
             boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
             if (isProviderEnabled) {
                 long minTimeForProvider = getMinTimeLocked(provider);
-                p.setMinTime(minTimeForProvider);
+                p.setMinTime(minTimeForProvider, mTmpWorkSource);
                 // try requesting single shot if singleShot is true, and fall back to
                 // regular location tracking if requestSingleShotFix() is not supported
                 if (!singleShot || !p.requestSingleShotFix()) {
@@ -1223,7 +1242,7 @@
                 LocationProviderInterface p = mProvidersByName.get(provider);
                 if (p != null) {
                     if (hasOtherListener) {
-                        p.setMinTime(getMinTimeLocked(provider));
+                        p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
                     } else {
                         p.enableLocationTracking(false);
                     }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2e32e60..4d68b52 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -50,6 +50,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.WorkSource;
 import android.provider.Settings.SettingNotFoundException;
 import android.provider.Settings;
 import android.util.EventLog;
@@ -310,7 +311,7 @@
                 long ident = Binder.clearCallingIdentity();
                 try {
                     PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
-                            MY_UID, MY_PID, mTag);
+                            MY_UID, MY_PID, mTag, null);
                     mHeld = true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -607,6 +608,7 @@
         final int uid;
         final int pid;
         final int monitorType;
+        WorkSource ws;
         boolean activated = true;
         int minState;
     }
@@ -630,35 +632,74 @@
                 || n == PowerManager.SCREEN_DIM_WAKE_LOCK;
     }
 
-    public void acquireWakeLock(int flags, IBinder lock, String tag) {
+    void enforceWakeSourcePermission(int uid, int pid) {
+        if (uid == Process.myUid()) {
+            return;
+        }
+        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+                pid, uid, null);
+    }
+
+    public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) {
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
         if (uid != Process.myUid()) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
         }
+        if (ws != null) {
+            enforceWakeSourcePermission(uid, pid);
+        }
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mLocks) {
-                acquireWakeLockLocked(flags, lock, uid, pid, tag);
+                acquireWakeLockLocked(flags, lock, uid, pid, tag, ws);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
-    public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) {
-        int acquireUid = -1;
-        int acquirePid = -1;
-        String acquireName = null;
-        int acquireType = -1;
+    void noteStartWakeLocked(WakeLock wl, WorkSource ws) {
+        try {
+            if (ws != null) {
+                mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag,
+                        wl.monitorType);
+            } else {
+                mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
+            }
+        } catch (RemoteException e) {
+            // Ignore
+        }
+    }
 
+    void noteStopWakeLocked(WakeLock wl, WorkSource ws) {
+        try {
+            if (ws != null) {
+                mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag,
+                        wl.monitorType);
+            } else {
+                mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
+            }
+        } catch (RemoteException e) {
+            // Ignore
+        }
+    }
+
+    public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag,
+            WorkSource ws) {
         if (mSpew) {
             Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag);
         }
 
+        if (ws != null && ws.size() == 0) {
+            ws = null;
+        }
+
         int index = mLocks.getIndex(lock);
         WakeLock wl;
         boolean newlock;
+        boolean diffsource;
+        WorkSource oldsource;
         if (index < 0) {
             wl = new WakeLock(flags, lock, tag, uid, pid);
             switch (wl.flags & LOCK_MASK)
@@ -687,10 +728,31 @@
                     return;
             }
             mLocks.addLock(wl);
+            if (ws != null) {
+                wl.ws = new WorkSource(ws);
+            }
             newlock = true;
+            diffsource = false;
+            oldsource = null;
         } else {
             wl = mLocks.get(index);
             newlock = false;
+            oldsource = wl.ws;
+            if (oldsource != null) {
+                if (ws == null) {
+                    wl.ws = null;
+                    diffsource = true;
+                } else {
+                    diffsource = oldsource.diff(ws);
+                }
+            } else if (ws != null) {
+                diffsource = true;
+            } else {
+                diffsource = false;
+            }
+            if (diffsource) {
+                wl.ws = new WorkSource(ws);
+            }
         }
         if (isScreenLock(flags)) {
             // if this causes a wakeup, we reactivate all of the locks and
@@ -731,19 +793,41 @@
                 enableProximityLockLocked();
             }
         }
-        if (newlock) {
-            acquireUid = wl.uid;
-            acquirePid = wl.pid;
-            acquireName = wl.tag;
-            acquireType = wl.monitorType;
-        }
 
-        if (acquireType >= 0) {
-            try {
-                mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType);
-            } catch (RemoteException e) {
-                // Ignore
+        if (diffsource) {
+            // If the lock sources have changed, need to first release the
+            // old ones.
+            noteStopWakeLocked(wl, oldsource);
+        }
+        if (newlock || diffsource) {
+            noteStartWakeLocked(wl, ws);
+        }
+    }
+
+    public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
+        int uid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
+        if (ws != null && ws.size() == 0) {
+            ws = null;
+        }
+        if (ws != null) {
+            enforceWakeSourcePermission(uid, pid);
+        }
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLocks) {
+                int index = mLocks.getIndex(lock);
+                if (index < 0) {
+                    throw new IllegalArgumentException("Wake lock not active");
+                }
+                WakeLock wl = mLocks.get(index);
+                WorkSource oldsource = wl.ws;
+                wl.ws = ws != null ? new WorkSource(ws) : null;
+                noteStopWakeLocked(wl, oldsource);
+                noteStartWakeLocked(wl, ws);
             }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -759,11 +843,6 @@
     }
 
     private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
-        int releaseUid;
-        int releasePid;
-        String releaseName;
-        int releaseType;
-
         WakeLock wl = mLocks.removeLock(lock);
         if (wl == null) {
             return;
@@ -804,17 +883,11 @@
         }
         // Unlink the lock from the binder.
         wl.binder.unlinkToDeath(wl, 0);
-        releaseUid = wl.uid;
-        releasePid = wl.pid;
-        releaseName = wl.tag;
-        releaseType = wl.monitorType;
 
-        if (releaseType >= 0) {
+        if (wl.monitorType >= 0) {
             long origId = Binder.clearCallingIdentity();
             try {
-                mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType);
-            } catch (RemoteException e) {
-                // Ignore
+                noteStopWakeLocked(wl, wl.ws);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 2e7e3e1..f0b5955 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -29,6 +29,7 @@
 import android.os.IBinder;
 import android.os.Binder;
 import android.os.SystemClock;
+import android.os.WorkSource;
 import android.util.Slog;
 
 import java.util.LinkedList;
@@ -39,6 +40,7 @@
 
     private final LinkedList<Vibration> mVibrations;
     private Vibration mCurrentVibration;
+    private final WorkSource mTmpWorkSource = new WorkSource();
 
     private class Vibration implements IBinder.DeathRecipient {
         private final IBinder mToken;
@@ -46,22 +48,24 @@
         private final long    mStartTime;
         private final long[]  mPattern;
         private final int     mRepeat;
+        private final int     mUid;
 
-        Vibration(IBinder token, long millis) {
-            this(token, millis, null, 0);
+        Vibration(IBinder token, long millis, int uid) {
+            this(token, millis, null, 0, uid);
         }
 
-        Vibration(IBinder token, long[] pattern, int repeat) {
-            this(token, 0, pattern, repeat);
+        Vibration(IBinder token, long[] pattern, int repeat, int uid) {
+            this(token, 0, pattern, repeat, uid);
         }
 
         private Vibration(IBinder token, long millis, long[] pattern,
-                int repeat) {
+                int repeat, int uid) {
             mToken = token;
             mTimeout = millis;
             mStartTime = SystemClock.uptimeMillis();
             mPattern = pattern;
             mRepeat = repeat;
+            mUid = uid;
         }
 
         public void binderDied() {
@@ -98,7 +102,7 @@
         mContext = context;
         PowerManager pm = (PowerManager)context.getSystemService(
                 Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
         mWakeLock.setReferenceCounted(true);
 
         mVibrations = new LinkedList<Vibration>();
@@ -113,6 +117,7 @@
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
         }
+        int uid = Binder.getCallingUid();
         // We're running in the system server so we cannot crash. Check for a
         // timeout of 0 or negative. This will ensure that a vibration has
         // either a timeout of > 0 or a non-null pattern.
@@ -122,7 +127,7 @@
             // longer than milliseconds.
             return;
         }
-        Vibration vib = new Vibration(token, milliseconds);
+        Vibration vib = new Vibration(token, milliseconds, uid);
         synchronized (mVibrations) {
             removeVibrationLocked(token);
             doCancelVibrateLocked();
@@ -146,6 +151,7 @@
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
         }
+        int uid = Binder.getCallingUid();
         // so wakelock calls will succeed
         long identity = Binder.clearCallingIdentity();
         try {
@@ -165,7 +171,7 @@
                 return;
             }
 
-            Vibration vib = new Vibration(token, pattern, repeat);
+            Vibration vib = new Vibration(token, pattern, repeat, uid);
             try {
                 token.linkToDeath(vib, 0);
             } catch (RemoteException e) {
@@ -280,6 +286,8 @@
 
         VibrateThread(Vibration vib) {
             mVibration = vib;
+            mTmpWorkSource.set(vib.mUid);
+            mWakeLock.setWorkSource(mTmpWorkSource);
             mWakeLock.acquire();
         }
 
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 6ecc511..e8502fa 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -50,6 +50,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -1035,8 +1036,8 @@
     }
 
     private class WifiLock extends DeathRecipient {
-        WifiLock(int lockMode, String tag, IBinder binder) {
-            super(lockMode, tag, binder);
+        WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
+            super(lockMode, tag, binder, ws);
         }
 
         public void binderDied() {
@@ -1106,33 +1107,70 @@
         }
     }
 
-    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
+    void enforceWakeSourcePermission(int uid, int pid) {
+        if (uid == android.os.Process.myUid()) {
+            return;
+        }
+        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+                pid, uid, null);
+    }
+
+    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
         if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
             return false;
         }
-        WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
+        if (ws != null) {
+            enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
+        }
+        if (ws != null && ws.size() == 0) {
+            ws = null;
+        }
+        if (ws == null) {
+            ws = new WorkSource(Binder.getCallingUid());
+        }
+        WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
         synchronized (mLocks) {
             return acquireWifiLockLocked(wifiLock);
         }
     }
 
+    private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
+        switch(wifiLock.mMode) {
+            case WifiManager.WIFI_MODE_FULL:
+                mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+                break;
+            case WifiManager.WIFI_MODE_SCAN_ONLY:
+                mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+                break;
+        }
+    }
+
+    private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
+        switch(wifiLock.mMode) {
+            case WifiManager.WIFI_MODE_FULL:
+                mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
+                break;
+            case WifiManager.WIFI_MODE_SCAN_ONLY:
+                mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource);
+                break;
+        }
+    }
+
     private boolean acquireWifiLockLocked(WifiLock wifiLock) {
         Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
 
         mLocks.addLock(wifiLock);
 
-        int uid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
         try {
+            noteAcquireWifiLock(wifiLock);
             switch(wifiLock.mMode) {
             case WifiManager.WIFI_MODE_FULL:
                 ++mFullLocksAcquired;
-                mBatteryStats.noteFullWifiLockAcquired(uid);
                 break;
             case WifiManager.WIFI_MODE_SCAN_ONLY:
                 ++mScanLocksAcquired;
-                mBatteryStats.noteScanWifiLockAcquired(uid);
                 break;
             }
         } catch (RemoteException e) {
@@ -1144,6 +1182,33 @@
         return true;
     }
 
+    public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
+        int uid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
+        if (ws != null && ws.size() == 0) {
+            ws = null;
+        }
+        if (ws != null) {
+            enforceWakeSourcePermission(uid, pid);
+        }
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLocks) {
+                int index = mLocks.findLockByBinder(lock);
+                if (index < 0) {
+                    throw new IllegalArgumentException("Wifi lock not active");
+                }
+                WifiLock wl = mLocks.mList.get(index);
+                noteReleaseWifiLock(wl);
+                wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
+                noteAcquireWifiLock(wl);
+            }
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     public boolean releaseWifiLock(IBinder lock) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
         synchronized (mLocks) {
@@ -1161,17 +1226,15 @@
         hadLock = (wifiLock != null);
 
         if (hadLock) {
-            int uid = Binder.getCallingUid();
             long ident = Binder.clearCallingIdentity();
             try {
+                noteAcquireWifiLock(wifiLock);
                 switch(wifiLock.mMode) {
                     case WifiManager.WIFI_MODE_FULL:
                         ++mFullLocksReleased;
-                        mBatteryStats.noteFullWifiLockReleased(uid);
                         break;
                     case WifiManager.WIFI_MODE_SCAN_ONLY:
                         ++mScanLocksReleased;
-                        mBatteryStats.noteScanWifiLockReleased(uid);
                         break;
                 }
             } catch (RemoteException e) {
@@ -1189,12 +1252,14 @@
         String mTag;
         int mMode;
         IBinder mBinder;
+        WorkSource mWorkSource;
 
-        DeathRecipient(int mode, String tag, IBinder binder) {
+        DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
             super();
             mTag = tag;
             mMode = mode;
             mBinder = binder;
+            mWorkSource = ws;
             try {
                 mBinder.linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -1209,7 +1274,7 @@
 
     private class Multicaster extends DeathRecipient {
         Multicaster(String tag, IBinder binder) {
-            super(Binder.getCallingUid(), tag, binder);
+            super(Binder.getCallingUid(), tag, binder, null);
         }
 
         public void binderDied() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 73c1790..5432890 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6021,12 +6021,18 @@
                             finisher = new IIntentReceiver.Stub() {
                                 public void performReceive(Intent intent, int resultCode,
                                         String data, Bundle extras, boolean ordered,
-                                        boolean sticky)
-                                        throws RemoteException {
-                                    synchronized (ActivityManagerService.this) {
-                                        mDidUpdate = true;
-                                    }
-                                    systemReady(goingCallback);
+                                        boolean sticky) {
+                                    // The raw IIntentReceiver interface is called
+                                    // with the AM lock held, so redispatch to
+                                    // execute our code without the lock.
+                                    mHandler.post(new Runnable() {
+                                        public void run() {
+                                            synchronized (ActivityManagerService.this) {
+                                                mDidUpdate = true;
+                                            }
+                                            systemReady(goingCallback);
+                                        }
+                                    });
                                 }
                             };
                         }
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index a0c21dd..4fc8020 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1104,7 +1104,7 @@
         // Okay we are now going to start a switch, to 'next'.  We may first
         // have to pause the current activity, but this is an important point
         // where we have decided to go to 'next' so keep track of that.
-        if (mLastStartedActivity != null) {
+        if (mLastStartedActivity != null && !mLastStartedActivity.finishing) {
             long now = SystemClock.uptimeMillis();
             final boolean inTime = mLastStartedActivity.startTime != 0
                     && (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 7314e04..bb40967 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -23,6 +23,7 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.os.ServiceManager;
+import android.os.WorkSource;
 import android.telephony.SignalStrength;
 import android.util.Slog;
 
@@ -107,6 +108,20 @@
         }
     }
 
+    public void noteStartWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteStartWakeFromSourceLocked(ws, pid, name, type);
+        }
+    }
+
+    public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteStopWakeFromSourceLocked(ws, pid, name, type);
+        }
+    }
+
     public void noteStartSensor(int uid, int sensor) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -317,6 +332,48 @@
         }
     }
 
+    public void noteFullWifiLockAcquiredFromSource(WorkSource ws) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteFullWifiLockAcquiredFromSourceLocked(ws);
+        }
+    }
+
+    public void noteFullWifiLockReleasedFromSource(WorkSource ws) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteFullWifiLockReleasedFromSourceLocked(ws);
+        }
+    }
+
+    public void noteScanWifiLockAcquiredFromSource(WorkSource ws) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteScanWifiLockAcquiredFromSourceLocked(ws);
+        }
+    }
+
+    public void noteScanWifiLockReleasedFromSource(WorkSource ws) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteScanWifiLockReleasedFromSourceLocked(ws);
+        }
+    }
+
+    public void noteWifiMulticastEnabledFromSource(WorkSource ws) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteWifiMulticastEnabledFromSourceLocked(ws);
+        }
+    }
+
+    public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteWifiMulticastDisabledFromSourceLocked(ws);
+        }
+    }
+
     public boolean isOnBattery() {
         return mStats.isOnBattery();
     }
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index c1165c7..3bf6ee4 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -44,6 +44,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.WorkSource;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -736,7 +737,7 @@
         startNavigating(true);
     }
 
-    public void setMinTime(long minTime) {
+    public void setMinTime(long minTime, WorkSource ws) {
         if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
         
         if (minTime >= 0) {
@@ -779,7 +780,7 @@
     public void addListener(int uid) {
         synchronized (mWakeLock) {
             mPendingListenerMessages++;
-           mWakeLock.acquire();
+            mWakeLock.acquire();
             Message m = Message.obtain(mHandler, ADD_LISTENER);
             m.arg1 = uid;
             mHandler.sendMessage(m);
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 084ab81..858a582 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -20,6 +20,7 @@
 import android.location.Location;
 import android.net.NetworkInfo;
 import android.os.Bundle;
+import android.os.WorkSource;
 
 /**
  * Location Manager's interface for location providers.
@@ -47,7 +48,7 @@
     /* returns false if single shot is not supported */
     boolean requestSingleShotFix();
     String getInternalState();
-    void setMinTime(long minTime);
+    void setMinTime(long minTime, WorkSource ws);
     void updateNetworkState(int state, NetworkInfo info);
     void updateLocation(Location location);
     boolean sendExtraCommand(String command, Bundle extras);
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 24d7737..7dc9920 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -29,6 +29,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.WorkSource;
 import android.util.Log;
 
 import com.android.internal.location.DummyLocationProvider;
@@ -52,6 +53,7 @@
     private boolean mLocationTracking = false;
     private boolean mEnabled = false;
     private long mMinTime = -1;
+    private WorkSource mMinTimeSource = new WorkSource();
     private int mNetworkState;
     private NetworkInfo mNetworkInfo;
 
@@ -122,7 +124,7 @@
                     provider.enableLocationTracking(true);
                 }
                 if (mMinTime >= 0) {
-                    provider.setMinTime(mMinTime);
+                    provider.setMinTime(mMinTime, mMinTimeSource);
                 }
                 if (mNetworkInfo != null) {
                     provider.updateNetworkState(mNetworkState, mNetworkInfo);
@@ -318,6 +320,7 @@
         mLocationTracking = enable;
         if (!enable) {
             mMinTime = -1;
+            mMinTimeSource.clear();
         }
         ILocationProvider provider;
         synchronized (mServiceConnection) {
@@ -339,15 +342,16 @@
         return mMinTime;
     }
 
-    public void setMinTime(long minTime) {
-       mMinTime = minTime;
+    public void setMinTime(long minTime, WorkSource ws) {
+        mMinTime = minTime;
+        mMinTimeSource.set(ws);
         ILocationProvider provider;
         synchronized (mServiceConnection) {
             provider = mProvider;
         }
         if (provider != null) {
             try {
-                provider.setMinTime(minTime);
+                provider.setMinTime(minTime, ws);
             } catch (RemoteException e) {
             }
         }
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 01b34b7..09d799f 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -23,6 +23,7 @@
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.WorkSource;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 
@@ -201,7 +202,7 @@
         return false;
     }
 
-    public void setMinTime(long minTime) {
+    public void setMinTime(long minTime, WorkSource ws) {
     }
 
     public void updateNetworkState(int state, NetworkInfo info) {
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index 7fc93f8..ea0d1b0 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -24,6 +24,7 @@
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.WorkSource;
 import android.util.Log;
 
 /**
@@ -123,7 +124,7 @@
         return false;
     }
 
-    public void setMinTime(long minTime) {
+    public void setMinTime(long minTime, WorkSource ws) {
     }
 
     public void updateNetworkState(int state, NetworkInfo info) {
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
index b4dc190..5233677 100644
--- a/tests/DumpRenderTree2/AndroidManifest.xml
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -40,6 +40,7 @@
         </activity>
 
         <activity android:name=".LayoutTestsExecutor"
+                  android:theme="@style/WhiteBackground"
                   android:label="Layout tests' executor"
                   android:process=":executor">
         </activity>
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
index bd6e457..c584d596 100755
--- a/tests/DumpRenderTree2/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -3,29 +3,30 @@
 """Run layout tests on the device.
 
   It runs the specified tests on the device, downloads the summaries to the temporary directory
-  and opens html details in the default browser.
+  and optionally shows the detailed results the host's default browser.
 
   Usage:
-    run_layout_tests.py PATH
+    run_layout_tests.py --show-results-in-browser test-relative-path
 """
 
-import sys
-import os
-import subprocess
 import logging
-import webbrowser
+import optparse
+import os
+import sys
+import subprocess
 import tempfile
+import webbrowser
 
 #TODO: These should not be hardcoded
 RESULTS_ABSOLUTE_PATH = "/sdcard/layout-test-results/"
 DETAILS_HTML = "details.html"
 SUMMARY_TXT = "summary.txt"
 
-def main():
-  if len(sys.argv) > 1:
-    path = sys.argv[1]
+def main(options, args):
+  if args:
+    path = " ".join(args);
   else:
-    path = ""
+    path = "";
 
   logging.basicConfig(level=logging.INFO, format='%(message)s')
 
@@ -61,7 +62,12 @@
   logging.info("")
 
   # Open the browser with summary
-  webbrowser.open(details_html_tmp_path)
+  if options.show_results_in_browser != "false":
+    webbrowser.open(details_html_tmp_path)
 
 if __name__ == "__main__":
-  main();
+  option_parser = optparse.OptionParser(usage="Usage: %prog [options] test-relative-path")
+  option_parser.add_option("", "--show-results-in-browser", default="true",
+                           help="Show the results the host's default web browser, default=true")
+  options, args = option_parser.parse_args();
+  main(options, args);
diff --git a/tests/DumpRenderTree2/res/values/style.xml b/tests/DumpRenderTree2/res/values/style.xml
new file mode 100644
index 0000000..35f3419
--- /dev/null
+++ b/tests/DumpRenderTree2/res/values/style.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 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.
+-->
+<resources>
+    <style name="WhiteBackground">
+        <item name="android:background">@android:color/white</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
index a82037e..c9e194f 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -35,12 +35,9 @@
         mLayoutTestsExecutor = layoutTestsExecutor;
     }
 
-    public void waitUntilDone() {
-        mLayoutTestsExecutor.waitUntilDone();
-    }
-
-    public void notifyDone() {
-        mLayoutTestsExecutor.notifyDone();
+    public void clearAllDatabases() {
+        Log.i(LOG_TAG, "clearAllDatabases() called");
+        WebStorage.getInstance().deleteAllData();
     }
 
     public void dumpAsText() {
@@ -55,19 +52,22 @@
         mLayoutTestsExecutor.dumpChildFramesAsText();
     }
 
-    public void clearAllDatabases() {
-        Log.i(LOG_TAG, "clearAllDatabases() called");
-        WebStorage.getInstance().deleteAllData();
+    public void dumpDatabaseCallbacks() {
+        mLayoutTestsExecutor.dumpDatabaseCallbacks();
+    }
+
+    public void notifyDone() {
+        mLayoutTestsExecutor.notifyDone();
+    }
+
+    public void overridePreference(String key, boolean value) {
+        mLayoutTestsExecutor.overridePreference(key, value);
     }
 
     public void setCanOpenWindows() {
         mLayoutTestsExecutor.setCanOpenWindows();
     }
 
-    public void dumpDatabaseCallbacks() {
-        mLayoutTestsExecutor.dumpDatabaseCallbacks();
-    }
-
     public void setDatabaseQuota(long quota) {
         /** TODO: Reset this before every test! */
         Log.i(LOG_TAG, "setDatabaseQuota() called with: " + quota);
@@ -79,21 +79,6 @@
         mLayoutTestsExecutor.setGeolocationPermission(allow);
     }
 
-    public void overridePreference(String key, boolean value) {
-        mLayoutTestsExecutor.overridePreference(key, value);
-    }
-
-    public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
-        Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
-                " longitude=" + longitude + " accuracy=" + accuracy);
-        MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
-    }
-
-    public void setMockGeolocationError(int code, String message) {
-        Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
-        MockGeolocation.getInstance().setError(code, message);
-    }
-
     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
         // Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
@@ -104,4 +89,19 @@
         mLayoutTestsExecutor.setMockDeviceOrientation(
                 canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
     }
-}
\ No newline at end of file
+
+    public void setMockGeolocationError(int code, String message) {
+        Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
+        MockGeolocation.getInstance().setError(code, message);
+    }
+
+    public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
+        Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
+                " longitude=" + longitude + " accuracy=" + accuracy);
+        MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
+    }
+
+    public void waitUntilDone() {
+        mLayoutTestsExecutor.waitUntilDone();
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index c997911..4c7124b 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -519,24 +519,12 @@
             assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
 
             switch (msg.what) {
-                case MSG_WAIT_UNTIL_DONE:
-                    mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
-                    break;
-
-                case MSG_NOTIFY_DONE:
-                    if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
-                        onTestFinished();
-                    }
-                    break;
-
                 case MSG_DUMP_AS_TEXT:
                     if (mCurrentResult == null) {
                         mCurrentResult = new TextResult(mCurrentTestRelativePath);
                     }
-
                     assert mCurrentResult instanceof TextResult
                             : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
-
                     break;
 
                 case MSG_DUMP_CHILD_FRAMES_AS_TEXT:
@@ -551,27 +539,13 @@
                     ((TextResult)mCurrentResult).setDumpChildFramesAsText(true);
                     break;
 
-                case MSG_SET_CAN_OPEN_WINDOWS:
-                    mCanOpenWindows = true;
-                    break;
-
                 case MSG_DUMP_DATABASE_CALLBACKS:
                     mDumpDatabaseCallbacks = true;
                     break;
 
-                case MSG_SET_GEOLOCATION_PERMISSION:
-                    mIsGeolocationPermissionSet = true;
-                    mGeolocationPermission = msg.arg1 == 1;
-
-                    if (mPendingGeolocationPermissionCallbacks != null) {
-                        Iterator<GeolocationPermissions.Callback> iter =
-                                mPendingGeolocationPermissionCallbacks.keySet().iterator();
-                        while (iter.hasNext()) {
-                            GeolocationPermissions.Callback callback = iter.next();
-                            String origin = mPendingGeolocationPermissionCallbacks.get(callback);
-                            callback.invoke(origin, mGeolocationPermission, false);
-                        }
-                        mPendingGeolocationPermissionCallbacks = null;
+                case MSG_NOTIFY_DONE:
+                    if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
+                        onTestFinished();
                     }
                     break;
 
@@ -591,6 +565,30 @@
                     }
                     break;
 
+                case MSG_SET_CAN_OPEN_WINDOWS:
+                    mCanOpenWindows = true;
+                    break;
+
+                case MSG_SET_GEOLOCATION_PERMISSION:
+                    mIsGeolocationPermissionSet = true;
+                    mGeolocationPermission = msg.arg1 == 1;
+
+                    if (mPendingGeolocationPermissionCallbacks != null) {
+                        Iterator<GeolocationPermissions.Callback> iter =
+                                mPendingGeolocationPermissionCallbacks.keySet().iterator();
+                        while (iter.hasNext()) {
+                            GeolocationPermissions.Callback callback = iter.next();
+                            String origin = mPendingGeolocationPermissionCallbacks.get(callback);
+                            callback.invoke(origin, mGeolocationPermission, false);
+                        }
+                        mPendingGeolocationPermissionCallbacks = null;
+                    }
+                    break;
+
+                case MSG_WAIT_UNTIL_DONE:
+                    mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
+                    break;
+
                 default:
                     assert false : "msg.what=" + msg.what;
                     break;
@@ -605,16 +603,6 @@
         mPendingGeolocationPermissionCallbacks = null;
     }
 
-    public void waitUntilDone() {
-        Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
-        mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
-    }
-
-    public void notifyDone() {
-        Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
-        mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
-    }
-
     public void dumpAsText(boolean enablePixelTest) {
         Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpAsText(" + enablePixelTest + ") called");
         /** TODO: Implement */
@@ -629,22 +617,14 @@
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
     }
 
-    public void setCanOpenWindows() {
-        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
-        mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
-    }
-
     public void dumpDatabaseCallbacks() {
         Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
     }
 
-    public void setGeolocationPermission(boolean allow) {
-        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
-                ") called");
-        Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
-        msg.arg1 = allow ? 1 : 0;
-        msg.sendToTarget();
+    public void notifyDone() {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
     }
 
     public void overridePreference(String key, boolean value) {
@@ -656,6 +636,19 @@
         msg.sendToTarget();
     }
 
+    public void setCanOpenWindows() {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
+    }
+
+    public void setGeolocationPermission(boolean allow) {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
+                ") called");
+        Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
+        msg.arg1 = allow ? 1 : 0;
+        msg.sendToTarget();
+    }
+
     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
         Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
@@ -664,4 +657,9 @@
         mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
                 canProvideGamma, gamma);
     }
+
+    public void waitUntilDone() {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
+    }
 }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index d30a723..5597415 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -118,6 +118,16 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name="ThinPatchesActivity"
+                android:label="_9patchThin"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         
         <activity
                 android:name="NinePatchesActivity"
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png b/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
new file mode 100644
index 0000000..26ee1c2
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png b/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
new file mode 100644
index 0000000..53e95af
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
index 5b7753e..d9a2893 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
@@ -39,6 +39,7 @@
             super(c);
 
             mLayerPaint = new Paint();
+            mLayerPaint.setAlpha(127);
             mRectPaint = new Paint();
             mRectPaint.setAntiAlias(true);
             mRectPaint.setTextSize(24.0f);
@@ -47,28 +48,21 @@
         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
-            
             canvas.drawRGB(128, 255, 128);
+
+            canvas.save();
+
             canvas.translate(140.0f, 100.0f);
-            
-            mLayerPaint.setAlpha(127);
-            int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
-                    Canvas.ALL_SAVE_FLAG);
-
-            mRectPaint.setColor(0x7fff0000);
-            canvas.drawRect(-20.0f, -20.0f, 220.0f, 120.0f, mRectPaint);
-
-            mRectPaint.setColor(0xff000000);
-            canvas.drawText("This is a very long string to overlap between layers and framebuffer",
-                    -100.0f, 50.0f, mRectPaint);
-            
-            canvas.restoreToCount(count);
+            drawStuff(canvas, Canvas.ALL_SAVE_FLAG);
 
             canvas.translate(0.0f, 200.0f);
+            drawStuff(canvas, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
             
-            mLayerPaint.setAlpha(127);
-            count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
-                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+            canvas.restore();
+        }
+
+        private void drawStuff(Canvas canvas, int saveFlags) {
+            int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint, saveFlags);
 
             mRectPaint.setColor(0x7fff0000);
             canvas.drawRect(-20.0f, -20.0f, 220.0f, 120.0f, mRectPaint);
@@ -76,7 +70,7 @@
             mRectPaint.setColor(0xff000000);
             canvas.drawText("This is a very long string to overlap between layers and framebuffer",
                     -100.0f, 50.0f, mRectPaint);
-            
+
             canvas.restoreToCount(count);
         }
     }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
new file mode 100644
index 0000000..d374c32
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ThinPatchesActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        FrameLayout layout = new FrameLayout(this);
+        PatchView b = new PatchView(this);
+        b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT));
+        layout.addView(b);
+        layout.setBackgroundColor(0xffffffff);
+        
+        setContentView(layout);
+    }
+
+    private class PatchView extends View {
+        private Drawable mPatch;
+
+        public PatchView(Activity activity) {
+            super(activity);
+
+            final Resources resources = activity.getResources();
+            mPatch = resources.getDrawable(R.drawable.btn_toggle_on);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            final int width = 100;
+            final int height = 60;
+
+            final int left = (getWidth() - width) / 2;
+            final int top  = (getHeight() - height) / 2;
+
+            mPatch.setBounds(left, top, left + width, top + height);
+            mPatch.draw(canvas);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1913fa0..e73bca0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -21,6 +21,8 @@
 import android.net.wifi.ScanResult;
 import android.net.DhcpInfo;
 
+import android.os.WorkSource;
+
 /**
  * Interface that allows controlling and querying Wi-Fi connectivity.
  *
@@ -66,7 +68,9 @@
 
     DhcpInfo getDhcpInfo();
 
-    boolean acquireWifiLock(IBinder lock, int lockType, String tag);
+    boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws);
+
+    void updateWifiLockWorkSource(IBinder lock, in WorkSource ws);
 
     boolean releaseWifiLock(IBinder lock);
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8c3ec5f..26ed878 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -23,6 +23,7 @@
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.WorkSource;
 
 import java.util.List;
 
@@ -1050,6 +1051,7 @@
         int mLockType;
         private boolean mRefCounted;
         private boolean mHeld;
+        private WorkSource mWorkSource;
 
         private WifiLock(int lockType, String tag) {
             mTag = tag;
@@ -1075,7 +1077,7 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
                     try {
-                        mService.acquireWifiLock(mBinder, mLockType, mTag);
+                        mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
                                 mService.releaseWifiLock(mBinder);
@@ -1147,6 +1149,32 @@
             }
         }
 
+        public void setWorkSource(WorkSource ws) {
+            synchronized (mBinder) {
+                if (ws != null && ws.size() == 0) {
+                    ws = null;
+                }
+                boolean changed = true;
+                if (ws == null) {
+                    mWorkSource = null;
+                } else if (mWorkSource == null) {
+                    changed = mWorkSource != null;
+                    mWorkSource = new WorkSource(ws);
+                } else {
+                    changed = mWorkSource.diff(ws);
+                    if (changed) {
+                        mWorkSource.set(ws);
+                    }
+                }
+                if (changed && mHeld) {
+                    try {
+                        mService.updateWifiLockWorkSource(mBinder, mWorkSource);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+        }
+
         public String toString() {
             String s1, s2, s3;
             synchronized (mBinder) {