Merge "Add support for ColorFilters."
diff --git a/api/current.xml b/api/current.xml
index 276c64c..e31e3f9 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -41061,6 +41061,17 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
+<method name="keySet"
+ return="java.util.Set&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="put"
  return="void"
  abstract="false"
@@ -61622,6 +61633,19 @@
 <parameter name="name" type="java.lang.String">
 </parameter>
 </method>
+<method name="getSqlStatementType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sql" type="java.lang.String">
+</parameter>
+</method>
 <method name="longForQuery"
  return="long"
  abstract="false"
@@ -61772,6 +61796,83 @@
 <parameter name="e" type="java.lang.Exception">
 </parameter>
 </method>
+<field name="STATEMENT_ABORT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_ATTACH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_BEGIN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_COMMIT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_OTHER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_SELECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_UPDATE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="DatabaseUtils.InsertHelper"
  extends="java.lang.Object"
@@ -178689,6 +178790,17 @@
  visibility="public"
 >
 </method>
+<method name="getMenuInflater"
+ return="android.view.MenuInflater"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getSubtitle"
  return="java.lang.CharSequence"
  abstract="true"
@@ -219371,6 +219483,17 @@
  visibility="public"
 >
 </method>
+<method name="getCheckedItemCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getCheckedItemIds"
  return="long[]"
  abstract="false"
@@ -219626,6 +219749,19 @@
 <parameter name="itemsCanFocus" type="boolean">
 </parameter>
 </method>
+<method name="setMultiChoiceModeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.ListView.MultiChoiceModeListener">
+</parameter>
+</method>
 <method name="setSelection"
  return="void"
  abstract="false"
@@ -219689,6 +219825,17 @@
  visibility="public"
 >
 </field>
+<field name="CHOICE_MODE_MULTIPLE_MODAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CHOICE_MODE_NONE"
  type="int"
  transient="false"
@@ -219759,6 +219906,35 @@
 >
 </field>
 </class>
+<interface name="ListView.MultiChoiceModeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.ActionMode.Callback">
+</implements>
+<method name="onItemCheckedStateChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="android.view.ActionMode">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="id" type="long">
+</parameter>
+<parameter name="checked" type="boolean">
+</parameter>
+</method>
+</interface>
 <class name="MediaController"
  extends="android.widget.FrameLayout"
  abstract="false"
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index 75787cd..e6dedc1 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -446,6 +446,15 @@
         return mValues.entrySet();
     }
 
+    /**
+     * Returns a set of all of the keys
+     *
+     * @return a set of all of the keys
+     */
+    public Set<String> keySet() {
+        return mValues.keySet();
+    }
+
     public static final Parcelable.Creator<ContentValues> CREATOR =
             new Parcelable.Creator<ContentValues>() {
         @SuppressWarnings({"deprecation", "unchecked"})
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8e93855..bc78d37 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -587,7 +587,7 @@
      * location from the apk location at the given file path.
      * @param packageFilePath file location of the apk
      * @param flags Special parse flags
-     * @return PackageLite object with package information.
+     * @return PackageLite object with package information or null on failure.
      */
     public static PackageLite parsePackageLite(String packageFilePath, int flags) {
         XmlResourceParser parser = null;
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 4063534..0687659 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -52,6 +52,21 @@
 
     private static final String[] countProjection = new String[]{"count(*)"};
 
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_SELECT = 1;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_UPDATE = 2;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_ATTACH = 3;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_BEGIN = 4;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_COMMIT = 5;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_ABORT = 6;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_OTHER = 7;
+
     /**
      * Special function for writing an exception result at the header of
      * a parcel, to be used when returning an exception from a transaction.
@@ -1159,4 +1174,45 @@
         db.setVersion(dbVersion);
         db.close();
     }
+
+    /**
+     * Returns one of the following which represent the type of the given SQL statement.
+     * <ol>
+     *   <li>{@link #STATEMENT_SELECT}</li>
+     *   <li>{@link #STATEMENT_UPDATE}</li>
+     *   <li>{@link #STATEMENT_ATTACH}</li>
+     *   <li>{@link #STATEMENT_BEGIN}</li>
+     *   <li>{@link #STATEMENT_COMMIT}</li>
+     *   <li>{@link #STATEMENT_ABORT}</li>
+     *   <li>{@link #STATEMENT_OTHER}</li>
+     * </ol>
+     * @param sql the SQL statement whose type is returned by this method
+     * @return one of the values listed above
+     */
+    public static int getSqlStatementType(String sql) {
+        sql = sql.trim();
+        if (sql.length() < 3) {
+            return STATEMENT_OTHER;
+        }
+        String prefixSql = sql.substring(0, 3).toUpperCase();
+        if (prefixSql.equals("SEL")) {
+            return STATEMENT_SELECT;
+        } else if (prefixSql.equals("INS") ||
+                prefixSql.equals("UPD") ||
+                prefixSql.equals("REP") ||
+                prefixSql.equals("DEL")) {
+            return STATEMENT_UPDATE;
+        } else if (prefixSql.equals("ATT")) {
+            return STATEMENT_ATTACH;
+        } else if (prefixSql.equals("COM")) {
+            return STATEMENT_COMMIT;
+        } else if (prefixSql.equals("END")) {
+            return STATEMENT_COMMIT;
+        } else if (prefixSql.equals("ROL")) {
+            return STATEMENT_ABORT;
+        } else if (prefixSql.equals("BEG")) {
+            return STATEMENT_BEGIN;
+        }
+        return STATEMENT_OTHER;
+    }
 }
diff --git a/core/java/android/database/sqlite/DatabaseConnectionPool.java b/core/java/android/database/sqlite/DatabaseConnectionPool.java
index 50b2919..54b0605 100644
--- a/core/java/android/database/sqlite/DatabaseConnectionPool.java
+++ b/core/java/android/database/sqlite/DatabaseConnectionPool.java
@@ -63,6 +63,9 @@
      */
     /* package */ void close() {
         synchronized(mParentDbObj) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Closing the connection pool on " + mParentDbObj.getPath() + toString());
+            }
             for (int i = mPool.size() - 1; i >= 0; i--) {
                 mPool.get(i).mDb.close();
             }
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 3fe6087..43dd5d7 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -393,9 +393,12 @@
             if (!sAlreadyWarned.containsKey(databasePath)) {
                 sAlreadyWarned.put(databasePath, true);
                 String packageName = ActivityThread.currentPackageName();
+                Throwable t = null;
+                // BEGIN STOPSHIP remove the following line
+                t = new RequeryOnUiThreadException(packageName);
+                // END STOPSHIP
                 Log.w(TAG, "should not attempt requery on main (UI) thread: app = " +
-                        packageName == null ? "'unknown'" : packageName,
-                        new RequeryOnUiThreadException(packageName));
+                        packageName == null ? "'unknown'" : packageName, t);
             }
         }
     }
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 623821b..a2fff73 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -192,6 +192,11 @@
      */
     private SQLiteTransactionListener mTransactionListener;
 
+    /**
+     * this member is set if {@link #execSQL(String)} is used to begin and end transactions.
+     */
+    private boolean mTransactionUsingExecSql;
+
     /** Synchronize on this when accessing the database */
     private final ReentrantLock mLock = new ReentrantLock(true);
 
@@ -236,9 +241,6 @@
     /** Used by native code, do not rename. make it volatile, so it is thread-safe. */
     /* package */ volatile int mNativeHandle = 0;
 
-    /** Used to make temp table names unique */
-    /* package */ int mTempTableSequence = 0;
-
     /**
      * The size, in bytes, of a block on "/data". This corresponds to the Unix
      * statfs.f_bsize field. note that this field is lazily initialized.
@@ -621,9 +623,6 @@
 
             // This thread didn't already have the lock, so begin a database
             // transaction now.
-            // STOPSHIP - uncomment the following 1 line
-            // if (exclusive) {
-            // STOPSHIP - remove the following 1 line
             if (exclusive && mConnectionPool == null) {
                 execSQL("BEGIN EXCLUSIVE;");
             } else {
@@ -740,7 +739,50 @@
      * return true if there is a transaction pending
      */
     public boolean inTransaction() {
-        return mLock.getHoldCount() > 0;
+        return mLock.getHoldCount() > 0 || mTransactionUsingExecSql;
+    }
+
+    /* package */ synchronized void setTransactionUsingExecSqlFlag() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.i(TAG, "found execSQL('begin transaction')");
+        }
+        mTransactionUsingExecSql = true;
+    }
+
+    /* package */ synchronized void resetTransactionUsingExecSqlFlag() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (mTransactionUsingExecSql) {
+                Log.i(TAG, "found execSQL('commit or end or rollback')");
+            }
+        }
+        mTransactionUsingExecSql = false;
+    }
+
+    /**
+     * Returns true if the caller is considered part of the current transaction, if any.
+     * <p>
+     * Caller is part of the current transaction if either of the following is true
+     * <ol>
+     *   <li>If transaction is started by calling beginTransaction() methods AND if the caller is
+     *   in the same thread as the thread that started the transaction.
+     *   </li>
+     *   <li>If the transaction is started by calling {@link #execSQL(String)} like this:
+     *   execSQL("BEGIN transaction"). In this case, every thread in the process is considered
+     *   part of the current transaction.</li>
+     * </ol>
+     *
+     * @return true if the caller is considered part of the current transaction, if any.
+     */
+    /* package */ synchronized boolean amIInTransaction() {
+        // always do this test on the main database connection - NOT on pooled database connection
+        // since transactions always occur on the main database connections only.
+        SQLiteDatabase db = (isPooledConnection()) ? mParentConnObj : this;
+        boolean b = (!db.inTransaction()) ? false :
+                db.mTransactionUsingExecSql || db.mLock.isHeldByCurrentThread();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.i(TAG, "amIinTransaction: " + b);
+        }
+        return b;
     }
 
     /**
@@ -932,6 +974,9 @@
             DatabaseErrorHandler errorHandler, short connectionNum) {
         SQLiteDatabase db = new SQLiteDatabase(path, factory, flags, errorHandler, connectionNum);
         try {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.i(TAG, "opening the db : " + path);
+            }
             // Open the database.
             db.dbopen(path, flags);
             db.setLocale(Locale.getDefault());
@@ -1008,7 +1053,7 @@
         if (!isOpen()) {
             return; // already closed
         }
-        if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.i(TAG, "closing db: " + mPath + " (connection # " + mConnectionNum);
         }
         lock();
@@ -1020,6 +1065,10 @@
             // close this database instance - regardless of its reference count value
             dbclose();
             if (mConnectionPool != null) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    assert mConnectionPool != null;
+                    Log.i(TAG, mConnectionPool.toString());
+                }
                 mConnectionPool.close();
             }
         } finally {
@@ -1586,7 +1635,6 @@
     public long insertWithOnConflict(String table, String nullColumnHack,
             ContentValues initialValues, int conflictAlgorithm) {
         verifyDbIsOpen();
-        BlockGuard.getThreadPolicy().onWriteToDisk();
 
         // Measurements show most sql lengths <= 152
         StringBuilder sql = new StringBuilder(152);
@@ -1625,7 +1673,6 @@
         sql.append(values);
         sql.append(");");
 
-        lock();
         SQLiteStatement statement = null;
         try {
             statement = compileStatement(sql.toString());
@@ -1649,7 +1696,6 @@
             if (statement != null) {
                 statement.close();
             }
-            unlock();
         }
     }
 
@@ -1665,8 +1711,6 @@
      */
     public int delete(String table, String whereClause, String[] whereArgs) {
         verifyDbIsOpen();
-        BlockGuard.getThreadPolicy().onWriteToDisk();
-        lock();
         SQLiteStatement statement = null;
         try {
             statement = compileStatement("DELETE FROM " + table
@@ -1686,7 +1730,6 @@
             if (statement != null) {
                 statement.close();
             }
-            unlock();
         }
     }
 
@@ -1717,7 +1760,6 @@
      */
     public int updateWithOnConflict(String table, ContentValues values,
             String whereClause, String[] whereArgs, int conflictAlgorithm) {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
         if (values == null || values.size() == 0) {
             throw new IllegalArgumentException("Empty values");
         }
@@ -1746,7 +1788,6 @@
         }
 
         verifyDbIsOpen();
-        lock();
         SQLiteStatement statement = null;
         try {
             statement = compileStatement(sql.toString());
@@ -1781,7 +1822,6 @@
             if (statement != null) {
                 statement.close();
             }
-            unlock();
         }
     }
 
@@ -1789,9 +1829,7 @@
      * Execute a single SQL statement that is NOT a SELECT
      * or any other SQL statement that returns data.
      * <p>
-     * Use of this method is discouraged as it doesn't perform well when issuing the same SQL
-     * statement repeatedly (see {@link #compileStatement(String)} to prepare statements for
-     * repeated use), and it has no means to return any data (such as the number of affected rows).
+     * It has no means to return any data (such as the number of affected rows).
      * Instead, you're encouraged to use {@link #insert(String, String, ContentValues)},
      * {@link #update(String, ContentValues, String, String[])}, et al, when possible.
      * </p>
@@ -1807,35 +1845,17 @@
      * @throws SQLException If the SQL string is invalid for some reason
      */
     public void execSQL(String sql) throws SQLException {
-        sql = sql.trim();
-        String prefix = sql.substring(0, 6);
-        if (prefix.equalsIgnoreCase("ATTACH")) {
+        int stmtType = DatabaseUtils.getSqlStatementType(sql);
+        if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
             disableWriteAheadLogging();
         }
-        verifyDbIsOpen();
-        BlockGuard.getThreadPolicy().onWriteToDisk();
         long timeStart = SystemClock.uptimeMillis();
-        lock();
         logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
-        SQLiteStatement stmt = null;
-        try {
-            closePendingStatements();
-            stmt = compileStatement(sql);
-            stmt.execute();
-        } catch (SQLiteDatabaseCorruptException e) {
-            onCorruption();
-            throw e;
-        } finally {
-            if (stmt != null) {
-                stmt.close();
-            }
-            unlock();
-        }
+        executeSql(sql, null);
 
         // Log commit statements along with the most recently executed
-        // SQL statement for disambiguation.  Note that instance
-        // equality to COMMIT_SQL is safe here.
-        if (sql == COMMIT_SQL) {
+        // SQL statement for disambiguation.
+        if (stmtType == DatabaseUtils.STATEMENT_COMMIT) {
             logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
         } else {
             logTimeStat(sql, timeStart, null);
@@ -1886,13 +1906,15 @@
      * @throws SQLException If the SQL string is invalid for some reason
      */
     public void execSQL(String sql, Object[] bindArgs) throws SQLException {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
         if (bindArgs == null) {
             throw new IllegalArgumentException("Empty bindArgs");
         }
+        executeSql(sql, bindArgs);
+    }
+
+    private void executeSql(String sql, Object[] bindArgs) throws SQLException {
         verifyDbIsOpen();
         long timeStart = SystemClock.uptimeMillis();
-        lock();
         SQLiteStatement statement = null;
         try {
             statement = compileStatement(sql);
@@ -1902,7 +1924,7 @@
                     DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
                 }
             }
-            statement.executeUpdateDelete();
+            statement.execute();
         } catch (SQLiteDatabaseCorruptException e) {
             onCorruption();
             throw e;
@@ -1910,7 +1932,6 @@
             if (statement != null) {
                 statement.close();
             }
-            unlock();
         }
         logTimeStat(sql, timeStart);
     }
@@ -2142,7 +2163,8 @@
         }
     }
 
-    private void deallocCachedSqlStatements() {
+    /** package-level access for testing purposes */
+    /* package */ void deallocCachedSqlStatements() {
         synchronized (mCompiledQueries) {
             for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
                 compiledSql.releaseSqlStatement();
@@ -2220,11 +2242,7 @@
         }
     }
 
-    /**
-     * public visibility only for testing. otherwise, package visibility is sufficient
-     * @hide
-     */
-    public void closePendingStatements() {
+    /* package */ void closePendingStatements() {
         if (!isOpen()) {
             // since this database is already closed, no need to finalize anything.
             mClosedStatementIds.clear();
@@ -2246,9 +2264,8 @@
 
     /**
      * for testing only
-     * @hide
      */
-    public ArrayList<Integer> getQueuedUpStmtList() {
+    /* package */ ArrayList<Integer> getQueuedUpStmtList() {
         return mClosedStatementIds;
     }
 
@@ -2303,7 +2320,10 @@
         // make sure this database has NO attached databases because sqlite's write-ahead-logging
         // doesn't work for databases with attached databases
         if (getAttachedDbs().size() > 1) {
-            Log.i(TAG, "this database: " + mPath + " has attached databases. can't  enable WAL.");
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG,
+                        "this database: " + mPath + " has attached databases. can't  enable WAL.");
+            }
             return false;
         }
         if (mConnectionPool == null) {
@@ -2331,7 +2351,8 @@
     /* package */ SQLiteDatabase getDatabaseHandle(String sql) {
         if (isPooledConnection()) {
             // this is a pooled database connection
-            if (isOpen()) {
+            // use it if it is open AND if I am not currently part of a transaction
+            if (isOpen() && !amIInTransaction()) {
                 // TODO: use another connection from the pool
                 // if this connection is currently in use by some other thread
                 // AND if there are free connections in the pool
@@ -2394,22 +2415,17 @@
         if (isPooledConnection()) {
             throw new IllegalStateException("incorrect database connection handle");
         }
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            // this method shoudl never be called with anything other than SELECT
-            if (sql.substring(0, 6).equalsIgnoreCase("SELECT")) {
-                throw new IllegalStateException("unexpected SQL statement: " + sql);
-            }
-        }
 
         // use the current connection handle if
-        // 1. if this thread is in a transaction
+        // 1. if the caller is part of the ongoing transaction, if any
         // 2. OR, if there is NO connection handle pool setup
-        if ((inTransaction() && mLock.isHeldByCurrentThread()) || mConnectionPool == null) {
+        if (amIInTransaction() || mConnectionPool == null) {
             return this;
         } else {
             // get a connection handle from the pool
             if (Log.isLoggable(TAG, Log.DEBUG)) {
                 assert mConnectionPool != null;
+                Log.i(TAG, mConnectionPool.toString());
             }
             return mConnectionPool.get(sql);
         }
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 017b65f..a4ebe5a 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,6 +16,7 @@
 
 package android.database.sqlite;
 
+import android.database.DatabaseUtils;
 import android.util.Log;
 import android.util.Pair;
 
@@ -31,11 +32,6 @@
 
     private static final String TAG = "SQLiteProgram";
 
-    /** the type of sql statement being processed by this object */
-    /* package */ static final int SELECT_STMT = 1;
-    private static final int UPDATE_STMT = 2;
-    private static final int OTHER_STMT = 3;
-
     /** The database this program is compiled against.
      * @deprecated do not use this
      */
@@ -88,7 +84,9 @@
      * <p>
      * It is protected (in multi-threaded environment) by {@link SQLiteProgram}.this
      */
-    private ArrayList<Pair<Integer, Object>> bindArgs = null;
+    private ArrayList<Pair<Integer, Object>> mBindArgs = null;
+
+    /* package */ final int mStatementType;
 
     /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
         this(db, sql, true);
@@ -96,6 +94,7 @@
 
     /* package */ SQLiteProgram(SQLiteDatabase db, String sql, boolean compileFlag) {
         mSql = sql.trim();
+        mStatementType = DatabaseUtils.getSqlStatementType(mSql);
         db.acquireReference();
         db.addSQLiteClosable(this);
         mDatabase = db;
@@ -107,7 +106,8 @@
 
     private void compileSql() {
         // only cache CRUD statements
-        if (getSqlStatementType(mSql) == OTHER_STMT) {
+        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.
@@ -150,22 +150,6 @@
         nStatement = mCompiledSql.nStatement;
     }
 
-    /* package */ int getSqlStatementType(String sql) {
-        if (mSql.length() < 6) {
-            return OTHER_STMT;
-        }
-        String prefixSql = mSql.substring(0, 6);
-        if (prefixSql.equalsIgnoreCase("SELECT")) {
-            return SELECT_STMT;
-        } else if (prefixSql.equalsIgnoreCase("INSERT") ||
-                prefixSql.equalsIgnoreCase("UPDATE") ||
-                prefixSql.equalsIgnoreCase("REPLAC") ||
-                prefixSql.equalsIgnoreCase("DELETE")) {
-            return UPDATE_STMT;
-        }
-        return OTHER_STMT;
-    }
-
     @Override
     protected void onAllReferencesReleased() {
         releaseCompiledSqlIfNotInCache();
@@ -361,7 +345,7 @@
      */
     public void clearBindings() {
         synchronized (this) {
-            bindArgs = null;
+            mBindArgs = null;
             if (this.nStatement == 0) {
                 return;
             }
@@ -380,7 +364,7 @@
      */
     public void close() {
         synchronized (this) {
-            bindArgs = null;
+            mBindArgs = null;
             if (nHandle == 0 || !mDatabase.isOpen()) {
                 return;
             }
@@ -389,19 +373,19 @@
     }
 
     private synchronized void addToBindArgs(int index, Object value) {
-        if (bindArgs == null) {
-            bindArgs = new ArrayList<Pair<Integer, Object>>();
+        if (mBindArgs == null) {
+            mBindArgs = new ArrayList<Pair<Integer, Object>>();
         }
-        bindArgs.add(new Pair<Integer, Object>(index, value));
+        mBindArgs.add(new Pair<Integer, Object>(index, value));
     }
 
     /* package */ synchronized void compileAndbindAllArgs() {
         assert nStatement == 0;
         compileSql();
-        if (bindArgs == null) {
+        if (mBindArgs == null) {
             return;
         }
-        for (Pair<Integer, Object> p : bindArgs) {
+        for (Pair<Integer, Object> p : mBindArgs) {
             if (p.second == null) {
                 native_bind_null(p.first);
             } else if (p.second instanceof Long) {
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 7a683e4..619764a 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -16,6 +16,7 @@
 
 package android.database.sqlite;
 
+import android.database.DatabaseUtils;
 import android.os.SystemClock;
 
 import dalvik.system.BlockGuard;
@@ -36,6 +37,11 @@
     private static final boolean WRITE = false;
 
     private SQLiteDatabase mOrigDb;
+    private int state;
+    /** possible value for {@link #state}. indicates that a transaction is started.} */
+    private static final int TRANS_STARTED = 1;
+    /** possible value for {@link #state}. indicates that a lock is acquired.} */
+    private static final int LOCK_ACQUIRED = 2;
 
     /**
      * Don't use SQLiteStatement constructor directly, please use
@@ -150,18 +156,20 @@
      *   <li>make sure the database is open</li>
      *   <li>get a database connection from the connection pool,if possible</li>
      *   <li>notifies {@link BlockGuard} of read/write</li>
-     *   <li>get lock on the database</li>
+     *   <li>if the SQL statement is an update, start transaction if not already in one.
+     *   otherwise, get lock on the database</li>
      *   <li>acquire reference on this object</li>
      *   <li>and then return the current time _before_ the database lock was acquired</li>
      * </ul>
      * <p>
-     * This method removes the duplcate code from the other public
+     * This method removes the duplicate code from the other public
      * methods in this class.
      */
     private long acquireAndLock(boolean rwFlag) {
+        state = 0;
         // use pooled database connection handles for SELECT SQL statements
         mDatabase.verifyDbIsOpen();
-        SQLiteDatabase db = (getSqlStatementType(mSql) != SELECT_STMT) ? mDatabase
+        SQLiteDatabase db = (mStatementType != DatabaseUtils.STATEMENT_SELECT) ? mDatabase
                 : mDatabase.getDbConnection(mSql);
         // use the database connection obtained above
         mOrigDb = mDatabase;
@@ -172,20 +180,57 @@
         } else {
             BlockGuard.getThreadPolicy().onReadFromDisk();
         }
-        long startTime = SystemClock.uptimeMillis();
-        mDatabase.lock();
+
+        /*
+         * Special case handling of SQLiteDatabase.execSQL("BEGIN transaction").
+         * we know it is execSQL("BEGIN transaction") from the caller IF there is no lock held.
+         * beginTransaction() methods in SQLiteDatabase call lockForced() before
+         * calling execSQL("BEGIN transaction").
+         */
+        if (mStatementType == DatabaseUtils.STATEMENT_BEGIN) {
+            if (!mDatabase.isDbLockedByCurrentThread()) {
+                // transaction is  NOT started by calling beginTransaction() methods in
+                // SQLiteDatabase
+                mDatabase.setTransactionUsingExecSqlFlag();
+            }
+        } else if (mStatementType == DatabaseUtils.STATEMENT_UPDATE) {
+            // got update SQL statement. if there is NO pending transaction, start one
+            if (!mDatabase.inTransaction()) {
+                mDatabase.beginTransactionNonExclusive();
+                state = TRANS_STARTED;
+            }
+        }
+        // do I have database lock? if not, grab it.
+        if (!mDatabase.isDbLockedByCurrentThread()) {
+            mDatabase.lock();
+            state = LOCK_ACQUIRED;
+        }
+
         acquireReference();
+        long startTime = SystemClock.uptimeMillis();
         mDatabase.closePendingStatements();
         compileAndbindAllArgs();
         return startTime;
     }
 
     /**
-     * this method releases locks and references acquired in {@link #acquireAndLock(boolean)}.
+     * this method releases locks and references acquired in {@link #acquireAndLock(boolean)}
      */
     private void releaseAndUnlock() {
         releaseReference();
-        mDatabase.unlock();
+        if (state == TRANS_STARTED) {
+            try {
+                mDatabase.setTransactionSuccessful();
+            } finally {
+                mDatabase.endTransaction();
+            }
+        } else if (state == LOCK_ACQUIRED) {
+            mDatabase.unlock();
+        }
+        if (mStatementType == DatabaseUtils.STATEMENT_COMMIT ||
+                mStatementType == DatabaseUtils.STATEMENT_ABORT) {
+            mDatabase.resetTransactionUsingExecSqlFlag();
+        }
         clearBindings();
         // release the compiled sql statement so that the caller's SQLiteStatement no longer
         // has a hard reference to a database object that may get deallocated at any point.
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 22c30a5..188a4aa 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -16,32 +16,168 @@
 
 package android.net;
 
-import org.apache.http.HttpHost;
-
 import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.util.Log;
 
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
 import java.net.URI;
 import java.net.UnknownHostException;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import junit.framework.Assert;
 
+import org.apache.http.HttpHost;
+
 /**
  * A convenience class for accessing the user and default proxy
  * settings.
  */
-final public class Proxy {
+public final class Proxy {
 
     // Set to true to enable extra debugging.
-    static final private boolean DEBUG = false;
+    private static final boolean DEBUG = false;
 
-    static final public String PROXY_CHANGE_ACTION =
+    public static final String PROXY_CHANGE_ACTION =
         "android.intent.action.PROXY_CHANGE";
 
+    private static ReadWriteLock sProxyInfoLock = new ReentrantReadWriteLock();
+
+    private static SettingsObserver sGlobalProxyChangedObserver = null;
+
+    private static ProxySpec sGlobalProxySpec = null;
+
+    // Hostname / IP REGEX validation
+    // Matches blank input, ips, and domain names
+    private static final String NAME_IP_REGEX =
+        "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
+
+    private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
+
+    private static final Pattern HOSTNAME_PATTERN;
+
+    private static final String EXCLLIST_REGEXP = "$|^(.?" + NAME_IP_REGEX
+        + ")+(,(.?" + NAME_IP_REGEX + "))*$";
+
+    private static final Pattern EXCLLIST_PATTERN;
+
+    static {
+        HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
+        EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
+    }
+
+    private static class ProxySpec {
+        String[] exclusionList = null;
+        InetSocketAddress proxyAddress = null;
+        public ProxySpec() { };
+    }
+
+    private static boolean isURLInExclusionListReadLocked(String url, String[] exclusionList) {
+        if (exclusionList == null) {
+            return false;
+        }
+        Uri u = Uri.parse(url);
+        String urlDomain = u.getHost();
+        // If the domain is defined as ".android.com" or "android.com", we wish to match
+        // http://android.com as well as http://xxx.android.com , but not
+        // http://myandroid.com . This code works out the logic.
+        for (String excludedDomain : exclusionList) {
+            String dotDomain = "." + excludedDomain;
+            if (urlDomain.equals(excludedDomain)) {
+                return true;
+            }
+            if (urlDomain.endsWith(dotDomain)) {
+                return true;
+            }
+        }
+        // No match
+        return false;
+    }
+
+    private static String parseHost(String proxySpec) {
+        int i = proxySpec.indexOf(':');
+        if (i == -1) {
+            if (DEBUG) {
+                Assert.assertTrue(proxySpec.length() == 0);
+            }
+            return null;
+        }
+        return proxySpec.substring(0, i);
+    }
+
+    private static int parsePort(String proxySpec) {
+        int i = proxySpec.indexOf(':');
+        if (i == -1) {
+            if (DEBUG) {
+                Assert.assertTrue(proxySpec.length() == 0);
+            }
+            return -1;
+        }
+        if (DEBUG) {
+            Assert.assertTrue(i < proxySpec.length());
+        }
+        return Integer.parseInt(proxySpec.substring(i+1));
+    }
+
+    /**
+     * Return the proxy object to be used for the URL given as parameter.
+     * @param ctx A Context used to get the settings for the proxy host.
+     * @param url A URL to be accessed. Used to evaluate exclusion list.
+     * @return Proxy (java.net) object containing the host name. If the
+     *         user did not set a hostname it returns the default host.
+     *         A null value means that no host is to be used.
+     * {@hide}
+     */
+    public static final java.net.Proxy getProxy(Context ctx, String url) {
+        sProxyInfoLock.readLock().lock();
+        try {
+            if (sGlobalProxyChangedObserver == null) {
+                registerContentObserversReadLocked(ctx);
+                parseGlobalProxyInfoReadLocked(ctx);
+            }
+            if (sGlobalProxySpec != null) {
+                // Proxy defined - Apply exclusion rules
+                if (isURLInExclusionListReadLocked(url, sGlobalProxySpec.exclusionList)) {
+                    // Return no proxy
+                    return java.net.Proxy.NO_PROXY;
+                }
+                java.net.Proxy retProxy =
+                    new java.net.Proxy(java.net.Proxy.Type.HTTP, sGlobalProxySpec.proxyAddress);
+                sProxyInfoLock.readLock().unlock();
+                if (isLocalHost(url)) {
+                    return java.net.Proxy.NO_PROXY;
+                }
+                sProxyInfoLock.readLock().lock();
+                return retProxy;
+            } else {
+                // If network is WiFi, return no proxy.
+                // Otherwise, return the Mobile Operator proxy.
+                if (!isNetworkWifi(ctx)) {
+                    java.net.Proxy retProxy = getDefaultProxy(url);
+                    sProxyInfoLock.readLock().unlock();
+                    if (isLocalHost(url)) {
+                        return java.net.Proxy.NO_PROXY;
+                    }
+                    sProxyInfoLock.readLock().lock();
+                    return retProxy;
+                } else {
+                    return java.net.Proxy.NO_PROXY;
+                }
+            }
+        } finally {
+            sProxyInfoLock.readLock().unlock();
+        }
+    }
+
+    // TODO: deprecate this function
     /**
      * Return the proxy host set by the user.
      * @param ctx A Context used to get the settings for the proxy host.
@@ -49,58 +185,53 @@
      *         name it returns the default host. A null value means that no
      *         host is to be used.
      */
-    static final public String getHost(Context ctx) {
-        ContentResolver contentResolver = ctx.getContentResolver();
-        Assert.assertNotNull(contentResolver);
-        String host = Settings.Secure.getString(
-                contentResolver,
-                Settings.Secure.HTTP_PROXY);
-        if (host != null) {
-            int i = host.indexOf(':');
-            if (i == -1) {
-                if (DEBUG) {
-                    Assert.assertTrue(host.length() == 0);
-                }
-                return null;
+    public static final String getHost(Context ctx) {
+        sProxyInfoLock.readLock().lock();
+        try {
+            if (sGlobalProxyChangedObserver == null) {
+                registerContentObserversReadLocked(ctx);
+                parseGlobalProxyInfoReadLocked(ctx);
             }
-            return host.substring(0, i);
+            if (sGlobalProxySpec != null) {
+                InetSocketAddress sa = sGlobalProxySpec.proxyAddress;
+                return sa.getHostName();
+            }
+            return getDefaultHost();
+        } finally {
+            sProxyInfoLock.readLock().unlock();
         }
-        return getDefaultHost();
     }
 
+    // TODO: deprecate this function
     /**
      * Return the proxy port set by the user.
      * @param ctx A Context used to get the settings for the proxy port.
      * @return The port number to use or -1 if no proxy is to be used.
      */
-    static final public int getPort(Context ctx) {
-        ContentResolver contentResolver = ctx.getContentResolver();
-        Assert.assertNotNull(contentResolver);
-        String host = Settings.Secure.getString(
-                contentResolver,
-                Settings.Secure.HTTP_PROXY);
-        if (host != null) {
-            int i = host.indexOf(':');
-            if (i == -1) {
-                if (DEBUG) {
-                    Assert.assertTrue(host.length() == 0);
-                }
-                return -1;
+    public static final int getPort(Context ctx) {
+        sProxyInfoLock.readLock().lock();
+        try {
+            if (sGlobalProxyChangedObserver == null) {
+                registerContentObserversReadLocked(ctx);
+                parseGlobalProxyInfoReadLocked(ctx);
             }
-            if (DEBUG) {
-                Assert.assertTrue(i < host.length());
+            if (sGlobalProxySpec != null) {
+                InetSocketAddress sa = sGlobalProxySpec.proxyAddress;
+                return sa.getPort();
             }
-            return Integer.parseInt(host.substring(i+1));
+            return getDefaultPort();
+        } finally {
+            sProxyInfoLock.readLock().unlock();
         }
-        return getDefaultPort();
     }
 
+    // TODO: deprecate this function
     /**
      * Return the default proxy host specified by the carrier.
      * @return String containing the host name or null if there is no proxy for
      * this carrier.
      */
-    static final public String getDefaultHost() {
+    public static final String getDefaultHost() {
         String host = SystemProperties.get("net.gprs.http-proxy");
         if (host != null) {
             Uri u = Uri.parse(host);
@@ -111,12 +242,13 @@
         }
     }
 
+    // TODO: deprecate this function
     /**
      * Return the default proxy port specified by the carrier.
      * @return The port number to be used with the proxy host or -1 if there is
      * no proxy for this carrier.
      */
-    static final public int getDefaultPort() {
+    public static final int getDefaultPort() {
         String host = SystemProperties.get("net.gprs.http-proxy");
         if (host != null) {
             Uri u = Uri.parse(host);
@@ -126,6 +258,20 @@
         }
     }
 
+    private static final java.net.Proxy getDefaultProxy(String url) {
+        // TODO: This will go away when information is collected from ConnectivityManager...
+        // There are broadcast of network proxies, so they are parse manually.
+        String host = SystemProperties.get("net.gprs.http-proxy");
+        if (host != null) {
+            Uri u = Uri.parse(host);
+            return new java.net.Proxy(java.net.Proxy.Type.HTTP,
+                    new InetSocketAddress(u.getHost(), u.getPort()));
+        } else {
+            return java.net.Proxy.NO_PROXY;
+        }
+    }
+
+    // TODO: remove this function / deprecate
     /**
      * Returns the preferred proxy to be used by clients. This is a wrapper
      * around {@link android.net.Proxy#getHost()}. Currently no proxy will
@@ -138,26 +284,23 @@
      * android.permission.ACCESS_NETWORK_STATE
      * @return The preferred proxy to be used by clients, or null if there
      * is no proxy.
-     *
      * {@hide}
      */
-    static final public HttpHost getPreferredHttpHost(Context context,
+    public static final HttpHost getPreferredHttpHost(Context context,
             String url) {
-        if (!isLocalHost(url) && !isNetworkWifi(context)) {
-            final String proxyHost = Proxy.getHost(context);
-            if (proxyHost != null) {
-                return new HttpHost(proxyHost, Proxy.getPort(context), "http");
-            }
+        java.net.Proxy prefProxy = getProxy(context, url);
+        if (prefProxy.equals(java.net.Proxy.NO_PROXY)) {
+            return null;
+        } else {
+            InetSocketAddress sa = (InetSocketAddress)prefProxy.address();
+            return new HttpHost(sa.getHostName(), sa.getPort(), "http");
         }
-
-        return null;
     }
 
-    static final private boolean isLocalHost(String url) {
+    private static final boolean isLocalHost(String url) {
         if (url == null) {
             return false;
         }
-
         try {
             final URI uri = URI.create(url);
             final String host = uri.getHost();
@@ -174,15 +317,13 @@
         } catch (IllegalArgumentException iex) {
             // Ignore (URI.create)
         }
-
         return false;
     }
 
-    static final private boolean isNetworkWifi(Context context) {
+    private static final boolean isNetworkWifi(Context context) {
         if (context == null) {
             return false;
         }
-
         final ConnectivityManager connectivity = (ConnectivityManager)
             context.getSystemService(Context.CONNECTIVITY_SERVICE);
         if (connectivity != null) {
@@ -192,7 +333,120 @@
                 return true;
             }
         }
-
         return false;
     }
-};
+
+    private static class SettingsObserver extends ContentObserver {
+
+        private Context mContext;
+
+        SettingsObserver(Context ctx) {
+            super(new Handler());
+            mContext = ctx;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            sProxyInfoLock.readLock().lock();
+            parseGlobalProxyInfoReadLocked(mContext);
+            sProxyInfoLock.readLock().unlock();
+        }
+    }
+
+    private static final void registerContentObserversReadLocked(Context ctx) {
+        Uri uriGlobalProxy = Settings.Secure.getUriFor(Settings.Secure.HTTP_PROXY);
+        Uri uriGlobalExclList =
+            Settings.Secure.getUriFor(Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
+
+        // No lock upgrading (from read to write) allowed
+        sProxyInfoLock.readLock().unlock();
+        sProxyInfoLock.writeLock().lock();
+        sGlobalProxyChangedObserver = new SettingsObserver(ctx);
+        // Downgrading locks (from write to read) is allowed
+        sProxyInfoLock.readLock().lock();
+        sProxyInfoLock.writeLock().unlock();
+        ctx.getContentResolver().registerContentObserver(uriGlobalProxy, false,
+                sGlobalProxyChangedObserver);
+        ctx.getContentResolver().registerContentObserver(uriGlobalExclList, false,
+                sGlobalProxyChangedObserver);
+    }
+
+    private static final void parseGlobalProxyInfoReadLocked(Context ctx) {
+        ContentResolver contentResolver = ctx.getContentResolver();
+        String proxyHost =  Settings.Secure.getString(
+                contentResolver,
+                Settings.Secure.HTTP_PROXY);
+        if (proxyHost == null) {
+            return;
+        }
+        String exclusionListSpec = Settings.Secure.getString(
+                contentResolver,
+                Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
+        String host = parseHost(proxyHost);
+        int port = parsePort(proxyHost);
+        if (proxyHost != null) {
+            sGlobalProxySpec = new ProxySpec();
+            sGlobalProxySpec.proxyAddress = new InetSocketAddress(host, port);
+            if (exclusionListSpec != null) {
+                String[] exclusionListEntries = exclusionListSpec.toLowerCase().split(",");
+                String[] processedEntries = new String[exclusionListEntries.length];
+                for (int i = 0; i < exclusionListEntries.length; i++) {
+                    String entry = exclusionListEntries[i].trim();
+                    if (entry.startsWith(".")) {
+                        entry = entry.substring(1);
+                    }
+                    processedEntries[i] = entry;
+                }
+                sProxyInfoLock.readLock().unlock();
+                sProxyInfoLock.writeLock().lock();
+                sGlobalProxySpec.exclusionList = processedEntries;
+            } else {
+                sProxyInfoLock.readLock().unlock();
+                sProxyInfoLock.writeLock().lock();
+                sGlobalProxySpec.exclusionList = null;
+            }
+        } else {
+            sProxyInfoLock.readLock().unlock();
+            sProxyInfoLock.writeLock().lock();
+            sGlobalProxySpec = null;
+        }
+        sProxyInfoLock.readLock().lock();
+        sProxyInfoLock.writeLock().unlock();
+    }
+
+    /**
+     * Validate syntax of hostname, port and exclusion list entries
+     * {@hide}
+     */
+    public static void validate(String hostname, String port, String exclList) {
+        Matcher match = HOSTNAME_PATTERN.matcher(hostname);
+        Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
+
+        if (!match.matches()) {
+            throw new IllegalArgumentException();
+        }
+
+        if (!listMatch.matches()) {
+            throw new IllegalArgumentException();
+        }
+
+        if (hostname.length() > 0 && port.length() == 0) {
+            throw new IllegalArgumentException();
+        }
+
+        if (port.length() > 0) {
+            if (hostname.length() == 0) {
+                throw new IllegalArgumentException();
+            }
+            int portVal = -1;
+            try {
+                portVal = Integer.parseInt(port);
+            } catch (NumberFormatException ex) {
+                throw new IllegalArgumentException();
+            }
+            if (portVal <= 0 || portVal > 0xFFFF) {
+                throw new IllegalArgumentException();
+            }
+        }
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4ec5363..a4f9f4e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2321,11 +2321,26 @@
         public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
 
         /**
-         * Host name and port for a user-selected proxy.
+         * Host name and port for global proxy.
          */
         public static final String HTTP_PROXY = "http_proxy";
 
         /**
+         * Exclusion list for global proxy. This string contains a list of comma-separated
+         * domains where the global proxy does not apply. Domains should be listed in a comma-
+         * separated list. Example of acceptable formats: ".domain1.com,my.domain2.com"
+         * @hide
+         */
+        public static final String HTTP_PROXY_EXCLUSION_LIST = "http_proxy_exclusion_list";
+
+        /**
+         * Enables the UI setting to allow the user to specify the global HTTP proxy
+         * and associated exclusion list.
+         * @hide
+         */
+        public static final String SET_GLOBAL_HTTP_PROXY = "set_global_http_proxy";
+
+        /**
          * Whether the package installer should allow installation of apps downloaded from
          * sources other than the Android Market (vending machine).
          *
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
index 4a7d7ad..bfafa98 100644
--- a/core/java/android/view/ActionMode.java
+++ b/core/java/android/view/ActionMode.java
@@ -118,6 +118,11 @@
     public abstract View getCustomView();
 
     /**
+     * Returns a {@link MenuInflater} with the ActionMode's context.
+     */
+    public abstract MenuInflater getMenuInflater();
+
+    /**
      * Callback interface for action modes. Supplied to
      * {@link View#startActionMode(Callback)}, a Callback
      * configures and handles events raised by a user's interaction with an action mode.
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1658b2f..372cc83 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1772,7 +1772,7 @@
         }
     }
 
-    private boolean performLongPress(final View child,
+    boolean performLongPress(final View child,
             final int longPressPosition, final long longPressId) {
         boolean handled = false;
 
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index ad9d930..0bb41e5 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -33,8 +33,12 @@
 import android.util.AttributeSet;
 import android.util.LongSparseArray;
 import android.util.SparseBooleanArray;
+import android.view.ActionMode;
 import android.view.FocusFinder;
+import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.SoundEffectConstants;
 import android.view.View;
@@ -90,6 +94,11 @@
     public static final int CHOICE_MODE_MULTIPLE = 2;
 
     /**
+     * The list allows multiple choices in a modal selection mode
+     */
+    public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
+
+    /**
      * When arrow scrolling, ListView will never scroll more than this factor
      * times the height of the list.
      */
@@ -147,7 +156,12 @@
 
     // Keeps focused children visible through resizes
     private FocusSelector mFocusSelector;
-    
+
+    // Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
+    private ActionMode mChoiceActionMode;
+    private MultiChoiceModeWrapper mMultiChoiceModeCallback;
+    private int mCheckedItemCount;
+
     public ListView(Context context) {
         this(context, null);
     }
@@ -3365,6 +3379,10 @@
      */
     public void setChoiceMode(int choiceMode) {
         mChoiceMode = choiceMode;
+        if (mChoiceActionMode != null) {
+            mChoiceActionMode.finish();
+            mChoiceActionMode = null;
+        }
         if (mChoiceMode != CHOICE_MODE_NONE) {
             if (mCheckStates == null) {
                 mCheckStates = new SparseBooleanArray();
@@ -3372,9 +3390,47 @@
             if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
                 mCheckedIdStates = new LongSparseArray<Boolean>();
             }
+            // Modal multi-choice mode only has choices when the mode is active. Clear them.
+            if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+                clearChoices();
+                setLongClickable(true);
+            }
         }
     }
 
+    /**
+     * Set a {@link MultiChoiceModeListener} that will manage the lifecycle of the
+     * selection {@link ActionMode}. Only used when the choice mode is set to
+     * {@link #CHOICE_MODE_MULTIPLE_MODAL}.
+     *
+     * @param listener Listener that will manage the selection mode
+     *
+     * @see #setChoiceMode(int)
+     */
+    public void setMultiChoiceModeListener(MultiChoiceModeListener listener) {
+        if (mMultiChoiceModeCallback == null) {
+            mMultiChoiceModeCallback = new MultiChoiceModeWrapper();
+        }
+        mMultiChoiceModeCallback.setWrapped(listener);
+    }
+
+    @Override
+    boolean performLongPress(final View child,
+            final int longPressPosition, final long longPressId) {
+        boolean handled = false;
+        if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+            handled = true;
+            if (mChoiceActionMode == null) {
+                mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+                setItemChecked(longPressPosition, true);
+            }
+            // TODO Should we select the long pressed item if we were already in
+            // selection mode? (i.e. treat it like an item click?)
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        }
+        return handled | super.performLongPress(child, longPressPosition, longPressId);
+    }
+
     @Override
     public boolean performItemClick(View view, int position, long id) {
         boolean handled = false;
@@ -3382,7 +3438,8 @@
         if (mChoiceMode != CHOICE_MODE_NONE) {
             handled = true;
 
-            if (mChoiceMode == CHOICE_MODE_MULTIPLE) {
+            if (mChoiceMode == CHOICE_MODE_MULTIPLE ||
+                    (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {
                 boolean newValue = !mCheckStates.get(position, false);
                 mCheckStates.put(position, newValue);
                 if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
@@ -3392,7 +3449,16 @@
                         mCheckedIdStates.delete(mAdapter.getItemId(position));
                     }
                 }
-            } else {
+                if (newValue) {
+                    mCheckedItemCount++;
+                } else {
+                    mCheckedItemCount--;
+                }
+                if (mChoiceActionMode != null) {
+                    mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
+                            position, id, newValue);
+                }
+            } else if (mChoiceMode == CHOICE_MODE_SINGLE) {
                 boolean newValue = !mCheckStates.get(position, false);
                 if (newValue) {
                     mCheckStates.clear();
@@ -3401,7 +3467,10 @@
                         mCheckedIdStates.clear();
                         mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
                     }
-                } 
+                    mCheckedItemCount = 1;
+                } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
+                    mCheckedItemCount = 0;
+                }
             }
 
             mDataChanged = true;
@@ -3427,7 +3496,13 @@
             return;
         }
 
-        if (mChoiceMode == CHOICE_MODE_MULTIPLE) {
+        // Start selection mode if needed. We don't need to if we're unchecking something.
+        if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
+            mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+        }
+
+        if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+            boolean oldValue = mCheckStates.get(position);
             mCheckStates.put(position, value);
             if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
                 if (value) {
@@ -3436,6 +3511,18 @@
                     mCheckedIdStates.delete(mAdapter.getItemId(position));
                 }
             }
+            if (oldValue != value) {
+                if (value) {
+                    mCheckedItemCount++;
+                } else {
+                    mCheckedItemCount--;
+                }
+            }
+            if (mChoiceActionMode != null) {
+                final long id = mAdapter.getItemId(position);
+                mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
+                        position, id, value);
+            }
         } else {
             boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
             // Clear all values if we're checking something, or unchecking the currently
@@ -3453,6 +3540,9 @@
                 if (updateIds) {
                     mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
                 }
+                mCheckedItemCount = 1;
+            } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
+                mCheckedItemCount = 0;
             }
         }
 
@@ -3465,6 +3555,23 @@
     }
 
     /**
+     * Returns the number of items currently selected. This will only be valid
+     * if the choice mode is not {@link #CHOICE_MODE_NONE} (default).
+     * 
+     * <p>To determine the specific items that are currently selected, use one of
+     * the <code>getChecked*</code> methods.
+     *
+     * @return The number of items currently selected
+     *
+     * @see #getCheckedItemPosition()
+     * @see #getCheckedItemPositions()
+     * @see #getCheckedItemIds()
+     */
+    public int getCheckedItemCount() {
+        return mCheckedItemCount;
+    }
+
+    /**
      * Returns the checked state of the specified position. The result is only
      * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
      * or {@link #CHOICE_MODE_MULTIPLE}.
@@ -3525,6 +3632,7 @@
      *         
      * @deprecated Use {@link #getCheckedItemIds()} instead.
      */
+    @Deprecated
     public long[] getCheckItemIds() {
         // Use new behavior that correctly handles stable ID mapping.
         if (mAdapter != null && mAdapter.hasStableIds()) {
@@ -3594,6 +3702,76 @@
         if (mCheckedIdStates != null) {
             mCheckedIdStates.clear();
         }
+        mCheckedItemCount = 0;
+    }
+
+    /**
+     * A MultiChoiceModeListener receives events for {@link ListView#CHOICE_MODE_MULTIPLE_MODAL}.
+     * It acts as the {@link ActionMode.Callback} for the selection mode and also receives
+     * {@link #onItemCheckedStateChanged(ActionMode, int, long, boolean)} events when the user
+     * selects and deselects list items.
+     */
+    public interface MultiChoiceModeListener extends ActionMode.Callback {
+        /**
+         * Called when an item is checked or unchecked during selection mode.
+         *
+         * @param mode The {@link ActionMode} providing the selection mode
+         * @param position Adapter position of the item that was checked or unchecked
+         * @param id Adapter ID of the item that was checked or unchecked
+         * @param checked <code>true</code> if the item is now checked, <code>false</code>
+         *                if the item is now unchecked.
+         */
+        public void onItemCheckedStateChanged(ActionMode mode,
+                int position, long id, boolean checked);
+    }
+
+    private class MultiChoiceModeWrapper implements MultiChoiceModeListener {
+        private MultiChoiceModeListener mWrapped;
+
+        public void setWrapped(MultiChoiceModeListener wrapped) {
+            mWrapped = wrapped;
+        }
+
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            if (mWrapped.onCreateActionMode(mode, menu)) {
+                // Initialize checked graphic state?
+                setLongClickable(false);
+                return true;
+            }
+            return false;
+        }
+
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return mWrapped.onPrepareActionMode(mode, menu);
+        }
+
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            return mWrapped.onActionItemClicked(mode, item);
+        }
+
+        public void onDestroyActionMode(ActionMode mode) {
+            mWrapped.onDestroyActionMode(mode);
+            mChoiceActionMode = null;
+
+            // Ending selection mode means deselecting everything.
+            clearChoices();
+
+            mDataChanged = true;
+            rememberSyncState();
+            requestLayout();
+
+            setLongClickable(true);
+        }
+
+        public void onItemCheckedStateChanged(ActionMode mode,
+                int position, long id, boolean checked) {
+            mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
+
+            // If there are no items selected we no longer need the selection mode.
+            if (getCheckedItemCount() == 0) {
+                mode.finish();
+            }
+        }
     }
 
     static class SavedState extends BaseSavedState {
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 0860bfe..281f32c 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -30,6 +30,7 @@
 import android.os.Handler;
 import android.view.ActionMode;
 import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -392,7 +393,12 @@
             mMenu = new MenuBuilder(mActionView.getContext());
             mMenu.setCallback(this);
         }
-        
+
+        @Override
+        public MenuInflater getMenuInflater() {
+            return new MenuInflater(mActivity);
+        }
+
         @Override
         public Menu getMenu() {
             return mMenu;
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index d8fd364..e6d6ba0 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.view.ActionMode;
 import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 
@@ -109,6 +110,11 @@
         return mCustomView != null ? mCustomView.get() : null;
     }
 
+    @Override
+    public MenuInflater getMenuInflater() {
+        return new MenuInflater(mContext);
+    }
+
     public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
         return mCallback.onActionItemClicked(this, item);
     }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index f0d9f60..c379505 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -28,19 +28,19 @@
  */
 public class ActionMenuItemView extends ImageButton implements MenuView.ItemView {
     private static final String TAG = "ActionMenuItemView";
-    
+
     private MenuItemImpl mItemData;
     private CharSequence mTitle;
     private MenuBuilder.ItemInvoker mItemInvoker;
-    
+
     public ActionMenuItemView(Context context) {
         this(context, null);
     }
-    
+
     public ActionMenuItemView(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.actionButtonStyle);
     }
-    
+
     public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
     }
@@ -51,24 +51,24 @@
 
     public void initialize(MenuItemImpl itemData, int menuType) {
         mItemData = itemData;
-        
+
         setClickable(true);
         setFocusable(true);
         setTitle(itemData.getTitle());
         setIcon(itemData.getIcon());
         setId(itemData.getItemId());
-        
+
         setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
         setEnabled(itemData.isEnabled());
     }
-    
+
     @Override
     public boolean performClick() {
         // Let the view's listener have top priority
         if (super.performClick()) {
             return true;
         }
-        
+
         if (mItemInvoker != null && mItemInvoker.invokeItem(mItemData)) {
             playSoundEffect(SoundEffectConstants.CLICK);
             return true;
@@ -76,7 +76,7 @@
             return false;
         }
     }
-    
+
     public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
         mItemInvoker = invoker;
     }
@@ -103,6 +103,9 @@
 
     public void setTitle(CharSequence title) {
         mTitle = title;
+
+        // populate accessibility description with title
+        setContentDescription(title);
     }
 
     public boolean showsIcon() {
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 0bedc4e..5518b3e 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -124,11 +124,13 @@
             }
             if (mSubtitle != null) {
                 mSubtitleView.setText(mSubtitle);
+                mSubtitleView.setVisibility(VISIBLE);
             }
             addView(mTitleLayout);
         } else {
             mTitleView.setText(mTitle);
             mSubtitleView.setText(mSubtitle);
+            mSubtitleView.setVisibility(mSubtitle != null ? VISIBLE : GONE);
             if (mTitleLayout.getParent() == null) {
                 addView(mTitleLayout);
             }
diff --git a/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
deleted file mode 100755
index ca76375..0000000
--- a/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_off.png b/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_off.png
deleted file mode 100755
index d73db48..0000000
--- a/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_off.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_on.png b/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_on.png
deleted file mode 100755
index 90da6e3..0000000
--- a/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_on.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-hdpi/ic_jog_dial_unlock.png b/core/res/res/drawable-land-hdpi/ic_jog_dial_unlock.png
deleted file mode 100755
index a9af1af..0000000
--- a/core/res/res/drawable-land-hdpi/ic_jog_dial_unlock.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/emo_im_embarrassed.png b/core/res/res/drawable-mdpi/emo_im_embarrassed.png
similarity index 100%
rename from core/res/res/drawable/emo_im_embarrassed.png
rename to core/res/res/drawable-mdpi/emo_im_embarrassed.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_back.png b/core/res/res/drawable-mdpi/ic_btn_back.png
similarity index 100%
rename from core/res/res/drawable/ic_btn_back.png
rename to core/res/res/drawable-mdpi/ic_btn_back.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_next.png b/core/res/res/drawable-mdpi/ic_btn_next.png
similarity index 100%
rename from core/res/res/drawable/ic_btn_next.png
rename to core/res/res/drawable-mdpi/ic_btn_next.png
Binary files differ
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6c14a3b..974733f 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1790,6 +1790,8 @@
             <enum name="singleChoice" value="1" />
             <!-- The list allows multiple choices. -->
             <enum name="multipleChoice" value="2" />
+            <!-- The list allows multiple choices in a custom selection mode. -->
+            <enum name="multipleChoiceModal" value="3" />
         </attr>
         <!-- When set to false, the ListView will not draw the divider after each header view.
              The default value is true. -->
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
index 0265c87..0733229 100644
--- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -33,7 +33,6 @@
 import android.test.PerformanceTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
@@ -41,8 +40,6 @@
 import java.util.Arrays;
 import java.util.Random;
 
-import junit.framework.TestCase;
-
 public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTestCase {
 
     private static final String sString1 = "this is a test";
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 662ba97..dc5613e 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -16,6 +16,7 @@
 
 package android.database.sqlite;
 
+import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
@@ -33,10 +34,13 @@
 
 public class SQLiteDatabaseTest extends AndroidTestCase {
     private static final String TAG = "DatabaseGeneralTest";
-
+    private static final String TEST_TABLE = "test";
     private static final int CURRENT_DATABASE_VERSION = 42;
     private SQLiteDatabase mDatabase;
     private File mDatabaseFile;
+    private static final int INSERT = 1;
+    private static final int UPDATE = 2;
+    private static final int DELETE = 3;
 
     @Override
     protected void setUp() throws Exception {
@@ -150,6 +154,100 @@
     }
 
     /**
+     * a transaction should be started before a standalone-update/insert/delete statement
+     */
+    @SmallTest
+    public void testStartXactBeforeUpdateSql() throws InterruptedException {
+        runTestForStartXactBeforeUpdateSql(INSERT);
+        runTestForStartXactBeforeUpdateSql(UPDATE);
+        runTestForStartXactBeforeUpdateSql(DELETE);
+    }
+    private void runTestForStartXactBeforeUpdateSql(int stmtType) throws InterruptedException {
+        createTableAndClearCache();
+
+        ContentValues values = new ContentValues();
+        // make some changes to data in TEST_TABLE
+        for (int i = 0; i < 5; i++) {
+            values.put("i", i);
+            values.put("j", "i" + System.currentTimeMillis());
+            mDatabase.insert(TEST_TABLE, null, values);
+            switch (stmtType) {
+                case UPDATE:
+                    values.put("j", "u" + System.currentTimeMillis());
+                    mDatabase.update(TEST_TABLE, values, "i = " + i, null);
+                    break;
+                case DELETE:
+                    mDatabase.delete(TEST_TABLE, "i = 1", null);
+                    break;
+            }
+        }
+        // do a query. even though query uses a different database connection,
+        // it should still see the above changes to data because the above standalone
+        // insert/update/deletes are done in transactions automatically.
+        String sql = "select count(*) from " + TEST_TABLE;
+        SQLiteStatement stmt = mDatabase.compileStatement(sql);
+        final int expectedValue = (stmtType == DELETE) ? 4 : 5;
+        assertEquals(expectedValue, stmt.simpleQueryForLong());
+        stmt.close();
+        Cursor c = mDatabase.rawQuery(sql, null);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        assertEquals(expectedValue, c.getLong(0));
+        c.close();
+
+        // do 5 more changes in a transaction but do a query before and after the commit
+        mDatabase.beginTransaction();
+        for (int i = 10; i < 15; i++) {
+            values.put("i", i);
+            values.put("j", "i" + System.currentTimeMillis());
+            mDatabase.insert(TEST_TABLE, null, values);
+            switch (stmtType) {
+                case UPDATE:
+                    values.put("j", "u" + System.currentTimeMillis());
+                    mDatabase.update(TEST_TABLE, values, "i = " + i, null);
+                    break;
+                case DELETE:
+                    mDatabase.delete(TEST_TABLE, "i = 1", null);
+                    break;
+            }
+        }
+        mDatabase.setTransactionSuccessful();
+        // do a query before commit - should still have 5 rows
+        // this query should run in a different thread to force it to use a different database
+        // connection
+        Thread t = new Thread() {
+            @Override public void run() {
+                String sql = "select count(*) from " + TEST_TABLE;
+                SQLiteStatement stmt = getDb().compileStatement(sql);
+                assertEquals(expectedValue, stmt.simpleQueryForLong());
+                stmt.close();
+                Cursor c = getDb().rawQuery(sql, null);
+                assertEquals(1, c.getCount());
+                c.moveToFirst();
+                assertEquals(expectedValue, c.getLong(0));
+                c.close();
+            }
+        };
+        t.start();
+        // wait until the above thread is done
+        t.join();
+        // commit and then query. should see changes from the transaction
+        mDatabase.endTransaction();
+        stmt = mDatabase.compileStatement(sql);
+        final int expectedValue2 = (stmtType == DELETE) ? 9 : 10;
+        assertEquals(expectedValue2, stmt.simpleQueryForLong());
+        stmt.close();
+        c = mDatabase.rawQuery(sql, null);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        assertEquals(expectedValue2, c.getLong(0));
+        c.close();
+    }
+    private synchronized SQLiteDatabase getDb() {
+        return mDatabase;
+    }
+
+    /**
      * Test to ensure that readers are able to read the database data (old versions)
      * EVEN WHEN the writer is in a transaction on the same database.
      *<p>
@@ -198,6 +296,8 @@
                 // set up connection pool
                 mDatabase.enableWriteAheadLogging();
                 mDatabase.setConnectionPoolSize(i + 1);
+            } else {
+                mDatabase.disableWriteAheadLogging();
             }
             mDatabase.execSQL("CREATE TABLE t1 (i int, j int);");
             mDatabase.execSQL("CREATE TABLE t2 (i int, j int);");
@@ -345,8 +445,7 @@
 
     @SmallTest
     public void testLruCachingOfSqliteCompiledSqlObjs() {
-        mDatabase.disableWriteAheadLogging();
-        mDatabase.execSQL("CREATE TABLE test (i int, j int);");
+        createTableAndClearCache();
         // set cache size
         int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
         mDatabase.setMaxSqlCacheSize(N);
@@ -393,14 +492,27 @@
          */
     }
 
+    private void createTableAndClearCache() {
+        mDatabase.disableWriteAheadLogging();
+        mDatabase.execSQL("DROP TABLE IF EXISTS " + TEST_TABLE);
+        mDatabase.execSQL("CREATE TABLE " + TEST_TABLE + " (i int, j int);");
+        mDatabase.enableWriteAheadLogging();
+        mDatabase.lock();
+        // flush the above statement from cache and close all the pending statements to be released
+        mDatabase.deallocCachedSqlStatements();
+        mDatabase.closePendingStatements();
+        mDatabase.unlock();
+        assertEquals(0, mDatabase.getQueuedUpStmtList().size());
+    }
+
     /**
      * test to make sure the statement finalizations are not done right away but
      * piggy-backed onto the next sql statement execution on the same database.
      */
     @SmallTest
     public void testStatementClose() {
-        mDatabase.execSQL("CREATE TABLE test (i int, j int);");
-        // fill up statement cache in mDatabase\
+        createTableAndClearCache();
+        // fill up statement cache in mDatabase
         int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
         mDatabase.setMaxSqlCacheSize(N);
         SQLiteStatement stmt;
@@ -429,7 +541,7 @@
         // execute something to see if this statement gets finalized
         mDatabase.execSQL("delete from test where i = 10;");
         statementIds = mDatabase.getQueuedUpStmtList();
-        assertEquals(0, statementIds.size());
+        assertFalse(statementIds.contains(stmt0Id));
     }
 
     /**
@@ -439,16 +551,16 @@
      */
     @LargeTest
     public void testStatementCloseDiffThread() throws InterruptedException {
-        mDatabase.execSQL("CREATE TABLE test (i int, j int);");
+        createTableAndClearCache();
+        final int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
+        mDatabase.setMaxSqlCacheSize(N);
         // fill up statement cache in mDatabase in a thread
         Thread t1 = new Thread() {
             @Override public void run() {
-                int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
-                mDatabase.setMaxSqlCacheSize(N);
                 SQLiteStatement stmt;
-                for (int i = 0; i < N; i ++) {
+                for (int i = 0; i < N; i++) {
                     ClassToTestSqlCompilationAndCaching c =
-                        ClassToTestSqlCompilationAndCaching.create(mDatabase,
+                        ClassToTestSqlCompilationAndCaching.create(getDb(),
                                 "insert into test values(" + i + ", ?);");
                     // keep track of 0th entry
                     if (i == 0) {
@@ -461,45 +573,44 @@
         t1.start();
         // wait for the thread to finish
         t1.join();
+        // mDatabase shouldn't have any statements to be released
+        assertEquals(0, mDatabase.getQueuedUpStmtList().size());
 
         // add one more to the cache - and the above 'stmt0Id' should fall out of cache
         // just for the heck of it, do it in a separate thread
         Thread t2 = new Thread() {
             @Override public void run() {
                 ClassToTestSqlCompilationAndCaching stmt1 =
-                        ClassToTestSqlCompilationAndCaching.create(mDatabase,
-                                "insert into test values(100, ?);");
+                    ClassToTestSqlCompilationAndCaching.create(getDb(),
+                            "insert into test values(100, ?);");
+                stmt1.bindLong(1, 1);
                 stmt1.close();
             }
         };
         t2.start();
         t2.join();
 
-        // close() in the above thread should have queuedUp the statement for finalization
-        ArrayList<Integer> statementIds = mDatabase.getQueuedUpStmtList();
-        assertTrue(getStmt0Id() > 0);
-        assertTrue(statementIds.contains(stmt0Id));
+        // close() in the above thread should have queuedUp the stmt0Id for finalization
+        ArrayList<Integer> statementIds = getDb().getQueuedUpStmtList();
+        assertTrue(statementIds.contains(getStmt0Id()));
         assertEquals(1, statementIds.size());
 
         // execute something to see if this statement gets finalized
         // again do it in a separate thread
         Thread t3 = new Thread() {
             @Override public void run() {
-                mDatabase.execSQL("delete from test where i = 10;");
+                getDb().execSQL("delete from test where i = 10;");
             }
         };
         t3.start();
         t3.join();
 
         // is the statement finalized?
-        statementIds = mDatabase.getQueuedUpStmtList();
-        assertEquals(0, statementIds.size());
+        statementIds = getDb().getQueuedUpStmtList();
+        assertFalse(statementIds.contains(getStmt0Id()));
     }
 
     private volatile int stmt0Id = 0;
-    private synchronized void setStmt0Id(int stmt0Id) {
-        this.stmt0Id = stmt0Id;
-    }
     private synchronized int getStmt0Id() {
         return this.stmt0Id;
     }
@@ -510,16 +621,16 @@
      */
     @LargeTest
     public void testStatementCloseByDbClose() throws InterruptedException {
-        mDatabase.execSQL("CREATE TABLE test (i int, j int);");
+        createTableAndClearCache();
         // fill up statement cache in mDatabase in a thread
         Thread t1 = new Thread() {
             @Override public void run() {
                 int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
-                mDatabase.setMaxSqlCacheSize(N);
+                getDb().setMaxSqlCacheSize(N);
                 SQLiteStatement stmt;
                 for (int i = 0; i < N; i ++) {
                     ClassToTestSqlCompilationAndCaching c =
-                            ClassToTestSqlCompilationAndCaching.create(mDatabase,
+                            ClassToTestSqlCompilationAndCaching.create(getDb(),
                                     "insert into test values(" + i + ", ?);");
                     // keep track of 0th entry
                     if (i == 0) {
@@ -538,7 +649,7 @@
         Thread t2 = new Thread() {
             @Override public void run() {
                 ClassToTestSqlCompilationAndCaching stmt1 =
-                        ClassToTestSqlCompilationAndCaching.create(mDatabase,
+                        ClassToTestSqlCompilationAndCaching.create(getDb(),
                                 "insert into test values(100, ?);");
                 stmt1.bindLong(1, 1);
                 stmt1.close();
@@ -548,7 +659,7 @@
         t2.join();
 
         // close() in the above thread should have queuedUp the statement for finalization
-        ArrayList<Integer> statementIds = mDatabase.getQueuedUpStmtList();
+        ArrayList<Integer> statementIds = getDb().getQueuedUpStmtList();
         assertTrue(getStmt0Id() > 0);
         assertTrue(statementIds.contains(stmt0Id));
         assertEquals(1, statementIds.size());
@@ -558,14 +669,244 @@
         // again do it in a separate thread
         Thread t3 = new Thread() {
             @Override public void run() {
-                mDatabase.close();
+                getDb().close();
             }
         };
         t3.start();
         t3.join();
 
         // check mClosedStatementIds in mDatabase. it should be empty
-        statementIds = mDatabase.getQueuedUpStmtList();
+        statementIds = getDb().getQueuedUpStmtList();
         assertEquals(0, statementIds.size());
     }
+
+    /**
+     * This test tests usage execSQL() to begin transaction works in the following way
+     *   Thread #1 does
+     *       execSQL("begin transaction");
+     *       insert()
+     *   Thread # 2
+     *       query()
+     *   Thread#1 ("end transaction")
+     * Thread # 2 query will execute - because java layer will not have locked the SQLiteDatabase
+     * object and sqlite will consider this query to be part of the transaction.
+     *
+     * but if thread # 1 uses beginTransaction() instead of execSQL() to start transaction,
+     * then Thread # 2's query will have been blocked by java layer
+     * until Thread#1 ends transaction.
+     *
+     * @throws InterruptedException
+     */
+    @SmallTest
+    public void testExecSqlToStartAndEndTransaction() throws InterruptedException {
+        runExecSqlToStartAndEndTransaction("END");
+        // same as above, instead now do "COMMIT" or "ROLLBACK" instead of "END" transaction
+        runExecSqlToStartAndEndTransaction("COMMIT");
+        runExecSqlToStartAndEndTransaction("ROLLBACK");
+    }
+    private void runExecSqlToStartAndEndTransaction(String str) throws InterruptedException {
+        createTableAndClearCache();
+        // disable WAL just so queries and updates use the same database connection
+        mDatabase.disableWriteAheadLogging();
+        mDatabase.execSQL("BEGIN transaction");
+        // even though mDatabase.beginTransaction() is not called to start transaction,
+        // mDatabase connection should now be in transaction as a result of
+        // mDatabase.execSQL("BEGIN transaction")
+        // but mDatabase.mLock should not be held by any thread
+        assertTrue(mDatabase.inTransaction());
+        assertFalse(mDatabase.isDbLockedByCurrentThread());
+        assertFalse(mDatabase.isDbLockedByOtherThreads());
+        assertTrue(mDatabase.amIInTransaction());
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        assertTrue(mDatabase.inTransaction());
+        assertFalse(mDatabase.isDbLockedByCurrentThread());
+        assertFalse(mDatabase.isDbLockedByOtherThreads());
+        assertTrue(mDatabase.amIInTransaction());
+        Thread t = new Thread() {
+            @Override public void run() {
+                assertTrue(mDatabase.amIInTransaction());
+                assertEquals(999, DatabaseUtils.longForQuery(getDb(),
+                        "select j from " + TEST_TABLE + " WHERE i = 10", null));
+                assertTrue(getDb().inTransaction());
+                assertFalse(getDb().isDbLockedByCurrentThread());
+                assertFalse(getDb().isDbLockedByOtherThreads());
+                assertTrue(mDatabase.amIInTransaction());
+            }
+        };
+        t.start();
+        t.join();
+        assertTrue(mDatabase.amIInTransaction());
+        assertTrue(mDatabase.inTransaction());
+        assertFalse(mDatabase.isDbLockedByCurrentThread());
+        assertFalse(mDatabase.isDbLockedByOtherThreads());
+        mDatabase.execSQL(str);
+        assertFalse(mDatabase.amIInTransaction());
+        assertFalse(mDatabase.inTransaction());
+        assertFalse(mDatabase.isDbLockedByCurrentThread());
+        assertFalse(mDatabase.isDbLockedByOtherThreads());
+    }
+
+    /**
+     * test the following
+     * http://b/issue?id=2871037
+     *          Cursor cursor = db.query(...);
+     *          // with WAL enabled, the above uses a pooled database connection
+     *          db.beginTransaction()
+     *          try {
+     *            db.insert(......);
+     *            cursor.requery();
+     *            // since the cursor uses pooled database connection, the above requery
+     *            // will not return the results that were inserted above since the insert is
+     *            // done using main database connection AND the transaction is not committed yet.
+     *            // fix is to make the above cursor use the main database connection - and NOT
+     *            // the pooled database connection
+     *            db.setTransactionSuccessful()
+     *          } finally {
+     *            db.endTransaction()
+     *          }
+     *
+     * @throws InterruptedException
+     */
+    @SmallTest
+    public void testTransactionAndWalInterplay1() throws InterruptedException {
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        String sql = "select * from " + TEST_TABLE;
+        Cursor c = mDatabase.rawQuery(sql, null);
+        // should have 1 row in the table
+        assertEquals(1, c.getCount());
+        mDatabase.beginTransactionNonExclusive();
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // requery on the previously opened cursor
+            // cursor should now use the main database connection and see 2 rows
+            c.requery();
+            assertEquals(2, c.getCount());
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
+        }
+        c.close();
+
+        // do the same test but now do the requery in a separate thread.
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        final Cursor c1 = mDatabase.rawQuery("select count(*) from " + TEST_TABLE, null);
+        // should have 1 row in the table
+        assertEquals(1, c1.getCount());
+        mDatabase.beginTransactionNonExclusive();
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // query in a different thread. that causes the cursor to use a pooled connection
+            // and since this thread hasn't committed its changes, the cursor should still see only
+            // 1 row
+            Thread t = new Thread() {
+                @Override public void run() {
+                    c1.requery();
+                    assertEquals(1, c1.getCount());
+                }
+            };
+            t.start();
+            t.join();
+            // should be 2 rows now - including the the row inserted above
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
+        }
+        c1.close();
+    }
+
+    /**
+     * This test is same as {@link #testTransactionAndWalInterplay1()} except the following:
+     * instead of mDatabase.beginTransactionNonExclusive(), use execSQL("BEGIN transaction")
+     * and instead of mDatabase.endTransaction(), use execSQL("END");
+     */
+    @SmallTest
+    public void testTransactionAndWalInterplay2() throws InterruptedException {
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        String sql = "select * from " + TEST_TABLE;
+        Cursor c = mDatabase.rawQuery(sql, null);
+        // should have 1 row in the table
+        assertEquals(1, c.getCount());
+        mDatabase.execSQL("BEGIN transaction");
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // requery on the previously opened cursor
+            // cursor should now use the main database connection and see 2 rows
+            c.requery();
+            assertEquals(2, c.getCount());
+        } finally {
+            mDatabase.execSQL("commit;");
+        }
+        c.close();
+
+        // do the same test but now do the requery in a separate thread.
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        final Cursor c1 = mDatabase.rawQuery("select count(*) from " + TEST_TABLE, null);
+        // should have 1 row in the table
+        assertEquals(1, c1.getCount());
+        mDatabase.execSQL("BEGIN transaction");
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // query in a different thread. but since the transaction is started using
+            // execSQ() instead of beginTransaction(), cursor's query is considered part of
+            // the same ransaction - and hence it should see the above inserted row
+            Thread t = new Thread() {
+                @Override public void run() {
+                    c1.requery();
+                    assertEquals(1, c1.getCount());
+                }
+            };
+            t.start();
+            t.join();
+            // should be 2 rows now - including the the row inserted above
+        } finally {
+            mDatabase.execSQL("commit");
+        }
+        c1.close();
+    }
+
+    /**
+     * This test is same as {@link #testTransactionAndWalInterplay2()} except the following:
+     * instead of commiting the data, do rollback and make sure the data seen by the query
+     * within the transaction is now gone.
+     */
+    @SmallTest
+    public void testTransactionAndWalInterplay3() throws InterruptedException {
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        String sql = "select * from " + TEST_TABLE;
+        Cursor c = mDatabase.rawQuery(sql, null);
+        // should have 1 row in the table
+        assertEquals(1, c.getCount());
+        mDatabase.execSQL("BEGIN transaction");
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // requery on the previously opened cursor
+            // cursor should now use the main database connection and see 2 rows
+            c.requery();
+            assertEquals(2, c.getCount());
+        } finally {
+            // rollback the change
+            mDatabase.execSQL("rollback;");
+        }
+        // since the change is rolled back, do the same query again and should now find only 1 row
+        c.requery();
+        assertEquals(1, c.getCount());
+        assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "select count(*) from " + TEST_TABLE, null));
+        c.close();
+    }
 }
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 854afdd..23d8f56 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -32,6 +32,7 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include "OMX_Video.h"
+#include <limits.h>
 
 namespace android {
 
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 6eaf0cc..f1c6532 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -125,8 +125,6 @@
             metrics.setToDefaults();
             PackageParser.PackageLite pkg = packageParser.parsePackageLite(
                     archiveFilePath, 0);
-            ret.packageName = pkg.packageName;
-            ret.installLocation = pkg.installLocation;
             // Nuke the parser reference right away and force a gc
             packageParser = null;
             Runtime.getRuntime().gc();
@@ -136,6 +134,7 @@
                 return ret;
             }
             ret.packageName = pkg.packageName;
+            ret.installLocation = pkg.installLocation;
             ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath, flags);
             return ret;
         }
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 4c83768..910e84e 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -10,4 +10,6 @@
 LOCAL_PACKAGE_NAME := SystemUI
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+
 include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/res/drawable/dots_empty.png b/packages/SystemUI/res/drawable-mdpi/dots_empty.png
similarity index 100%
rename from packages/SystemUI/res/drawable/dots_empty.png
rename to packages/SystemUI/res/drawable-mdpi/dots_empty.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/dots_full.png b/packages/SystemUI/res/drawable-mdpi/dots_full.png
similarity index 100%
rename from packages/SystemUI/res/drawable/dots_full.png
rename to packages/SystemUI/res/drawable-mdpi/dots_full.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/notification_dragger.png b/packages/SystemUI/res/drawable-mdpi/notification_dragger.png
similarity index 100%
rename from packages/SystemUI/res/drawable/notification_dragger.png
rename to packages/SystemUI/res/drawable-mdpi/notification_dragger.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_back_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
similarity index 100%
rename from packages/SystemUI/res/drawable/status_bar_back_default.png
rename to packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
similarity index 100%
rename from packages/SystemUI/res/drawable/status_bar_back_pressed.png
rename to packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_home_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
similarity index 100%
rename from packages/SystemUI/res/drawable/status_bar_home_default.png
rename to packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
similarity index 100%
rename from packages/SystemUI/res/drawable/status_bar_home_pressed.png
rename to packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_icon_tray.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable/status_bar_icon_tray.9.png
rename to packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_menu_default.png
similarity index 100%
rename from packages/SystemUI/res/drawable/status_bar_menu_default.png
rename to packages/SystemUI/res/drawable-mdpi/status_bar_menu_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_menu_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_menu_pressed.png
similarity index 100%
rename from packages/SystemUI/res/drawable/status_bar_menu_pressed.png
rename to packages/SystemUI/res/drawable-mdpi/status_bar_menu_pressed.png
Binary files differ
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 311efc8..a204b36 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -605,7 +605,7 @@
                         !network.isTeardownRequested()) {
                     if (ni.isConnected() == true) {
                         // add the pid-specific dns
-                        handleDnsConfigurationChange();
+                        handleDnsConfigurationChange(networkType);
                         if (DBG) Slog.d(TAG, "special network already active");
                         return Phone.APN_ALREADY_ACTIVE;
                     }
@@ -967,7 +967,7 @@
             }
         }
         // do this before we broadcast the change
-        handleConnectivityChange();
+        handleConnectivityChange(prevNetType);
 
         sendStickyBroadcast(intent);
         /*
@@ -1123,9 +1123,6 @@
             }
         }
 
-        // do this before we broadcast the change
-        handleConnectivityChange();
-
         sendStickyBroadcast(intent);
         /*
          * If the failover network is already connected, then immediately send
@@ -1204,7 +1201,7 @@
         }
         thisNet.setTeardownRequested(false);
         updateNetworkSettings(thisNet);
-        handleConnectivityChange();
+        handleConnectivityChange(type);
         sendConnectedBroadcast(info);
     }
 
@@ -1231,38 +1228,29 @@
     }
 
     /**
-     * After any kind of change in the connectivity state of any network,
-     * make sure that anything that depends on the connectivity state of
-     * more than one network is set up correctly. We're mainly concerned
-     * with making sure that the list of DNS servers is set up  according
-     * to which networks are connected, and ensuring that the right routing
-     * table entries exist.
+     * After a change in the connectivity state of a network. We're mainly
+     * concerned with making sure that the list of DNS servers is set up
+     * according to which networks are connected, and ensuring that the
+     * right routing table entries exist.
      */
-    private void handleConnectivityChange() {
+    private void handleConnectivityChange(int netType) {
         /*
          * If a non-default network is enabled, add the host routes that
-         * will allow it's DNS servers to be accessed.  Only
-         * If both mobile and wifi are enabled, add the host routes that
-         * will allow MMS traffic to pass on the mobile network. But
-         * remove the default route for the mobile network, so that there
-         * will be only one default route, to ensure that all traffic
-         * except MMS will travel via Wi-Fi.
+         * will allow it's DNS servers to be accessed.
          */
-        handleDnsConfigurationChange();
+        handleDnsConfigurationChange(netType);
 
-        for (int netType : mPriorityList) {
-            if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
-                if (mNetAttributes[netType].isDefault()) {
-                    addDefaultRoute(mNetTrackers[netType]);
-                } else {
-                    addPrivateDnsRoutes(mNetTrackers[netType]);
-                }
+        if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
+            if (mNetAttributes[netType].isDefault()) {
+                addDefaultRoute(mNetTrackers[netType]);
             } else {
-                if (mNetAttributes[netType].isDefault()) {
-                    removeDefaultRoute(mNetTrackers[netType]);
-                } else {
-                    removePrivateDnsRoutes(mNetTrackers[netType]);
-                }
+                addPrivateDnsRoutes(mNetTrackers[netType]);
+            }
+        } else {
+            if (mNetAttributes[netType].isDefault()) {
+                removeDefaultRoute(mNetTrackers[netType]);
+            } else {
+                removePrivateDnsRoutes(mNetTrackers[netType]);
             }
         }
     }
@@ -1310,18 +1298,13 @@
         if (p == null) return;
         String interfaceName = p.getInterfaceName();
         InetAddress defaultGatewayAddr = p.getGateway();
-        boolean defaultRouteSet = nt.isDefaultRouteSet();
 
-        if ((interfaceName != null) && (defaultGatewayAddr != null ) &&
-                (defaultRouteSet == false)) {
-            boolean error = (NetworkUtils.setDefaultRoute(interfaceName, defaultGatewayAddr) < 0);
-
-            if (DBG && !error) {
+        if ((interfaceName != null) && (defaultGatewayAddr != null )) {
+            if ((NetworkUtils.setDefaultRoute(interfaceName, defaultGatewayAddr) >= 0) && DBG) {
                 NetworkInfo networkInfo = nt.getNetworkInfo();
                 Slog.d(TAG, "addDefaultRoute for " + networkInfo.getTypeName() +
                         " (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr);
             }
-            nt.defaultRouteSet(!error);
         }
     }
 
@@ -1330,16 +1313,13 @@
         NetworkProperties p = nt.getNetworkProperties();
         if (p == null) return;
         String interfaceName = p.getInterfaceName();
-        boolean defaultRouteSet = nt.isDefaultRouteSet();
 
-        if (interfaceName != null && defaultRouteSet == true) {
-            boolean error = (NetworkUtils.removeDefaultRoute(interfaceName) < 0);
-            if (DBG && !error) {
+        if (interfaceName != null) {
+            if ((NetworkUtils.removeDefaultRoute(interfaceName) >= 0) && DBG) {
                 NetworkInfo networkInfo = nt.getNetworkInfo();
                 Slog.d(TAG, "removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
                         interfaceName + ")");
             }
-            nt.defaultRouteSet(error);
         }
     }
 
@@ -1480,42 +1460,37 @@
         SystemProperties.set("net.dnschange", "" + (n+1));
     }
 
-    private void handleDnsConfigurationChange() {
+    private void handleDnsConfigurationChange(int netType) {
         // add default net's dns entries
-        for (int x = mPriorityList.length-1; x>= 0; x--) {
-            int netType = mPriorityList[x];
-            NetworkStateTracker nt = mNetTrackers[netType];
-            if (nt != null && nt.getNetworkInfo().isConnected() &&
-                    !nt.isTeardownRequested()) {
-                NetworkProperties p = nt.getNetworkProperties();
-                if (p == null) continue;
-                Collection<InetAddress> dnses = p.getDnses();
-                if (mNetAttributes[netType].isDefault()) {
-                    int j = 1;
-                    for (InetAddress dns : dnses) {
-                        if (DBG) {
-                            Slog.d(TAG, "adding dns " + dns + " for " +
-                                    nt.getNetworkInfo().getTypeName());
-                        }
-                        SystemProperties.set("net.dns" + j++, dns.getHostAddress());
+        NetworkStateTracker nt = mNetTrackers[netType];
+        if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
+            NetworkProperties p = nt.getNetworkProperties();
+            if (p == null) return;
+            Collection<InetAddress> dnses = p.getDnses();
+            if (mNetAttributes[netType].isDefault()) {
+                int j = 1;
+                for (InetAddress dns : dnses) {
+                    if (DBG) {
+                        Slog.d(TAG, "adding dns " + dns + " for " +
+                                nt.getNetworkInfo().getTypeName());
                     }
-                    for (int k=j ; k<mNumDnsEntries; k++) {
-                        if (DBG) Slog.d(TAG, "erasing net.dns" + k);
-                        SystemProperties.set("net.dns" + k, "");
-                    }
-                    mNumDnsEntries = j;
-                } else {
-                    // set per-pid dns for attached secondary nets
-                    List pids = mNetRequestersPids[netType];
-                    for (int y=0; y< pids.size(); y++) {
-                        Integer pid = (Integer)pids.get(y);
-                        writePidDns(dnses, pid.intValue());
-                    }
+                    SystemProperties.set("net.dns" + j++, dns.getHostAddress());
+                }
+                for (int k=j ; k<mNumDnsEntries; k++) {
+                    if (DBG) Slog.d(TAG, "erasing net.dns" + k);
+                    SystemProperties.set("net.dns" + k, "");
+                }
+                mNumDnsEntries = j;
+            } else {
+                // set per-pid dns for attached secondary nets
+                List pids = mNetRequestersPids[netType];
+                for (int y=0; y< pids.size(); y++) {
+                    Integer pid = (Integer)pids.get(y);
+                    writePidDns(dnses, pid.intValue());
                 }
             }
+            bumpDns();
         }
-
-        bumpDns();
     }
 
     private int getRestoreDefaultNetworkDelay() {
@@ -1654,9 +1629,11 @@
                             (Notification) msg.obj);
 
                 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
-                    handleDnsConfigurationChange();
+                    // TODO - make this handle ip/proxy/gateway/dns changes
+                    info = (NetworkInfo) msg.obj;
+                    type = info.getType();
+                    handleDnsConfigurationChange(type);
                     break;
-
                 case NetworkStateTracker.EVENT_ROAMING_CHANGED:
                     // fill me in
                     break;
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 7398f69..353dfcb 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5501,7 +5501,7 @@
                 throw new SecurityException(
                         "Injecting to another application requires INJECT_EVENTS permission");
             case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
-                Slog.v(TAG, "Input event injection succeeded.");
+                //Slog.v(TAG, "Input event injection succeeded.");
                 return true;
             case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
                 Slog.w(TAG, "Input event injection timed out.");
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 8f46df7..11315ce 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -146,9 +146,9 @@
         }
 
         // TODO: We don't check for SecurityException here (requires
-        // READ_PHONE_STATE permission).
+        // CALL_PRIVILEGED permission).
         if (scheme.equals("voicemail")) {
-            return TelephonyManager.getDefault().getVoiceMailNumber();
+            return TelephonyManager.getDefault().getCompleteVoiceMailNumber();
         }
 
         if (context == null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ab63017..aa916e0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -665,6 +665,25 @@
     }
 
     /**
+     * Returns the complete voice mail number. Return null if it is unavailable.
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#CALL_PRIVILEGED CALL_PRIVILEGED}
+     *
+     * @hide
+     */
+    public String getCompleteVoiceMailNumber() {
+        try {
+            return getSubscriberInfo().getCompleteVoiceMailNumber();
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
      * Returns the voice mail count. Return 0 if unavailable.
      * <p>
      * Requires Permission:
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index e74b9e4..5cba2e1 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -59,6 +59,11 @@
     String getVoiceMailNumber();
 
     /**
+     * Retrieves the complete voice mail number.
+     */
+    String getCompleteVoiceMailNumber();
+
+    /**
      * Retrieves the alpha identifier associated with the voice mail number.
      */
     String getVoiceMailAlphaTag();
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
index 4f71bb1..86c86bb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.telephony.PhoneNumberUtils;
 import android.util.Log;
 
 public class PhoneSubInfo extends IPhoneSubInfo.Stub {
@@ -29,6 +30,9 @@
     private Context mContext;
     private static final String READ_PHONE_STATE =
         android.Manifest.permission.READ_PHONE_STATE;
+    private static final String CALL_PRIVILEGED =
+        // TODO Add core/res/AndriodManifest.xml#READ_PRIVILEGED_PHONE_STATE
+        android.Manifest.permission.CALL_PRIVILEGED;
 
     public PhoneSubInfo(Phone phone) {
         mPhone = phone;
@@ -101,7 +105,22 @@
      */
     public String getVoiceMailNumber() {
         mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
-        return (String) mPhone.getVoiceMailNumber();
+        String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
+        Log.d(LOG_TAG, "VM: PhoneSubInfo.getVoiceMailNUmber: "); // + number);
+        return number;
+    }
+
+    /**
+     * Retrieves the compelete voice mail number.
+     *
+     * @hide
+     */
+    public String getCompleteVoiceMailNumber() {
+        mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
+                "Requires CALL_PRIVILEGED");
+        String number = mPhone.getVoiceMailNumber();
+        Log.d(LOG_TAG, "VM: PhoneSubInfo.getCompleteVoiceMailNUmber: "); // + number);
+        return number;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
index 202ded2..7009893 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
@@ -82,6 +82,13 @@
     }
 
     /**
+     * Retrieves the complete voice mail number.
+     */
+    public String getCompleteVoiceMailNumber() {
+        return mPhoneSubInfo.getCompleteVoiceMailNumber();
+    }
+
+    /**
      * Retrieves the alpha identifier associated with the voice mail number.
      */
     public String getVoiceMailAlphaTag() {
diff --git a/telephony/mockril/Android.mk b/telephony/mockril/Android.mk
new file mode 100644
index 0000000..95ae84c
--- /dev/null
+++ b/telephony/mockril/Android.mk
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+#
+
+LOCAL_PATH:=$(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := core framework
+
+LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
+
+LOCAL_MODULE := mockrilcontroller
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
new file mode 100644
index 0000000..39ad07d
--- /dev/null
+++ b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
@@ -0,0 +1,107 @@
+/*
+ * 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.internal.telephony.mockril;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.communication.MsgHeader;
+import com.android.internal.communication.Msg;
+import com.android.internal.telephony.RilChannel;
+import com.android.internal.telephony.ril_proto.RilCtrlCmds;
+import com.android.internal.telephony.ril_proto.RilCmds;
+import com.google.protobuf.micro.MessageMicro;
+
+import java.io.IOException;
+
+/**
+ * Contain a list of commands to control Mock RIL. Before using these commands the devices
+ * needs to be set with Mock RIL. Refer to hardware/ril/mockril/README.txt for details.
+ *
+ */
+public class MockRilController {
+    private static final String TAG = "MockRILController";
+    private RilChannel mRilChannel = null;
+    private Msg mMessage = null;
+
+    public MockRilController() throws IOException {
+        mRilChannel = RilChannel.makeRilChannel();
+    }
+
+    /**
+     * Close the channel after the communication is done.
+     * This method has to be called after the test is finished.
+     */
+    public void closeChannel() {
+        mRilChannel.close();
+    }
+
+    /**
+     * Send commands and return true on success
+     * @param cmd for MsgHeader
+     * @param token for MsgHeader
+     * @param status for MsgHeader
+     * @param pbData for Msg data
+     * @return true if command is sent successfully, false if it fails
+     */
+    private boolean sendCtrlCommand(int cmd, long token, int status, MessageMicro pbData) {
+        try {
+            Msg.send(mRilChannel, cmd, token, status, pbData);
+        } catch (IOException e) {
+            Log.v(TAG, "send command : %d failed: " + e.getStackTrace());
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get control response
+     * @return Msg if response is received, else return null.
+     */
+    private Msg getCtrlResponse() {
+        Msg response = null;
+        try {
+            response = Msg.recv(mRilChannel);
+        } catch (IOException e) {
+            Log.v(TAG, "receive response for getRadioState() error: " + e.getStackTrace());
+            return null;
+        }
+        return response;
+    }
+
+    /**
+     * @return the radio state if it is valid, otherwise return -1
+     */
+    public int getRadioState() {
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_GET_RADIO_STATE, 0, 0, null)) {
+            return -1;
+        }
+        Msg response = getCtrlResponse();
+        if (response == null) {
+            Log.v(TAG, "failed to get response");
+            return -1;
+        }
+        response.printHeader(TAG);
+        RilCtrlCmds.CtrlRspRadioState resp =
+            response.getDataAs(RilCtrlCmds.CtrlRspRadioState.class);
+        int state = resp.getState();
+        if ((state >= RilCmds.RADIO_STATE_OFF) && (state <= RilCmds.RADIO_STATE_NV_READY))
+            return state;
+        else
+            return -1;
+    }
+}
diff --git a/telephony/tests/telephonymockriltests/Android.mk b/telephony/tests/telephonymockriltests/Android.mk
new file mode 100644
index 0000000..9731d0d
--- /dev/null
+++ b/telephony/tests/telephonymockriltests/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_STATIC_JAVA_LIBRARIES := mockrilcontroller
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := TelephonyMockRilTests
+
+include $(BUILD_PACKAGE)
diff --git a/telephony/tests/telephonymockriltests/AndroidManifest.xml b/telephony/tests/telephonymockriltests/AndroidManifest.xml
new file mode 100644
index 0000000..63f44a2
--- /dev/null
+++ b/telephony/tests/telephonymockriltests/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2009 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.telephonymockriltests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:label="TelephonyMockRilTest"
+                android:name="TelephonyMockRilTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name=".TelephonyMockTestRunner"
+        android:targetPackage="com.android.telephonymockriltests"
+        android:label="Test runner for Telephony Tests Using Mock RIL"
+    />
+
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+
+</manifest>
diff --git a/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/TelephonyMockTestRunner.java b/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/TelephonyMockTestRunner.java
new file mode 100644
index 0000000..78ee738
--- /dev/null
+++ b/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/TelephonyMockTestRunner.java
@@ -0,0 +1,64 @@
+/*
+ * 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.telephonymockriltests;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import com.android.internal.telephony.mockril.MockRilController;
+import android.util.Log;
+
+import com.android.telephonymockriltests.functional.SimpleTestUsingMockRil;
+
+import java.io.IOException;
+import junit.framework.TestSuite;
+import junit.framework.TestCase;
+
+/**
+ * Test runner for telephony tests that using Mock RIL
+ *
+ */
+public class TelephonyMockTestRunner extends InstrumentationTestRunner {
+    private static final String TAG="TelephonyMockTestRunner";
+    public MockRilController mController;
+
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(SimpleTestUsingMockRil.class);
+        return suite;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        try {
+            mController = new MockRilController();
+        } catch (IOException e) {
+            e.printStackTrace();
+            TestCase.assertTrue("Create Mock RIl Controller failed", false);
+        }
+        TestCase.assertNotNull(mController);
+        super.onCreate(icicle);
+    }
+
+    @Override
+    public void finish(int resultCode, Bundle results) {
+        if (mController != null)
+            mController.closeChannel();
+        super.finish(resultCode, results);
+    }
+}
diff --git a/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/functional/SimpleTestUsingMockRil.java b/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/functional/SimpleTestUsingMockRil.java
new file mode 100644
index 0000000..87001c8
--- /dev/null
+++ b/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/functional/SimpleTestUsingMockRil.java
@@ -0,0 +1,49 @@
+/*
+ * 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.telephonymockriltests.functional;
+
+import com.android.internal.telephony.mockril.MockRilController;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import com.android.telephonymockriltests.TelephonyMockTestRunner;
+
+/**
+ * A simple test that using Mock RIL Controller
+ */
+public class SimpleTestUsingMockRil extends InstrumentationTestCase {
+    private static final String TAG = "SimpleTestUsingMockRil";
+    private MockRilController mMockRilCtrl = null;
+    private TelephonyMockTestRunner mRunner;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mRunner = (TelephonyMockTestRunner)getInstrumentation();
+        mMockRilCtrl = mRunner.mController;
+        assertNotNull(mMockRilCtrl);
+    }
+
+    /**
+     * Get the current radio state of RIL
+     */
+    public void testGetRadioState() {
+        int state = mMockRilCtrl.getRadioState();
+        Log.v(TAG, "testGetRadioState: " + state);
+        assertTrue(state >= 0 && state <= 9);
+    }
+}
diff --git a/tests/StatusBar/res/drawable/app_gmail.png b/tests/StatusBar/res/drawable-mdpi/app_gmail.png
similarity index 100%
rename from tests/StatusBar/res/drawable/app_gmail.png
rename to tests/StatusBar/res/drawable-mdpi/app_gmail.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/app_phone.png b/tests/StatusBar/res/drawable-mdpi/app_phone.png
similarity index 100%
rename from tests/StatusBar/res/drawable/app_phone.png
rename to tests/StatusBar/res/drawable-mdpi/app_phone.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_chat.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png
similarity index 100%
rename from tests/StatusBar/res/drawable/ic_statusbar_chat.png
rename to tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_email.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png
similarity index 100%
rename from tests/StatusBar/res/drawable/ic_statusbar_email.png
rename to tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_missedcall.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png
similarity index 100%
rename from tests/StatusBar/res/drawable/ic_statusbar_missedcall.png
rename to tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon1.png b/tests/StatusBar/res/drawable-mdpi/icon1.png
similarity index 100%
rename from tests/StatusBar/res/drawable/icon1.png
rename to tests/StatusBar/res/drawable-mdpi/icon1.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon2.png b/tests/StatusBar/res/drawable-mdpi/icon2.png
similarity index 100%
rename from tests/StatusBar/res/drawable/icon2.png
rename to tests/StatusBar/res/drawable-mdpi/icon2.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon3.png b/tests/StatusBar/res/drawable-mdpi/icon3.png
similarity index 100%
rename from tests/StatusBar/res/drawable/icon3.png
rename to tests/StatusBar/res/drawable-mdpi/icon3.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon4.png b/tests/StatusBar/res/drawable-mdpi/icon4.png
similarity index 100%
rename from tests/StatusBar/res/drawable/icon4.png
rename to tests/StatusBar/res/drawable-mdpi/icon4.png
Binary files differ