Merge "Adding enable/disable option for sendCommand()"
diff --git a/api/current.xml b/api/current.xml
index ed51bbf..310944f 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"
@@ -233790,6 +234035,17 @@
  visibility="public"
 >
 </method>
+<method name="isIconified"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isSubmitButtonEnabled"
  return="boolean"
  abstract="false"
@@ -233801,6 +234057,19 @@
  visibility="public"
 >
 </method>
+<method name="setIconified"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="iconify" type="boolean">
+</parameter>
+</method>
 <method name="setIconifiedByDefault"
  return="void"
  abstract="false"
@@ -233814,6 +234083,19 @@
 <parameter name="iconified" type="boolean">
 </parameter>
 </method>
+<method name="setOnCloseListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.SearchView.OnCloseListener">
+</parameter>
+</method>
 <method name="setOnQueryChangeListener"
  return="void"
  abstract="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/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/database/sqlite/DatabaseConnectionPool.java b/core/java/android/database/sqlite/DatabaseConnectionPool.java
index aede438..54b0605 100644
--- a/core/java/android/database/sqlite/DatabaseConnectionPool.java
+++ b/core/java/android/database/sqlite/DatabaseConnectionPool.java
@@ -99,7 +99,7 @@
                         poolObj = mPool.get(0);
                     } else {
                         for (int i = 0; i < mMaxPoolSize; i++) {
-                            if (mPool.get(i).mDb.mCache.isSqlInStatementCache(sql)) {
+                            if (mPool.get(i).mDb.isSqlInStatementCache(sql)) {
                                 poolObj = mPool.get(i);
                                 break;
                             }
@@ -125,8 +125,7 @@
                 // there are free connections available. pick one
                 // preferably a connection caching the pre-compiled statement of the given SQL
                 for (int i = 0; i < poolSize; i++) {
-                    if (mPool.get(i).isFree() &&
-                            mPool.get(i).mDb.mCache.isSqlInStatementCache(sql)) {
+                    if (mPool.get(i).isFree() && mPool.get(i).mDb.isSqlInStatementCache(sql)) {
                         poolObj = mPool.get(i);
                         break;
                     }
diff --git a/core/java/android/database/sqlite/SQLiteCache.java b/core/java/android/database/sqlite/SQLiteCache.java
deleted file mode 100644
index 049fb62..0000000
--- a/core/java/android/database/sqlite/SQLiteCache.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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 android.database.sqlite;
-
-import android.util.Log;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * For each instance of {@link SQLiteDatabase}, this class maintains a LRU cache to store
- * the compiled query statement ids returned by sqlite database.
- *<p>
- *<ul>
- *     <li>key = SQL statement with "?" for bind args</li>
- *     <li>value = {@link SQLiteCompiledSql}</li>
- *</ul>
- * If an application opens the database and keeps it open during its entire life, then
- * there will not be an overhead of compilation of SQL statements by sqlite.
- *<p>
- * Why is this cache NOT static? because sqlite attaches compiled-sql statements to the
- * database connections.
- *<p>
- * This cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
- * (@link #setMaxSqlCacheSize(int)}).
- */
-/* package */ class SQLiteCache {
-    private static final String TAG = "SQLiteCache";
-
-    /** The {@link SQLiteDatabase} instance this cache is attached to */
-    private final SQLiteDatabase mDatabase;
-
-    /** Default statement-cache size per database connection ( = instance of this class) */
-    private int mMaxSqlCacheSize = 25;
-
-    /** The LRU cache */
-    private final Map<String, SQLiteCompiledSql> mCompiledQueries =
-        new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
-            @Override
-            public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
-                // eldest = least-recently used entry
-                // if it needs to be removed to accommodate a new entry,
-                //     close {@link SQLiteCompiledSql} represented by this entry, if not in use
-                //     and then let it be removed from the Map.
-                mDatabase.verifyLockOwner();
-                if (this.size() <= mMaxSqlCacheSize) {
-                    // cache is not full. nothing needs to be removed
-                    return false;
-                }
-                // cache is full. eldest will be removed.
-                eldest.getValue().releaseIfNotInUse();
-                // return true, so that this entry is removed automatically by the caller.
-                return true;
-            }
-        };
-
-    /** Maintains whether or not cacheFullWarning has been logged */
-    private boolean mCacheFullWarning;
-
-    /** The following 2 members maintain stats about cache hits and misses */
-    private int mNumCacheHits;
-    private int mNumCacheMisses;
-
-    /**
-     * Constructor used by {@link SQLiteDatabase}.
-     * @param db
-     */
-    /* package */ SQLiteCache(SQLiteDatabase db) {
-        mDatabase = db;
-    }
-
-    /**
-     * Adds the given SQL and its compiled-statement to the cache, if the given SQL statement
-     * doesn't already exist in cache.
-     *
-     * @return true if added to cache. false otherwise.
-     */
-    /* package */ synchronized boolean addToCompiledQueries(String sql,
-            SQLiteCompiledSql compiledStatement) {
-        if (mCompiledQueries.containsKey(sql)) {
-            // already exists.
-            return false;
-        }
-
-        /* add the given SQLiteCompiledSql compiledStatement to cache.
-         * no need to worry about the cache size - because {@link #mCompiledQueries}
-         * self-limits its size to {@link #mMaxSqlCacheSize}.
-         */
-        mCompiledQueries.put(sql, compiledStatement);
-
-        // need to log a warning to say that the cache is full?
-        if (!isCacheFullWarningLogged() && mCompiledQueries.size() == mMaxSqlCacheSize) {
-            /*
-             * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
-             * log a warning.
-             * chances are it is NOT using ? for bindargs - or cachesize is too small.
-             */
-            Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
-                    mDatabase.getPath() +
-                    ". Use setMaxSqlCacheSize() in SQLiteDatabase to increase cachesize. ");
-            setCacheFullWarningLogged();
-        }
-        return true;
-    }
-
-    /**
-     * Returns the compiled-statement for the given SQL statement, if the entry exists in cache
-     * and is free to use. Returns null otherwise.
-     * <p>
-     * If a compiled-sql statement is returned for the caller, it is reserved for the caller.
-     * So, don't use this method unless the caller needs to acquire the object.
-     */
-    /* package */ synchronized SQLiteCompiledSql getCompiledStatementForSql(String sql) {
-        SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
-        if (compiledStatement == null) {
-            mNumCacheMisses++;
-            return null;
-        }
-        mNumCacheHits++;
-        // reserve it for the caller, if it is not already in use
-        if (!compiledStatement.acquire()) {
-            // couldn't acquire it since it is already in use. bug in app?
-            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-                Log.w(TAG, "Possible bug: Either using the same SQL in 2 threads at " +
-                        " the same time, or previous instance of this SQL statement was " +
-                        "never close()d. " + compiledStatement.toString());
-            }
-            return null;
-        }
-        return compiledStatement;
-    }
-
-    /**
-     * If the given statement is in cache, it is released back to cache and it is made available for
-     * others to use.
-     * <p>
-     * return true if the statement is put back in cache, false otherwise (false = the statement
-     * is NOT in cache)
-     */
-    /* package */ synchronized boolean releaseBackToCache(SQLiteCompiledSql stmt) {
-        if (!mCompiledQueries.containsValue(stmt)) {
-            return false;
-        }
-        // it is in cache. release it from the caller, make it available for others to use
-        stmt.free();
-        return true;
-    }
-
-    /**
-     * releases all compiled-sql statements in the cache.
-     */
-    /* package */ synchronized void dealloc() {
-        for (SQLiteCompiledSql stmt : mCompiledQueries.values()) {
-            stmt.setState(SQLiteCompiledSql.NSTATE_CACHE_DEALLOC);
-            stmt.releaseFromDatabase();
-        }
-        mCompiledQueries.clear();
-    }
-
-    /**
-     * see documentation on {@link SQLiteDatabase#setMaxSqlCacheSize(int)}.
-     */
-    /* package */ synchronized void setMaxSqlCacheSize(int cacheSize) {
-        if (cacheSize > SQLiteDatabase.MAX_SQL_CACHE_SIZE || cacheSize < 0) {
-            throw new IllegalStateException("expected value between 0 and " +
-                    SQLiteDatabase.MAX_SQL_CACHE_SIZE);
-        } else if (cacheSize < mMaxSqlCacheSize) {
-            throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
-                    "set with previous setMaxSqlCacheSize() call.");
-        }
-        mMaxSqlCacheSize = cacheSize;
-    }
-
-    /* package */ synchronized boolean isSqlInStatementCache(String sql) {
-        return mCompiledQueries.containsKey(sql);
-    }
-
-    private synchronized boolean isCacheFullWarningLogged() {
-        return mCacheFullWarning;
-    }
-
-    private synchronized void setCacheFullWarningLogged() {
-        mCacheFullWarning = true;
-    }
-    /* package */ synchronized int getCacheHitNum() {
-        return mNumCacheHits;
-    }
-    /* package */ synchronized int getCacheMissNum() {
-        return mNumCacheMisses;
-    }
-    /* package */ synchronized int getCachesize() {
-        return mCompiledQueries.size();
-    }
-
-    // only for testing
-    /* package */ synchronized Set<String> getKeys() {
-        return mCompiledQueries.keySet();
-    }
-}
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 120d090..0b5a4df 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -16,7 +16,6 @@
 
 package android.database.sqlite;
 
-import android.database.DatabaseUtils;
 import android.util.Log;
 
 /**
@@ -25,19 +24,19 @@
  * and it is released in one of the 2 following ways
  * 1. when {@link SQLiteDatabase} object is closed.
  * 2. if this is not cached in {@link SQLiteDatabase}, {@link android.database.Cursor#close()}
- * releases this obj.
+ * releaases this obj.
  */
 /* package */ class SQLiteCompiledSql {
 
     private static final String TAG = "SQLiteCompiledSql";
 
     /** The database this program is compiled against. */
-    /* package */ final SQLiteDatabase mDatabase;
+    /* package */ SQLiteDatabase mDatabase;
 
     /**
      * Native linkage, do not modify. This comes from the database.
      */
-    /* package */ final int nHandle;
+    /* package */ int nHandle = 0;
 
     /**
      * Native linkage, do not modify. When non-0 this holds a reference to a valid
@@ -47,53 +46,52 @@
      */
     /* package */ int nStatement = 0;
 
-    /** the following 3 members are for debugging purposes */
-    private final String mSqlStmt;
-    private final Throwable mStackTrace;
-    private int nState = 0;
-    /** values the above member can take */
-    private static final int NSTATE_CACHEABLE = 64;
-    private static final int NSTATE_IN_CACHE = 32;
-    private static final int NSTATE_INUSE = 16;
-    private static final int NSTATE_INUSE_RESETMASK = 0x0f;
-    /* package */ static final int NSTATE_CLOSE_NOOP = 1;
-    private static final int NSTATE_EVICTED_FROM_CACHE = 2;
-    /* package */ static final int NSTATE_CACHE_DEALLOC = 4;
-    private static final int NSTATE_IN_FINALIZER_Q = 8;
+    /** the following are for debugging purposes */
+    private String mSqlStmt = null;
+    private Throwable mStackTrace = null;
 
-    private SQLiteCompiledSql(SQLiteDatabase db, String sql) {
-        db.verifyDbIsOpen();
-        db.verifyLockOwner();
+    /** when in cache and is in use, this member is set */
+    private boolean mInUse = false;
+
+    /* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) {
+        if (!db.isOpen()) {
+            throw new IllegalStateException("database " + db.getPath() + " already closed");
+        }
         mDatabase = db;
         mSqlStmt = sql;
         mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
-        nHandle = db.mNativeHandle;
-        native_compile(sql);
+        this.nHandle = db.mNativeHandle;
+        compile(sql, true);
     }
 
-    /* package */ static SQLiteCompiledSql get(SQLiteDatabase db, String sql, int type) {
-        // only CRUD statements are cached.
-        if (type != DatabaseUtils.STATEMENT_SELECT && type != DatabaseUtils.STATEMENT_UPDATE) {
-            return new SQLiteCompiledSql(db, sql);
+    /**
+     * Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
+     * this method has been called previously without a call to close and forCompilation is set
+     * to false the previous compilation will be used. Setting forceCompilation to true will
+     * always re-compile the program and should be done if you pass differing SQL strings to this
+     * method.
+     *
+     * <P>Note: this method acquires the database lock.</P>
+     *
+     * @param sql the SQL string to compile
+     * @param forceCompilation forces the SQL to be recompiled in the event that there is an
+     *  existing compiled SQL program already around
+     */
+    private void compile(String sql, boolean forceCompilation) {
+        mDatabase.verifyLockOwner();
+        // Only compile if we don't have a valid statement already or the caller has
+        // explicitly requested a recompile.
+        if (forceCompilation) {
+            // Note that the native_compile() takes care of destroying any previously
+            // existing programs before it compiles.
+            native_compile(sql);
         }
-        // the given SQL statement is cacheable.
-        SQLiteCompiledSql stmt = db.mCache.getCompiledStatementForSql(sql);
-        if (stmt != null) {
-            return stmt;
-        }
-        // either the entry doesn't exist in cache or the one in cache is currently in use.
-        // try to add it to cache and let cache worry about what copy to keep
-        stmt = new SQLiteCompiledSql(db, sql);
-        stmt.nState |= NSTATE_CACHEABLE |
-                ((db.mCache.addToCompiledQueries(sql, stmt)) ? NSTATE_IN_CACHE : 0);
-        return stmt;
     }
 
-    /* package */ synchronized void releaseFromDatabase() {
+    /* package */ void releaseSqlStatement() {
         // Note that native_finalize() checks to make sure that nStatement is
         // non-null before destroying it.
         if (nStatement != 0) {
-            nState |= NSTATE_IN_FINALIZER_Q;
             mDatabase.finalizeStatementLater(nStatement);
             nStatement = 0;
         }
@@ -103,47 +101,20 @@
      * returns true if acquire() succeeds. false otherwise.
      */
     /* package */ synchronized boolean acquire() {
-        if ((nState & NSTATE_INUSE) > 0 ) {
-            // this object is already in use
+        if (mInUse) {
+            // someone already has acquired it.
             return false;
         }
-        nState |= NSTATE_INUSE;
+        mInUse = true;
         return true;
     }
 
-    /* package */ synchronized void free() {
-        nState &= NSTATE_INUSE_RESETMASK;
+    /* package */ synchronized void release() {
+        mInUse = false;
     }
 
-    /* package */ void release(int type) {
-        if (type != DatabaseUtils.STATEMENT_SELECT && type != DatabaseUtils.STATEMENT_UPDATE) {
-            // it is not cached. release its memory from the database.
-            releaseFromDatabase();
-            return;
-        }
-        // if in cache, reset its in-use flag
-        if (!mDatabase.mCache.releaseBackToCache(this)) {
-            // not in cache. release its memory from the database.
-            releaseFromDatabase();
-        }
-    }
-
-    /* package */ synchronized void releaseIfNotInUse() {
-        nState |= NSTATE_EVICTED_FROM_CACHE;
-        // if it is not in use, release its memory from the database
-        if ((nState & NSTATE_INUSE) == 0) {
-            releaseFromDatabase();
-        }
-    }
-
-    // only for testing purposes
     /* package */ synchronized boolean isInUse() {
-        return (nState & NSTATE_INUSE) > 0;
-    }
-
-    /* package */ synchronized SQLiteCompiledSql setState(int val) {
-        nState = nState & val;
-        return this; // for chaining
+        return mInUse;
     }
 
     /**
@@ -154,18 +125,11 @@
         try {
             if (nStatement == 0) return;
             // finalizer should NEVER get called
-            // but if the database itself is not closed and is GC'ed, then
-            // all sub-objects attached to the database could end up getting GC'ed too.
-            // in that case, don't print any warning.
-            if ((nState & NSTATE_INUSE) == 0) {
-                // no need to print warning
-            } else {
-                int len = mSqlStmt.length();
-                Log.w(TAG, "Releasing SQL statement in finalizer. " +
-                        "Could be due to close() not being called on the cursor or on the database. " +
-                        toString(), mStackTrace);
-            }
-            releaseFromDatabase();
+            int len = mSqlStmt.length();
+            Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
+                    "that you explicitly call close() on your cursor: " +
+                    mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
+            releaseSqlStatement();
         } finally {
             super.finalize();
         }
@@ -176,27 +140,6 @@
             StringBuilder buff = new StringBuilder();
             buff.append(" nStatement=");
             buff.append(nStatement);
-            if ((nState & NSTATE_CACHEABLE) > 0) {
-                buff.append(",cacheable");
-            }
-            if ((nState & NSTATE_IN_CACHE) > 0) {
-                buff.append(",cached");
-            }
-            if ((nState & NSTATE_INUSE) > 0) {
-                buff.append(",in_use");
-            }
-            if ((nState & NSTATE_CLOSE_NOOP) > 0) {
-                buff.append(",no_op_close");
-            }
-            if ((nState & NSTATE_EVICTED_FROM_CACHE) > 0) {
-                buff.append(",evicted_from_cache");
-            }
-            if ((nState & NSTATE_CACHE_DEALLOC) > 0) {
-                buff.append(",dealloc_cache");
-            }
-            if ((nState & NSTATE_IN_FINALIZER_Q) > 0) {
-                buff.append(",in dbFInalizerQ");
-            }
             buff.append(", db=");
             buff.append(mDatabase.getPath());
             buff.append(", db_connectionNum=");
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 70bd3d8..4200701 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -41,6 +41,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Random;
@@ -259,11 +260,60 @@
     private final WeakHashMap<SQLiteClosable, Object> mPrograms;
 
     /**
-     * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
-     * Size of each prepared-statement is between 1K - 6K, depending on the complexity of the
+     * for each instance of this class, a LRU cache is maintained to store
+     * the compiled query statement ids returned by sqlite database.
+     *     key = SQL statement with "?" for bind args
+     *     value = {@link SQLiteCompiledSql}
+     * If an application opens the database and keeps it open during its entire life, then
+     * there will not be an overhead of compilation of SQL statements by sqlite.
+     *
+     * why is this cache NOT static? because sqlite attaches compiledsql statements to the
+     * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
+     * invoked.
+     *
+     * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
+     * (@link #setMaxSqlCacheSize(int)}).
+     */
+    // default statement-cache size per database connection ( = instance of this class)
+    private int mMaxSqlCacheSize = 25;
+    /* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries =
+        new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
+            @Override
+            public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
+                // eldest = least-recently used entry
+                // if it needs to be removed to accommodate a new entry,
+                //     close {@link SQLiteCompiledSql} represented by this entry, if not in use
+                //     and then let it be removed from the Map.
+                // when this is called, the caller must be trying to add a just-compiled stmt
+                // to cache; i.e., caller should already have acquired database lock AND
+                // the lock on mCompiledQueries. do as assert of these two 2 facts.
+                verifyLockOwner();
+                if (this.size() <= mMaxSqlCacheSize) {
+                    // cache is not full. nothing needs to be removed
+                    return false;
+                }
+                // cache is full. eldest will be removed.
+                SQLiteCompiledSql entry = eldest.getValue();
+                if (!entry.isInUse()) {
+                    // this {@link SQLiteCompiledSql} is not in use. release it.
+                    entry.releaseSqlStatement();
+                }
+                // return true, so that this entry is removed automatically by the caller.
+                return true;
+            }
+        };
+    /**
+     * absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}
+     * size of each prepared-statement is between 1K - 6K, depending on the complexity of the
      * SQL statement & schema.
      */
     public static final int MAX_SQL_CACHE_SIZE = 100;
+    private int mCacheFullWarnings;
+    private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
+
+    /** maintain stats about number of cache hits and misses */
+    private int mNumCacheHits;
+    private int mNumCacheMisses;
 
     /** Used to find out where this object was created in case it never got closed. */
     private final Throwable mStackTrace;
@@ -272,9 +322,6 @@
     private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
     private final int mSlowQueryThreshold;
 
-    /** the LRU cache */
-    /* package */ final SQLiteCache mCache = new SQLiteCache(this);
-
     /** stores the list of statement ids that need to be finalized by sqlite */
     private final ArrayList<Integer> mClosedStatementIds = new ArrayList<Integer>();
 
@@ -1040,7 +1087,7 @@
          * to be closed. sqlite doesn't let a database close if there are
          * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
          */
-        mCache.dealloc();
+        deallocCachedSqlStatements();
 
         Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
         while (iter.hasNext()) {
@@ -1848,11 +1895,12 @@
         executeSql(sql, bindArgs);
     }
 
-    private void executeSql(String sql, Object[] bindArgs) throws SQLException {
+    private int executeSql(String sql, Object[] bindArgs) throws SQLException {
         long timeStart = SystemClock.uptimeMillis();
+        int n;
         SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
         try {
-            statement.executeUpdateDelete();
+            n = statement.executeUpdateDelete();
         } catch (SQLiteDatabaseCorruptException e) {
             onCorruption();
             throw e;
@@ -1860,6 +1908,7 @@
             statement.close();
         }
         logTimeStat(sql, timeStart);
+        return n;
     }
 
     @Override
@@ -2043,20 +2092,115 @@
         }
     }
 
+    /*
+     * ============================================================================
+     *
+     *       The following methods deal with compiled-sql cache
+     * ============================================================================
+     */
+    /**
+     * Adds the given SQL and its compiled-statement-id-returned-by-sqlite to the
+     * cache of compiledQueries attached to 'this'.
+     * <p>
+     * If there is already a {@link SQLiteCompiledSql} in compiledQueries for the given SQL,
+     * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
+     * mapping is NOT replaced with the new mapping).
+     */
+    /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
+        synchronized(mCompiledQueries) {
+            // don't insert the new mapping if a mapping already exists
+            if (mCompiledQueries.containsKey(sql)) {
+                return;
+            }
+
+            if (mCompiledQueries.size() == mMaxSqlCacheSize) {
+                /*
+                 * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
+                 * log a warning.
+                 * chances are it is NOT using ? for bindargs - or cachesize is too small.
+                 */
+                if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
+                    Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
+                            getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
+                }
+            } 
+            /* add the given SQLiteCompiledSql compiledStatement to cache.
+             * no need to worry about the cache size - because {@link #mCompiledQueries}
+             * self-limits its size to {@link #mMaxSqlCacheSize}.
+             */
+            mCompiledQueries.put(sql, compiledStatement);
+            if (SQLiteDebug.DEBUG_SQL_CACHE) {
+                Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
+                        mCompiledQueries.size() + "|" + sql);
+            }
+        }
+    }
+
+    /** package-level access for testing purposes */
+    /* package */ void deallocCachedSqlStatements() {
+        synchronized (mCompiledQueries) {
+            for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
+                compiledSql.releaseSqlStatement();
+            }
+            mCompiledQueries.clear();
+        }
+    }
+
+    /**
+     * From the compiledQueries cache, returns the compiled-statement-id for the given SQL.
+     * Returns null, if not found in the cache.
+     */
+    /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
+        SQLiteCompiledSql compiledStatement = null;
+        boolean cacheHit;
+        synchronized(mCompiledQueries) {
+            cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
+        }
+        if (cacheHit) {
+            mNumCacheHits++;
+        } else {
+            mNumCacheMisses++;
+        }
+
+        if (SQLiteDebug.DEBUG_SQL_CACHE) {
+            Log.v(TAG, "|cache_stats|" +
+                    getPath() + "|" + mCompiledQueries.size() +
+                    "|" + mNumCacheHits + "|" + mNumCacheMisses +
+                    "|" + cacheHit + "|" + sql);
+        }
+        return compiledStatement;
+    }
+
     /**
      * Sets the maximum size of the prepared-statement cache for this database.
      * (size of the cache = number of compiled-sql-statements stored in the cache).
      *<p>
-     * Maximum cache size can ONLY be increased from its current size (default = 25).
+     * Maximum cache size can ONLY be increased from its current size (default = 10).
      * If this method is called with smaller size than the current maximum value,
      * then IllegalStateException is thrown.
+     *<p>
+     * This method is thread-safe.
      *
      * @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
      * @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
      * the value set with previous setMaxSqlCacheSize() call.
      */
     public void setMaxSqlCacheSize(int cacheSize) {
-        mCache.setMaxSqlCacheSize(cacheSize);
+        synchronized(this) {
+            if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
+                throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
+            } else if (cacheSize < mMaxSqlCacheSize) {
+                throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
+                        "set with previous setMaxSqlCacheSize() call.");
+            }
+            mMaxSqlCacheSize = cacheSize;
+        }
+    }
+
+    /* package */ boolean isSqlInStatementCache(String sql) {
+        synchronized (mCompiledQueries) {
+            return mCompiledQueries.containsKey(sql);
+        }
     }
 
     /* package */ void finalizeStatementLater(int id) {
@@ -2340,16 +2484,16 @@
                         }
                         if (pageCount > 0) {
                             dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
-                                    lookasideUsed, db.mCache.getCacheHitNum(),
-                                    db.mCache.getCacheMissNum(), db.mCache.getCachesize()));
+                                    lookasideUsed, db.mNumCacheHits, db.mNumCacheMisses,
+                                    db.mCompiledQueries.size()));
                         }
                     }
                     // if there are pooled connections, return the cache stats for them also.
                     if (db.mConnectionPool != null) {
                         for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) {
                             dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
-                                    + lastnode, 0, 0, 0, pDb.mCache.getCacheHitNum(),
-                                    pDb.mCache.getCacheMissNum(), pDb.mCache.getCachesize()));
+                                    + lastnode, 0, 0, 0, pDb.mNumCacheHits, pDb.mNumCacheMisses,
+                                    pDb.mCompiledQueries.size()));
                         }
                     }
                 } catch (SQLiteException e) {
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index b390369..c7c0c79 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -110,6 +110,42 @@
         }
     }
 
+    private void compileSql() {
+        // only cache CRUD statements
+        if (mStatementType != DatabaseUtils.STATEMENT_SELECT &&
+                mStatementType != DatabaseUtils.STATEMENT_UPDATE) {
+            mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
+            nStatement = mCompiledSql.nStatement;
+            // since it is not in the cache, no need to acquire() it.
+            return;
+        }
+
+        mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
+        if (mCompiledSql == null) {
+            // create a new compiled-sql obj
+            mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
+
+            // add it to the cache of compiled-sqls
+            // but before adding it and thus making it available for anyone else to use it,
+            // make sure it is acquired by me.
+            mCompiledSql.acquire();
+            mDatabase.addToCompiledQueries(mSql, mCompiledSql);
+        } else {
+            // it is already in compiled-sql cache.
+            // try to acquire the object.
+            if (!mCompiledSql.acquire()) {
+                int last = mCompiledSql.nStatement;
+                // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
+                // we can't have two different SQLiteProgam objects can't share the same
+                // CompiledSql object. create a new one.
+                // finalize it when I am done with it in "this" object.
+                mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
+                // since it is not in the cache, no need to acquire() it.
+            }
+        }
+        nStatement = mCompiledSql.nStatement;
+    }
+
     @Override
     protected void onAllReferencesReleased() {
         release();
@@ -127,7 +163,16 @@
         if (mCompiledSql == null) {
             return;
         }
-        mCompiledSql.release(mStatementType);
+        synchronized(mDatabase.mCompiledQueries) {
+            if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
+                // it is NOT in compiled-sql cache. i.e., responsibility of
+                // releasing this statement is on me.
+                mCompiledSql.releaseSqlStatement();
+            } else {
+                // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
+                mCompiledSql.release();
+            }
+        }
         mCompiledSql = null;
         nStatement = 0;
     }
@@ -288,9 +333,6 @@
         synchronized (this) {
             mBindArgs = null;
             if (nHandle == 0 || !mDatabase.isOpen()) {
-                if (mCompiledSql != null) {
-                    mCompiledSql.setState(SQLiteCompiledSql.NSTATE_CLOSE_NOOP);
-                }
                 return;
             }
             releaseReference();
@@ -307,8 +349,7 @@
     /* package */ synchronized void compileAndbindAllArgs() {
         if (nStatement == 0) {
             // SQL statement is not compiled yet. compile it now.
-            mCompiledSql = SQLiteCompiledSql.get(mDatabase, mSql, mStatementType);
-            nStatement = mCompiledSql.nStatement;
+            compileSql();
         }
         if (mBindArgs == null) {
             return;
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index a5a8a92..658fc58 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -37,7 +37,6 @@
 @SuppressWarnings("deprecation")
 public class SQLiteStatement extends SQLiteProgram
 {
-
     private static final String TAG = "SQLiteStatement";
 
     private static final boolean READ = true;
@@ -45,9 +44,9 @@
 
     private SQLiteDatabase mOrigDb;
     private int mState;
-    /** possible value for {@link #mState}. indicates that a transaction is started.} */
+    /** possible value for {@link #mState}. indicates that a transaction is started. */
     private static final int TRANS_STARTED = 1;
-    /** possible value for {@link #mState}. indicates that a lock is acquired.} */
+    /** possible value for {@link #mState}. indicates that a lock is acquired. */
     private static final int LOCK_ACQUIRED = 2;
 
     /**
@@ -81,8 +80,8 @@
      */
     public int executeUpdateDelete() {
         synchronized(this) {
-            long timeStart = acquireAndLock(WRITE);
             try {
+                long timeStart = acquireAndLock(WRITE);
                 int numChanges = native_execute();
                 mDatabase.logTimeStat(mSql, timeStart);
                 return numChanges;
@@ -103,8 +102,8 @@
      */
     public long executeInsert() {
         synchronized(this) {
-            long timeStart = acquireAndLock(WRITE);
             try {
+                long timeStart = acquireAndLock(WRITE);
                 long lastInsertedRowId = native_executeInsert();
                 mDatabase.logTimeStat(mSql, timeStart);
                 return lastInsertedRowId;
@@ -124,8 +123,8 @@
      */
     public long simpleQueryForLong() {
         synchronized(this) {
-            long timeStart = acquireAndLock(READ);
             try {
+                long timeStart = acquireAndLock(READ);
                 long retValue = native_1x1_long();
                 mDatabase.logTimeStat(mSql, timeStart);
                 return retValue;
@@ -145,8 +144,8 @@
      */
     public String simpleQueryForString() {
         synchronized(this) {
-            long timeStart = acquireAndLock(READ);
             try {
+                long timeStart = acquireAndLock(READ);
                 String retValue = native_1x1_string();
                 mDatabase.logTimeStat(mSql, timeStart);
                 return retValue;
@@ -166,8 +165,8 @@
      */
     public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() {
         synchronized(this) {
-            long timeStart = acquireAndLock(READ);
             try {
+                long timeStart = acquireAndLock(READ);
                 ParcelFileDescriptor retValue = native_1x1_blob_ashmem();
                 mDatabase.logTimeStat(mSql, timeStart);
                 return retValue;
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/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/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d4acff8..9c59db3 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3788,7 +3788,7 @@
         InputMethodManager imm = (InputMethodManager)
                 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
-        // bring it back to the reading level scale so that user can enter text
+        // bring it back to the default level scale so that user can enter text
         boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
         if (zoom) {
             mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d87f55f..f3dfb77 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -16,11 +16,12 @@
 
 package android.widget;
 
+import com.android.internal.R;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
@@ -56,8 +57,6 @@
 import android.view.inputmethod.InputConnectionWrapper;
 import android.view.inputmethod.InputMethodManager;
 
-import com.android.internal.R;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -3074,8 +3073,10 @@
 
             // Estimate how many screens we should travel
             final float screenTravelCount = (float) viewTravelCount / childCount;
-            mScrollDuration = (int) (SCROLL_DURATION / screenTravelCount);
+            mScrollDuration = screenTravelCount < 1 ? (int) (screenTravelCount * SCROLL_DURATION) :
+                    (int) (SCROLL_DURATION / screenTravelCount);
             mLastSeenPos = INVALID_POSITION;
+
             post(this);
         }
 
@@ -3213,9 +3214,15 @@
             }
 
             case MOVE_OFFSET: {
-                final int childCount = getChildCount();
+                if (mLastSeenPos == firstPos) {
+                    // No new views, let things keep going.
+                    post(this);
+                    return;
+                }
 
                 mLastSeenPos = firstPos;
+
+                final int childCount = getChildCount();
                 final int position = mTargetPos;
                 final int lastPos = firstPos + childCount - 1;
 
@@ -3228,7 +3235,9 @@
                 } else {
                     // On-screen, just scroll.
                     final int targetTop = getChildAt(position - firstPos).getTop();
-                    smoothScrollBy(targetTop - mOffsetFromTop, mScrollDuration);
+                    final int distance = targetTop - mOffsetFromTop;
+                    smoothScrollBy(distance,
+                            (int) (mScrollDuration * ((float) distance / getHeight())));
                 }
                 break;
             }
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index fe1c9da..57dc725 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -57,6 +57,7 @@
     private OnCloseListener mOnCloseListener;
 
     private boolean mIconifiedByDefault;
+    private boolean mIconified;
     private CursorAdapter mSuggestionsAdapter;
     private View mSearchButton;
     private View mSubmitButton;
@@ -139,8 +140,6 @@
         mQueryTextView.setOnItemClickListener(mOnItemClickListener);
         mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
 
-        mSubmitButtonEnabled = false;
-
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchView, 0, 0);
         setIconifiedByDefault(a.getBoolean(R.styleable.SearchView_iconifiedByDefault, true));
         a.recycle();
@@ -175,6 +174,15 @@
     }
 
     /**
+     * Sets a listener to inform when the user closes the SearchView.
+     *
+     * @param listener the listener to call when the user closes the SearchView.
+     */
+    public void setOnCloseListener(OnCloseListener listener) {
+        mOnCloseListener = listener;
+    }
+
+    /**
      * Sets a query string in the text field and optionally submits the query as well.
      *
      * @param query the query string. This replaces any query text already present in the
@@ -205,20 +213,54 @@
      * Sets the default or resting state of the search field. If true, a single search icon is
      * shown by default and expands to show the text field and other buttons when pressed. Also,
      * if the default state is iconified, then it collapses to that state when the close button
-     * is pressed.
+     * is pressed. Changes to this property will take effect immediately.
      *
-     * @param iconified
+     * <p>The default value is false.</p>
+     *
+     * @param iconified whether the search field should be iconified by default
      */
     public void setIconifiedByDefault(boolean iconified) {
         mIconifiedByDefault = iconified;
         updateViewsVisibility(iconified);
     }
 
+    /**
+     * Returns the default iconified state of the search field.
+     * @return
+     */
     public boolean isIconfiedByDefault() {
         return mIconifiedByDefault;
     }
 
     /**
+     * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
+     * a temporary state and does not override the default iconified state set by
+     * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then
+     * a false here will only be valid until the user closes the field. And if the default
+     * state is expanded, then a true here will only clear the text field and not close it.
+     *
+     * @param iconify a true value will collapse the SearchView to an icon, while a false will
+     * expand it.
+     */
+    public void setIconified(boolean iconify) {
+        if (iconify) {
+            onCloseClicked();
+        } else {
+            onSearchClicked();
+        }
+    }
+
+    /**
+     * Returns the current iconified state of the SearchView.
+     *
+     * @return true if the SearchView is currently iconified, false if the search field is
+     * fully visible.
+     */
+    public boolean isIconified() {
+        return mIconified;
+    }
+
+    /**
      * Enables showing a submit button when the query is non-empty. In cases where the SearchView
      * is being used to filter the contents of the current activity and doesn't launch a separate
      * results activity, then the submit button should be disabled.
@@ -263,11 +305,12 @@
         return mSuggestionsAdapter;
     }
 
-    private void updateViewsVisibility(boolean collapsed) {
+    private void updateViewsVisibility(final boolean collapsed) {
+        mIconified = collapsed;
         // Visibility of views that are visible when collapsed
-        int visCollapsed = collapsed? VISIBLE : GONE;
+        final int visCollapsed = collapsed ? VISIBLE : GONE;
         // Visibility of views that are visible when expanded
-        int visExpanded = collapsed? GONE : VISIBLE;
+        final int visExpanded = collapsed ? GONE : VISIBLE;
 
         mSearchButton.setVisibility(visCollapsed);
         mSubmitButton.setVisibility(mSubmitButtonEnabled ? visExpanded : GONE);
@@ -322,7 +365,8 @@
         // entered query with the action key
         SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
         if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
-            launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText().toString());
+            launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText()
+                    .toString());
             return true;
         }
 
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/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/core/tests/coretests/src/android/database/sqlite/SQLiteCacheTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCacheTest.java
deleted file mode 100644
index 2459815..0000000
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCacheTest.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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 android.database.sqlite;
-
-
-import android.content.Context;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.File;
-import java.util.ArrayList;
-
-public class SQLiteCacheTest extends AndroidTestCase {
-    private SQLiteDatabase mDatabase;
-    private File mDatabaseFile;
-    private static final String TABLE_NAME = "testCache";
-    private SQLiteCache mCache;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
-        mDatabaseFile = new File(dbDir, "test.db");
-        if (mDatabaseFile.exists()) {
-            mDatabaseFile.delete();
-        }
-        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
-        assertNotNull(mDatabase);
-        mCache = mDatabase.mCache;
-        assertNotNull(mCache);
-
-        // create a test table
-        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mDatabase.lock();
-        // flush the above statement from cache and close all the pending statements to be released
-        mCache.dealloc();
-        mDatabase.closePendingStatements();
-        mDatabase.unlock();
-        assertEquals(0, mDatabase.getQueuedUpStmtList().size());
-        mDatabase.close();
-        mDatabaseFile.delete();
-        super.tearDown();
-    }
-
-    /**
-     * do N+1 queries - and when the 0th entry is removed from LRU cache due to the
-     * insertion of (N+1)th entry, make sure 0th entry is closed
-     */
-    @SmallTest
-    public void testLruCaching() {
-        mDatabase.disableWriteAheadLogging();
-        // set cache size
-        int N = 25;
-        mDatabase.setMaxSqlCacheSize(N);
-
-        ArrayList<Integer> stmtObjs = new ArrayList<Integer>();
-        ArrayList<String> sqlStrings = new ArrayList<String>();
-        int stmt0 = 0;
-        for (int i = 0; i < N+1; i++) {
-            String s = "insert into " + TABLE_NAME + " values(" + i + ",?);";
-            sqlStrings.add(s);
-            ClassToTestSqlCompilationAndCaching c =
-                    ClassToTestSqlCompilationAndCaching.create(mDatabase, s);
-            int n = c.getSqlStatementId();
-            stmtObjs.add(i, n);
-            if (i == 0) {
-                // save the statementId of this obj. we want to make sure it is thrown out of
-                // the cache at the end of this test.
-                stmt0 = n;
-            }
-            c.close();
-        }
-        assertEquals(N, mCache.getCachesize());
-        // is 0'th entry out of the cache? it should be in the list of statementIds
-        // corresponding to the pre-compiled sql statements to be finalized.
-        assertTrue(mDatabase.getQueuedUpStmtList().contains(stmt0));
-        for (int i = 1; i < N+1; i++) {
-            SQLiteCompiledSql compSql =
-                    mDatabase.mCache.getCompiledStatementForSql(sqlStrings.get(i));
-            assertNotNull(compSql);
-            assertTrue(stmtObjs.contains(compSql.nStatement));
-        }
-        assertEquals(N, mCache.getCachesize());
-
-    }
-
-    /**
-     * Cache should only have Select / Insert / Update / Delete / Replace.
-     */
-    @SmallTest
-    public void testCachingOfCRUDstatementsOnly() {
-        ClassToTestSqlCompilationAndCaching c;
-        // do some CRUD sql
-        int crudSqlNum = 7 * 4;
-        mDatabase.setMaxSqlCacheSize(crudSqlNum);
-        for (int i = 0; i < crudSqlNum / 4; i++) {
-            c= ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                    "insert into " + TABLE_NAME + " values(" + i + ",?);");
-            c.close();
-            c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                    "update " + TABLE_NAME + " set i = " + i);
-            c.close();
-            c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                    "select * from " + TABLE_NAME + " where i = " + i);
-            c.close();
-            c= ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                    "delete from " + TABLE_NAME + " where i = " + i);
-            c.close();
-        }
-        // do some non-CRUD sql
-        c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                "create table j (i int);");
-        c.close();
-        c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                "pragma database_list;");
-        c.close();
-        c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                "begin transaction;");
-        c.close();
-        c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                "commit;");
-        c.close();
-        c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                "attach database \"blah\" as blah_db;");
-        c.close();
-        // cache size should be crudSqlNum
-        assertEquals(crudSqlNum, mCache.getCachesize());
-        // everything in the cache should be a CRUD sql statement
-        for (String s : mCache.getKeys()) {
-            int type = DatabaseUtils.getSqlStatementType(s);
-            assertTrue(type == DatabaseUtils.STATEMENT_SELECT ||
-                    type == DatabaseUtils.STATEMENT_UPDATE);
-        }
-    }
-
-    /**
-     * calling SQLiteCache.getCompiledStatementForSql() should reserve the cached-entry
-     * for the caller, if the entry exists
-     */
-    @SmallTest
-    public void testGetShouldReserveEntry() {
-        String sql = "insert into " + TABLE_NAME + " values(1,?);";
-        ClassToTestSqlCompilationAndCaching c =
-                ClassToTestSqlCompilationAndCaching.create(mDatabase, sql);
-        c.close();
-        SQLiteCompiledSql compiledSql = mCache.getCompiledStatementForSql(sql);
-        assertNotNull(compiledSql);
-        assertTrue(compiledSql.isInUse());
-        // get entry for the same sql again. should get null and a warning in log
-        assertNull(mCache.getCompiledStatementForSql(sql));
-        compiledSql.free();
-        assertFalse(compiledSql.isInUse());
-    }
-}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompiledSqlTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompiledSqlTest.java
deleted file mode 100644
index 1606cf6..0000000
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompiledSqlTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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 android.database.sqlite;
-
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.File;
-
-public class SQLiteCompiledSqlTest extends AndroidTestCase {
-    private SQLiteDatabase mDatabase;
-    private File mDatabaseFile;
-    private static final String TABLE_NAME = "testCache";
-    private SQLiteCache mCache;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
-        mDatabaseFile = new File(dbDir, "sqlitecursor_test.db");
-        if (mDatabaseFile.exists()) {
-            mDatabaseFile.delete();
-        }
-        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
-        assertNotNull(mDatabase);
-        mCache = mDatabase.mCache;
-        assertNotNull(mCache);
-
-        // create a test table
-        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mDatabase.close();
-        mDatabaseFile.delete();
-        super.tearDown();
-    }
-
-    /**
-     * releaseIfNotInUse() should release only if it is not in use
-     */
-    @SmallTest
-    public void testReleaseIfNotInUse() {
-        ClassToTestSqlCompilationAndCaching c;
-        // do some CRUD sql
-        int crudSqlNum = 20 * 4;
-        mDatabase.setMaxSqlCacheSize(crudSqlNum);
-        for (int i = 0; i < crudSqlNum / 4; i++) {
-            c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                    "insert into " + TABLE_NAME + " values(" + i + ",?);");
-            c.close();
-            c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                    "update " + TABLE_NAME + " set i = " + i);
-            c.close();
-            c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                    "select * from " + TABLE_NAME + " where i = " + i);
-            c.close();
-            c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                    "delete from " + TABLE_NAME + " where i = " + i);
-            c.close();
-        }
-        assertEquals(crudSqlNum, mCache.getCachesize());
-        String sql = "insert into " + TABLE_NAME + " values(1,?);";
-        SQLiteCompiledSql compiledSql = mCache.getCompiledStatementForSql(sql);
-        assertNotNull(compiledSql);
-        assertTrue(compiledSql.isInUse());
-        // the object is in use. try to release it
-        compiledSql.releaseIfNotInUse();
-        // compiledSql should not be released yet
-        int stmtId = compiledSql.nStatement;
-        assertTrue(stmtId > 0);
-        // free the object and call releaseIfNotInUse() again - and it should work this time
-        compiledSql.free();
-        assertFalse(compiledSql.isInUse());
-        compiledSql.releaseIfNotInUse();
-        assertEquals(0, compiledSql.nStatement);
-        assertTrue(mDatabase.getQueuedUpStmtList().contains(stmtId));
-    }
-}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 3249cb6..86eda71 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -447,6 +447,42 @@
         }
     }
 
+    @SmallTest
+    public void testLruCachingOfSqliteCompiledSqlObjs() {
+        createTableAndClearCache();
+        // set cache size
+        int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
+        mDatabase.setMaxSqlCacheSize(N);
+
+        // do N+1 queries - and when the 0th entry is removed from LRU cache due to the
+        // insertion of (N+1)th entry, make sure 0th entry is closed
+        ArrayList<Integer> stmtObjs = new ArrayList<Integer>();
+        ArrayList<String> sqlStrings = new ArrayList<String>();
+        int stmt0 = 0;
+        for (int i = 0; i < N+1; i++) {
+            String s = "insert into test values(" + i + ",?);";
+            sqlStrings.add(s);
+            ClassToTestSqlCompilationAndCaching c =
+                    ClassToTestSqlCompilationAndCaching.create(mDatabase, s);
+            int n = c.getSqlStatementId();
+            stmtObjs.add(i, n);
+            if (i == 0) {
+                // save the statementId of this obj. we want to make sure it is thrown out of
+                // the cache at the end of this test.
+                stmt0 = n;
+            }
+            c.close();
+        }
+        // is 0'th entry out of the cache? it should be in the list of statementIds
+        // corresponding to the pre-compiled sql statements to be finalized.
+        assertTrue(mDatabase.getQueuedUpStmtList().contains(stmt0));
+        for (int i = 1; i < N+1; i++) {
+            SQLiteCompiledSql compSql = mDatabase.getCompiledStatementForSql(sqlStrings.get(i));
+            assertNotNull(compSql);
+            assertTrue(stmtObjs.contains(compSql.nStatement));
+        }
+    }
+
     @MediumTest
     public void testDbCloseReleasingAllCachedSql() {
         mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
@@ -467,7 +503,7 @@
         mDatabase.enableWriteAheadLogging();
         mDatabase.lock();
         // flush the above statement from cache and close all the pending statements to be released
-        mDatabase.mCache.dealloc();
+        mDatabase.deallocCachedSqlStatements();
         mDatabase.closePendingStatements();
         mDatabase.unlock();
         assertEquals(0, mDatabase.getQueuedUpStmtList().size());
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
index bdd6d5c..217545f 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
@@ -53,12 +53,6 @@
      * Start 2 threads to repeatedly execute the above SQL statement.
      * Even though 2 threads are executing the same SQL, they each should get their own copy of
      * prepared SQL statement id and there SHOULD NOT be an error from sqlite or android.
-     *<p>
-     * This method will produce a lot of the following warnings:
-     *  Possible bug: Either using the same SQL in 2 threads at the same time, or
-     *  previous instance of this SQL statement was never close()d.
-     * That is expected behavior.
-     *
      * @throws InterruptedException thrown if the test threads started by this test are interrupted
      */
     @LargeTest
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index 04091a3..00c5cf1 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -62,9 +62,9 @@
         }
     }
 
-    public static class Builder {
+    public static class Builder extends ShaderBuilder {
         public static final int MAX_TEXTURE = 2;
-        RenderScript mRS;
+        int mNumTextures;
         boolean mPointSpriteEnable;
         boolean mVaryingColorEnable;
 
@@ -101,7 +101,72 @@
         }
         Slot[] mSlots;
 
+        private void buildShaderString() {
+            mShader  = "//rs_shader_internal\n";
+            mShader += "varying lowp vec4 varColor;\n";
+            mShader += "varying vec4 varTex0;\n";
+
+            mShader += "void main() {\n";
+            if (mVaryingColorEnable) {
+                mShader += "  lowp vec4 col = varColor;\n";
+            } else {
+                mShader += "  lowp vec4 col = UNI_Color;\n";
+            }
+
+            if (mNumTextures != 0) {
+                if (mPointSpriteEnable) {
+                    mShader += "  vec2 t0 = gl_PointCoord;\n";
+                } else {
+                    mShader += "  vec2 t0 = varTex0.xy;\n";
+                }
+            }
+
+            for(int i = 0; i < mNumTextures; i ++) {
+                switch(mSlots[i].env) {
+                case REPLACE:
+                    switch (mSlots[i].format) {
+                    case ALPHA:
+                        mShader += "  col.a = texture2D(UNI_Tex0, t0).a;\n";
+                        break;
+                    case LUMINANCE_ALPHA:
+                        mShader += "  col.rgba = texture2D(UNI_Tex0, t0).rgba;\n";
+                        break;
+                    case RGB:
+                        mShader += "  col.rgb = texture2D(UNI_Tex0, t0).rgb;\n";
+                        break;
+                    case RGBA:
+                        mShader += "  col.rgba = texture2D(UNI_Tex0, t0).rgba;\n";
+                        break;
+                    }
+                    break;
+                case MODULATE:
+                    switch (mSlots[i].format) {
+                    case ALPHA:
+                        mShader += "  col.a *= texture2D(UNI_Tex0, t0).a;\n";
+                        break;
+                    case LUMINANCE_ALPHA:
+                        mShader += "  col.rgba *= texture2D(UNI_Tex0, t0).rgba;\n";
+                        break;
+                    case RGB:
+                        mShader += "  col.rgb *= texture2D(UNI_Tex0, t0).rgb;\n";
+                        break;
+                    case RGBA:
+                        mShader += "  col.rgba *= texture2D(UNI_Tex0, t0).rgba;\n";
+                        break;
+                    }
+                    break;
+                case DECAL:
+                    mShader += "  col = texture2D(UNI_Tex0, t0);\n";
+                    break;
+                }
+            }
+
+            mShader += "  gl_FragColor = col;\n";
+            mShader += "}\n";
+        }
+
         public Builder(RenderScript rs) {
+            super(rs);
             mRS = rs;
             mSlots = new Slot[MAX_TEXTURE];
             mPointSpriteEnable = false;
@@ -126,22 +191,35 @@
             return this;
         }
 
+        @Override
         public ProgramFragment create() {
-            mRS.validate();
-            int[] tmp = new int[MAX_TEXTURE * 2 + 2];
-            if (mSlots[0] != null) {
-                tmp[0] = mSlots[0].env.mID;
-                tmp[1] = mSlots[0].format.mID;
+            mNumTextures = 0;
+            for(int i = 0; i < MAX_TEXTURE; i ++) {
+                if(mSlots[i] != null) {
+                    mNumTextures ++;
+                }
             }
-            if (mSlots[1] != null) {
-                tmp[2] = mSlots[1].env.mID;
-                tmp[3] = mSlots[1].format.mID;
+            buildShaderString();
+            Type constType = null;
+            if (!mVaryingColorEnable) {
+                Element.Builder b = new Element.Builder(mRS);
+                b.add(Element.F32_4(mRS), "Color");
+                Type.Builder typeBuilder = new Type.Builder(mRS, b.create());
+                typeBuilder.add(Dimension.X, 1);
+                constType = typeBuilder.create();
+                addConstant(constType);
             }
-            tmp[4] = mPointSpriteEnable ? 1 : 0;
-            tmp[5] = mVaryingColorEnable ? 1 : 0;
-            int id = mRS.nProgramFragmentCreate(tmp);
-            ProgramFragment pf = new ProgramFragment(id, mRS);
+            setTextureCount(mNumTextures);
+
+            ProgramFragment pf = super.create();
             pf.mTextureCount = MAX_TEXTURE;
+            if (!mVaryingColorEnable) {
+                Allocation constantData = Allocation.createTyped(mRS,constType);
+                float[] data = new float[4];
+                data[0] = data[1] = data[2] = data[3] = 1.0f;
+                constantData.data(data);
+                pf.bindConstants(constantData, 0);
+            }
             return pf;
         }
     }
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index b072433..119db69 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -17,6 +17,7 @@
 package android.renderscript;
 
 
+import android.graphics.Matrix;
 import android.util.Config;
 import android.util.Log;
 
@@ -38,29 +39,6 @@
         bindConstants(va.mAlloc, 0);
     }
 
-
-    public static class Builder {
-        RenderScript mRS;
-        boolean mTextureMatrixEnable;
-
-        public Builder(RenderScript rs, Element in, Element out) {
-            mRS = rs;
-        }
-        public Builder(RenderScript rs) {
-            mRS = rs;
-        }
-
-        public Builder setTextureMatrixEnable(boolean enable) {
-            mTextureMatrixEnable = enable;
-            return this;
-        }
-
-        public ProgramVertex create() {
-            int id = mRS.nProgramVertexCreate(mTextureMatrixEnable);
-            return new ProgramVertex(id, mRS);
-        }
-    }
-
     public static class ShaderBuilder extends BaseProgramBuilder {
         public ShaderBuilder(RenderScript rs) {
             super(rs);
@@ -93,6 +71,68 @@
         }
     }
 
+    public static class Builder extends ShaderBuilder {
+        boolean mTextureMatrixEnable;
+
+        public Builder(RenderScript rs, Element in, Element out) {
+            super(rs);
+        }
+        public Builder(RenderScript rs) {
+            super(rs);
+        }
+
+        public Builder setTextureMatrixEnable(boolean enable) {
+            mTextureMatrixEnable = enable;
+            return this;
+        }
+        static Type getConstantInputType(RenderScript rs) {
+            Element.Builder b = new Element.Builder(rs);
+            b.add(Element.MATRIX4X4(rs), "MV");
+            b.add(Element.MATRIX4X4(rs), "P");
+            b.add(Element.MATRIX4X4(rs), "TexMatrix");
+            b.add(Element.MATRIX4X4(rs), "MVP");
+
+            Type.Builder typeBuilder = new Type.Builder(rs, b.create());
+            typeBuilder.add(Dimension.X, 1);
+            return typeBuilder.create();
+        }
+
+        private void buildShaderString() {
+
+            mShader  = "//rs_shader_internal\n";
+            mShader += "varying vec4 varColor;\n";
+            mShader += "varying vec4 varTex0;\n";
+
+            mShader += "void main() {\n";
+            mShader += "  gl_Position = UNI_MVP * ATTRIB_position;\n";
+            mShader += "  gl_PointSize = 1.0;\n";
+
+            mShader += "  varColor = ATTRIB_color;\n";
+            if (mTextureMatrixEnable) {
+                mShader += "  varTex0 = UNI_TexMatrix * ATTRIB_texture0;\n";
+            } else {
+                mShader += "  varTex0 = ATTRIB_texture0;\n";
+            }
+            mShader += "}\n";
+        }
+
+        @Override
+        public ProgramVertex create() {
+            buildShaderString();
+
+            addConstant(getConstantInputType(mRS));
+
+            Element.Builder b = new Element.Builder(mRS);
+            b.add(Element.F32_4(mRS), "position");
+            b.add(Element.F32_4(mRS), "color");
+            b.add(Element.F32_3(mRS), "normal");
+            b.add(Element.F32_4(mRS), "texture0");
+            addInput(b.create());
+
+            return super.create();
+        }
+    }
+
 
 
     public static class MatrixAllocation {
@@ -105,9 +145,14 @@
         Matrix4f mTexture;
 
         public Allocation mAlloc;
+        private FieldPacker mIOBuffer;
 
         public MatrixAllocation(RenderScript rs) {
-            mAlloc = Allocation.createSized(rs, Element.createUser(rs, Element.DataType.FLOAT_32), 48);
+            Type constInputType = ProgramVertex.Builder.getConstantInputType(rs);
+            mAlloc = Allocation.createTyped(rs, constInputType);
+            int bufferSize = constInputType.getElement().getSizeBytes()*
+                             constInputType.getElementCount();
+            mIOBuffer = new FieldPacker(bufferSize);
             loadModelview(new Matrix4f());
             loadProjection(new Matrix4f());
             loadTexture(new Matrix4f());
@@ -118,24 +163,32 @@
             mAlloc = null;
         }
 
+        private void addToBuffer(int offset, Matrix4f m) {
+            mIOBuffer.reset(offset);
+            for(int i = 0; i < 16; i ++) {
+                mIOBuffer.addF32(m.mMat[i]);
+            }
+            mAlloc.data(mIOBuffer.getData());
+        }
+
         public void loadModelview(Matrix4f m) {
             mModel = m;
-            mAlloc.subData1D(MODELVIEW_OFFSET, 16, m.mMat);
+            addToBuffer(MODELVIEW_OFFSET*4, m);
         }
 
         public void loadProjection(Matrix4f m) {
             mProjection = m;
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, m.mMat);
+            addToBuffer(PROJECTION_OFFSET*4, m);
         }
 
         public void loadTexture(Matrix4f m) {
             mTexture = m;
-            mAlloc.subData1D(TEXTURE_OFFSET, 16, m.mMat);
+            addToBuffer(TEXTURE_OFFSET*4, m);
         }
 
         public void setupOrthoWindow(int w, int h) {
             mProjection.loadOrtho(0,w, h,0, -1,1);
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+            addToBuffer(PROJECTION_OFFSET*4, mProjection);
         }
 
         public void setupOrthoNormalized(int w, int h) {
@@ -147,7 +200,7 @@
                 float aspect = ((float)h) / w;
                 mProjection.loadOrtho(-1,1, -aspect,aspect,  -1,1);
             }
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+            addToBuffer(PROJECTION_OFFSET*4, mProjection);
         }
 
         public void setupProjectionNormalized(int w, int h) {
@@ -173,7 +226,7 @@
             m1.loadMultiply(m1, m2);
 
             mProjection = m1;
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+            addToBuffer(PROJECTION_OFFSET*4, mProjection);
         }
 
     }
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/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
index 9225904..c83ece4 100644
--- a/libs/rs/RenderScriptEnv.h
+++ b/libs/rs/RenderScriptEnv.h
@@ -28,4 +28,4 @@
 #define RS_PROGRAM_VERTEX_MODELVIEW_OFFSET 0
 #define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16
 #define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32
-
+#define RS_PROGRAM_VERTEX_MVP_OFFSET 48
diff --git a/libs/rs/java/Samples/res/raw/shaderf.glsl b/libs/rs/java/Samples/res/raw/shaderf.glsl
index fdcf481..fcbe7ee 100644
--- a/libs/rs/java/Samples/res/raw/shaderf.glsl
+++ b/libs/rs/java/Samples/res/raw/shaderf.glsl
@@ -3,10 +3,11 @@
 varying lowp float light0_Specular;
 varying lowp float light1_Diffuse;
 varying lowp float light1_Specular;
+varying vec2 varTex0;
 
 void main() {
    vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(uni_Tex0, t0).rgba;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
    col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor + light1_Diffuse * UNI_light1_DiffuseColor);
    col.xyz += light0_Specular * UNI_light0_SpecularColor;
    col.xyz += light1_Specular * UNI_light1_SpecularColor;
diff --git a/libs/rs/java/Samples/res/raw/shaderv.glsl b/libs/rs/java/Samples/res/raw/shaderv.glsl
index 7f61197..867589cf 100644
--- a/libs/rs/java/Samples/res/raw/shaderv.glsl
+++ b/libs/rs/java/Samples/res/raw/shaderv.glsl
@@ -2,24 +2,12 @@
 varying float light0_Specular;
 varying float light1_Diffuse;
 varying float light1_Specular;
-
-/*
-rs_matrix4x4 model;
- float3 light0_Posision;
- float light0_Diffuse;
- float light0_Specular;
- float light0_CosinePower;
-
- float3 light1_Posision;
- float light1_Diffuse;
- float light1_Specular;
- float light1_CosinePower;
-*/
+varying vec2 varTex0;
 
 // This is where actual shader code begins
 void main() {
    vec4 worldPos = UNI_model * ATTRIB_position;
-   gl_Position = UNI_MVP * worldPos;
+   gl_Position = UNI_proj * worldPos;
 
    mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
    vec3 worldNorm = model3 * ATTRIB_normal;
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
index e76e740..d920527 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -67,7 +67,6 @@
 
     // Custom shaders
     private ProgramVertex mProgVertexCustom;
-    private ProgramVertex.MatrixAllocation mPVACustom;
     private ProgramFragment mProgFragmentCustom;
     private ScriptField_VertexShaderConstants_s mVSConst;
     private ScriptField_FragentShaderConstants_s mFSConst;
@@ -202,9 +201,7 @@
         pvbCustom.addConstant(mVSConst.getAllocation().getType());
         mProgVertexCustom = pvbCustom.create();
         // Bind the source of constant data
-        mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 1);
-        mPVACustom = new ProgramVertex.MatrixAllocation(mRS);
-        mProgVertexCustom.bindAllocation(mPVACustom);
+        mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0);
 
         ProgramFragment.ShaderBuilder pfbCustom = new ProgramFragment.ShaderBuilder(mRS);
         // Specify the resource that contains the shader string
@@ -215,7 +212,7 @@
         pfbCustom.addConstant(mFSConst.getAllocation().getType());
         mProgFragmentCustom = pfbCustom.create();
         // Bind the source of constant data
-        mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 1);
+        mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0);
 
         mScript.set_gProgVertexCustom(mProgVertexCustom);
         mScript.set_gProgFragmentCustom(mProgFragmentCustom);
diff --git a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
index 4bcf7f5..c7bea93 100644
--- a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
+++ b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -395,14 +395,12 @@
     rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -10.0f);
     rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
     rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
+    // Setup the projectioni matrix
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
     setupCustomShaderLights();
 
     rsgBindProgramVertex(gProgVertexCustom);
-    // Setup the projectioni matrix with 60 degree field of view
-    rs_matrix4x4 proj;
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
 
     // Fragment shader with texture
     rsgBindProgramStore(gProgStoreBlendNoneDepth);
@@ -416,7 +414,7 @@
 
     rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
     rsgBindFont(gFontMono);
-    rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
+    //rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
 }
 
 int root(int launchID) {
diff --git a/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh b/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh
index 1b697ca..e3f6206 100644
--- a/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh
+++ b/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh
@@ -18,6 +18,7 @@
 
 typedef struct VertexShaderConstants_s {
     rs_matrix4x4 model;
+    rs_matrix4x4 proj;
     float3 light0_Posision;
     float light0_Diffuse;
     float light0_Specular;
@@ -27,7 +28,6 @@
     float light1_Diffuse;
     float light1_Specular;
     float light1_CosinePower;
-
 } VertexShaderConstants;
 
 typedef struct FragentShaderConstants_s {
@@ -42,6 +42,6 @@
 typedef struct VertexShaderInputs_s {
     float4 position;
     float3 normal;
-    float4 texture0;
+    float2 texture0;
 } VertexShaderInputs;
 
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 0b7bb27..d0909c8 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -314,6 +314,11 @@
 
 ElementState::ElementState()
 {
+    const uint32_t initialCapacity = 32;
+    mBuilderElements.setCapacity(initialCapacity);
+    mBuilderNameStrings.setCapacity(initialCapacity);
+    mBuilderNameLengths.setCapacity(initialCapacity);
+    mBuilderArrays.setCapacity(initialCapacity);
 }
 
 ElementState::~ElementState()
@@ -321,6 +326,29 @@
     rsAssert(!mElements.size());
 }
 
+void ElementState::elementBuilderBegin() {
+    mBuilderElements.clear();
+    mBuilderNameStrings.clear();
+    mBuilderNameLengths.clear();
+    mBuilderArrays.clear();
+}
+
+void ElementState::elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize) {
+    mBuilderElements.push(e);
+    mBuilderNameStrings.push(nameStr);
+    mBuilderNameLengths.push(strlen(nameStr));
+    mBuilderArrays.push(arraySize);
+
+}
+
+const Element *ElementState::elementBuilderCreate(Context *rsc) {
+    return Element::create(rsc, mBuilderElements.size(),
+                                &(mBuilderElements.editArray()[0]),
+                                &(mBuilderNameStrings.editArray()[0]),
+                                mBuilderNameLengths.editArray(),
+                                mBuilderArrays.editArray());
+}
+
 
 /////////////////////////////////////////
 //
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 42eef4a..ae6a6cc 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -99,8 +99,17 @@
     ElementState();
     ~ElementState();
 
+    void elementBuilderBegin();
+    void elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize);
+    const Element *elementBuilderCreate(Context *rsc);
+
     // Cache of all existing elements.
     Vector<Element *> mElements;
+private:
+    Vector<const Element *> mBuilderElements;
+    Vector<const char*> mBuilderNameStrings;
+    Vector<size_t> mBuilderNameLengths;
+    Vector<uint32_t> mBuilderArrays;
 };
 
 
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 5889bfb..0f815a2 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -387,14 +387,34 @@
 
 void FontState::initRenderState()
 {
-    uint32_t tmp[] = {
-        RS_TEX_ENV_MODE_REPLACE, 1,
-        RS_TEX_ENV_MODE_NONE, 0,
-        0, 0
-    };
-    ProgramFragment *pf = new ProgramFragment(mRSC, tmp, 6);
+    String8 shaderString("varying vec4 varTex0;\n");
+    shaderString.append("void main() {\n");
+    shaderString.append("  lowp vec4 col = UNI_Color;\n");
+    shaderString.append("  col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n");
+    shaderString.append("  gl_FragColor = col;\n");
+    shaderString.append("}\n");
+
+    const Element *colorElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
+    mRSC->mStateElement.elementBuilderBegin();
+    mRSC->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
+    const Element *constInput = mRSC->mStateElement.elementBuilderCreate(mRSC);
+
+    Type *inputType = new Type(mRSC);
+    inputType->setElement(constInput);
+    inputType->setDimX(1);
+    inputType->compute();
+
+    uint32_t tmp[4];
+    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+    tmp[1] = (uint32_t)inputType;
+    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+    tmp[3] = 1;
+
+    mFontShaderFConstant.set(new Allocation(mRSC, inputType));
+    ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
+                                              shaderString.length(), tmp, 4);
     mFontShaderF.set(pf);
-    mFontShaderF->init(mRSC);
+    mFontShaderF->bindAllocation(mFontShaderFConstant.get(), 0);
 
     Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
                                       RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
@@ -539,7 +559,7 @@
     mRSC->setFragmentStore(mFontProgramStore.get());
 
     if(mFontColorDirty) {
-        mFontShaderF->setConstantColor(mFontColor[0], mFontColor[1], mFontColor[2], mFontColor[3]);
+        mFontShaderFConstant->data(mRSC, &mFontColor, 4*sizeof(float));
         mFontColorDirty = false;
     }
 
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index defe38b..027ed1d 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -171,6 +171,7 @@
     Vector<Font*> mActiveFonts;
 
     // Render state for the font
+    ObjectBaseRef<Allocation> mFontShaderFConstant;
     ObjectBaseRef<ProgramFragment> mFontShaderF;
     ObjectBaseRef<Sampler> mFontSampler;
     ObjectBaseRef<ProgramStore> mFontProgramStore;
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 6041db8..2441491 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -46,6 +46,7 @@
     mOutputCount = 0;
     mConstantCount = 0;
     mIsValid = false;
+    mIsInternal = false;
 }
 
 Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength,
@@ -97,6 +98,14 @@
             mConstantTypes[constant++].set(reinterpret_cast<Type *>(params[ct+1]));
         }
     }
+    mIsInternal = false;
+    uint32_t internalTokenLen = strlen(RS_SHADER_INTERNAL);
+    if(shaderLength > internalTokenLen &&
+       strncmp(RS_SHADER_INTERNAL, shaderText, internalTokenLen) == 0) {
+        mIsInternal = true;
+        shaderText += internalTokenLen;
+        shaderLength -= internalTokenLen;
+    }
     mUserShader.setTo(shaderText, shaderLength);
 }
 
@@ -281,9 +290,9 @@
 }
 
 void Program::setupUserConstants(ShaderCache *sc, bool isFragment) {
-    uint32_t uidx = 1;
+    uint32_t uidx = 0;
     for (uint32_t ct=0; ct < mConstantCount; ct++) {
-        Allocation *alloc = mConstants[ct+1].get();
+        Allocation *alloc = mConstants[ct].get();
         if (!alloc) {
             continue;
         }
@@ -313,6 +322,9 @@
             if (slot >= 0) {
                 if(f->getType() == RS_TYPE_MATRIX_4X4) {
                     glUniformMatrix4fv(slot, 1, GL_FALSE, fd);
+                    /*for(int i = 0; i < 4; i++) {
+                        LOGE("Mat = %f %f %f %f", fd[i*4 + 0], fd[i*4 + 1], fd[i*4 + 2], fd[i*4 + 3]);
+                    }*/
                 }
                 else if(f->getType() == RS_TYPE_MATRIX_3X3) {
                     glUniformMatrix3fv(slot, 1, GL_FALSE, fd);
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index ddc5e8a..e7329c2 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -23,10 +23,10 @@
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-
-
 class ShaderCache;
 
+#define RS_SHADER_INTERNAL "//rs_shader_internal\n"
+
 class Program : public ObjectBase
 {
 public:
@@ -42,7 +42,7 @@
     void bindAllocation(Allocation *, uint32_t slot);
     virtual void createShader();
 
-    bool isUserProgram() const {return mUserShader.size() > 0;}
+    bool isUserProgram() const {return !mIsInternal;}
 
     void bindTexture(uint32_t slot, Allocation *);
     void bindSampler(uint32_t slot, Sampler *);
@@ -71,6 +71,7 @@
     uint32_t mOutputCount;
     uint32_t mConstantCount;
     bool mIsValid;
+    bool mIsInternal;
 
     // Applies to vertex and fragment shaders only
     void appendUserConstants();
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 83321d3..3174e82 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -92,10 +92,14 @@
 
 void ProgramFragment::setConstantColor(float r, float g, float b, float a)
 {
+    if(isUserProgram()) {
+        return;
+    }
     mConstantColor[0] = r;
     mConstantColor[1] = g;
     mConstantColor[2] = b;
     mConstantColor[3] = a;
+    memcpy(mConstants[0]->getPtr(), mConstantColor, 4*sizeof(float));
     mDirty = true;
 }
 
@@ -114,13 +118,6 @@
 
     rsc->checkError("ProgramFragment::setupGL2 start");
 
-    if (!mVaryingColor &&
-        (sc->fragUniformSlot(mConstantColorUniformIndex) >= 0)) {
-        //LOGE("mConstantColorUniformIndex %i %i", mConstantColorUniformIndex, sc->fragUniformSlot(mConstantColorUniformIndex));
-        glUniform4fv(sc->fragUniformSlot(mConstantColorUniformIndex), 1, mConstantColor);
-        rsc->checkError("ProgramFragment::color setup");
-    }
-
     rsc->checkError("ProgramFragment::setupGL2 begin uniforms");
     setupUserConstants(sc, true);
 
@@ -158,124 +155,32 @@
 
 void ProgramFragment::createShader()
 {
-    mShader.setTo("precision mediump float;\n");
-    mShader.append("varying lowp vec4 varColor;\n");
-    mShader.append("varying vec4 varTex0;\n");
-    mShader.append("uniform vec4 uni_Color;\n");
-
     if (mUserShader.length() > 1) {
+        mShader.append("precision mediump float;\n");
         appendUserConstants();
         for (uint32_t ct=0; ct < mTextureCount; ct++) {
             char buf[256];
-            sprintf(buf, "uniform sampler2D uni_Tex%i;\n", ct);
+            sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct);
             mShader.append(buf);
         }
         mShader.append(mUserShader);
     } else {
-        uint32_t mask = mTextureEnableMask;
-        uint32_t texNum = 0;
-        while (mask) {
-            if (mask & 1) {
-                char buf[64];
-                mShader.append("uniform sampler2D uni_Tex");
-                sprintf(buf, "%i", texNum);
-                mShader.append(buf);
-                mShader.append(";\n");
-            }
-            mask >>= 1;
-            texNum++;
-        }
-
-
-        mShader.append("void main() {\n");
-        if (mVaryingColor) {
-            mShader.append("  lowp vec4 col = varColor;\n");
-        } else {
-            mShader.append("  lowp vec4 col = uni_Color;\n");
-        }
-
-        if (mTextureEnableMask) {
-            if (mPointSpriteEnable) {
-                mShader.append("  vec2 t0 = gl_PointCoord;\n");
-            } else {
-                mShader.append("  vec2 t0 = varTex0.xy;\n");
-            }
-        }
-
-        mask = mTextureEnableMask;
-        texNum = 0;
-        while (mask) {
-            if (mask & 1) {
-                switch(mEnvModes[texNum]) {
-                case RS_TEX_ENV_MODE_NONE:
-                    rsAssert(0);
-                    break;
-                case RS_TEX_ENV_MODE_REPLACE:
-                    switch(mTextureFormats[texNum]) {
-                    case 1:
-                        mShader.append("  col.a = texture2D(uni_Tex0, t0).a;\n");
-                        break;
-                    case 2:
-                        mShader.append("  col.rgba = texture2D(uni_Tex0, t0).rgba;\n");
-                        break;
-                    case 3:
-                        mShader.append("  col.rgb = texture2D(uni_Tex0, t0).rgb;\n");
-                        break;
-                    case 4:
-                        mShader.append("  col.rgba = texture2D(uni_Tex0, t0).rgba;\n");
-                        break;
-                    }
-                    break;
-                case RS_TEX_ENV_MODE_MODULATE:
-                    switch(mTextureFormats[texNum]) {
-                    case 1:
-                        mShader.append("  col.a *= texture2D(uni_Tex0, t0).a;\n");
-                        break;
-                    case 2:
-                        mShader.append("  col.rgba *= texture2D(uni_Tex0, t0).rgba;\n");
-                        break;
-                    case 3:
-                        mShader.append("  col.rgb *= texture2D(uni_Tex0, t0).rgb;\n");
-                        break;
-                    case 4:
-                        mShader.append("  col.rgba *= texture2D(uni_Tex0, t0).rgba;\n");
-                        break;
-                    }
-                    break;
-                case RS_TEX_ENV_MODE_DECAL:
-                    mShader.append("  col = texture2D(uni_Tex0, t0);\n");
-                    break;
-                }
-
-            }
-            mask >>= 1;
-            texNum++;
-        }
-
-        //mShader.append("  col.a = 1.0;\n");
-        //mShader.append("  col.r = 0.5;\n");
-
-        mShader.append("  gl_FragColor = col;\n");
-        mShader.append("}\n");
+        LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
+        rsAssert(0);
     }
 }
 
 void ProgramFragment::init(Context *rsc)
 {
     mUniformCount = 0;
-    //if (!mVaryingColor) {
-        mConstantColorUniformIndex = mUniformCount;
-        mUniformNames[mUniformCount++].setTo("uni_Color");
-    //}
-
     if (mUserShader.size() > 0) {
         for (uint32_t ct=0; ct < mConstantCount; ct++) {
             initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_");
         }
     }
     mTextureUniformIndexStart = mUniformCount;
-    mUniformNames[mUniformCount++].setTo("uni_Tex0");
-    mUniformNames[mUniformCount++].setTo("uni_Tex1");
+    mUniformNames[mUniformCount++].setTo("UNI_Tex0");
+    mUniformNames[mUniformCount++].setTo("UNI_Tex1");
 
     createShader();
 }
@@ -303,12 +208,36 @@
 
 void ProgramFragmentState::init(Context *rsc)
 {
-    uint32_t tmp[] = {
-        RS_TEX_ENV_MODE_NONE, 0,
-        RS_TEX_ENV_MODE_NONE, 0,
-        0, 0
-    };
-    ProgramFragment *pf = new ProgramFragment(rsc, tmp, 6);
+    String8 shaderString(RS_SHADER_INTERNAL);
+    shaderString.append("varying lowp vec4 varColor;\n");
+    shaderString.append("varying vec4 varTex0;\n");
+    shaderString.append("void main() {\n");
+    shaderString.append("  lowp vec4 col = UNI_Color;\n");
+    shaderString.append("  gl_FragColor = col;\n");
+    shaderString.append("}\n");
+
+    const Element *colorElem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
+    rsc->mStateElement.elementBuilderBegin();
+    rsc->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
+    const Element *constInput = rsc->mStateElement.elementBuilderCreate(rsc);
+
+    Type *inputType = new Type(rsc);
+    inputType->setElement(constInput);
+    inputType->setDimX(1);
+    inputType->compute();
+
+    uint32_t tmp[4];
+    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+    tmp[1] = (uint32_t)inputType;
+    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+    tmp[3] = 0;
+
+    Allocation *constAlloc = new Allocation(rsc, inputType);
+    ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(),
+                                              shaderString.length(), tmp, 4);
+    pf->bindAllocation(constAlloc, 0);
+    pf->setConstantColor(1.0f, 1.0f, 1.0f, 1.0f);
+
     mDefault.set(pf);
 }
 
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 68e3705..28084d7 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -79,15 +79,7 @@
 
 void ProgramVertex::createShader()
 {
-    mShader.setTo("");
-
-    mShader.append("varying vec4 varColor;\n");
-    mShader.append("varying vec4 varTex0;\n");
-
     if (mUserShader.length() > 1) {
-        mShader.append("uniform mat4 ");
-        mShader.append(mUniformNames[0]);
-        mShader.append(";\n");
 
         appendUserConstants();
 
@@ -118,28 +110,8 @@
         }
         mShader.append(mUserShader);
     } else {
-        mShader.append("attribute vec4 ATTRIB_position;\n");
-        mShader.append("attribute vec4 ATTRIB_color;\n");
-        mShader.append("attribute vec3 ATTRIB_normal;\n");
-        mShader.append("attribute vec4 ATTRIB_texture0;\n");
-
-        for (uint32_t ct=0; ct < mUniformCount; ct++) {
-            mShader.append("uniform mat4 ");
-            mShader.append(mUniformNames[ct]);
-            mShader.append(";\n");
-        }
-
-        mShader.append("void main() {\n");
-        mShader.append("  gl_Position = UNI_MVP * ATTRIB_position;\n");
-        mShader.append("  gl_PointSize = 1.0;\n");
-
-        mShader.append("  varColor = ATTRIB_color;\n");
-        if (mTextureMatrixEnable) {
-            mShader.append("  varTex0 = UNI_TexMatrix * ATTRIB_texture0;\n");
-        } else {
-            mShader.append("  varTex0 = ATTRIB_texture0;\n");
-        }
-        mShader.append("}\n");
+        LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
+        rsAssert(0);
     }
 }
 
@@ -152,18 +124,16 @@
 
     rsc->checkError("ProgramVertex::setupGL2 start");
 
-    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
-
-    Matrix mvp;
-    mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
-    Matrix t;
-    t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
-    mvp.multiply(&t);
-
-    glUniformMatrix4fv(sc->vtxUniformSlot(0), 1, GL_FALSE, mvp.m);
-    if (mTextureMatrixEnable) {
-        glUniformMatrix4fv(sc->vtxUniformSlot(1), 1, GL_FALSE,
-                           &f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
+    if(!isUserProgram()) {
+        float *f = static_cast<float *>(mConstants[0]->getPtr());
+        Matrix mvp;
+        mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
+        Matrix t;
+        t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
+        mvp.multiply(&t);
+        for(uint32_t i = 0; i < 16; i ++) {
+            f[RS_PROGRAM_VERTEX_MVP_OFFSET + i] = mvp.m[i];
+        }
     }
 
     rsc->checkError("ProgramVertex::setupGL2 begin uniforms");
@@ -183,6 +153,9 @@
 
 void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
 {
+    if(isUserProgram()) {
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
@@ -190,6 +163,9 @@
 
 void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
 {
+    if(isUserProgram()) {
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
@@ -197,6 +173,9 @@
 
 void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
 {
+    if(isUserProgram()) {
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
@@ -204,12 +183,18 @@
 
 void ProgramVertex::getProjectionMatrix(rsc_Matrix *m) const
 {
+    if(isUserProgram()) {
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix));
 }
 
 void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const
 {
+    if(isUserProgram()) {
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     Matrix mvp;
     mvp.loadMultiply((Matrix *)&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET],
@@ -225,17 +210,11 @@
             initAddUserElement(mInputElements[ct].get(), mAttribNames, &mAttribCount, "ATTRIB_");
         }
 
-        mUniformCount = 1;
-        mUniformNames[0].setTo("UNI_MVP");
+        mUniformCount = 0;
         for (uint32_t ct=0; ct < mConstantCount; ct++) {
             initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_");
         }
-    } else {
-        mUniformCount = 2;
-        mUniformNames[0].setTo("UNI_MVP");
-        mUniformNames[1].setTo("UNI_TexMatrix");
     }
-
     createShader();
 }
 
@@ -262,41 +241,78 @@
 
 void ProgramVertexState::init(Context *rsc)
 {
-#ifndef ANDROID_RS_BUILD_FOR_HOST
-    RsElement e = (RsElement) Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
+    const Element *matrixElem = Element::create(rsc, RS_TYPE_MATRIX_4X4, RS_KIND_USER, false, 1);
+    const Element *f3Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
+    const Element *f4Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
 
-    rsi_TypeBegin(rsc, e);
-    rsi_TypeAdd(rsc, RS_DIMENSION_X, 48);
-    mAllocType.set((Type *)rsi_TypeCreate(rsc));
+    rsc->mStateElement.elementBuilderBegin();
+    rsc->mStateElement.elementBuilderAdd(matrixElem, "MV", 1);
+    rsc->mStateElement.elementBuilderAdd(matrixElem, "P", 1);
+    rsc->mStateElement.elementBuilderAdd(matrixElem, "TexMatrix", 1);
+    rsc->mStateElement.elementBuilderAdd(matrixElem, "MVP", 1);
+    const Element *constInput = rsc->mStateElement.elementBuilderCreate(rsc);
 
-    ProgramVertex *pv = new ProgramVertex(rsc, false);
-    Allocation *alloc = (Allocation *)rsi_AllocationCreateTyped(rsc, mAllocType.get());
+    rsc->mStateElement.elementBuilderBegin();
+    rsc->mStateElement.elementBuilderAdd(f4Elem, "position", 1);
+    rsc->mStateElement.elementBuilderAdd(f4Elem, "color", 1);
+    rsc->mStateElement.elementBuilderAdd(f3Elem, "normal", 1);
+    rsc->mStateElement.elementBuilderAdd(f4Elem, "texture0", 1);
+    const Element *attrElem = rsc->mStateElement.elementBuilderCreate(rsc);
+
+    Type *inputType = new Type(rsc);
+    inputType->setElement(constInput);
+    inputType->setDimX(1);
+    inputType->compute();
+
+    String8 shaderString(RS_SHADER_INTERNAL);
+    shaderString.append("varying vec4 varColor;\n");
+    shaderString.append("varying vec4 varTex0;\n");
+    shaderString.append("void main() {\n");
+    shaderString.append("  gl_Position = UNI_MVP * ATTRIB_position;\n");
+    shaderString.append("  gl_PointSize = 1.0;\n");
+    shaderString.append("  varColor = ATTRIB_color;\n");
+    shaderString.append("  varTex0 = ATTRIB_texture0;\n");
+    shaderString.append("}\n");
+
+    uint32_t tmp[6];
+    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+    tmp[1] = (uint32_t)inputType;
+    tmp[2] = RS_PROGRAM_PARAM_INPUT;
+    tmp[3] = (uint32_t)attrElem;
+    tmp[4] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+    tmp[5] = 0;
+
+    ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(),
+                                          shaderString.length(), tmp, 6);
+    Allocation *alloc = new Allocation(rsc, inputType);
+    pv->bindAllocation(alloc, 0);
 
     mDefaultAlloc.set(alloc);
     mDefault.set(pv);
-    pv->init(rsc);
     pv->bindAllocation(alloc, 0);
 
     updateSize(rsc);
-#endif //ANDROID_RS_BUILD_FOR_HOST
 
 }
 
 void ProgramVertexState::updateSize(Context *rsc)
 {
+    float *f = static_cast<float *>(mDefaultAlloc->getPtr());
+
     Matrix m;
     m.loadOrtho(0,rsc->getWidth(), rsc->getHeight(),0, -1,1);
-    mDefaultAlloc->subData(rsc, RS_PROGRAM_VERTEX_PROJECTION_OFFSET, 16, &m.m[0], 16*4);
+    memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m.m, sizeof(m));
+    memcpy(&f[RS_PROGRAM_VERTEX_MVP_OFFSET], m.m, sizeof(m));
 
     m.loadIdentity();
-    mDefaultAlloc->subData(rsc, RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, 16, &m.m[0], 16*4);
+    memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m.m, sizeof(m));
+    memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m.m, sizeof(m));
 }
 
 void ProgramVertexState::deinit(Context *rsc)
 {
     mDefaultAlloc.clear();
     mDefault.clear();
-    mAllocType.clear();
     mLast.clear();
 }
 
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 59fd3195..d6b3f5a 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -79,8 +79,6 @@
     ObjectBaseRef<ProgramVertex> mDefault;
     ObjectBaseRef<ProgramVertex> mLast;
     ObjectBaseRef<Allocation> mDefaultAlloc;
-
-    ObjectBaseRef<Type> mAllocType;
 };
 
 
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/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/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..d5bf8b3 100755
--- a/tests/DumpRenderTree2/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -3,34 +3,39 @@
 """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')
 
   tmpdir = tempfile.gettempdir()
 
+  # Restart the server
+  cmd = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "run_apache2.py") + " restart"
+  os.system(cmd);
+
   # Run the tests in path
   cmd = "adb shell am instrument "
   cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests "
@@ -61,7 +66,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..ec8409a 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,27 @@
         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 setAppCacheMaximumSize(long size) {
+        Log.i(LOG_TAG, "setAppCacheMaximumSize() called with: " + size);
+        WebStorage.getInstance().setAppCacheMaximumSize(size);
     }
 
     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 +84,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 +94,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/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 3763bf2..2cde296 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -258,7 +258,7 @@
         StringBuilder txt = new StringBuilder();
 
         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
-        txt.append(mTestsRelativePath + "\n");
+        txt.append("Path: " + mTestsRelativePath + "\n");
         txt.append("Date: " + dateFormat.format(mDate) + "\n");
         txt.append("Build fingerprint: " + Build.FINGERPRINT + "\n");
         txt.append("WebKit version: " + getWebKitVersionFromUserAgentString() + "\n");
@@ -338,7 +338,8 @@
 
     private void createTopSummaryTable(String webKitRevision, StringBuilder html) {
         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
-        html.append("<h1>" + mTestsRelativePath + "</h1>");
+        html.append("<h1>" + "Layout tests' results for: " +
+                (mTestsRelativePath.equals("") ? "all tests" : mTestsRelativePath) + "</h1>");
         html.append("<h3>" + "Date: " + dateFormat.format(new Date()) + "</h3>");
         html.append("<h3>" + "Build fingerprint: " + Build.FINGERPRINT + "</h3>");
         html.append("<h3>" + "WebKit version: " + getWebKitVersionFromUserAgentString() + "</h3>");
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) {