Merge "Implement the GL11ExtensionPack APIs."
diff --git a/Android.mk b/Android.mk
index 7520afe..f225ecd8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -112,6 +112,7 @@
 	core/java/android/os/ICheckinService.aidl \
 	core/java/android/os/IMessenger.aidl \
 	core/java/android/os/IMountService.aidl \
+	core/java/android/os/IMountServiceObserver.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/INetStatService.aidl \
 	core/java/android/os/IParentalControlCallback.aidl \
@@ -119,6 +120,8 @@
 	core/java/android/os/IPowerManager.aidl \
     core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/IVibratorService.aidl \
+	core/java/android/service/urlrenderer/IUrlRendererService.aidl \
+	core/java/android/service/urlrenderer/IUrlRendererCallback.aidl \
     core/java/android/service/wallpaper/IWallpaperConnection.aidl \
     core/java/android/service/wallpaper/IWallpaperEngine.aidl \
     core/java/android/service/wallpaper/IWallpaperService.aidl \
@@ -396,7 +399,7 @@
 
 ## SDK version identifiers used in the published docs
   # major[.minor] version for current SDK. (full releases only)
-framework_docs_SDK_VERSION:=2.0.1
+framework_docs_SDK_VERSION:=2.1
   # release version (ie "Release x")  (full releases only)
 framework_docs_SDK_REL_ID:=1
   # name of current SDK directory (full releases only)
diff --git a/api/current.xml b/api/current.xml
index 814c8b1..d6c305e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -20421,6 +20421,19 @@
  visibility="public"
 >
 </method>
+<method name="getMaximumFailedPasswordsForWipe"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
 <method name="getMaximumTimeToLock"
  return="long"
  abstract="false"
@@ -20431,8 +20444,10 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
 </method>
-<method name="getMinimumPasswordLength"
+<method name="getPasswordMaximumLength"
  return="int"
  abstract="false"
  native="false"
@@ -20442,6 +20457,21 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<method name="getPasswordMinimumLength"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
 </method>
 <method name="getPasswordMode"
  return="int"
@@ -20453,6 +20483,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
 </method>
 <method name="isActivePasswordSufficient"
  return="boolean"
@@ -20545,7 +20577,7 @@
 <parameter name="timeMs" type="long">
 </parameter>
 </method>
-<method name="setMinimumPasswordLength"
+<method name="setPasswordMinimumLength"
  return="void"
  abstract="false"
  native="false"
@@ -52339,6 +52371,25 @@
 <exception name="SQLException" type="android.database.SQLException">
 </exception>
 </method>
+<method name="insertWithOnConflict"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="table" type="java.lang.String">
+</parameter>
+<parameter name="nullColumnHack" type="java.lang.String">
+</parameter>
+<parameter name="initialValues" type="android.content.ContentValues">
+</parameter>
+<parameter name="conflictAlgorithm" type="int">
+</parameter>
+</method>
 <method name="isDbLockedByCurrentThread"
  return="boolean"
  abstract="false"
@@ -52774,6 +52825,27 @@
 <parameter name="whereArgs" type="java.lang.String[]">
 </parameter>
 </method>
+<method name="updateWithOnConflict"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="table" type="java.lang.String">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+<parameter name="whereClause" type="java.lang.String">
+</parameter>
+<parameter name="whereArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="conflictAlgorithm" type="int">
+</parameter>
+</method>
 <method name="yieldIfContended"
  return="boolean"
  abstract="false"
@@ -52865,6 +52937,81 @@
 >
 </field>
 </class>
+<class name="SQLiteDatabase.ConflictAlgorithm"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="ABORT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FAIL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IGNORE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="REPLACE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ROLLBACK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <interface name="SQLiteDatabase.CursorFactory"
  abstract="true"
  static="true"
@@ -65845,6 +65992,102 @@
 >
 </constructor>
 </class>
+<class name="YuvImage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="YuvImage"
+ type="android.graphics.YuvImage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="yuv" type="byte[]">
+</parameter>
+<parameter name="format" type="int">
+</parameter>
+<parameter name="strides" type="int[]">
+</parameter>
+</constructor>
+<method name="compressToJpeg"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+<parameter name="offsets" type="int[]">
+</parameter>
+<parameter name="quality" type="int">
+</parameter>
+<parameter name="stream" type="java.io.OutputStream">
+</parameter>
+</method>
+<method name="getStrides"
+ return="int[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getYuvData"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getYuvFormat"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="validate"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="format" type="int">
+</parameter>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+<parameter name="offsets" type="int[]">
+</parameter>
+</method>
+</class>
 </package>
 <package name="android.graphics.drawable"
 >
@@ -120094,22 +120337,11 @@
  visibility="public"
 >
 </field>
-<field name="EXTRA_HEADERS_KEY"
+<field name="EXTRA_HEADERS"
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;com.android.browser.headers_key&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_HEADERS_VALUE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;com.android.browser.headers_value&quot;"
+ value="&quot;com.android.browser.headers&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -178320,6 +178552,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_ALLOW_LOCK_WHILE_SCREEN_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_ALT_FOCUSABLE_IM"
  type="int"
  transient="false"
diff --git a/awt/javax/imageio/metadata/IIOMetadataNode.java b/awt/javax/imageio/metadata/IIOMetadataNode.java
index adc6d67..efbaae8 100644
--- a/awt/javax/imageio/metadata/IIOMetadataNode.java
+++ b/awt/javax/imageio/metadata/IIOMetadataNode.java
@@ -27,6 +27,8 @@
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
+import org.w3c.dom.TypeInfo;
+import org.w3c.dom.UserDataHandler;
 
 //???AWT
 //import org.w3c.dom.TypeInfo;
@@ -924,6 +926,14 @@
         return userObject;
     }
 
+    public TypeInfo getSchemaTypeInfo() {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object setUserData(String key, Object data, UserDataHandler handler) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * Sets the user object associated with this node.
      * 
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 81d60dc..405d471 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -197,7 +197,7 @@
 static sp<MediaPlayer> newMediaPlayer(const char *file)
 {
     sp<MediaPlayer> mp = new MediaPlayer();
-    if (mp->setDataSource(file) == NO_ERROR) {
+    if (mp->setDataSource(file, NULL /* headers */) == NO_ERROR) {
         mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
         mp->prepare();
     } else {
diff --git a/common/java/com/android/common/speech/LoggingEvents.java b/common/java/com/android/common/speech/LoggingEvents.java
new file mode 100644
index 0000000..eb9476f
--- /dev/null
+++ b/common/java/com/android/common/speech/LoggingEvents.java
@@ -0,0 +1,133 @@
+/*
+ * 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.common.speech;
+
+import android.content.Intent;
+import android.content.Context;
+
+/**
+ * Logging event constants used for Voice Search and VoiceIME. These are the
+ * keys and values of extras to be specified in logging broadcast intents.
+ * This class is used by clients of the android.speech APIs to log how the
+ * user interacts with the IME settings and speech recognition result.
+ */
+public class LoggingEvents {
+    // The name of the broadcast intent for logging.
+    public static final String ACTION_LOG_EVENT = "com.android.common.speech.LOG_EVENT";
+
+    // The extra key used for the name of the app being logged.
+    public static final String EXTRA_APP_NAME = "app_name";
+
+    // The extra key used for the name of the app issuing the VoiceSearch
+    // or VoiceIME request
+    public static final String EXTRA_CALLING_APP_NAME = "";
+
+    // The extra key used for the event value. The possible event values depend
+    // on the app being logged for, and are defined in the subclasses below.
+    public static final String EXTRA_EVENT = "extra_event";
+
+    // The extra key used to log the time in milliseconds at which the EXTRA_EVENT
+    // occurred in the client.
+    public static final String EXTRA_TIMESTAMP = "timestamp";
+
+    // The extra key used (with a boolean value of 'true') as a way to trigger a
+    // flush of the log events to the server.
+    public static final String EXTRA_FLUSH = "flush";
+
+    /**
+     * Logging event constants for voice search. Below are the extra values for
+     * {@link LoggingEvents#EXTRA_EVENT}, clustered with keys to additional
+     * extras for some events that need to be included as additional fields in
+     * the event. Note that this is not representative of *all* voice search
+     * events - only the ones that need to be reported from outside the voice
+     * search app, such as from Browser.
+     */
+    public class VoiceSearch {
+        // The app name to be used for logging VoiceSearch events.
+        public static final String APP_NAME = "googlemobile";
+
+        public static final int RETRY = 0;
+
+        public static final int N_BEST_REVEAL = 1;
+
+        public static final int N_BEST_CHOOSE = 2;
+        public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index";  // value should be int
+
+        public static final int QUERY_UPDATED = 3;
+        public static final String EXTRA_QUERY_UPDATED_VALUE = "value";  // value should be String
+    }
+
+    /**
+     * Logging event constants for VoiceIME. Below are the extra values for
+     * {@link LoggingEvents#EXTRA_EVENT}, clustered with keys to additional
+     * extras for some events that need to be included as additional fields in
+     * the event.
+     */
+    public class VoiceIme {
+        // The app name to be used for logging VoiceIME events.
+        public static final String APP_NAME = "voiceime";
+
+        public static final int KEYBOARD_WARNING_DIALOG_SHOWN = 0;
+
+        public static final int KEYBOARD_WARNING_DIALOG_DISMISSED = 1;
+
+        public static final int KEYBOARD_WARNING_DIALOG_OK = 2;
+
+        public static final int KEYBOARD_WARNING_DIALOG_CANCEL = 3;
+
+        public static final int SETTINGS_WARNING_DIALOG_SHOWN = 4;
+
+        public static final int SETTINGS_WARNING_DIALOG_DISMISSED = 5;
+
+        public static final int SETTINGS_WARNING_DIALOG_OK = 6;
+
+        public static final int SETTINGS_WARNING_DIALOG_CANCEL = 7;
+
+        public static final int SWIPE_HINT_DISPLAYED = 8;
+
+        public static final int PUNCTUATION_HINT_DISPLAYED = 9;
+
+        public static final int CANCEL_DURING_LISTENING = 10;
+
+        public static final int CANCEL_DURING_WORKING = 11;
+
+        public static final int CANCEL_DURING_ERROR = 12;
+
+        public static final int ERROR = 13;
+        public static final String EXTRA_ERROR_CODE = "code";  // value should be int
+
+        public static final int START = 14;
+        public static final String EXTRA_START_LOCALE = "locale";  // value should be String
+        public static final String EXTRA_START_SWIPE = "swipe";  // value should be boolean
+
+        public static final int VOICE_INPUT_DELIVERED = 15;
+
+        public static final int N_BEST_CHOOSE = 16;
+        public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index";  // value should be int
+
+        public static final int TEXT_MODIFIED = 17;
+
+        public static final int INPUT_ENDED = 18;
+
+        public static final int VOICE_INPUT_SETTING_ENABLED = 19;
+
+        public static final int VOICE_INPUT_SETTING_DISABLED = 20;
+
+        public static final int IME_TEXT_ACCEPTED = 21;
+    }
+
+}
diff --git a/common/java/com/android/common/userhappiness/UserHappinessSignals.java b/common/java/com/android/common/userhappiness/UserHappinessSignals.java
new file mode 100644
index 0000000..347bdaa
--- /dev/null
+++ b/common/java/com/android/common/userhappiness/UserHappinessSignals.java
@@ -0,0 +1,45 @@
+/*
+ * 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.common.userhappiness;
+
+import android.content.Intent;
+import android.content.Context;
+import com.android.common.speech.LoggingEvents;
+
+/**
+ * Metrics for User Happiness are recorded here. Each app can define when to
+ * call these User Happiness metrics.
+ */
+public class UserHappinessSignals {
+
+    /**
+     *  Log when a user "accepted" IME text. Each application can define what
+     *  it means to "accept" text. In the case of Gmail, pressing the "Send"
+     *  button indicates text acceptance. We broadcast this information to
+     *  VoiceSearch LoggingEvents and use it to aggregate VoiceIME Happiness Metrics
+     */
+    public static void userAcceptedImeText(Context context) {
+        // Create a Voice IME Logging intent.
+        Intent i = new Intent(LoggingEvents.ACTION_LOG_EVENT);
+        i.putExtra(LoggingEvents.EXTRA_APP_NAME, LoggingEvents.VoiceIme.APP_NAME);
+        i.putExtra(LoggingEvents.EXTRA_EVENT, LoggingEvents.VoiceIme.IME_TEXT_ACCEPTED);
+        i.putExtra(LoggingEvents.EXTRA_CALLING_APP_NAME, context.getPackageName());
+        i.putExtra(LoggingEvents.EXTRA_TIMESTAMP, System.currentTimeMillis());
+        context.sendBroadcast(i);
+    }
+
+}
diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java
index 88fdab2..b9a84b7 100644
--- a/core/java/android/app/DeviceAdmin.java
+++ b/core/java/android/app/DeviceAdmin.java
@@ -99,11 +99,10 @@
     /**
      * Action sent to a device administrator when the user has changed the
      * password of their device.  You can at this point check the characteristics
-     * of the new password with {@link DevicePolicyManager#getPasswordMode()
-     * DevicePolicyManager.getActivePasswordMode()} and
-     * {@link DevicePolicyManager#getMinimumPasswordLength()
-     * DevicePolicyManager.getMinimumPasswordLength()}.  You will generally
-     * handle this in {@link DeviceAdmin#onPasswordChanged(Context, Intent)}.
+     * of the new password with {@link DevicePolicyManager#isActivePasswordSufficient()
+     * DevicePolicyManager.isActivePasswordSufficient()}.
+     * You will generally
+     * handle this in {@link DeviceAdmin#onPasswordChanged}.
      * 
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to receive
@@ -117,9 +116,9 @@
      * Action sent to a device administrator when the user has failed at
      * attempted to enter the password.  You can at this point check the
      * number of failed password attempts there have been with
-     * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()
+     * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts
      * DevicePolicyManager.getCurrentFailedPasswordAttempts()}.  You will generally
-     * handle this in {@link DeviceAdmin#onPasswordFailed(Context, Intent)}.
+     * handle this in {@link DeviceAdmin#onPasswordFailed}.
      * 
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive
diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java
index e50db89..50b342b 100644
--- a/core/java/android/app/DeviceAdminInfo.java
+++ b/core/java/android/app/DeviceAdminInfo.java
@@ -51,7 +51,7 @@
     /**
      * A type of policy that this device admin can use: limit the passwords
      * that the user can select, via {@link DevicePolicyManager#setPasswordMode}
-     * and {@link DevicePolicyManager#setMinimumPasswordLength}.
+     * and {@link DevicePolicyManager#setPasswordMinimumLength}.
      * 
      * <p>To control this policy, the device admin must have a "limit-password"
      * tag in the "uses-policies" section of its meta-data.
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
index 135851f..779db3a 100644
--- a/core/java/android/app/DevicePolicyManager.java
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -89,7 +89,7 @@
     /**
      * Activity action: have the user enter a new password.  This activity
      * should be launched after using {@link #setPasswordMode(ComponentName, int)}
-     * or {@link #setMinimumPasswordLength(ComponentName, int)} to have the
+     * or {@link #setPasswordMinimumLength(ComponentName, int)} to have the
      * user enter a new password that meets the current requirements.  You can
      * use {@link #isActivePasswordSufficient()} to determine whether you need
      * to have the user select a new password in order to meet the current
@@ -210,13 +210,15 @@
     }
     
     /**
-     * Retrieve the current password mode that is in effect due to all
-     * device admins.
+     * Retrieve the current minimum password mode for all admins
+     * or a particular one.
+     * @param admin The name of the admin component to check, or null to aggregate
+     * all admins.
      */
-    public int getPasswordMode() {
+    public int getPasswordMode(ComponentName admin) {
         if (mService != null) {
             try {
-                return mService.getPasswordMode();
+                return mService.getPasswordMode(admin);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -244,10 +246,10 @@
      * @param length The new desired minimum password length.  A value of 0
      * means there is no restriction.
      */
-    public void setMinimumPasswordLength(ComponentName admin, int length) {
+    public void setPasswordMinimumLength(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setMinimumPasswordLength(admin, length);
+                mService.setPasswordMinimumLength(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -255,13 +257,15 @@
     }
     
     /**
-     * Retrieve the current minimum password length that is in effect due to all
-     * device admins.
+     * Retrieve the current minimum password length for all admins
+     * or a particular one.
+     * @param admin The name of the admin component to check, or null to aggregate
+     * all admins.
      */
-    public int getMinimumPasswordLength() {
+    public int getPasswordMinimumLength(ComponentName admin) {
         if (mService != null) {
             try {
-                return mService.getMinimumPasswordLength();
+                return mService.getPasswordMinimumLength(admin);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -270,6 +274,17 @@
     }
     
     /**
+     * Return the maximum password length that the device supports for a
+     * particular password mode.
+     * @param mode The mode being interrogated.
+     * @return Returns the maximum length that the user can enter.
+     */
+    public int getPasswordMaximumLength(int mode) {
+        // Kind-of arbitrary.
+        return 16;
+    }
+    
+    /**
      * Determine whether the current password the user has set is sufficient
      * to meet the policy requirements (mode, minimum length) that have been
      * requested.
@@ -335,11 +350,30 @@
     }
     
     /**
+     * Retrieve the current maximum number of login attempts that are allowed
+     * before the device wipes itself, for all admins
+     * or a particular one.
+     * @param admin The name of the admin component to check, or null to aggregate
+     * all admins.
+     */
+    public int getMaximumFailedPasswordsForWipe(ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getMaximumFailedPasswordsForWipe(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+    
+    /**
      * Force a new password on the user.  This takes effect immediately.  The
      * given password must meet the current password minimum length constraint
      * or it will be rejected.  The given password will be accepted regardless
      * of the current password mode, automatically adjusting the password mode
-     * higher if needed.  (The string you give here is acceptable for any mode;
+     * higher if needed to meet the requirements of all active administrators.
+     * (The string you give here is acceptable for any mode;
      * if it contains only digits, that is still an acceptable alphanumeric
      * password.)
      * 
@@ -386,13 +420,15 @@
     }
     
     /**
-     * Retrieve the current maximum time to lock that is in effect due to all
-     * device admins.  Returns 0 if no maximum is set.
+     * Retrieve the current maximum time to unlock for all admins
+     * or a particular one.
+     * @param admin The name of the admin component to check, or null to aggregate
+     * all admins.
      */
-    public long getMaximumTimeToLock() {
+    public long getMaximumTimeToLock(ComponentName admin) {
         if (mService != null) {
             try {
-                return mService.getMaximumTimeToLock();
+                return mService.getMaximumTimeToLock(admin);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl
index a5508cd..8d804f9 100644
--- a/core/java/android/app/IDevicePolicyManager.aidl
+++ b/core/java/android/app/IDevicePolicyManager.aidl
@@ -26,19 +26,21 @@
  */
 interface IDevicePolicyManager {
     void setPasswordMode(in ComponentName who, int mode);
-    int getPasswordMode();
+    int getPasswordMode(in ComponentName who);
     
-    void setMinimumPasswordLength(in ComponentName who, int length);
-    int getMinimumPasswordLength();
+    void setPasswordMinimumLength(in ComponentName who, int length);
+    int getPasswordMinimumLength(in ComponentName who);
     
     boolean isActivePasswordSufficient();
     int getCurrentFailedPasswordAttempts();
+    
     void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);
+    int getMaximumFailedPasswordsForWipe(in ComponentName admin);
     
     boolean resetPassword(String password);
     
     void setMaximumTimeToLock(in ComponentName who, long timeMs);
-    long getMaximumTimeToLock();
+    long getMaximumTimeToLock(in ComponentName who);
     
     void lockNow();
     
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bc59c94..d10c8f8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -27,6 +27,8 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Environment;
+import android.os.StatFs;
 import android.util.AndroidException;
 import android.util.DisplayMetrics;
 
@@ -602,6 +604,89 @@
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
+
+    // No-installation limit for internal flash: 10% or less space available
+    private static final double LOW_NAND_FLASH_TRESHOLD = 0.1;
+
+    // SD-to-internal app size threshold: currently set to 1 MB
+    private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
+
+    private static final int INSTALL_ON_INTERNAL_FLASH = 0;
+
+    /**
+     * Determines best place to install an application: either SD or internal FLASH.
+     * Tweak the algorithm for best results.
+     * @param tmpPackageFile APK file containing the application to install.
+     * @return <code>PKG_INSTALL_INTERNAL</code> if it is best to install package on internal
+     * storage, <code>PKG_INSTALL_ON_SD</code> if it is best to install package on SD card,
+     * and <code>PKG_CANNOT_FIT</code> if insufficient space to safely install the app.
+     * This response does not take into account the package's own flags.
+     * @hide
+     */
+    public static int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
+        // Initial implementation:
+        // Package size = code size + cache size + data size
+        // If code size > 1 MB, install on SD card.
+        // Else install on internal NAND flash, unless space on NAND is less than 5%
+        // 0 = install on internal FLASH
+        // 1 = install on SD card
+        // (-1) = insufficient space - package cannot be installed.
+
+        if ((packageURI == null) || (appInfo == null)) {
+            return (-1);
+        }
+
+        StatFs internalFlashStats = new StatFs(Environment.getDataDirectory().getPath());
+        StatFs sdcardStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
+
+        long totalInternalFlashSize = (long)internalFlashStats.getBlockCount() *
+                (long)internalFlashStats.getBlockSize();
+        long availInternalFlashSize = (long)internalFlashStats.getAvailableBlocks() *
+                (long)internalFlashStats.getBlockSize();
+        long availSDSize = (long)sdcardStats.getAvailableBlocks() *
+                (long)sdcardStats.getBlockSize();
+
+        double pctNandFree = (double)availInternalFlashSize / (double)totalInternalFlashSize;
+
+        final String archiveFilePath = packageURI.getPath();
+        File apkFile = new File(archiveFilePath);
+        long pkgLen = apkFile.length();
+
+        // Consider application flags preferences as well...
+        boolean installOnlyOnSD = ((appInfo.flags & PackageManager.INSTALL_ON_SDCARD) != 0);
+
+        // These are not very precise measures, but I guess it is hard to estimate sizes
+        // before installing the package.
+        // As a shortcut, I am assuming that the package fits on NAND flash if the available
+        // space is three times that of the APK size. For SD, we only worry about the APK size.
+        // Since packages are downloaded into SD, this might not even be necessary.
+        boolean fitsOnSD = (pkgLen < availSDSize) && ((2 * pkgLen) < availInternalFlashSize);
+        boolean fitsOnInternalFlash = ((pkgLen * 3) < availInternalFlashSize);
+
+        // Does not fit, recommend no installation.
+        if (!fitsOnSD && !fitsOnInternalFlash) {
+            return (-1);
+        }
+
+        if (pkgLen < (INSTALL_ON_SD_THRESHOLD) && fitsOnInternalFlash && !(installOnlyOnSD)) {
+            // recommend internal NAND likely
+            if (pctNandFree < LOW_NAND_FLASH_TRESHOLD) {
+                // Low space on NAND (<10%) - install on SD
+                return INSTALL_ON_SDCARD;
+            }
+            return INSTALL_ON_INTERNAL_FLASH;
+        } else {
+            if (fitsOnSD) {
+                // Recommend SD card
+                return INSTALL_ON_SDCARD;
+            } else if (fitsOnInternalFlash && (pctNandFree >= LOW_NAND_FLASH_TRESHOLD) &&
+                    !(installOnlyOnSD)) {
+                return INSTALL_ON_INTERNAL_FLASH;
+            } else {
+                return (-1);
+            }
+        }
+    }
     
     /**
      * Retrieve overall information about an application package that is
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 82490bb..e3c25ec 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -65,9 +65,8 @@
     /**
      * Algorithms used in ON CONFLICT clause
      * http://www.sqlite.org/lang_conflict.html
-     * @hide
      */
-    public enum ConflictAlgorithm {
+    public static final class ConflictAlgorithm {
         /**
          *  When a constraint violation occurs, an immediate ROLLBACK occurs,
          * thus ending the current transaction, and the command aborts with a
@@ -75,14 +74,14 @@
          * (other than the implied transaction that is created on every command)
          *  then this algorithm works the same as ABORT.
          */
-        ROLLBACK("ROLLBACK"),
+        public static final int ROLLBACK = 1;
 
         /**
          * When a constraint violation occurs,no ROLLBACK is executed
          * so changes from prior commands within the same transaction
          * are preserved. This is the default behavior.
          */
-        ABORT("ABORT"),
+        public static final int ABORT = 2;
 
         /**
          * When a constraint violation occurs, the command aborts with a return
@@ -90,7 +89,7 @@
          * the command made prior to encountering the constraint violation
          * are preserved and are not backed out.
          */
-        FAIL("FAIL"),
+        public static final int FAIL = 3;
 
         /**
          * When a constraint violation occurs, the one row that contains
@@ -99,7 +98,7 @@
          * after the row that contained the constraint violation continue to be
          * inserted or updated normally. No error is returned.
          */
-        IGNORE("IGNORE"),
+        public static final int IGNORE = 4;
 
         /**
          * When a UNIQUE constraint violation occurs, the pre-existing rows that
@@ -114,15 +113,16 @@
          * it does not invoke delete triggers on those rows.
          *  This behavior might change in a future release.
          */
-        REPLACE("REPLACE");
+        public static final int REPLACE = 5;
 
-        private final String mValue;
-        ConflictAlgorithm(String value) {
-            mValue = value;
-        }
-        public String value() {
-            return mValue;
-        }
+        /**
+         * use the following when no conflict action is specified.
+         */
+        public static final int NONE = 0;
+        private static final String[] VALUES = new String[]
+                {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
+
+        private ConflictAlgorithm() {}  // disable instantiation of this class
     }
 
     /**
@@ -1334,7 +1334,7 @@
      */
     public long insert(String table, String nullColumnHack, ContentValues values) {
         try {
-            return insertWithOnConflict(table, nullColumnHack, values, null);
+            return insertWithOnConflict(table, nullColumnHack, values, ConflictAlgorithm.NONE);
         } catch (SQLException e) {
             Log.e(TAG, "Error inserting " + values, e);
             return -1;
@@ -1356,7 +1356,7 @@
      */
     public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
             throws SQLException {
-        return insertWithOnConflict(table, nullColumnHack, values, null);
+        return insertWithOnConflict(table, nullColumnHack, values, ConflictAlgorithm.NONE);
     }
 
     /**
@@ -1408,12 +1408,14 @@
      * @param initialValues this map contains the initial column values for the
      *            row. The keys should be the column names and the values the
      *            column values
-     * @param algorithm  {@link ConflictAlgorithm} for insert conflict resolver
-     * @return the row ID of the newly inserted row, or -1 if an error occurred
-     * @hide
+     * @param conflictAlgorithm  {@link ConflictAlgorithm} for insert conflict resolver
+     * @return the row ID of the newly inserted row
+     * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
+     * {@link ConflictAlgorithm#IGNORE}
+     * OR -1 if any error
      */
     public long insertWithOnConflict(String table, String nullColumnHack,
-            ContentValues initialValues, ConflictAlgorithm algorithm) {
+            ContentValues initialValues, int conflictAlgorithm) {
         if (!isOpen()) {
             throw new IllegalStateException("database not open");
         }
@@ -1421,10 +1423,7 @@
         // Measurements show most sql lengths <= 152
         StringBuilder sql = new StringBuilder(152);
         sql.append("INSERT");
-        if (algorithm != null) {
-            sql.append(" OR ");
-            sql.append(algorithm.value());
-        }
+        sql.append(ConflictAlgorithm.VALUES[conflictAlgorithm]);
         sql.append(" INTO ");
         sql.append(table);
         // Measurements show most values lengths < 40
@@ -1548,7 +1547,7 @@
      * @return the number of rows affected
      */
     public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
-        return updateWithOnConflict(table, values, whereClause, whereArgs, null);
+        return updateWithOnConflict(table, values, whereClause, whereArgs, ConflictAlgorithm.NONE);
     }
 
     /**
@@ -1559,12 +1558,11 @@
      *            valid value that will be translated to NULL.
      * @param whereClause the optional WHERE clause to apply when updating.
      *            Passing null will update all rows.
-     * @param algorithm  {@link ConflictAlgorithm} for update conflict resolver
+     * @param conflictAlgorithm  {@link ConflictAlgorithm} for update conflict resolver
      * @return the number of rows affected
-     * @hide
      */
     public int updateWithOnConflict(String table, ContentValues values,
-            String whereClause, String[] whereArgs, ConflictAlgorithm algorithm) {
+            String whereClause, String[] whereArgs, int conflictAlgorithm) {
         if (!isOpen()) {
             throw new IllegalStateException("database not open");
         }
@@ -1575,12 +1573,7 @@
 
         StringBuilder sql = new StringBuilder(120);
         sql.append("UPDATE ");
-        if (algorithm != null) {
-            sql.append("OR ");
-            sql.append(algorithm.value());
-            sql.append(" ");
-        }
-
+        sql.append(ConflictAlgorithm.VALUES[conflictAlgorithm]);
         sql.append(table);
         sql.append(" SET ");
 
diff --git a/core/java/com/google/android/mms/util/SqliteWrapper.java b/core/java/android/database/sqlite/SqliteWrapper.java
similarity index 84%
rename from core/java/com/google/android/mms/util/SqliteWrapper.java
rename to core/java/android/database/sqlite/SqliteWrapper.java
index bcdac22..b019618 100644
--- a/core/java/com/google/android/mms/util/SqliteWrapper.java
+++ b/core/java/android/database/sqlite/SqliteWrapper.java
@@ -15,9 +15,8 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.util;
+package android.database.sqlite;
 
-import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -27,6 +26,10 @@
 import android.util.Log;
 import android.widget.Toast;
 
+/**
+ * @hide
+ */
+
 public final class SqliteWrapper {
     private static final String TAG = "SqliteWrapper";
     private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
@@ -36,21 +39,6 @@
         // Forbidden being instantiated.
     }
 
-    // FIXME: It looks like outInfo.lowMemory does not work well as we expected.
-    // after run command: adb shell fillup -p 100, outInfo.lowMemory is still false.
-    private static boolean isLowMemory(Context context) {
-        if (null == context) {
-            return false;
-        }
-
-        ActivityManager am = (ActivityManager)
-                        context.getSystemService(Context.ACTIVITY_SERVICE);
-        ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
-        am.getMemoryInfo(outInfo);
-
-        return outInfo.lowMemory;
-    }
-
     // FIXME: need to optimize this method.
     private static boolean isLowMemory(SQLiteException e) {
         return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE);
diff --git a/core/java/android/ddm/DdmHandleProfiling.java b/core/java/android/ddm/DdmHandleProfiling.java
index beed505..63ee445 100644
--- a/core/java/android/ddm/DdmHandleProfiling.java
+++ b/core/java/android/ddm/DdmHandleProfiling.java
@@ -32,6 +32,8 @@
 
     public static final int CHUNK_MPRS = type("MPRS");
     public static final int CHUNK_MPRE = type("MPRE");
+    public static final int CHUNK_MPSS = type("MPSS");
+    public static final int CHUNK_MPSE = type("MPSE");
     public static final int CHUNK_MPRQ = type("MPRQ");
 
     private static DdmHandleProfiling mInstance = new DdmHandleProfiling();
@@ -46,6 +48,8 @@
     public static void register() {
         DdmServer.registerHandler(CHUNK_MPRS, mInstance);
         DdmServer.registerHandler(CHUNK_MPRE, mInstance);
+        DdmServer.registerHandler(CHUNK_MPSS, mInstance);
+        DdmServer.registerHandler(CHUNK_MPSE, mInstance);
         DdmServer.registerHandler(CHUNK_MPRQ, mInstance);
     }
 
@@ -73,6 +77,10 @@
             return handleMPRS(request);
         } else if (type == CHUNK_MPRE) {
             return handleMPRE(request);
+        } else if (type == CHUNK_MPSS) {
+            return handleMPSS(request);
+        } else if (type == CHUNK_MPSE) {
+            return handleMPSE(request);
         } else if (type == CHUNK_MPRQ) {
             return handleMPRQ(request);
         } else {
@@ -124,6 +132,50 @@
     }
 
     /*
+     * Handle a "Method Profiling w/Streaming Start" request.
+     */
+    private Chunk handleMPSS(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+
+        int bufferSize = in.getInt();
+        int flags = in.getInt();
+        if (Config.LOGV) {
+            Log.v("ddm-heap", "Method prof stream start: size=" + bufferSize
+                + ", flags=" + flags);
+        }
+
+        try {
+            Debug.startMethodTracingDdms(bufferSize, flags);
+            return null;        // empty response
+        } catch (RuntimeException re) {
+            return createFailChunk(1, re.getMessage());
+        }
+    }
+
+    /*
+     * Handle a "Method Profiling w/Streaming End" request.
+     */
+    private Chunk handleMPSE(Chunk request) {
+        byte result;
+
+        if (Config.LOGV) {
+            Log.v("ddm-heap", "Method prof stream end");
+        }
+
+        try {
+            Debug.stopMethodTracing();
+            result = 0;
+        } catch (RuntimeException re) {
+            Log.w("ddm-heap", "Method prof stream end failed: "
+                + re.getMessage());
+            return createFailChunk(1, re.getMessage());
+        }
+
+        /* VM sent the (perhaps very large) response directly */
+        return null;
+    }
+
+    /*
      * Handle a "Method PRofiling Query" request.
      */
     private Chunk handleMPRQ(Chunk request) {
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 46b2bee..2214405 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -196,26 +196,31 @@
          */
         public DName(String dName) {
             if (dName != null) {
-                X509Name x509Name = new X509Name(mDName = dName);
+                mDName = dName;
+                try {
+                    X509Name x509Name = new X509Name(dName);
 
-                Vector val = x509Name.getValues();
-                Vector oid = x509Name.getOIDs();
+                    Vector val = x509Name.getValues();
+                    Vector oid = x509Name.getOIDs();
 
-                for (int i = 0; i < oid.size(); i++) {
-                    if (oid.elementAt(i).equals(X509Name.CN)) {
-                        mCName = (String) val.elementAt(i);
-                        continue;
+                    for (int i = 0; i < oid.size(); i++) {
+                        if (oid.elementAt(i).equals(X509Name.CN)) {
+                            mCName = (String) val.elementAt(i);
+                            continue;
+                        }
+
+                        if (oid.elementAt(i).equals(X509Name.O)) {
+                            mOName = (String) val.elementAt(i);
+                            continue;
+                        }
+
+                        if (oid.elementAt(i).equals(X509Name.OU)) {
+                            mUName = (String) val.elementAt(i);
+                            continue;
+                        }
                     }
-
-                    if (oid.elementAt(i).equals(X509Name.O)) {
-                        mOName = (String) val.elementAt(i);
-                        continue;
-                    }
-
-                    if (oid.elementAt(i).equals(X509Name.OU)) {
-                        mUName = (String) val.elementAt(i);
-                        continue;
-                    }
+                } catch (IllegalArgumentException ex) {
+                    // thrown if there is an error parsing the string
                 }
             }
         }
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 7043c2e..8e9b11b 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -469,6 +469,17 @@
     }
 
     /**
+     * Starts method tracing without a backing file.  When stopMethodTracing
+     * is called, the result is sent directly to DDMS.  (If DDMS is not
+     * attached when tracing ends, the profiling data will be discarded.)
+     *
+     * @hide
+     */
+    public static void startMethodTracingDdms(int bufferSize, int flags) {
+        VMDebug.startMethodTracingDdms(bufferSize, flags);
+    }
+
+    /**
      * Determine whether method tracing is currently active.
      * @hide
      */
diff --git a/core/java/android/os/IMountServiceObserver.aidl b/core/java/android/os/IMountServiceObserver.aidl
new file mode 100644
index 0000000..f753649
--- /dev/null
+++ b/core/java/android/os/IMountServiceObserver.aidl
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * Callback class for receiving events from MountService.
+ *
+ * @hide
+ */
+interface IMountServiceObserver {
+    /**
+     * A sharing method has changed availability state.
+     *
+     * @param method The share method which has changed.
+     * @param available The share availability state.
+     */
+    void shareAvailabilityChange(String method, boolean available);
+
+    /**
+     * Media has been inserted
+     *
+     * @param label The volume label.
+     * @param path The volume mount path.
+     * @param major The backing device major number.
+     * @param minor The backing device minor number.
+     */
+    void mediaInserted(String label, String path, int major, int minor);
+
+    /**
+     * Media has been removed
+     *
+     * @param label The volume label.
+     * @param path The volume mount path.
+     * @param major The backing device major number.
+     * @param minor The backing device minor number.
+     * @param clean Indicates if the removal was clean (unmounted first).
+     */
+    void mediaRemoved(String label, String path, int major, int minor, boolean clean);
+
+    /**
+     *  Volume state has changed.
+     *
+     * @param label The volume label.
+     * @param path The volume mount path.
+     * @param oldState The old state of the volume.
+     * @param newState The new state of the volume.
+     *
+     * Note: State is one of the values returned by Environment.getExternalStorageState()
+     */
+    void volumeStateChange(String label, String path, String oldState, String newState);
+
+}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index b5408ae..dedc347 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -22,6 +22,7 @@
 {
     void acquireWakeLock(int flags, IBinder lock, String tag);
     void goToSleep(long time);
+    void goToSleepWithReason(long time, int reason);
     void releaseWakeLock(IBinder lock, int flags);
     void userActivity(long when, boolean noChangeLights);
     void userActivityWithForce(long when, boolean noChangeLights, boolean force);
diff --git a/core/java/android/os/MountServiceObserver.java b/core/java/android/os/MountServiceObserver.java
new file mode 100644
index 0000000..3020562
--- /dev/null
+++ b/core/java/android/os/MountServiceObserver.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Callback class for receiving progress reports during a restore operation.  These
+ * methods will all be called on your application's main thread.
+ * @hide
+ */
+public abstract class MountServiceObserver {
+    /**
+     * A sharing method has changed availability state.
+     *
+     * @param method The share method which has changed.
+     * @param available The share availability state.
+     */
+    void shareAvailabilityChange(String method, boolean available) {
+    }
+
+    /**
+     * Media has been inserted
+     *
+     * @param label The volume label.
+     * @param path The volume mount path.
+     * @param major The backing device major number.
+     * @param minor The backing device minor number.
+     */
+    void mediaInserted(String label, String path, int major, int minor) {
+    }
+
+    /**
+     * Media has been removed
+     *
+     * @param label The volume label.
+     * @param path The volume mount path.
+     * @param major The backing device major number.
+     * @param minor The backing device minor number.
+     * @param clean Indicates if the removal was clean (unmounted first).
+     */
+    void mediaRemoved(String label, String path, int major, int minor, boolean clean) {
+    }
+
+    /**
+     *  Volume state has changed.
+     *
+     * @param label The volume label.
+     * @param path The volume mount path.
+     * @param oldState The old state of the volume.
+     * @param newState The new state of the volume.
+     *
+     * Note: State is one of the values returned by Environment.getExternalStorageState()
+     */
+    void volumeStateChange(String label, String path, String oldState, String newState) {
+    }
+}
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index ef43afe..0b1b16b 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -79,25 +79,13 @@
     public static final String EXTRA_POST_DATA = "com.android.browser.post_data";
 
     /**
-     * The name of the extra data in the VIEW intent. The data is in the format
-     * of String array. This should be paired with EXTRA_HEADERS_VALUE.
-     * <p>
-     * The keys will be combined with the values and sent in the HTTP request
+     * The name of the extra data in the VIEW intent. The data are key/value
+     * pairs in the format of Bundle. They will be sent in the HTTP request
      * headers for the provided url. The keys can't be the standard HTTP headers
      * as they are set by the WebView. The url's schema must be http(s).
      * <p>
      */
-    public static final String EXTRA_HEADERS_KEY = "com.android.browser.headers_key";
-
-    /**
-     * The name of the extra data in the VIEW intent. The data is in the format
-     * of String array. This should be paired with EXTRA_HEADERS_KEY.
-     * <p>
-     * The values will be combined with the keys and sent in the HTTP request
-     * headers for the provided url. The url's schema must be http(s).
-     * <p>
-     */
-    public static final String EXTRA_HEADERS_VALUE = "com.android.browser.headers_value";
+    public static final String EXTRA_HEADERS = "com.android.browser.headers";
 
     /* if you change column order you must also change indices
        below */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 747ae30..adeef54 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -16,8 +16,6 @@
 
 package android.provider;
 
-import com.google.android.mms.util.SqliteWrapper;
-
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ContentResolver;
@@ -25,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
+import android.database.sqlite.SqliteWrapper;
 import android.net.Uri;
 import android.telephony.SmsMessage;
 import android.text.TextUtils;
diff --git a/core/java/android/service/urlrenderer/IUrlRendererCallback.aidl b/core/java/android/service/urlrenderer/IUrlRendererCallback.aidl
new file mode 100644
index 0000000..004aca7
--- /dev/null
+++ b/core/java/android/service/urlrenderer/IUrlRendererCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.urlrenderer;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * {@hide}
+ */
+oneway interface IUrlRendererCallback {
+    void complete(String url, in ParcelFileDescriptor result);
+}
diff --git a/core/java/android/service/urlrenderer/IUrlRendererService.aidl b/core/java/android/service/urlrenderer/IUrlRendererService.aidl
new file mode 100644
index 0000000..d561fdc
--- /dev/null
+++ b/core/java/android/service/urlrenderer/IUrlRendererService.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.urlrenderer;
+
+import android.service.urlrenderer.IUrlRendererCallback;
+
+/**
+ * {@hide}
+ */
+interface IUrlRendererService {
+    void render(in List<String> urls, int width, int height,
+            IUrlRendererCallback cb);
+}
diff --git a/core/java/android/service/urlrenderer/UrlRenderer.java b/core/java/android/service/urlrenderer/UrlRenderer.java
new file mode 100644
index 0000000..6057d6c
--- /dev/null
+++ b/core/java/android/service/urlrenderer/UrlRenderer.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.urlrenderer;
+
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * TODO(phanna): Document this class.
+ * {@hide} while developing
+ */
+public final class UrlRenderer {
+    /**
+     * Interface for clients to receive the result of calls to
+     * {@link UrlRenderer#render}.
+     * {@hide} while developing
+     */
+    public interface Callback {
+        /**
+         * Calls to {@link render} will result in multiple invokations of this
+         * method for each url.  A null result means that there was a server
+         * error or a problem rendering the url.
+         * @param url  The url that has been rendered.
+         * @param result  A ParcelFileDescriptor containing the encoded image
+         *                data. The client is responsible for closing the stream
+         *                to free resources.  A null result indicates a failure
+         *                to render.
+         */
+        public void complete(String url, ParcelFileDescriptor result);
+    }
+
+    private IUrlRendererService mService;
+
+    /**
+     * Create a new UrlRenderer to remotely render urls.
+     * @param service  An IBinder service usually obtained through
+     *                 {@link ServiceConnection#onServiceConnected}
+     */
+    public UrlRenderer(IBinder service) {
+        mService = IUrlRendererService.Stub.asInterface(service);
+    }
+
+    private static class InternalCallback extends IUrlRendererCallback.Stub {
+        private final Callback mCallback;
+        InternalCallback(Callback cb) {
+            mCallback = cb;
+        }
+
+        public void complete(String url, ParcelFileDescriptor result) {
+            mCallback.complete(url, result);
+        }
+    }
+
+    /**
+     * Render the list of <var>urls</var> and invoke the <var>callback</var>
+     * for each result.
+     * @param urls  A List of urls to render.
+     * @param width  The desired width of the result.
+     * @param height  The desired height of the result.
+     * @param callback  An instance of {@link Callback} invoked for each url.
+     */
+    public void render(List<String> urls, int width, int height,
+            Callback callback) {
+        if (mService != null) {
+            try {
+                mService.render(urls, width, height,
+                        new InternalCallback(callback));
+            } catch (RemoteException ex) {
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/urlrenderer/UrlRendererService.java b/core/java/android/service/urlrenderer/UrlRendererService.java
new file mode 100644
index 0000000..f7bf7d7
--- /dev/null
+++ b/core/java/android/service/urlrenderer/UrlRendererService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.urlrenderer;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * TODO(phanna): Complete documentation.
+ * {@hide} while developing
+ */
+public abstract class UrlRendererService extends Service {
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.urlrenderer.UrlRendererService";
+
+    static final String TAG = "UrlRendererService";
+
+    private static class InternalCallback implements UrlRenderer.Callback {
+        private final IUrlRendererCallback mCallback;
+        InternalCallback(IUrlRendererCallback cb) {
+            mCallback = cb;
+        }
+
+        public void complete(String url, ParcelFileDescriptor result) {
+            try {
+                mCallback.complete(url, result);
+            } catch (RemoteException ex) {
+            }
+        }
+    }
+
+    private final IUrlRendererService.Stub mBinderInterface =
+            new IUrlRendererService.Stub() {
+                public void render(List<String> urls, int width, int height,
+                        IUrlRendererCallback cb) {
+                    processRequest(urls, width, height,
+                            new InternalCallback(cb));
+                }
+            };
+
+    /**
+     * Implement to return the implementation of the internal accessibility
+     * service interface.  Subclasses should not override.
+     */
+    @Override
+    public final android.os.IBinder onBind(android.content.Intent intent) {
+        return mBinderInterface;
+    }
+
+    /**
+     * When all clients unbind from the service, stop the service.  Subclasses
+     * should not override.
+     */
+    @Override
+    public final boolean onUnbind(android.content.Intent intent) {
+        stopSelf();
+        return false;
+    }
+
+    /**
+     * Subclasses implement this function to process the given urls.  When each
+     * url is complete, the subclass must invoke the callback with the result.
+     * @param urls  A list of urls to render at the given dimensions.
+     * @param width  The desired width of the result.
+     * @param height  The desired height of the result.
+     * @param cb  The callback to invoke when each url is complete.
+     */
+    public abstract void processRequest(List<String> urls, int width,
+            int height, UrlRenderer.Callback cb);
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8e15f89..5ffc09a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -369,6 +369,12 @@
          */
         public int flags;
         
+        /** Window flag: as long as this window is visible to the user, allow
+         *  the lock screen to activate while the screen is on. 
+         *  This can be used independently, or in combination with 
+         *  {@link #FLAG_KEEP_SCREEN_ON} and/or {@link FLAG_SHOW_WHEN_LOCKED} */
+        public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;
+
         /** Window flag: everything behind this window will be dimmed.
          *  Use {@link #dimAmount} to control the amount of dim. */
         public static final int FLAG_DIM_BEHIND        = 0x00000002;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index bbe9c1f..7bc5cce 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -357,12 +357,17 @@
      * previous activity, and both are on top of he wallpaper. */
     public final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15 | TRANSIT_EXIT_MASK;
     
+    // NOTE: screen off reasons are in order of significance, with more
+    // important ones lower than less important ones.
+    
+    /** Screen turned off because of a device admin */
+    public final int OFF_BECAUSE_OF_ADMIN = 1;
     /** Screen turned off because of power button */
-    public final int OFF_BECAUSE_OF_USER = 1;
+    public final int OFF_BECAUSE_OF_USER = 2;
     /** Screen turned off because of timeout */
-    public final int OFF_BECAUSE_OF_TIMEOUT = 2;
+    public final int OFF_BECAUSE_OF_TIMEOUT = 3;
     /** Screen turned off because of proximity sensor */
-    public final int OFF_BECAUSE_OF_PROX_SENSOR = 3;
+    public final int OFF_BECAUSE_OF_PROX_SENSOR = 4;
 
     /**
      * Magic constant to {@link IWindowManager#setRotation} to not actually
@@ -822,6 +827,12 @@
     public void systemReady();
 
     /**
+     * Called when userActivity is signalled in the power manager.
+     * This is safe to call from any thread, with any window manager locks held or not.
+     */
+    public void userActivity();
+
+    /**
      * Called when we have finished booting and can now display the home
      * screen to the user.  This wilWl happen after systemReady(), and at
      * this point the display is active.
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 429b335..85f1d5c 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -73,6 +73,8 @@
     private static final int ENDED             = 201;
     private static final int POSTER_FETCHED    = 202;
 
+    private static final String COOKIE = "Cookie";
+
     // Timer thread -> UI thread
     private static final int TIMEUPDATE = 300;
 
@@ -165,7 +167,15 @@
             mVideoView = new VideoView(proxy.getContext());
             mVideoView.setWillNotDraw(false);
             mVideoView.setMediaController(new MediaController(proxy.getContext()));
-            mVideoView.setVideoURI(Uri.parse(url));
+
+            String cookieValue = CookieManager.getInstance().getCookie(url);
+            Map<String, String> headers = null;
+            if (cookieValue != null) {
+                headers = new HashMap<String, String>();
+                headers.put(COOKIE, cookieValue);
+            }
+
+            mVideoView.setVideoURI(Uri.parse(url), headers);
             mVideoView.setOnCompletionListener(proxy);
             mVideoView.setOnPreparedListener(proxy);
             mVideoView.setOnErrorListener(proxy);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ecea55f..39b0a63 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -499,7 +499,7 @@
     static final int UPDATE_TEXT_ENTRY_MSG_ID           = 15;
     static final int WEBCORE_INITIALIZED_MSG_ID         = 16;
     static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 17;
-    static final int FIND_AGAIN                         = 18;
+    static final int UPDATE_ZOOM_RANGE                  = 18;
     static final int MOVE_OUT_OF_PLUGIN                 = 19;
     static final int CLEAR_TEXT_ENTRY                   = 20;
     static final int UPDATE_TEXT_SELECTION_MSG_ID       = 21;
@@ -517,6 +517,7 @@
     static final int IMMEDIATE_REPAINT_MSG_ID           = 32;
     static final int SET_ROOT_LAYER_MSG_ID              = 33;
     static final int RETURN_LABEL                       = 34;
+    static final int FIND_AGAIN                         = 35;
 
     static final String[] HandlerDebugString = {
         "REMEMBER_PASSWORD", //              = 1;
@@ -536,7 +537,7 @@
         "UPDATE_TEXT_ENTRY_MSG_ID", //       = 15;
         "WEBCORE_INITIALIZED_MSG_ID", //     = 16;
         "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 17;
-        "FIND_AGAIN", //                     = 18;
+        "UPDATE_ZOOM_RANGE", //              = 18;
         "MOVE_OUT_OF_PLUGIN", //             = 19;
         "CLEAR_TEXT_ENTRY", //               = 20;
         "UPDATE_TEXT_SELECTION_MSG_ID", //   = 21;
@@ -552,7 +553,8 @@
         "DOM_FOCUS_CHANGED", //              = 31;
         "IMMEDIATE_REPAINT_MSG_ID", //       = 32;
         "SET_ROOT_LAYER_MSG_ID", //          = 33;
-        "RETURN_LABEL" //                    = 34;
+        "RETURN_LABEL", //                   = 34;
+        "FIND_AGAIN" //                      = 35;
     };
 
     // If the site doesn't use the viewport meta tag to specify the viewport,
@@ -2999,6 +3001,10 @@
 
     @Override
     public boolean performLongClick() {
+        // performLongClick() is the result of a delayed message. If we switch
+        // to windows overview, the WebView will be temporarily removed from the
+        // view system. In that case, do nothing.
+        if (getParent() == null) return false;
         if (mNativeClass != 0 && nativeCursorIsTextInput()) {
             // Send the click so that the textfield is in focus
             centerKeyPressOnTextField();
@@ -5583,7 +5589,7 @@
             // exclude INVAL_RECT_MSG_ID since it is frequently output
             if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
                 Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
-                        > RETURN_LABEL ? Integer.toString(msg.what)
+                        > FIND_AGAIN ? Integer.toString(msg.what)
                         : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
             }
             if (mWebViewCore == null) {
@@ -5679,6 +5685,14 @@
                 case SPAWN_SCROLL_TO_MSG_ID:
                     spawnContentScrollTo(msg.arg1, msg.arg2);
                     break;
+                case UPDATE_ZOOM_RANGE: {
+                    WebViewCore.RestoreState restoreState
+                            = (WebViewCore.RestoreState) msg.obj;
+                    // mScrollX contains the new minPrefWidth
+                    updateZoomRange(restoreState, getViewWidth(),
+                            restoreState.mScrollX, false);
+                    break;
+                }
                 case NEW_PICTURE_MSG_ID: {
                     WebSettings settings = mWebViewCore.getSettings();
                     // called for new content
@@ -5691,32 +5705,8 @@
                     boolean hasRestoreState = restoreState != null;
                     if (hasRestoreState) {
                         mInZoomOverview = false;
-                        if (restoreState.mMinScale == 0) {
-                            if (restoreState.mMobileSite) {
-                                if (draw.mMinPrefWidth >
-                                        Math.max(0, draw.mViewPoint.x)) {
-                                    mMinZoomScale = (float) viewWidth
-                                            / draw.mMinPrefWidth;
-                                    mMinZoomScaleFixed = false;
-                                    mInZoomOverview = useWideViewport &&
-                                            settings.getLoadWithOverviewMode();
-                                } else {
-                                    mMinZoomScale = restoreState.mDefaultScale;
-                                    mMinZoomScaleFixed = true;
-                                }
-                            } else {
-                                mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
-                                mMinZoomScaleFixed = false;
-                            }
-                        } else {
-                            mMinZoomScale = restoreState.mMinScale;
-                            mMinZoomScaleFixed = true;
-                        }
-                        if (restoreState.mMaxScale == 0) {
-                            mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
-                        } else {
-                            mMaxZoomScale = restoreState.mMaxScale;
-                        }
+                        updateZoomRange(restoreState, viewSize.x,
+                                draw.mMinPrefWidth, true);
                         if (mInitialScaleInPercent > 0) {
                             setNewZoomScale(mInitialScaleInPercent / 100.0f,
                                     mInitialScaleInPercent != mTextWrapScale * 100,
@@ -5922,13 +5912,7 @@
                     // the states
                     mGotCenterDown = false;
                     mTrackballDown = false;
-                    // LONG_PRESS_CENTER is sent as a delayed message. If we
-                    // switch to windows overview, the WebView will be
-                    // temporarily removed from the view system. In that case,
-                    // do nothing.
-                    if (getParent() != null) {
-                        performLongClick();
-                    }
+                    performLongClick();
                     break;
 
                 case WEBCORE_NEED_TOUCH_EVENTS:
@@ -6413,6 +6397,37 @@
                 new InvokeListBox(array, enabledArray, selectedArray));
     }
 
+    private void updateZoomRange(WebViewCore.RestoreState restoreState,
+            int viewWidth, int minPrefWidth, boolean updateZoomOverview) {
+        if (restoreState.mMinScale == 0) {
+            if (restoreState.mMobileSite) {
+                if (minPrefWidth > Math.max(0, viewWidth)) {
+                    mMinZoomScale = (float) viewWidth / minPrefWidth;
+                    mMinZoomScaleFixed = false;
+                    if (updateZoomOverview) {
+                        WebSettings settings = getSettings();
+                        mInZoomOverview = settings.getUseWideViewPort() &&
+                                settings.getLoadWithOverviewMode();
+                    }
+                } else {
+                    mMinZoomScale = restoreState.mDefaultScale;
+                    mMinZoomScaleFixed = true;
+                }
+            } else {
+                mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
+                mMinZoomScaleFixed = false;
+            }
+        } else {
+            mMinZoomScale = restoreState.mMinScale;
+            mMinZoomScaleFixed = true;
+        }
+        if (restoreState.mMaxScale == 0) {
+            mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
+        } else {
+            mMaxZoomScale = restoreState.mMaxScale;
+        }
+    }
+
     /*
      * Request a dropdown menu for a listbox with single selection or a single
      * <select> element.
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 1e21cb4..310721f 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2053,7 +2053,19 @@
         }
 
         // if mViewportWidth is 0, it means device-width, always update.
-        if (mViewportWidth != 0 && !updateRestoreState) return;
+        if (mViewportWidth != 0 && !updateRestoreState) {
+            RestoreState restoreState = new RestoreState();
+            restoreState.mMinScale = mViewportMinimumScale / 100.0f;
+            restoreState.mMaxScale = mViewportMaximumScale / 100.0f;
+            restoreState.mDefaultScale = adjust;
+            // as mViewportWidth is not 0, it is not mobile site.
+            restoreState.mMobileSite = false;
+            // for non-mobile site, we don't need minPrefWidth, set it as 0
+            restoreState.mScrollX = 0;
+            Message.obtain(mWebView.mPrivateHandler,
+                    WebView.UPDATE_ZOOM_RANGE, restoreState).sendToTarget();
+            return;
+        }
 
         // now notify webview
         // webViewWidth refers to the width in the view system
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 4cc3b9e..0078fec 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -815,9 +815,19 @@
      * @param dy the number of pixels to scroll by on the Y axis
      */
     public final void smoothScrollBy(int dx, int dy) {
+        if (getChildCount() == 0) {
+            // Nothing to do.
+            return;
+        }
         long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
         if (duration > ANIMATED_SCROLL_GAP) {
-            mScroller.startScroll(mScrollX, mScrollY, dx, dy);
+            final int width = getWidth() - mPaddingRight - mPaddingLeft;
+            final int right = getChildAt(0).getWidth();
+            final int maxX = Math.max(0, right - width);
+            final int scrollX = mScrollX;
+            dx = Math.max(0, Math.min(scrollX + dx, maxX)) - scrollX;
+
+            mScroller.startScroll(scrollX, mScrollY, dx, 0);
             awakenScrollBars(mScroller.getDuration());
             invalidate();
         } else {
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 3fd5dcc..b22ae3c 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -49,7 +49,7 @@
     
     public OverScroller(Context context) {
         mDefaultScroller = new Scroller(context);
-        mDecelScroller = new Scroller(context, new DecelerateInterpolator(3.f));
+        mDecelScroller = new Scroller(context, new DecelerateInterpolator());
         mAccelDecelScroller = new Scroller(context, new AccelerateDecelerateInterpolator());
         mCurrScroller = mDefaultScroller;
     }
@@ -216,7 +216,7 @@
     /**
      * Start scrolling by providing a starting point and the distance to travel.
      * The scroll will use the default value of 250 milliseconds for the
-     * duration.
+     * duration. This version does not spring back to boundaries.
      * 
      * @param startX Starting horizontal scroll offset in pixels. Positive
      *        numbers will scroll the content to the left.
@@ -228,13 +228,45 @@
      *        content up.
      */
     public void startScroll(int startX, int startY, int dx, int dy) {
+        final int minX = Math.min(startX, startX + dx);
+        final int maxX = Math.max(startX, startX + dx);
+        final int minY = Math.min(startY, startY + dy);
+        final int maxY = Math.max(startY, startY + dy);
+        startScroll(startX, startY, dx, dy, minX, maxX, minY, maxY);
+    }
+    
+    /**
+     * Start scrolling by providing a starting point and the distance to travel.
+     * The scroll will use the default value of 250 milliseconds for the
+     * duration. This version will spring back to the provided boundaries if
+     * the scroll value would take it too far.
+     * 
+     * @param startX Starting horizontal scroll offset in pixels. Positive
+     *        numbers will scroll the content to the left.
+     * @param startY Starting vertical scroll offset in pixels. Positive numbers
+     *        will scroll the content up.
+     * @param dx Horizontal distance to travel. Positive numbers will scroll the
+     *        content to the left.
+     * @param dy Vertical distance to travel. Positive numbers will scroll the
+     *        content up.
+     * @param minX Minimum X value. The scroller will not scroll past this
+     *        point.
+     * @param maxX Maximum X value. The scroller will not scroll past this
+     *        point.
+     * @param minY Minimum Y value. The scroller will not scroll past this
+     *        point.
+     * @param maxY Maximum Y value. The scroller will not scroll past this
+     *        point.
+     */
+    public void startScroll(int startX, int startY, int dx, int dy,
+            int minX, int maxX, int minY, int maxY) {
         mCurrScroller.abortAnimation();
         mCurrScroller = mDefaultScroller;
         mScrollMode = MODE_DEFAULT;
-        mMinimumX = Math.min(startX, startX + dx);
-        mMinimumY = Math.min(startY, startY + dy);
-        mMaximumX = Math.max(startX, startX + dx);
-        mMaximumY = Math.max(startY, startY + dy);
+        mMinimumX = minX;
+        mMaximumX = maxX; 
+        mMinimumY = minY;
+        mMaximumY = maxY;
         mCurrScroller.startScroll(startX, startY, dx, dy);
     }
 
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 62797f3..4a1d871 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -817,9 +817,19 @@
      * @param dy the number of pixels to scroll by on the Y axis
      */
     public final void smoothScrollBy(int dx, int dy) {
+        if (getChildCount() == 0) {
+            // Nothing to do.
+            return;
+        }
         long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
         if (duration > ANIMATED_SCROLL_GAP) {
-            mScroller.startScroll(mScrollX, mScrollY, dx, dy);
+            final int height = getHeight() - mPaddingBottom - mPaddingTop;
+            final int bottom = getChildAt(0).getHeight();
+            final int maxY = Math.max(0, bottom - height);
+            final int scrollY = mScrollY;
+            dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
+
+            mScroller.startScroll(mScrollX, scrollY, 0, dy);
             awakenScrollBars(mScroller.getDuration());
             invalidate();
         } else {
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 8142a82..906bca1 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -38,6 +38,7 @@
 import android.widget.MediaController.*;
 
 import java.io.IOException;
+import java.util.Map;
 
 /**
  * Displays a video file.  The VideoView class
@@ -50,6 +51,7 @@
     private String TAG = "VideoView";
     // settable by the client
     private Uri         mUri;
+    private Map<String, String> mHeaders;
     private int         mDuration;
 
     // all possible internal states
@@ -90,12 +92,12 @@
         super(context);
         initVideoView();
     }
-    
+
     public VideoView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
         initVideoView();
     }
-    
+
     public VideoView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         initVideoView();
@@ -122,7 +124,7 @@
         //Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height);
         setMeasuredDimension(width, height);
     }
-    
+
     public int resolveAdjustedSize(int desiredSize, int measureSpec) {
         int result = desiredSize;
         int specMode = MeasureSpec.getMode(measureSpec);
@@ -137,13 +139,13 @@
                 break;
 
             case MeasureSpec.AT_MOST:
-                /* Parent says we can be as big as we want, up to specSize. 
-                 * Don't be larger than specSize, and don't be larger than 
+                /* Parent says we can be as big as we want, up to specSize.
+                 * Don't be larger than specSize, and don't be larger than
                  * the max size imposed on ourselves.
                  */
                 result = Math.min(desiredSize, specSize);
                 break;
-                
+
             case MeasureSpec.EXACTLY:
                 // No choice. Do what we are told.
                 result = specSize;
@@ -151,7 +153,7 @@
         }
         return result;
 }
-    
+
     private void initVideoView() {
         mVideoWidth = 0;
         mVideoHeight = 0;
@@ -169,13 +171,21 @@
     }
 
     public void setVideoURI(Uri uri) {
+        setVideoURI(uri, null);
+    }
+
+    /**
+     * @hide
+     */
+    public void setVideoURI(Uri uri, Map<String, String> headers) {
         mUri = uri;
+        mHeaders = headers;
         mSeekWhenPrepared = 0;
         openVideo();
         requestLayout();
         invalidate();
     }
-    
+
     public void stopPlayback() {
         if (mMediaPlayer != null) {
             mMediaPlayer.stop();
@@ -191,7 +201,7 @@
             // not ready for playback just yet, will try again later
             return;
         }
-        // Tell the music playback service to pause 
+        // Tell the music playback service to pause
         // TODO: these constants need to be published somewhere in the framework.
         Intent i = new Intent("com.android.music.musicservicecommand");
         i.putExtra("command", "pause");
@@ -209,7 +219,7 @@
             mMediaPlayer.setOnErrorListener(mErrorListener);
             mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
             mCurrentBufferPercentage = 0;
-            mMediaPlayer.setDataSource(mContext, mUri);
+            mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
             mMediaPlayer.setDisplay(mSurfaceHolder);
             mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
             mMediaPlayer.setScreenOnWhilePlaying(true);
@@ -232,7 +242,7 @@
             return;
         }
     }
-    
+
     public void setMediaController(MediaController controller) {
         if (mMediaController != null) {
             mMediaController.hide();
@@ -250,7 +260,7 @@
             mMediaController.setEnabled(isInPlaybackState());
         }
     }
-    
+
     MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
         new MediaPlayer.OnVideoSizeChangedListener() {
             public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
@@ -261,7 +271,7 @@
                 }
             }
     };
-    
+
     MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
         public void onPrepared(MediaPlayer mp) {
             mCurrentState = STATE_PREPARED;
@@ -490,7 +500,7 @@
         }
         return false;
     }
-    
+
     @Override
     public boolean onTrackballEvent(MotionEvent ev) {
         if (isInPlaybackState() && mMediaController != null) {
@@ -498,7 +508,7 @@
         }
         return false;
     }
-    
+
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event)
     {
@@ -519,7 +529,7 @@
                     mMediaController.hide();
                 }
                 return true;
-            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP 
+            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
                     && mMediaPlayer.isPlaying()) {
                 pause();
                 mMediaController.show();
@@ -532,13 +542,13 @@
     }
 
     private void toggleMediaControlsVisiblity() {
-        if (mMediaController.isShowing()) { 
+        if (mMediaController.isShowing()) {
             mMediaController.hide();
         } else {
             mMediaController.show();
         }
     }
-    
+
     public void start() {
         if (isInPlaybackState()) {
             mMediaPlayer.start();
@@ -546,7 +556,7 @@
         }
         mTargetState = STATE_PLAYING;
     }
-    
+
     public void pause() {
         if (isInPlaybackState()) {
             if (mMediaPlayer.isPlaying()) {
@@ -556,7 +566,7 @@
         }
         mTargetState = STATE_PAUSED;
     }
-    
+
     // cache duration as mDuration for faster access
     public int getDuration() {
         if (isInPlaybackState()) {
@@ -569,14 +579,14 @@
         mDuration = -1;
         return mDuration;
     }
-    
+
     public int getCurrentPosition() {
         if (isInPlaybackState()) {
             return mMediaPlayer.getCurrentPosition();
         }
         return 0;
     }
-    
+
     public void seekTo(int msec) {
         if (isInPlaybackState()) {
             mMediaPlayer.seekTo(msec);
@@ -584,12 +594,12 @@
         } else {
             mSeekWhenPrepared = msec;
         }
-    }    
-            
+    }
+
     public boolean isPlaying() {
         return isInPlaybackState() && mMediaPlayer.isPlaying();
     }
-    
+
     public int getBufferPercentage() {
         if (mMediaPlayer != null) {
             return mCurrentBufferPercentage;
diff --git a/core/java/com/android/internal/app/UsbStorageActivity.java b/core/java/com/android/internal/app/UsbStorageActivity.java
index b8a2136..37ea352 100644
--- a/core/java/com/android/internal/app/UsbStorageActivity.java
+++ b/core/java/com/android/internal/app/UsbStorageActivity.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.app;
 
-import android.app.AlertDialog;
+import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -28,16 +28,23 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.widget.ImageView;
+import android.widget.Button;
+import android.widget.TextView;
 import android.widget.Toast;
+import android.view.View;
 
 /**
  * This activity is shown to the user for him/her to enable USB mass storage
  * on-demand (that is, when the USB cable is connected). It uses the alert
  * dialog style. It will be launched from a notification.
  */
-public class UsbStorageActivity extends AlertActivity implements DialogInterface.OnClickListener {
-
-    private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
+public class UsbStorageActivity extends Activity {
+    private Button mMountButton;
+    private Button mUnmountButton;
+    private TextView mBanner;
+    private TextView mMessage;
+    private ImageView mIcon;
 
     /** Used to detect when the USB cable is unplugged, so we can call finish() */
     private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
@@ -53,16 +60,49 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        // Set up the "dialog"
-        final AlertController.AlertParams p = mAlertParams;
-        p.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
-        p.mTitle = getString(com.android.internal.R.string.usb_storage_title);
-        p.mMessage = getString(com.android.internal.R.string.usb_storage_message);
-        p.mPositiveButtonText = getString(com.android.internal.R.string.usb_storage_button_mount);
-        p.mPositiveButtonListener = this;
-        p.mNegativeButtonText = getString(com.android.internal.R.string.usb_storage_button_unmount);
-        p.mNegativeButtonListener = this;
-        setupAlert();
+        setTitle(getString(com.android.internal.R.string.usb_storage_activity_title));
+
+        setContentView(com.android.internal.R.layout.usb_storage_activity);
+
+        mIcon = (ImageView) findViewById(com.android.internal.R.id.icon);
+        mBanner = (TextView) findViewById(com.android.internal.R.id.banner);
+        mMessage = (TextView) findViewById(com.android.internal.R.id.message);
+
+        mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button);
+        mMountButton.setOnClickListener(
+            new View.OnClickListener() { 
+                 public void onClick(View v) {
+                     mountAsUsbStorage();
+                     // TODO: replace with forthcoming MountService callbacks
+                     switchDisplay(true);
+                 }
+            });
+
+        mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button);
+        mUnmountButton.setOnClickListener(
+            new View.OnClickListener() { 
+                 public void onClick(View v) {
+                     stopUsbStorage();
+                     // TODO: replace with forthcoming MountService callbacks
+                     switchDisplay(false);
+                 }
+            });
+    }
+
+    private void switchDisplay(boolean usbStorageInUse) {
+        if (usbStorageInUse) {
+            mUnmountButton.setVisibility(View.VISIBLE);
+            mMountButton.setVisibility(View.GONE);
+            mIcon.setImageResource(com.android.internal.R.drawable.usb_android_connected);
+            mBanner.setText(com.android.internal.R.string.usb_storage_stop_title);
+            mMessage.setText(com.android.internal.R.string.usb_storage_stop_message);
+        } else {
+            mUnmountButton.setVisibility(View.GONE);
+            mMountButton.setVisibility(View.VISIBLE);
+            mIcon.setImageResource(com.android.internal.R.drawable.usb_android);
+            mBanner.setText(com.android.internal.R.string.usb_storage_title);
+            mMessage.setText(com.android.internal.R.string.usb_storage_message);
+        }
     }
 
     @Override
@@ -70,6 +110,18 @@
         super.onResume();
 
         registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+        boolean umsOn = false;
+        try {
+            IMountService mountService = IMountService.Stub.asInterface(ServiceManager
+                    .getService("mount"));
+            if (mountService != null) {
+                umsOn = mountService.getMassStorageEnabled();
+            }
+        } catch (android.os.RemoteException exc) {
+            // pass
+        }
+        switchDisplay(umsOn);
     }
 
     @Override
@@ -79,19 +131,6 @@
         unregisterReceiver(mBatteryReceiver);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public void onClick(DialogInterface dialog, int which) {
-
-        if (which == POSITIVE_BUTTON) {
-            mountAsUsbStorage();
-        }
-
-        // No matter what, finish the activity
-        finish();
-    }
-
     private void mountAsUsbStorage() {
         IMountService mountService = IMountService.Stub.asInterface(ServiceManager
                 .getService("mount"));
@@ -108,6 +147,22 @@
         }
     }
 
+    private void stopUsbStorage() {
+        IMountService mountService = IMountService.Stub.asInterface(ServiceManager
+                .getService("mount"));
+        if (mountService == null) {
+            showStoppingError();
+            return;
+        }
+
+        try {
+            mountService.setMassStorageEnabled(false);
+        } catch (RemoteException e) {
+            showStoppingError();
+            return;
+        }
+    }
+
     private void handleBatteryChanged(Intent intent) {
         int pluggedType = intent.getIntExtra("plugged", 0);
         if (pluggedType == 0) {
@@ -120,5 +175,10 @@
         Toast.makeText(this, com.android.internal.R.string.usb_storage_error_message,
                 Toast.LENGTH_LONG).show();
     }
+    
+    private void showStoppingError() {
+        Toast.makeText(this, com.android.internal.R.string.usb_storage_stop_error_message,
+                Toast.LENGTH_LONG).show();
+    }
 
 }
diff --git a/core/java/com/android/internal/app/UsbStorageStopActivity.java b/core/java/com/android/internal/app/UsbStorageStopActivity.java
deleted file mode 100644
index 557a523..0000000
--- a/core/java/com/android/internal/app/UsbStorageStopActivity.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2007 Google Inc.
- *
- * 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.app;
-
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IMountService;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.widget.Toast;
-
-/**
- * This activity is shown to the user for him/her to disable USB mass storage.
- * It uses the alert dialog style. It will be launched from a notification.
- */
-public class UsbStorageStopActivity extends AlertActivity implements DialogInterface.OnClickListener {
-
-    private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
-
-    /** Used to detect when the USB cable is unplugged, so we can call finish() */
-    private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) {
-                handleBatteryChanged(intent);
-            }
-        }
-    };
-    
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // Set up the "dialog"
-        final AlertController.AlertParams p = mAlertParams;
-        p.mIconId = com.android.internal.R.drawable.ic_dialog_alert;
-        p.mTitle = getString(com.android.internal.R.string.usb_storage_stop_title);
-        p.mMessage = getString(com.android.internal.R.string.usb_storage_stop_message);
-        p.mPositiveButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_mount);
-        p.mPositiveButtonListener = this;
-        p.mNegativeButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_unmount);
-        p.mNegativeButtonListener = this;
-        setupAlert();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        
-        unregisterReceiver(mBatteryReceiver);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void onClick(DialogInterface dialog, int which) {
-
-        if (which == POSITIVE_BUTTON) {
-            stopUsbStorage();
-        }
-
-        // No matter what, finish the activity
-        finish();
-    }
-
-    private void stopUsbStorage() {
-        IMountService mountService = IMountService.Stub.asInterface(ServiceManager
-                .getService("mount"));
-        if (mountService == null) {
-            showStoppingError();
-            return;
-        }
-
-        try {
-            mountService.setMassStorageEnabled(false);
-        } catch (RemoteException e) {
-            showStoppingError();
-            return;
-        }
-    }
-
-    private void handleBatteryChanged(Intent intent) {
-        int pluggedType = intent.getIntExtra("plugged", 0);
-        if (pluggedType == 0) {
-            // It was disconnected from the plug, so finish
-            finish();
-        }
-    }
-    
-    private void showStoppingError() {
-        Toast.makeText(this, com.android.internal.R.string.usb_storage_stop_error_message,
-                Toast.LENGTH_LONG).show();
-    }
-
-}
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index a830ebd..9da1066 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -102,7 +102,7 @@
     int finishBackup();
 
     /**
-     * Get the set of backups currently available over this transport.
+     * Get the set of all backups currently available over this transport.
      *
      * @return Descriptions of the set of restore images available for this device,
      *   or null if an error occurred (the attempt should be rescheduled).
@@ -110,11 +110,22 @@
     RestoreSet[] getAvailableRestoreSets();
 
     /**
+     * Get the identifying token of the backup set currently being stored from
+     * this device.  This is used in the case of applications wishing to restore
+     * their last-known-good data.
+     *
+     * @return A token that can be passed to {@link #startRestore}, or 0 if there
+     *   is no backup set available corresponding to the current device state.
+     */
+    long getCurrentRestoreSet();
+
+    /**
      * Start restoring application data from backup.  After calling this function,
      * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
      * to walk through the actual application data.
      *
-     * @param token A backup token as returned by {@link #getAvailableRestoreSets}.
+     * @param token A backup token as returned by {@link #getAvailableRestoreSets}
+     *   or {@link #getCurrentRestoreSet}.
      * @param packages List of applications to restore (if data is available).
      *   Application data will be restored in the order given.
      * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 12bc5a8..23ec6470 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -33,6 +33,9 @@
     private static final String TRANSPORT_DIR_NAME
             = "com.android.internal.backup.LocalTransport";
 
+    // The single hardcoded restore set always has the same (nonzero!) token
+    private static final long RESTORE_TOKEN = 1;
+
     private Context mContext;
     private PackageManager mPackageManager;
     private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
@@ -149,11 +152,16 @@
     // Restore handling
     public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
         // one hardcoded restore set
-        RestoreSet set = new RestoreSet("Local disk image", "flash", 0);
+        RestoreSet set = new RestoreSet("Local disk image", "flash", RESTORE_TOKEN);
         RestoreSet[] array = { set };
         return array;
     }
 
+    public long getCurrentRestoreSet() {
+        // The hardcoded restore set always has the same token
+        return RESTORE_TOKEN;
+    }
+
     public int startRestore(long token, PackageInfo[] packages) {
         if (DEBUG) Log.v(TAG, "start restore " + token);
         mRestorePackages = packages;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index bd41a13..6347146 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -122,7 +122,7 @@
     }
 
     public int getRequestedMinimumPasswordLength() {
-        return mDevicePolicyManager.getMinimumPasswordLength();
+        return mDevicePolicyManager.getPasswordMinimumLength(null);
     }
 
     /**
@@ -132,7 +132,7 @@
      * @return
      */
     public int getRequestedPasswordMode() {
-        int policyMode = mDevicePolicyManager.getPasswordMode();
+        int policyMode = mDevicePolicyManager.getPasswordMode(null);
         switch (policyMode) {
             case DevicePolicyManager.PASSWORD_MODE_ALPHANUMERIC:
                 return MODE_PASSWORD;
diff --git a/core/java/com/google/android/mms/package.html b/core/java/com/google/android/mms/package.html
deleted file mode 100755
index c9f96a6..0000000
--- a/core/java/com/google/android/mms/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/core/java/com/google/android/mms/pdu/package.html b/core/java/com/google/android/mms/pdu/package.html
deleted file mode 100755
index c9f96a6..0000000
--- a/core/java/com/google/android/mms/pdu/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/core/java/com/google/android/mms/util/package.html b/core/java/com/google/android/mms/util/package.html
deleted file mode 100755
index c9f96a6..0000000
--- a/core/java/com/google/android/mms/util/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 54e15a5..665088a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1240,7 +1240,6 @@
             </intent-filter>
         </activity>
         <activity android:name="com.android.internal.app.UsbStorageActivity"
-                android:theme="@style/Theme.Dialog.Alert"
                 android:excludeFromRecents="true">
         </activity>
         <activity android:name="com.android.internal.app.UsbStorageStopActivity"
diff --git a/core/res/res/drawable-hdpi/search_plate_global.9.png b/core/res/res/drawable-hdpi/search_plate_global.9.png
index 32c6dc3..e9f4f12 100644
--- a/core/res/res/drawable-hdpi/search_plate_global.9.png
+++ b/core/res/res/drawable-hdpi/search_plate_global.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/search_source_selector_indicator.png b/core/res/res/drawable-hdpi/search_source_selector_indicator.png
index b93a0c0..e81d9ac 100644
--- a/core/res/res/drawable-hdpi/search_source_selector_indicator.png
+++ b/core/res/res/drawable-hdpi/search_source_selector_indicator.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android.png b/core/res/res/drawable-hdpi/usb_android.png
new file mode 100644
index 0000000..8153ec4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android_connected.png b/core/res/res/drawable-hdpi/usb_android_connected.png
new file mode 100644
index 0000000..6449b7c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/search_plate_global.9.png b/core/res/res/drawable-mdpi/search_plate_global.9.png
index 1cad902..bb7400e 100644
--- a/core/res/res/drawable-mdpi/search_plate_global.9.png
+++ b/core/res/res/drawable-mdpi/search_plate_global.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/search_source_selector_indicator.png b/core/res/res/drawable-mdpi/search_source_selector_indicator.png
index 26bf18a..b3efef2 100644
--- a/core/res/res/drawable-mdpi/search_source_selector_indicator.png
+++ b/core/res/res/drawable-mdpi/search_source_selector_indicator.png
Binary files differ
diff --git a/core/res/res/layout-land/usb_storage_activity.xml b/core/res/res/layout-land/usb_storage_activity.xml
new file mode 100644
index 0000000..d714479
--- /dev/null
+++ b/core/res/res/layout-land/usb_storage_activity.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/main"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:padding="18dip"
+    android:orientation="horizontal"
+    >
+
+    <ImageView android:id="@+id/icon"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentTop="true"
+        android:layout_height="wrap_content"
+        android:layout_width="0dip"
+        android:layout_weight="1"
+        android:src="@drawable/usb_android" />
+
+    <RelativeLayout 
+        android:layout_height="wrap_content"
+        android:layout_width="0dip"
+        android:layout_weight="1"
+        >
+
+        <TextView android:id="@+id/banner"
+            android:layout_centerHorizontal="true"
+            android:layout_below="@id/icon"
+            android:layout_marginTop="10dip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="24sp"
+            android:gravity="center"
+            android:text="@string/usb_storage_title" />
+
+        <TextView android:id="@+id/message"
+            android:layout_below="@id/banner"
+            android:layout_marginTop="10dip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="14sp"
+            android:gravity="center"
+            android:text="@string/usb_storage_message" />
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_below="@id/message"
+            android:layout_marginTop="20dip"
+            >
+
+            <Button android:id="@+id/mount_button" 
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingLeft="18dip"
+                android:paddingRight="18dip"
+                android:text="@string/usb_storage_button_mount"
+                />
+            <Button android:id="@+id/unmount_button"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingLeft="18dip"
+                android:paddingRight="18dip"
+                android:text="@string/usb_storage_stop_button_mount"
+                />
+
+        </RelativeLayout>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml
index 2fa7aaa..244afbe 100644
--- a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml
@@ -23,6 +23,28 @@
     android:background="@android:color/background_dark"
         >
 
+    <!-- header text ('Enter Pin Code') -->
+    <TextView android:id="@+id/headerText"
+        android:layout_above="@+id/carrier"
+        android:layout_centerHorizontal="true"
+        android:layout_marginBottom="30dip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"
+            />
+
+    <!-- Carrier info -->
+    <TextView android:id="@+id/carrier"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_above="@+id/pinDisplayGroup"
+        android:layout_marginTop="9dip"
+        android:gravity="left|bottom"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+    />
+
     <!-- displays dots as user enters pin -->
     <LinearLayout android:id="@+id/pinDisplayGroup"
         android:orientation="horizontal"
@@ -60,16 +82,6 @@
 
     </LinearLayout>
         
-    <!-- header text ('Enter Pin Code') -->
-    <TextView android:id="@+id/headerText"
-        android:layout_above="@id/pinDisplayGroup"
-        android:layout_centerHorizontal="true"
-        android:layout_marginBottom="30dip"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="24sp"
-            />
-
     <LinearLayout
         android:orientation="horizontal"
         android:layout_alignParentBottom="true"
diff --git a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
index 1f7f8f7..009148f 100644
--- a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
@@ -33,9 +33,19 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center"
-            android:lines="2"
+            android:singleLine="true"
             android:textAppearance="?android:attr/textAppearanceLarge"/>
 
+        <!-- Carrier info -->
+        <TextView android:id="@+id/carrier"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="9dip"
+            android:gravity="center"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
         <!-- password entry -->
         <LinearLayout
             android:layout_width="match_parent"
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml
index f9566d8..b404955 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml
@@ -28,7 +28,7 @@
     android:background="#70000000"
     android:gravity="center_horizontal"
     android:id="@+id/root">
-    
+
     <TextView
         android:id="@+id/carrier"
         android:layout_width="wrap_content"
@@ -37,6 +37,9 @@
         android:layout_alignParentRight="true"
         android:layout_marginTop="10dip"
         android:layout_marginRight="8dip"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:gravity="right|bottom"
         android:textAppearance="?android:attr/textAppearanceMedium"
         />
 
@@ -144,7 +147,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
-        android:layout_marginBottom="80dip" 
+        android:layout_marginBottom="80dip"
         />
 
 </RelativeLayout>
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
index 9965554..6ee659c 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
@@ -27,7 +27,7 @@
     android:orientation="horizontal"
     android:background="#70000000"
     android:id="@+id/root">
-     
+
     <!-- left side -->
     <RelativeLayout
             android:layout_width="0dip"
@@ -42,6 +42,9 @@
             android:layout_height="wrap_content"
             android:layout_alignParentTop="true"
             android:layout_marginTop="20dip"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:gravity="right|bottom"
             android:textAppearance="?android:attr/textAppearanceMedium"
             />
 
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index 58f36ed..c1b406f 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -54,6 +54,9 @@
             android:textSize="17sp"
             android:drawablePadding="4dip"
             android:layout_marginTop="32dip"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:gravity="right|bottom"
             />
         <com.android.internal.widget.DigitalClock android:id="@+id/time"
             android:layout_width="wrap_content"
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index ac6cf19..16cd48c 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -41,6 +41,10 @@
             android:layout_marginTop="6dip"
             android:layout_alignParentRight="true"
             android:layout_marginRight="8dip"
+            android:layout_toRightOf="@+id/time"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:gravity="right|bottom"
             android:textAppearance="?android:attr/textAppearanceMedium"
             />
 
@@ -93,7 +97,7 @@
             android:layout_marginLeft="24dip"
             android:textAppearance="?android:attr/textAppearanceMedium"
             />
-    
+
     </RelativeLayout>
 
     <View
diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml
index 12285fd..2e93a39 100644
--- a/core/res/res/layout/search_bar.xml
+++ b/core/res/res/layout/search_bar.xml
@@ -33,10 +33,9 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:paddingLeft="12dip"
-        android:paddingRight="12dip"
-        android:paddingTop="7dip"
-        android:paddingBottom="16dip"
+        android:paddingLeft="4dip"
+        android:paddingRight="4dip"
+        android:paddingTop="4dip"
         android:background="@drawable/search_plate_global" >
 
         <!-- This is actually used for the badge icon *or* the badge label (or neither) -->
@@ -63,10 +62,11 @@
 
             <view class="android.app.SearchDialog$SearchAutoComplete"
                 android:id="@+id/search_src_text"
-                android:background="@drawable/textfield_search"
+                android:background="@drawable/edit_text"
                 android:layout_height="wrap_content"
                 android:layout_width="0dip"
                 android:layout_weight="1.0"
+                android:layout_marginLeft="4dip"
                 android:paddingLeft="8dip"
                 android:paddingRight="6dip"
                 android:drawablePadding="2dip"
@@ -86,13 +86,15 @@
                 android:background="@drawable/btn_search_dialog"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
+                android:visibility="gone"
             />
 
             <ImageButton
                 android:id="@+id/search_voice_btn"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:layout_marginLeft="8dip"
+                android:layout_marginLeft="4dip"
+                android:layout_marginBottom="4dip"
                 android:background="@drawable/btn_search_dialog_voice"
                 android:src="@android:drawable/ic_btn_speak_now"
             />
diff --git a/core/res/res/layout/search_source_selector.xml b/core/res/res/layout/search_source_selector.xml
index c69dfc0..9312b08 100644
--- a/core/res/res/layout/search_source_selector.xml
+++ b/core/res/res/layout/search_source_selector.xml
@@ -16,10 +16,11 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="48dip"
+    android:layout_width="42dip"
     android:layout_height="match_parent"
+    android:layout_marginBottom="2dip"
     android:foreground="@drawable/search_source_selector_indicator"
-    android:foregroundGravity="bottom|right"
+    android:foregroundGravity="bottom|center_horizontal"
     >
 
     <ImageButton
@@ -27,6 +28,7 @@
         android:background="@drawable/search_source_selector_background"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:layout_marginBottom="6dip"
         android:scaleType="centerInside"
         android:focusable="true"
         android:clickable="true"
diff --git a/core/res/res/layout/usb_storage_activity.xml b/core/res/res/layout/usb_storage_activity.xml
new file mode 100644
index 0000000..86bfadb
--- /dev/null
+++ b/core/res/res/layout/usb_storage_activity.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/main"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:padding="18dip"
+    >
+
+    <ImageView android:id="@+id/icon"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentTop="true"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/usb_android" />
+
+    <TextView android:id="@+id/banner"
+        android:layout_centerHorizontal="true"
+        android:layout_below="@id/icon"
+        android:layout_marginTop="10dip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"
+        android:gravity="center"
+        android:text="@string/usb_storage_title" />
+
+    <TextView android:id="@+id/message"
+        android:layout_below="@id/banner"
+        android:layout_marginTop="10dip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="14sp"
+        android:gravity="center"
+        android:text="@string/usb_storage_message" />
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:layout_below="@id/message"
+        android:layout_marginTop="20dip"
+        >
+
+        <Button android:id="@+id/mount_button" 
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="18dip"
+            android:paddingRight="18dip"
+            android:text="@string/usb_storage_button_mount"
+            />
+        <Button android:id="@+id/unmount_button"
+            android:visibility="gone"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="18dip"
+            android:paddingRight="18dip"
+            android:text="@string/usb_storage_stop_button_mount"
+            />
+
+    </RelativeLayout>
+</RelativeLayout>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 42553d4..8f97c13 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2008,21 +2008,23 @@
     <string name="googlewebcontenthelper_loading">Loading\u2026</string>
 
     <!-- USB storage dialog strings -->
-    <!-- This is the label for the activity, and should never be visible to the user. -->
+    <!-- This is the title for the activity's window. -->
+    <string name="usb_storage_activity_title">USB Mass Storage</string>
+
     <!-- See USB_STORAGE.  USB_STORAGE_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to mount.  This is the title. -->
     <string name="usb_storage_title">USB connected</string>
     <!-- See USB_STORAGE.    This is the message. -->
-    <string name="usb_storage_message">You have connected your phone to your computer via USB. Select \"Mount\" if you want to copy files between your computer and your phone\'s SD card.</string>
+    <string name="usb_storage_message">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
     <!-- See USB_STORAGE.    This is the button text to mount the phone on the computer. -->
-    <string name="usb_storage_button_mount">Mount</string>
-    <!-- See USB_STORAGE.   This is the button text to ignore the plugging in of the phone.. -->
-    <string name="usb_storage_button_unmount">Don\'t mount</string>
+    <string name="usb_storage_button_mount">Turn on USB storage</string>
     <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. -->
     <string name="usb_storage_error_message">There is a problem using your SD card for USB storage.</string>
     <!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across.  This is the title -->
     <string name="usb_storage_notification_title">USB connected</string>
     <!-- See USB_STORAGE. This is the message. -->
     <string name="usb_storage_notification_message">Select to copy files to/from your computer.</string>
+
+
     <!-- USB_STORAGE_STOP: While USB storage is enabled, we show a notification dialog asking if he wants to stop. This is the title -->
     <string name="usb_storage_stop_notification_title">Turn off USB storage</string>
     <!-- See USB_STORAGE. This is the message. -->
@@ -2031,15 +2033,13 @@
     <!-- USB storage stop dialog strings -->
     <!-- This is the label for the activity, and should never be visible to the user. -->
     <!-- See USB_STORAGE_STOP.  USB_STORAGE_STOP_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to stop usb storage.  This is the title. -->
-    <string name="usb_storage_stop_title">Turn off USB storage</string>
+    <string name="usb_storage_stop_title">USB storage in use</string>
     <!-- See USB_STORAGE_STOP.    This is the message. -->
-    <string name="usb_storage_stop_message">Before turning off USB storage, make sure you have unmounted on the USB host. Select \"Turn Off\" to turn off USB storage.</string>
+    <string name="usb_storage_stop_message">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s SD card from your computer.</string>
     <!-- See USB_STORAGE_STOP.    This is the button text to stop usb storage. -->
-    <string name="usb_storage_stop_button_mount">Turn Off</string>
-    <!-- See USB_STORAGE_STOP.   This is the button text to cancel stoping usb storage. -->
-    <string name="usb_storage_stop_button_unmount">Cancel</string>
+    <string name="usb_storage_stop_button_mount">Turn off USB storage</string>
     <!-- See USB_STORAGE_STOP_DIALOG.  If there was an error stopping, this is the text. -->
-    <string name="usb_storage_stop_error_message">We\'ve encountered a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.</string>
+    <string name="usb_storage_stop_error_message">There was a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.</string>
 
     <!-- External media format dialog strings -->
     <!-- This is the label for the activity, and should never be visible to the user. -->
diff --git a/core/tests/coretests/src/android/net/http/SslCertificateTest.java b/core/tests/coretests/src/android/net/http/SslCertificateTest.java
new file mode 100644
index 0000000..147816b
--- /dev/null
+++ b/core/tests/coretests/src/android/net/http/SslCertificateTest.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package android.net.http;
+
+import android.net.http.SslCertificate;
+import android.test.suitebuilder.annotation.LargeTest;
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import junit.framework.TestCase;
+
+public class SslCertificateTest extends TestCase {
+
+    /**
+     * Problematic certificate from Issue 1597
+     * http://code.google.com/p/android/issues/detail?id=1597
+     */
+    private static final String Issue1597Certificate =
+        "-----BEGIN CERTIFICATE-----\n"+
+        "MIIBnjCCAQegAwIBAgIFAKvN774wDQYJKoZIhvcNAQEFBQAwADAeFw0yNzA5MjQw\n"+
+        "MDAwMDFaFw0zNzA5MjQwMDAwMDFaMAAwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ\n"+
+        "AoGBAMNAaUSKw3stg6UHx6bWHNn0T5WR39UB43EZqdhhM0hnfpzwAzNs1T3jOAzF\n"+
+        "OtgcX/XVt2Exc1vnwwuiJfvtPtBtQVsNu7wfk45cTUF45axBr4v8oFq7DOHCvs2C\n"+
+        "pBDnw/v9PoOihuBamOjzRPL+oVhVfzEqEOILnZD1qEeVJn4RAgMBAAGjJDAiMCAG\n"+
+        "A1UdEQQZMBeGD2h0dHBzOi8vMS4xLjEuMYcEAQEBATANBgkqhkiG9w0BAQUFAAOB\n"+
+        "gQA7CMJylEjCR9CjztZUMLOutLe64RNhMq9iKgbDfJwYrcgvUNOxjrCdFW66lE9N\n"+
+        "TDscc4zS2kpV41vcVYiGwabCNUPi2P6zfFSpYmGqwwu1NoEayqGPdDMrgCnMXVYV\n"+
+        "X7HoVif4IdGvjFQrYcyU2VWSWBq6IGMVCR6RkC2YWnnNhw==\n"+
+        "-----END CERTIFICATE-----\n";
+
+    @LargeTest
+    public void testSslCertificateWithEmptyIssuer() throws Exception {
+        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+        X509Certificate x509Certificate = (X509Certificate)
+            certificateFactory.generateCertificate(new ByteArrayInputStream(Issue1597Certificate.getBytes()));
+        assertEquals(x509Certificate.getIssuerDN().getName(), "");
+        SslCertificate sslCertificate = new SslCertificate(x509Certificate);
+        assertEquals(sslCertificate.getIssuedBy().getDName(), "");
+    }
+}
diff --git a/docs/html/guide/developing/debug-tasks.jd b/docs/html/guide/developing/debug-tasks.jd
index a980efc..500ef58 100644
--- a/docs/html/guide/developing/debug-tasks.jd
+++ b/docs/html/guide/developing/debug-tasks.jd
@@ -58,8 +58,8 @@
 <pre class="no-pretty-print">
 I/MyActivity( 1557): MyClass.getView() &mdash; get item number 1
 </pre>
-      <p>Logcat is also the place to look when debugging a web page in the Android browser. All
-browser bugs will be output to logcat with the {@code WebCore} tag.
+      <p>Logcat is also the place to look when debugging a web page in the Android Browser app. See
+<a href="#DebuggingWebPages">Debugging Web Pages</a> below.</p>
 </dl>
 
 <p>For more information about all the development tools provided with the Android SDK, see the <a
@@ -148,10 +148,10 @@
 
 <h2 id="DebuggingWebPages">Debugging Web Pages</h2>
 
-<p>If you're developing a web application for Android devices, you can debug your JavaScript on
-Android using the Console APIs, which will output messages to logcat. If you're familiar
+<p>If you're developing a web application for Android devices, you can debug your JavaScript in the
+Android Browser using the Console APIs, which will output messages to logcat. If you're familiar
 debugging web pages with Firefox's FireBug or WebKit's Web Inspector, then you're probably familiar
-with the Console APIs. The Android Browser (and {@link android.webkit.WebChromeClient}) supports
+with the Console APIs. The Android Browser (and the {@link android.webkit.WebChromeClient}) supports
 most of the same APIs.</p>
 
 <p>When you call a function from the Console APIs (in the DOM's {@code window.console} object),
@@ -162,19 +162,28 @@
 </pre>
 <p>Then the logcat output from the Android Browser will look like this:</p>
 <pre class="no-pretty-print">
-W/browser ( 202): Console: Hello World :0
+W/browser ( 202): Console: Hello World http://www.example.com/hello.html :82
 </pre>
 
-<p class="note"><strong>Note:</strong> All Console messages from the Android
-Browser are tagged with the name "browser" on Android platforms running API Level 7 or higher and
-tagged with the name "WebCore" for platforms running API Level 6 or lower.</p>
+<p>All Console messages from the Android Browser are tagged with the name "browser" on Android
+platforms running API Level 7 or higher. On platforms running API Level 6 or lower, Browser
+messages are tagged with the name "WebCore". The Android Browser also formats console messages
+with the log message
+preceded by "Console:" and then followed by the address and line number where the
+message occurred. (The format for the address and line number will appear different from the example
+above on platforms running API Level 6 or lower.)</p>
 
-<p>Not all of the Console APIs available in Firefox or other WebKit browsers are implemented
-on Android. Mostly, you need to depend on basic text logging provided by
-functions like {@code console.log(String)}, {@code console.info(String)}, {@code
-console.warn(String)}, and {@code console.error(String)}. Although other Console functions may not
-be implemented, they will not raise run-time errors, but will simply not behave as you might
-expect.</p>
+<p>The Android Browser (and {@link android.webkit.WebChromeClient}) does not implement all of the
+Console APIs provided by Firefox or other WebKit-based browsers. Primarily, you need to depend
+on the basic text logging functions:</p>
+<ul>
+  <li>{@code console.log(String)}</li>
+  <li>{@code console.info(String)}</li>
+  <li>{@code console.warn(String)}</li>
+  <li>{@code console.error(String)}</li>
+</ul>
+<p>Although the Android Browser may not fully implement other Console functions, they will not raise
+run-time errors, but may not behave the same as they do on other desktop browsers.</p>
 
 <p>If you've implemented a custom {@link android.webkit.WebView} in your application, then in order
 to receive messages that are sent through the Console APIs, you must provide a {@link
@@ -185,7 +194,7 @@
 <pre>
 myWebView.setWebChromeClient(new WebChromeClient() {
   public void onConsoleMessage(String message, int lineNumber, String sourceID) {
-    Log.d("MyApplication", message);
+    Log.d("MyApplication", message + " -- From line " + lineNumber + " of " + sourceID);
   }
 });
 </pre>
@@ -195,13 +204,14 @@
 <p>When the "Hello World" log is executed through your {@link android.webkit.WebView}, it will
 now look like this:</p>
 <pre class="no-pretty-print">
-D/MyApplication ( 430): Hello World
+D/MyApplication ( 430): Hello World -- From line 82 of http://www.example.com/hello.html
 </pre>
 
 <p class="note"><strong>Note:</strong> The {@link
 android.webkit.WebChromeClient#onConsoleMessage(String,int,String) onConsoleMessage()} callback
-method was added with API Level 7. If you are targetting platforms running API Level 6 or lower,
-then your Console messages will automatically be sent to logcat with the "WebCore" logging tag.</p>
+method was added with API Level 7. If you are using a custom {@link
+android.webkit.WebView} on a platform running API Level 6 or lower, then your Console messages will
+automatically be sent to logcat with the "WebCore" logging tag.</p>
 
 
 
diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd
index 643d307..0fad4c6 100644
--- a/docs/html/guide/practices/screens_support.jd
+++ b/docs/html/guide/practices/screens_support.jd
@@ -260,7 +260,7 @@
 same physical size on all screens in a given configuration. </p> -->
 
 <p>Although the platform currently supports the nine possible size-density
-configurations listed in the table, you do not necessarily need to custom
+configurations listed in the table, you do not necessarily need to create custom
 resources for each one of them. The platform provides robust compatibility
 features, described in the sections below, that can handle most of the work of
 rendering your application on the current device screen, provided that the UI is
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index d7fc563..8594452 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -14,23 +14,44 @@
 <dd><code><a
 href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code></dd>
 
+ <div class="sidebox-wrapper"> 
+  <img id="rule" src="{@docRoot}assets/images/grad-rule-qv.png"> 
+  <div id="qv-sub-rule"> 
+    <img src="{@docRoot}assets/images/icon_market.jpg" style="float:left;margin:0;padding:0;"> 
+    <p style="color:#669999;">Android Market and &lt;uses-feature&gt; elements</p> 
+    <p>Android Market filters the applications that are visible to users, so
+that users can see and download only those applications that are compatible with their
+devices. One of the ways Market filters applications is by feature compatibility.</p>
+
+<p style="margin-top:1em;">To do this, Market checks the
+<code>&lt;uses-feature&gt;</code> elements in each application's manifest, to
+establish the app's feature needs. Market then shows or hides the application to
+each user, based on a comparison with the features available on the user's
+device. </p>
+
+<p style="margin-top:1em;">By specifying the features your application requires,
+you enable Android Market to present your application only to users whose
+devices meet the application's feature requirements, rather than presenting it
+to all users. </p>
+</div>
+</div>
+
 <dt>description:</dt>
-<dd>This element declares a specific feature used by the application.
-Android provides some features that may not be equally supported by all
-Android devices. In a manner similar to the <code><a
-href="uses-sdk-element.html">&lt;uses-sdk></a></code>
-element, this element allows an application to specify which device-variable
-features it uses. In this way, the application
-will not be installed on devices that do not offer the feature.</p>
+<dd>This element declares a specific feature used by the application. Android
+provides some features that may not be equally supported by all Android devices.
+In a manner similar to the <a
+href="uses-sdk-element.html">{@code &lt;uses-sdk>}</a> element, this element
+allows an application to specify which device-variable features it uses. For
+example, an application might specify that it requires a camera with auto-focus
+capabilities.</p>
 
-<p>For example, an application might specify that it requires a camera with auto-focus capabilities.
-If a device does not provide a camera with auto-focus, then it will not allow
-installation of the application.</p>
-
-<p>In order to maintain strict device compatibility, it's very important that you use
-this element to declare all applicable features (listed below) that your application uses. Failure
-to declare a feature may result in your application being installed on a device
-that does not support the feature and your application failing.</p>
+<p>Declaring a {@code &lt;uses-feature>} element is informational only, meaning
+that the Android system itself does not check for matching feature support on
+the device before installing an application. However, note that other services
+(such as Android Market) or applications may check your application's 
+{@code &lt;uses-feature>} declarations as part of handling or interacting 
+with your application. For this reason, it's very important that you declare all of
+the features (from the list below) that your application uses. </p>
 
 <p>For some features, there may exist a specfic attribute that allows you to define
 a version of the feature, such as the version of Open GL used (declared with
@@ -38,11 +59,12 @@
 exist for a device, such as a camera, are declared using the
 <a href="#name">{@code name}</a> attribute.</p>
 
-<p>Any software or hardware features that may vary among Android-powered
-devices will be listed on this page among the attributes below. If you see any features
-here that you use in your application, you should include a {@code
-&lt;uses-feature>} element for each one. For example, if your application uses the device
-camera, then you should include the following in your {@code AndroidManifest.xml}:</p>
+<p>Any software or hardware features that may vary among Android-powered devices
+will be listed on this page among the attributes below. If you see any features
+here that you use in your application, you should include a
+{@code &lt;uses-feature>} element for each one. For example, if your
+application uses the device camera, then you should include the following in
+your {@code AndroidManifest.xml}:</p>
 
 <pre>
 &lt;uses-feature android:name="android.hardware.camera" />
@@ -77,10 +99,29 @@
 <dd>
 <dl class="attr">
   <dt><a name="glEsVersion"></a>{@code android:glEsVersion}</dt>
-  <dd>The GLES version needed by the application.
-     The higher 16 bits represent the major number and the lower 16 bits
-     represent the minor number. For example, for GL 1.2,
-     the value should be set as {@code 0x00010002}.
+  <dd>The OpenGL ES version required by the application. The higher 16 bits
+represent the major number and the lower 16 bits represent the minor number. For
+example, to specify OpenGL ES version 2.0, you would set the value as
+"0x00020000". To specify OpenGL ES 2.1, if/when such a version were made
+available, you would set the value as "0x00020001".
+
+  <p>An application should specify at most one <code>android:glEsVersion</code>
+attribute in its manifest. If it specifies more than one, the
+<code>android:glEsVersion</code> with the numerically highest value is used and
+any other values are ignored.</p>
+
+  <p>If an application does not specify an <code>android:glEsVersion</code>
+attribute, then it is assumed that the application requires only OpenGL ES 1.0,
+which is supported by all Android-powered devices.</p>
+
+  <p>An application can assume that if a platform supports a given OpenGL ES
+version, it also supports all numerically lower OpenGL ES versions. Therefore,
+an application that requires both OpenGL ES 1.0 and OpenGL ES 2.0 must specify
+that it requires OpenGL ES 2.0.</p>
+
+  <p>An application that can work with any of several OpenGL ES versions should
+only specify the numerically lowest version of OpenGL ES that it requires. (It
+can check at run-time whether a higher level of OpenGL ES is available.)</p>
   </dd>
 
   <dt><a name="name"></a>{@code android:name}</dt>
@@ -90,7 +131,7 @@
   <table>
     <tr> 
        <th>Feature</th>
-       <th>Value</th> 
+       <th>Attribute Value</th> 
        <th>Description</th> 
     </tr><tr>
        <td rowspan="2">Camera</td>
@@ -130,6 +171,11 @@
       <td>The application requires a device with a light sensor.
       </td>
     </tr><tr>
+      <td>Live Wallpaper</td>
+      <td>{@code android.software.live_wallpaper}</td>
+      <td>The application uses or provides Live Wallpapers and should be installed only on devices that support Live Wallpapers.
+      </td>
+    </tr><tr>
       <td>Proximity sensor</td>
       <td>{@code android.hardware.sensor.proximity}</td>
       <td>The application requires a device with a proximity sensor.
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 7c28583..e3bf685 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -11,14 +11,12 @@
                             </div><!-- end homeTitle -->
                             <div id="announcement-block">
                             <!-- total max width is 520px -->
-                                <img src="{@docRoot}assets/images/home/android_adc.png" alt="Android Developer Challenge 2" width="232px" />
-                                <div id="announcement" style="width:275px">
-                                  <p>The second Android Developer Challenge has begun! In this contest,
-                                  real-world users will help review and score applications and the overall winner will 
-                                  take away $250,000. The deadline for submissions was August 31, 2009.</p>
-                                  <p><a href="http://code.google.com/android/adc/">Learn more about ADC 2 &raquo;</a></p>
+                                  <img src="/assets/images/home/Android_Dev_Lab_l.png" alt="Android developer labs" width="100px" style="padding-left:78px;padding-right:46px;padding-bottom: 8px;"/>
+                                  <div id="announcement" style="width:275px">
+<p>We're hosting the next Android Developer Lab in Barcelona at <a href="http://www.mobileworldcongress.com/index.htm">Mobile World Congress&nbsp;&raquo;</a> on Wednesday February 17th at <a href="http://www.mobileworldcongress.com/exhibition/app_planet.htm">App Planet&nbsp;&raquo;</a>, located in Hall 7.  Come visit us to attend a technical presentation, talk to our Android developer relations team, and meet other members of the developer community.</p><p><a href="http://sites.google.com/site/androidmwc/home">Learn more &raquo;</a></p>
+<!--<p><a href="http://android-developers.blogspot.com/2009/11/bring-your-lab-coats.html">Learn more &raquo;</a></p>-->
                                 </div> <!-- end annoucement -->
-                            </div> <!-- end annoucement-block -->  
+                            </div> <!-- end annoucement-block -->
                         </div><!-- end topAnnouncement -->
                         <div id="carouselMain" style="height:200px"> <!-- this height can be
                                                             adjusted based on the content height -->
@@ -140,6 +138,20 @@
                + "component</a>."
     },
 
+    'adc2': {
+      'layout':"imgLeft",
+      'icon':"adc2_s.png",
+      'name':"ADC 2",
+      'img':"adc2_l.png",
+      'title':"Android Developer Challenge 2",
+      'desc': "<p>We're pleased to announce the "
+               + "<a href='http://code.google.com/android/adc/gallery_winners.html'>winners "
+               + "of the Android Developer Challenge 2</a>! Please see our "
+               + "<a href='http://android-developers.blogspot.com/2009/11/announcing-winners-of-adc-2.html'>blog post</a> "
+               + "for full information. Thanks to everyone who participated by submitting an application or judging!</p>"
+               + "<p><a href='http://code.google.com/android/adc/'>Learn more about ADC 2 &raquo;</a></p>"
+    },
+
     'devphone': {
       'layout':"imgLeft",
       'icon':"devphone-small.png",
diff --git a/docs/html/mwc2010/index.html b/docs/html/mwc2010/index.html
new file mode 100644
index 0000000..c91386f
--- /dev/null
+++ b/docs/html/mwc2010/index.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Redirecting...</title>
+<meta http-equiv="refresh" content="0;url=http://sites.google.com/site/androidmwc/home">
+</head>
+<body>
+</body>
+</html>
diff --git a/docs/html/sdk/adding-components.jd b/docs/html/sdk/adding-components.jd
index bc82170..d7c886e 100644
--- a/docs/html/sdk/adding-components.jd
+++ b/docs/html/sdk/adding-components.jd
@@ -122,6 +122,21 @@
 <p>Any SDK components available from the site will now be listed under
 <strong>Available Components</strong>.</p>
 
+<h3 id="troubleshooting">Troubleshooting</h3>
+
+<p><strong>Problems connecting to the SDK repository</strong></p>
+
+<p>If you are using the SDK and AVD Manager to download components and are encountering
+connection problems, try connecting over http, rather than https. To switch the
+protocol used by the SDK and AVD Manager, follow these steps: </p>
+
+<ol>
+  <li>With the Android SDK and AVD Manager window open, select "Settings" in the
+  left pane. </li>
+  <li>On the right, in the "Misc" section, check the checkbox labeled "Force
+  https://... sources to be fetched using http://..." </li>
+  <li>Click <strong>Save &amp; Apply</strong>.</li>
+</ol>
 
 <h2 id="dependencies">SDK Component Dependencies</h2>
 
@@ -138,7 +153,3 @@
 
 <p>Additionally, the development tools will notify you with debug warnings
 if there is dependency that you need to address. </p>
-
-
-
-
diff --git a/docs/html/sdk/android-2.1.jd b/docs/html/sdk/android-2.1.jd
index 7f9141e..b546f0f 100644
--- a/docs/html/sdk/android-2.1.jd
+++ b/docs/html/sdk/android-2.1.jd
@@ -76,22 +76,21 @@
 	<li>Alarm Clock</li>
 	<li>Browser</li>
 	<li>Calculator</li>
-	<li>Camcorder</li>
 	<li>Camera</li>
 	<li>Contacts</li>
 	<li>Custom Locale (developer app)</li>
 	<li>Dev Tools (developer app)</li>
-	<li>Dialer</li>
+    <li>Email</li>
 	</ul>
 </td>
 <td style="border:0;padding-bottom:0;margin-bottom:0;padding-left:5em;">
 	<ul>
-	<li>Email</li>
+
 	<li>Gallery</li>
-	<li>Gestures Builder</li>
-	<li>IME for Japanese text input</li>
+	<li>IMEs for Japanese, Chinese, and Latin text input</li>
 	<li>Messaging</li>
 	<li>Music</li>
+	<li>Phone</li>
 	<li>Settings</li>
 	<li>Spare Parts (developer app)</li>
 	</ul>
@@ -99,6 +98,7 @@
 </tr>
 </table>
 
+
 <h2 id="locs" style="margin-top:.75em;">Locales</h2>
 
 <p>The system image included in the downloadable platform provides a variety of
@@ -218,6 +218,21 @@
 <li>Updated {@link android.app.WallpaperManager}.</li>
 </ul>
 
+<p>Additionally, if your application uses or provides Live Wallpapers, you must
+remember to add a <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature></code></a>
+ element to the application's manifest, declaring the attribute
+<code>android:name="android.software.live_wallpaper"</code>. For example:</p>
+
+<pre class="no-pretty-print">
+&lt;uses-feature android:name="android.software.live_wallpaper" />
+</pre>
+
+<p>When you've published your application, Android Market checks for the
+presence of this element and uses it as a filter, ensuring that your application
+is not made available to users whose devices do not support Live Wallpapers.
+</p>
+
 <h4>Telephony</h4>
 
 <ul>
diff --git a/docs/html/search.jd b/docs/html/search.jd
index d0e7478..609ade9 100644
--- a/docs/html/search.jd
+++ b/docs/html/search.jd
@@ -2,7 +2,7 @@
 @jd:body

 

 <script src="http://www.google.com/jsapi" type="text/javascript"></script>
-<script src="/assets/jquery-history.js" type="text/javascript"></script>

+<script src="{@docRoot}assets/jquery-history.js" type="text/javascript"></script>

 <script type="text/javascript">      
       var tabIndex = 0;
             
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
index 4a3bd47..09b4bf4 100644
--- a/graphics/java/android/graphics/YuvImage.java
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -19,8 +19,6 @@
 import java.io.OutputStream;
 
 /**
- * @hide pending API council approval
- *
  * YuvImage contains YUV data and provides a method that compresses a region of
  * the YUV data to a Jpeg. The YUV data should be provided as a single byte
  * array irrespective of the number of image planes in it. The stride of each
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index d5c1594..31c0991 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -18,7 +18,9 @@
 #define ANDROID_IMEDIAPLAYERSERVICE_H
 
 #include <utils/Errors.h>  // for status_t
+#include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
+#include <utils/String8.h>
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
 
@@ -38,7 +40,7 @@
 
     virtual sp<IMediaRecorder>  createMediaRecorder(pid_t pid) = 0;
     virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid) = 0;
-    virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;
+    virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url, const KeyedVector<String8, String8> *headers = NULL) = 0;
     virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
     virtual sp<IMemory>         decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 588c51a..a5a1bb8 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -21,8 +21,10 @@
 
 #include <sys/types.h>
 #include <ui/ISurface.h>
-#include <utils/RefBase.h>
 #include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/RefBase.h>
 
 #include <media/mediaplayer.h>
 #include <media/AudioSystem.h>
@@ -96,7 +98,11 @@
     virtual             ~MediaPlayerBase() {}
     virtual status_t    initCheck() = 0;
     virtual bool        hardwareOutput() = 0;
-    virtual status_t    setDataSource(const char *url) = 0;
+
+    virtual status_t    setDataSource(
+            const char *url,
+            const KeyedVector<String8, String8> *headers = NULL) = 0;
+
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) = 0;
     virtual status_t    setVideoSurface(const sp<ISurface>& surface) = 0;
     virtual status_t    prepare() = 0;
diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h
index 8a66152e..df50981 100644
--- a/include/media/PVPlayer.h
+++ b/include/media/PVPlayer.h
@@ -38,7 +38,10 @@
     virtual             ~PVPlayer();
 
     virtual status_t    initCheck();
-    virtual status_t    setDataSource(const char *url);
+
+    virtual status_t    setDataSource(
+            const char *url, const KeyedVector<String8, String8> *headers);
+
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
     virtual status_t    setVideoSurface(const sp<ISurface>& surface);
     virtual status_t    prepare();
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 87d23f6..c9198d6 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -23,6 +23,9 @@
 #include <media/IMediaPlayer.h>
 #include <media/IMediaDeathNotifier.h>
 
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
 namespace android {
 
 enum media_event_type {
@@ -130,7 +133,11 @@
     ~MediaPlayer();
             void            died();
             void            disconnect();
-            status_t        setDataSource(const char *url);
+
+            status_t        setDataSource(
+                    const char *url,
+                    const KeyedVector<String8, String8> *headers);
+
             status_t        setDataSource(int fd, int64_t offset, int64_t length);
             status_t        setVideoSurface(const sp<Surface>& surface);
             status_t        setListener(const sp<MediaPlayerListener>& listener);
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 71344e6..26fcc95 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -47,7 +47,7 @@
     // Return time in us.
     virtual int64_t getRealTimeUs();
 
-    void start();
+    status_t start();
 
     void pause();
     void resume();
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 0c0ace0..913da47 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <utils/Errors.h>
+#include <utils/KeyedVector.h>
 #include <utils/List.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
@@ -35,7 +36,9 @@
         kWantsPrefetching = 1,
     };
 
-    static sp<DataSource> CreateFromURI(const char *uri);
+    static sp<DataSource> CreateFromURI(
+            const char *uri,
+            const KeyedVector<String8, String8> *headers = NULL);
 
     DataSource() {}
 
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
index 42444dc..895cda3 100644
--- a/include/media/stagefright/HTTPDataSource.h
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -19,6 +19,7 @@
 #define HTTP_DATASOURCE_H_
 
 #include <media/stagefright/DataSource.h>
+#include <utils/String8.h>
 
 namespace android {
 
@@ -26,8 +27,13 @@
 
 class HTTPDataSource : public DataSource {
 public:
-    HTTPDataSource(const char *host, int port, const char *path);
-    HTTPDataSource(const char *uri);
+    HTTPDataSource(
+            const char *host, int port, const char *path,
+            const KeyedVector<String8, String8> *headers = NULL);
+
+    HTTPDataSource(
+            const char *uri,
+            const KeyedVector<String8, String8> *headers = NULL);
 
     virtual status_t initCheck() const;
 
@@ -45,6 +51,8 @@
         kBufferSize = 64 * 1024
     };
 
+    String8 mHeaders;
+
     HTTPStream *mHttp;
     char *mHost;
     int mPort;
@@ -58,6 +66,7 @@
     status_t mInitCheck;
 
     ssize_t sendRangeRequest(size_t offset);
+    void initHeaders(const KeyedVector<String8, String8> *overrides);
 
     HTTPDataSource(const HTTPDataSource &);
     HTTPDataSource &operator=(const HTTPDataSource &);
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index 4bc996e..0ed7b40 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -31,9 +31,6 @@
     static sp<MediaExtractor> Create(
             const sp<DataSource> &source, const char *mime = NULL);
 
-    static sp<MediaExtractor> CreateFromURI(
-            const char *uri, const char *mime = NULL);
-
     virtual size_t countTracks() = 0;
     virtual sp<MediaSource> getTrack(size_t index) = 0;
 
diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp
index 055dbca..096aa73 100644
--- a/libs/audioflinger/AudioPolicyManagerBase.cpp
+++ b/libs/audioflinger/AudioPolicyManagerBase.cpp
@@ -15,8 +15,7 @@
  */
 
 #define LOG_TAG "AudioPolicyManagerBase"
-//
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #include <utils/Log.h>
 #include <hardware_legacy/AudioPolicyManagerBase.h>
 #include <media/mediarecorder.h>
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 07542ed7..89ee7d3 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -34,6 +34,7 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.util.Map;
 import java.util.Set;
 import java.lang.ref.WeakReference;
 
@@ -665,6 +666,20 @@
      */
     public void setDataSource(Context context, Uri uri)
         throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+        setDataSource(context, uri, null);
+    }
+
+    /**
+     * Sets the data source as a content Uri.
+     *
+     * @param context the Context to use when resolving the Uri
+     * @param uri the Content URI of the data you want to play
+     * @param headers the headers to be sent together with the request for the data
+     * @throws IllegalStateException if it is called in an invalid state
+     * @hide pending API council
+     */
+    public void setDataSource(Context context, Uri uri, Map<String, String> headers)
+        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
 
         String scheme = uri.getScheme();
         if(scheme == null || scheme.equals("file")) {
@@ -696,7 +711,7 @@
             }
         }
         Log.d(TAG, "Couldn't open file on client side, trying server side");
-        setDataSource(uri.toString());
+        setDataSource(uri.toString(), headers);
         return;
     }
 
@@ -709,6 +724,17 @@
     public native void setDataSource(String path) throws IOException, IllegalArgumentException, IllegalStateException;
 
     /**
+     * Sets the data source (file-path or http/rtsp URL) to use.
+     *
+     * @param path the path of the file, or the http/rtsp URL of the stream you want to play
+     * @param headers the headers associated with the http request for the stream you want to play
+     * @throws IllegalStateException if it is called in an invalid state
+     * @hide pending API council
+     */
+    public native void setDataSource(String path,  Map<String, String> headers)
+            throws IOException, IllegalArgumentException, IllegalStateException;
+
+    /**
      * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
      * to close the file descriptor. It is safe to do so as soon as this call returns.
      *
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 703afd2..27f5668 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -31,6 +31,8 @@
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "utils/Errors.h"  // for status_t
+#include "utils/KeyedVector.h"
+#include "utils/String8.h"
 #include "android_util_Binder.h"
 #include <binder/Parcel.h>
 
@@ -156,8 +158,8 @@
 }
 
 static void
-android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)
-{
+android_media_MediaPlayer_setDataSourceAndHeaders(
+        JNIEnv *env, jobject thiz, jstring path, jobject headers) {
     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
@@ -174,12 +176,96 @@
         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
         return;
     }
+
+    // headers is a Map<String, String>.
+    // We build a similar KeyedVector out of it.
+    KeyedVector<String8, String8> headersVector;
+    if (headers) {
+        // Get the Map's entry Set.
+        jclass mapClass = env->FindClass("java/util/Map");
+
+        jmethodID entrySet =
+            env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
+
+        jobject set = env->CallObjectMethod(headers, entrySet);
+        // Obtain an iterator over the Set
+        jclass setClass = env->FindClass("java/util/Set");
+
+        jmethodID iterator =
+            env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
+
+        jobject iter = env->CallObjectMethod(set, iterator);
+        // Get the Iterator method IDs
+        jclass iteratorClass = env->FindClass("java/util/Iterator");
+        jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
+
+        jmethodID next =
+            env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
+
+        // Get the Entry class method IDs
+        jclass entryClass = env->FindClass("java/util/Map$Entry");
+
+        jmethodID getKey =
+            env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
+
+        jmethodID getValue =
+            env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
+
+        // Iterate over the entry Set
+        while (env->CallBooleanMethod(iter, hasNext)) {
+            jobject entry = env->CallObjectMethod(iter, next);
+            jstring key = (jstring) env->CallObjectMethod(entry, getKey);
+            jstring value = (jstring) env->CallObjectMethod(entry, getValue);
+
+            const char* keyStr = env->GetStringUTFChars(key, NULL);
+            if (!keyStr) {  // Out of memory
+                jniThrowException(
+                        env, "java/lang/RuntimeException", "Out of memory");
+                return;
+            }
+
+            const char* valueStr = env->GetStringUTFChars(value, NULL);
+            if (!valueStr) {  // Out of memory
+                jniThrowException(
+                        env, "java/lang/RuntimeException", "Out of memory");
+                return;
+            }
+
+            headersVector.add(String8(keyStr), String8(valueStr));
+
+            env->DeleteLocalRef(entry);
+            env->ReleaseStringUTFChars(key, keyStr);
+            env->DeleteLocalRef(key);
+            env->ReleaseStringUTFChars(value, valueStr);
+            env->DeleteLocalRef(value);
+      }
+
+      env->DeleteLocalRef(entryClass);
+      env->DeleteLocalRef(iteratorClass);
+      env->DeleteLocalRef(iter);
+      env->DeleteLocalRef(setClass);
+      env->DeleteLocalRef(set);
+      env->DeleteLocalRef(mapClass);
+    }
+
     LOGV("setDataSource: path %s", pathStr);
-    status_t opStatus = mp->setDataSource(pathStr);
+    status_t opStatus =
+        mp->setDataSource(
+                String8(pathStr),
+                headers ? &headersVector : NULL);
 
     // Make sure that local ref is released before a potential exception
     env->ReleaseStringUTFChars(path, pathStr);
-    process_media_player_call( env, thiz, opStatus, "java/io/IOException", "setDataSource failed." );
+
+    process_media_player_call(
+            env, thiz, opStatus, "java/io/IOException",
+            "setDataSource failed." );
+}
+
+static void
+android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)
+{
+    android_media_MediaPlayer_setDataSourceAndHeaders(env, thiz, path, 0);
 }
 
 static void
@@ -610,6 +696,7 @@
 
 static JNINativeMethod gMethods[] = {
     {"setDataSource",       "(Ljava/lang/String;)V",            (void *)android_media_MediaPlayer_setDataSource},
+    {"setDataSource",       "(Ljava/lang/String;Ljava/util/Map;)V",(void *)android_media_MediaPlayer_setDataSourceAndHeaders},
     {"setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
     {"_setVideoSurface",    "()V",                              (void *)android_media_MediaPlayer_setVideoSurface},
     {"prepare",             "()V",                              (void *)android_media_MediaPlayer_prepare},
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index cca3e9b..71c5f86 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -56,13 +56,26 @@
         return interface_cast<IMediaMetadataRetriever>(reply.readStrongBinder());
     }
 
-    virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url)
-    {
+    virtual sp<IMediaPlayer> create(
+            pid_t pid, const sp<IMediaPlayerClient>& client,
+            const char* url, const KeyedVector<String8, String8> *headers) {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
         data.writeInt32(pid);
         data.writeStrongBinder(client->asBinder());
         data.writeCString(url);
+
+        if (headers == NULL) {
+            data.writeInt32(0);
+        } else {
+            // serialize the headers
+            data.writeInt32(headers->size());
+            for (size_t i = 0; i < headers->size(); ++i) {
+                data.writeString8(headers->keyAt(i));
+                data.writeString8(headers->valueAt(i));
+            }
+        }
+
         remote()->transact(CREATE_URL, data, &reply);
         return interface_cast<IMediaPlayer>(reply.readStrongBinder());
     }
@@ -142,9 +155,21 @@
         case CREATE_URL: {
             CHECK_INTERFACE(IMediaPlayerService, data, reply);
             pid_t pid = data.readInt32();
-            sp<IMediaPlayerClient> client = interface_cast<IMediaPlayerClient>(data.readStrongBinder());
+            sp<IMediaPlayerClient> client =
+                interface_cast<IMediaPlayerClient>(data.readStrongBinder());
             const char* url = data.readCString();
-            sp<IMediaPlayer> player = create(pid, client, url);
+
+            KeyedVector<String8, String8> headers;
+            int32_t numHeaders = data.readInt32();
+            for (int i = 0; i < numHeaders; ++i) {
+                String8 key = data.readString8();
+                String8 value = data.readString8();
+                headers.add(key, value);
+            }
+
+            sp<IMediaPlayer> player = create(
+                    pid, client, url, numHeaders > 0 ? &headers : NULL);
+
             reply->writeStrongBinder(player->asBinder());
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index c0664f3..cb5ee4b 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -32,6 +32,9 @@
 
 #include <binder/MemoryBase.h>
 
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
 namespace android {
 
 MediaPlayer::MediaPlayer()
@@ -122,14 +125,16 @@
     return err;
 }
 
-status_t MediaPlayer::setDataSource(const char *url)
+status_t MediaPlayer::setDataSource(
+        const char *url, const KeyedVector<String8, String8> *headers)
 {
     LOGV("setDataSource(%s)", url);
     status_t err = BAD_VALUE;
     if (url != NULL) {
         const sp<IMediaPlayerService>& service(getMediaPlayerService());
         if (service != 0) {
-            sp<IMediaPlayer> player(service->create(getpid(), this, url));
+            sp<IMediaPlayer> player(
+                    service->create(getpid(), this, url, headers));
             err = setDataSource(player);
         }
     }
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 5b061b1..8e61011 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -254,12 +254,14 @@
     return retriever;
 }
 
-sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url)
+sp<IMediaPlayer> MediaPlayerService::create(
+        pid_t pid, const sp<IMediaPlayerClient>& client, const char* url,
+        const KeyedVector<String8, String8> *headers)
 {
     int32_t connId = android_atomic_inc(&mNextConnId);
     sp<Client> c = new Client(this, pid, connId, client);
     LOGV("Create new client(%d) from pid %d, url=%s, connId=%d", connId, pid, url, connId);
-    if (NO_ERROR != c->setDataSource(url))
+    if (NO_ERROR != c->setDataSource(url, headers))
     {
         c.clear();
         return c;
@@ -803,7 +805,8 @@
     return p;
 }
 
-status_t MediaPlayerService::Client::setDataSource(const char *url)
+status_t MediaPlayerService::Client::setDataSource(
+        const char *url, const KeyedVector<String8, String8> *headers)
 {
     LOGV("setDataSource(%s)", url);
     if (url == NULL)
@@ -838,7 +841,7 @@
 
         // now set data source
         LOGV(" setDataSource");
-        mStatus = p->setDataSource(url);
+        mStatus = p->setDataSource(url, headers);
         if (mStatus == NO_ERROR) {
             mPlayer = p;
         } else {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index d243b96..ffe1ba0 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -23,6 +23,7 @@
 #include <utils/List.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
+#include <utils/String8.h>
 #include <utils/Vector.h>
 #include <ui/SurfaceComposerClient.h>
 
@@ -181,7 +182,10 @@
     virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid);
 
     // House keeping for media player clients
-    virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url);
+    virtual sp<IMediaPlayer>    create(
+            pid_t pid, const sp<IMediaPlayerClient>& client, const char* url,
+            const KeyedVector<String8, String8> *headers);
+
     virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length);
     virtual sp<IMemory>         decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
@@ -219,7 +223,11 @@
                                             Parcel *reply);
 
         sp<MediaPlayerBase>     createPlayer(player_type playerType);
-                status_t        setDataSource(const char *url);
+
+                status_t        setDataSource(
+                        const char *url,
+                        const KeyedVector<String8, String8> *headers);
+
                 status_t        setDataSource(int fd, int64_t offset, int64_t length);
         static  void            notify(void* cookie, int msg, int ext1, int ext2);
 
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index e9cbb97..1b0b05f 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -115,8 +115,8 @@
     release();
 }
 
-status_t MidiFile::setDataSource(const char* path)
-{
+status_t MidiFile::setDataSource(
+        const char* path, const KeyedVector<String8, String8> *) {
     LOGV("MidiFile::setDataSource url=%s", path);
     Mutex::Autolock lock(mMutex);
 
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 25d4a1b..4a60ece 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -30,7 +30,10 @@
                         ~MidiFile();
 
     virtual status_t    initCheck();
-    virtual status_t    setDataSource(const char* path);
+
+    virtual status_t    setDataSource(
+            const char* path, const KeyedVector<String8, String8> *headers);
+
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
     virtual status_t    setVideoSurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; }
     virtual status_t    prepare();
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.cpp b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
index 3795b7b..ad95fac 100644
--- a/media/libmediaplayerservice/MidiMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
@@ -43,7 +43,8 @@
     if (mMidiPlayer == 0) {
         mMidiPlayer = new MidiFile();
     }
-    return mMidiPlayer->setDataSource(url);
+    // TODO: support headers in MetadataRetriever interface!
+    return mMidiPlayer->setDataSource(url, NULL /* headers */);
 }
 
 status_t MidiMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 5915105..f42d55b 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -28,9 +28,10 @@
     return OK;
 }
 
-status_t StagefrightPlayer::setDataSource(const char *url) {
-    LOGV("setDataSource('%s')", url);
-    return mPlayer->setDataSource(url);
+status_t StagefrightPlayer::setDataSource(
+        const char *url, const KeyedVector<String8, String8> *headers) {
+    LOGI("setDataSource('%s')", url);
+    return mPlayer->setDataSource(url, headers);
 }
 
 // Warning: The filedescriptor passed into this method will only be valid until
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index 9d005cb..9e6674a 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -30,7 +30,10 @@
     virtual ~StagefrightPlayer();
 
     virtual status_t initCheck();
-    virtual status_t setDataSource(const char *url);
+
+    virtual status_t setDataSource(
+            const char *url, const KeyedVector<String8, String8> *headers);
+
     virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
     virtual status_t setVideoSurface(const sp<ISurface> &surface);
     virtual status_t prepare();
diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp
index aa49429..169e49a 100644
--- a/media/libmediaplayerservice/TestPlayerStub.cpp
+++ b/media/libmediaplayerservice/TestPlayerStub.cpp
@@ -112,8 +112,8 @@
 // Load the dynamic library.
 // Create the test player.
 // Call setDataSource on the test player with the url in param.
-status_t TestPlayerStub::setDataSource(const char *url)
-{
+status_t TestPlayerStub::setDataSource(
+        const char *url, const KeyedVector<String8, String8> *headers) {
     if (!isTestUrl(url) || NULL != mHandle) {
         return INVALID_OPERATION;
     }
@@ -162,7 +162,7 @@
     }
 
     mPlayer = (*mNewPlayer)();
-    return mPlayer->setDataSource(mContentUrl);
+    return mPlayer->setDataSource(mContentUrl, headers);
 }
 
 // Internal cleanup.
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
index 80d53a8..6e6c3cd 100644
--- a/media/libmediaplayerservice/TestPlayerStub.h
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -65,7 +65,8 @@
     virtual status_t initCheck();
 
     // @param url Should be a test url. See class comment.
-    virtual status_t setDataSource(const char* url);
+    virtual status_t setDataSource(
+            const char* url, const KeyedVector<String8, String8> *headers);
 
     // Test player for a file descriptor source is not supported.
     virtual status_t setDataSource(int, int64_t, int64_t)  {
diff --git a/media/libmediaplayerservice/VorbisMetadataRetriever.cpp b/media/libmediaplayerservice/VorbisMetadataRetriever.cpp
index e981678..eac74fc 100644
--- a/media/libmediaplayerservice/VorbisMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/VorbisMetadataRetriever.cpp
@@ -39,7 +39,8 @@
     if (mVorbisPlayer == 0) {
         mVorbisPlayer = new VorbisPlayer();
     }
-    return mVorbisPlayer->setDataSource(url);
+    // TODO: support headers in MetadataRetriever interface!
+    return mVorbisPlayer->setDataSource(url, NULL /* headers */);
 }
 
 status_t VorbisMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp
index 7f0ef21..8181999 100644
--- a/media/libmediaplayerservice/VorbisPlayer.cpp
+++ b/media/libmediaplayerservice/VorbisPlayer.cpp
@@ -86,9 +86,9 @@
     release();
 }
 
-status_t VorbisPlayer::setDataSource(const char* path)
-{
-    return setdatasource(path, -1, 0, 0x7ffffffffffffffLL); // intentionally less than LONG_MAX
+status_t VorbisPlayer::setDataSource(
+        const char *uri, const KeyedVector<String8, String8> *headers) {
+    return setdatasource(uri, -1, 0, 0x7ffffffffffffffLL); // intentionally less than LONG_MAX
 }
 
 status_t VorbisPlayer::setDataSource(int fd, int64_t offset, int64_t length)
diff --git a/media/libmediaplayerservice/VorbisPlayer.h b/media/libmediaplayerservice/VorbisPlayer.h
index 4024654..4a50835 100644
--- a/media/libmediaplayerservice/VorbisPlayer.h
+++ b/media/libmediaplayerservice/VorbisPlayer.h
@@ -37,7 +37,10 @@
 
     virtual void        onFirstRef();
     virtual status_t    initCheck();
-    virtual status_t    setDataSource(const char* path);
+
+    virtual status_t    setDataSource(
+            const char *uri, const KeyedVector<String8, String8> *headers);
+
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
     virtual status_t    setVideoSurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; }
     virtual status_t    prepare();
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 14842c0..efe7ebb 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -58,12 +58,15 @@
     mSource = source;
 }
 
-void AudioPlayer::start() {
+status_t AudioPlayer::start() {
     CHECK(!mStarted);
     CHECK(mSource != NULL);
 
     status_t err = mSource->start();
-    CHECK_EQ(err, OK);
+
+    if (err != OK) {
+        return err;
+    }
 
     sp<MetaData> format = mSource->getFormat();
     const char *mime;
@@ -83,7 +86,11 @@
                 mSampleRate, numChannels, AudioSystem::PCM_16_BIT,
                 DEFAULT_AUDIOSINK_BUFFERCOUNT,
                 &AudioPlayer::AudioSinkCallback, this);
-        CHECK_EQ(err, OK);
+        if (err != OK) {
+            mSource->stop();
+
+            return err;
+        }
 
         mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
         mFrameSize = mAudioSink->frameSize();
@@ -97,7 +104,14 @@
                     : AudioSystem::CHANNEL_OUT_MONO,
                 8192, 0, &AudioCallback, this, 0);
 
-        CHECK_EQ(mAudioTrack->initCheck(), OK);
+        if (mAudioTrack->initCheck() != OK) {
+            delete mAudioTrack;
+            mAudioTrack = NULL;
+
+            mSource->stop();
+
+            return mAudioTrack->initCheck();
+        }
 
         mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
         mFrameSize = mAudioTrack->frameSize();
@@ -106,6 +120,8 @@
     }
 
     mStarted = true;
+
+    return OK;
 }
 
 void AudioPlayer::pause() {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 42b9acc..4e7738e 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -152,12 +152,13 @@
     mListener = listener;
 }
 
-status_t AwesomePlayer::setDataSource(const char *uri) {
+status_t AwesomePlayer::setDataSource(
+        const char *uri, const KeyedVector<String8, String8> *headers) {
     Mutex::Autolock autoLock(mLock);
 
     reset_l();
 
-    sp<DataSource> dataSource = DataSource::CreateFromURI(uri);
+    sp<DataSource> dataSource = DataSource::CreateFromURI(uri, headers);
 
     if (dataSource == NULL) {
         return UNKNOWN_ERROR;
@@ -248,7 +249,16 @@
 
     if (mVideoSource != NULL) {
         mVideoSource->stop();
+
+        // The following hack is necessary to ensure that the OMX
+        // component is completely released by the time we may try
+        // to instantiate it again.
+        wp<MediaSource> tmp = mVideoSource;
         mVideoSource.clear();
+        while (tmp.promote() != NULL) {
+            usleep(1000);
+        }
+        IPCThreadState::self()->flushCommands();
     }
 
     mAudioSource.clear();
@@ -368,7 +378,16 @@
                         &AwesomePlayer::AudioNotify, this);
 
                 mAudioPlayer->setSource(mAudioSource);
-                mAudioPlayer->start();
+                status_t err = mAudioPlayer->start();
+
+                if (err != OK) {
+                    delete mAudioPlayer;
+                    mAudioPlayer = NULL;
+
+                    mFlags &= ~(PLAYING | FIRST_FRAME);
+
+                    return err;
+                }
 
                 delete mTimeSource;
                 mTimeSource = mAudioPlayer;
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 741e5e00..1696eb96 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -95,12 +95,13 @@
 }
 
 // static
-sp<DataSource> DataSource::CreateFromURI(const char *uri) {
+sp<DataSource> DataSource::CreateFromURI(
+        const char *uri, const KeyedVector<String8, String8> *headers) {
     sp<DataSource> source;
     if (!strncasecmp("file://", uri, 7)) {
         source = new FileSource(uri + 7);
     } else if (!strncasecmp("http://", uri, 7)) {
-        source = new HTTPDataSource(uri);
+        source = new HTTPDataSource(uri, headers);
         source = new CachingDataSource(source, 64 * 1024, 10);
     } else {
         // Assume it's a filename.
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
index cf189af..e1ddfef 100644
--- a/media/libstagefright/HTTPDataSource.cpp
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -31,14 +31,13 @@
 // accordingly and return true, otherwise return false and leave the stream
 // connected.
 static bool PerformRedirectIfNecessary(
-        HTTPStream *http, string *host, string *path, int *port) {
+        HTTPStream *http, const String8 &headers,
+        string *host, string *path, int *port) {
     String8 request;
     request.append("HEAD ");
     request.append(path->c_str());
     request.append(" HTTP/1.1\r\n");
-    request.append("User-Agent: ");
-    request.append(kUserAgent);
-    request.append("\r\n");
+    request.append(headers);
     request.append("Host: ");
     request.append(host->c_str());
     request.append("\r\n\r\n");
@@ -94,7 +93,8 @@
     return true;
 }
 
-HTTPDataSource::HTTPDataSource(const char *uri)
+HTTPDataSource::HTTPDataSource(
+        const char *uri, const KeyedVector<String8, String8> *headers)
     : mHttp(new HTTPStream),
       mHost(NULL),
       mPort(0),
@@ -105,6 +105,8 @@
       mFirstRequest(true) {
     CHECK(!strncasecmp("http://", uri, 7));
 
+    initHeaders(headers);
+
     string host;
     string path;
     int port;
@@ -140,14 +142,16 @@
         if (mInitCheck != OK) {
             return;
         }
-    } while (PerformRedirectIfNecessary(mHttp, &host, &path, &port));
+    } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port));
 
     mHost = strdup(host.c_str());
     mPort = port;
     mPath = strdup(path.c_str());
 }
 
-HTTPDataSource::HTTPDataSource(const char *_host, int port, const char *_path)
+HTTPDataSource::HTTPDataSource(
+        const char *_host, int port, const char *_path,
+        const KeyedVector<String8, String8> *headers)
     : mHttp(new HTTPStream),
       mHost(NULL),
       mPort(0),
@@ -156,6 +160,8 @@
       mBufferLength(0),
       mBufferOffset(0),
       mFirstRequest(true) {
+    initHeaders(headers);
+
     string host = _host;
     string path = _path;
 
@@ -168,7 +174,7 @@
         if (mInitCheck != OK) {
             return;
         }
-    } while (PerformRedirectIfNecessary(mHttp, &host, &path, &port));
+    } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port));
 
     mHost = strdup(host.c_str());
     mPort = port;
@@ -200,9 +206,6 @@
 }
 
 ssize_t HTTPDataSource::sendRangeRequest(size_t offset) {
-    char agent[128];
-    sprintf(agent, "User-Agent: %s\r\n", kUserAgent);
-
     char host[128];
     sprintf(host, "Host: %s\r\n", mHost);
 
@@ -221,7 +224,7 @@
         if ((err = mHttp->send("GET ")) != OK
             || (err = mHttp->send(mPath)) != OK
             || (err = mHttp->send(" HTTP/1.1\r\n")) != OK
-            || (err = mHttp->send(agent)) != OK
+            || (err = mHttp->send(mHeaders.string())) != OK
             || (err = mHttp->send(host)) != OK
             || (err = mHttp->send(range)) != OK
             || (err = mHttp->send("\r\n")) != OK
@@ -271,6 +274,9 @@
     ssize_t contentLength = 0;
     if (mFirstRequest || offset != mBufferOffset + mBufferLength) {
         if (!mFirstRequest) {
+            LOGV("new range offset=%ld (old=%ld)",
+                 offset, mBufferOffset + mBufferLength);
+
             mHttp->disconnect();
         }
         mFirstRequest = false;
@@ -304,5 +310,28 @@
     return copy;
 }
 
+void HTTPDataSource::initHeaders(
+        const KeyedVector<String8, String8> *overrides) {
+    mHeaders = String8();
+
+    mHeaders.append("User-Agent: ");
+    mHeaders.append(kUserAgent);
+    mHeaders.append("\r\n");
+
+    if (overrides == NULL) {
+        return;
+    }
+
+    for (size_t i = 0; i < overrides->size(); ++i) {
+        String8 line;
+        line.append(overrides->keyAt(i));
+        line.append(": ");
+        line.append(overrides->valueAt(i));
+        line.append("\r\n");
+
+        mHeaders.append(line);
+    }
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index ed12b6d..5370c39 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -84,6 +84,112 @@
     MPEG4Source &operator=(const MPEG4Source &);
 };
 
+// This custom data source wraps an existing one and satisfies requests
+// falling entirely within a cached range from the cache while forwarding
+// all remaining requests to the wrapped datasource.
+// This is used to cache the full sampletable metadata for a single track,
+// possibly wrapping multiple times to cover all tracks, i.e.
+// Each MPEG4DataSource caches the sampletable metadata for a single track.
+
+struct MPEG4DataSource : public DataSource {
+    MPEG4DataSource(const sp<DataSource> &source);
+
+    virtual status_t initCheck() const;
+    virtual ssize_t readAt(off_t offset, void *data, size_t size);
+    virtual status_t getSize(off_t *size);
+    virtual uint32_t flags();
+
+    status_t setCachedRange(off_t offset, size_t size);
+
+protected:
+    virtual ~MPEG4DataSource();
+
+private:
+    Mutex mLock;
+
+    sp<DataSource> mSource;
+    off_t mCachedOffset;
+    size_t mCachedSize;
+    uint8_t *mCache;
+
+    void clearCache();
+
+    MPEG4DataSource(const MPEG4DataSource &);
+    MPEG4DataSource &operator=(const MPEG4DataSource &);
+};
+
+MPEG4DataSource::MPEG4DataSource(const sp<DataSource> &source)
+    : mSource(source),
+      mCachedOffset(0),
+      mCachedSize(0),
+      mCache(NULL) {
+}
+
+MPEG4DataSource::~MPEG4DataSource() {
+    clearCache();
+}
+
+void MPEG4DataSource::clearCache() {
+    if (mCache) {
+        free(mCache);
+        mCache = NULL;
+    }
+
+    mCachedOffset = 0;
+    mCachedSize = 0;
+}
+
+status_t MPEG4DataSource::initCheck() const {
+    return mSource->initCheck();
+}
+
+ssize_t MPEG4DataSource::readAt(off_t offset, void *data, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (offset >= mCachedOffset
+            && offset + size <= mCachedOffset + mCachedSize) {
+        memcpy(data, &mCache[offset - mCachedOffset], size);
+        return size;
+    }
+
+    return mSource->readAt(offset, data, size);
+}
+
+status_t MPEG4DataSource::getSize(off_t *size) {
+    return mSource->getSize(size);
+}
+
+uint32_t MPEG4DataSource::flags() {
+    return mSource->flags();
+}
+
+status_t MPEG4DataSource::setCachedRange(off_t offset, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    clearCache();
+
+    mCache = (uint8_t *)malloc(size);
+
+    if (mCache == NULL) {
+        return -ENOMEM;
+    }
+
+    mCachedOffset = offset;
+    mCachedSize = size;
+
+    ssize_t err = mSource->readAt(mCachedOffset, mCache, mCachedSize);
+
+    if (err < (ssize_t)size) {
+        clearCache();
+
+        return ERROR_IO;
+    }
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 static void hexdump(const void *_data, size_t size) {
     const uint8_t *data = (const uint8_t *)_data;
     size_t offset = 0;
@@ -374,6 +480,19 @@
         case FOURCC('u', 'd', 't', 'a'):
         case FOURCC('i', 'l', 's', 't'):
         {
+            if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
+                LOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
+
+                if (mDataSource->flags() & DataSource::kWantsPrefetching) {
+                    sp<MPEG4DataSource> cachedSource =
+                        new MPEG4DataSource(mDataSource);
+
+                    if (cachedSource->setCachedRange(*offset, chunk_size) == OK) {
+                        mDataSource = cachedSource;
+                    }
+                }
+            }
+
             off_t stop_offset = *offset + chunk_size;
             *offset = data_offset;
             while (*offset < stop_offset) {
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index e46f00e..738e18a 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -67,16 +67,4 @@
     return NULL;
 }
 
-// static
-sp<MediaExtractor> MediaExtractor::CreateFromURI(
-        const char *uri, const char *mime) {
-    sp<DataSource> source = DataSource::CreateFromURI(uri);
-
-    if (source == NULL || source->initCheck() != OK) {
-        return NULL;
-    }
-
-    return Create(source, mime);
-}
-
 }  // namespace android
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index d874224..aa2a3d1 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -80,6 +80,8 @@
         stop();
     }
 
+    PVAVCCleanUpDecoder(mHandle);
+
     delete mHandle;
     mHandle = NULL;
 }
@@ -320,8 +322,10 @@
                 crop_top = crop_left = 0;
             }
 
-            mFormat->setInt32(kKeyWidth, crop_right - crop_left + 1);
-            mFormat->setInt32(kKeyHeight, crop_bottom - crop_top + 1);
+            int32_t aligned_width = (crop_right - crop_left + 1 + 15) & ~15;
+            int32_t aligned_height = (crop_bottom - crop_top + 1 + 15) & ~15;
+            mFormat->setInt32(kKeyWidth, aligned_width);
+            mFormat->setInt32(kKeyHeight, aligned_height);
 
             mInputBuffer->release();
             mInputBuffer = NULL;
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index c2e46c0..8bd6594 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -49,7 +49,10 @@
 
     void setListener(const wp<MediaPlayerBase> &listener);
 
-    status_t setDataSource(const char *uri);
+    status_t setDataSource(
+            const char *uri,
+            const KeyedVector<String8, String8> *headers = NULL);
+
     status_t setDataSource(int fd, int64_t offset, int64_t length);
 
     void reset();
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 5b45c1c..6c36163 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -245,10 +245,20 @@
     NodeReaper &operator=(const NodeReaper &);
 };
 
+static sp<MediaExtractor> CreateExtractorFromURI(const char *uri) {
+    sp<DataSource> source = DataSource::CreateFromURI(uri);
+
+    if (source == NULL) {
+        return NULL;
+    }
+
+    return MediaExtractor::Create(source);
+}
+
 static sp<MediaSource> MakeSource(
         const char *uri,
         const char *mimeType) {
-    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri);
+    sp<MediaExtractor> extractor = CreateExtractorFromURI(uri);
 
     if (extractor == NULL) {
         return NULL;
@@ -500,7 +510,7 @@
     const char *url = GetURLForMime(mime);
     CHECK(url != NULL);
 
-    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(url);
+    sp<MediaExtractor> extractor = CreateExtractorFromURI(url);
 
     CHECK(extractor != NULL);
 
diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp
index 77bb5b2..b3cc8b6 100644
--- a/media/tests/players/invoke_mock_media_player.cpp
+++ b/media/tests/players/invoke_mock_media_player.cpp
@@ -35,6 +35,8 @@
 using android::player_type;
 using android::sp;
 using android::status_t;
+using android::String8;
+using android::KeyedVector;
 
 // This file contains a test player that is loaded via the
 // TestPlayerStub class.  The player contains various implementation
@@ -53,7 +55,9 @@
     virtual status_t    initCheck() {return OK;}
     virtual bool        hardwareOutput() {return true;}
 
-    virtual status_t    setDataSource(const char *url) {
+    virtual status_t    setDataSource(
+            const char *url,
+            const KeyedVector<String8, String8> *) {
         LOGV("setDataSource %s", url);
         mTest = TEST_UNKNOWN;
         if (strncmp(url, kPing, strlen(kPing)) == 0) {
diff --git a/mms-common/Android.mk b/mms-common/Android.mk
new file mode 100644
index 0000000..de994c0
--- /dev/null
+++ b/mms-common/Android.mk
@@ -0,0 +1,29 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Note: the source code is in java/, not src/, because this code is also part of
+# the framework library, and build/core/pathmap.mk expects a java/ subdirectory.
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := mms-common
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Include this library in the build server's output directory
+$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):mms-common.jar)
+
+# Build the test package
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/java/com/google/android/mms/pdu/CharacterSets.java b/mms-common/java/com/android/common/CharacterSets.java
similarity index 98%
rename from core/java/com/google/android/mms/pdu/CharacterSets.java
rename to mms-common/java/com/android/common/CharacterSets.java
index 4e22ca5..f19b078 100644
--- a/core/java/com/google/android/mms/pdu/CharacterSets.java
+++ b/mms-common/java/com/android/common/CharacterSets.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon;
 
 import java.io.UnsupportedEncodingException;
 import java.util.HashMap;
diff --git a/core/java/com/google/android/mms/ContentType.java b/mms-common/java/com/android/common/ContentType.java
similarity index 98%
copy from core/java/com/google/android/mms/ContentType.java
copy to mms-common/java/com/android/common/ContentType.java
index 94bc9fd..ca449fe 100644
--- a/core/java/com/google/android/mms/ContentType.java
+++ b/mms-common/java/com/android/common/ContentType.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms;
+package com.android.mmscommon;
 
 import java.util.ArrayList;
 
@@ -26,6 +26,7 @@
     public static final String MMS_GENERIC       = "application/vnd.wap.mms-generic";
     public static final String MULTIPART_MIXED   = "application/vnd.wap.multipart.mixed";
     public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related";
+    public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative";
 
     public static final String TEXT_PLAIN        = "text/plain";
     public static final String TEXT_HTML         = "text/html";
diff --git a/core/java/com/google/android/mms/pdu/EncodedStringValue.java b/mms-common/java/com/android/common/EncodedStringValue.java
similarity index 99%
rename from core/java/com/google/android/mms/pdu/EncodedStringValue.java
rename to mms-common/java/com/android/common/EncodedStringValue.java
index a27962d..0a4424e 100644
--- a/core/java/com/google/android/mms/pdu/EncodedStringValue.java
+++ b/mms-common/java/com/android/common/EncodedStringValue.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon;
 
 import android.util.Config;
 import android.util.Log;
@@ -269,7 +269,7 @@
 
         return new EncodedStringValue(value.mCharacterSet, value.mData);
     }
-    
+
     public static EncodedStringValue[] encodeStrings(String[] array) {
         int count = array.length;
         if (count > 0) {
diff --git a/core/java/com/google/android/mms/InvalidHeaderValueException.java b/mms-common/java/com/android/common/InvalidHeaderValueException.java
similarity index 97%
rename from core/java/com/google/android/mms/InvalidHeaderValueException.java
rename to mms-common/java/com/android/common/InvalidHeaderValueException.java
index 73d7832..34d5871 100644
--- a/core/java/com/google/android/mms/InvalidHeaderValueException.java
+++ b/mms-common/java/com/android/common/InvalidHeaderValueException.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms;
+package com.android.mmscommon;
 
 /**
  * Thrown when an invalid header value was set.
diff --git a/core/java/com/google/android/mms/MmsException.java b/mms-common/java/com/android/common/MmsException.java
similarity index 97%
rename from core/java/com/google/android/mms/MmsException.java
rename to mms-common/java/com/android/common/MmsException.java
index 6ca0c7e..296a2c3 100644
--- a/core/java/com/google/android/mms/MmsException.java
+++ b/mms-common/java/com/android/common/MmsException.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms;
+package com.android.mmscommon;
 
 /**
  * A generic exception that is thrown by the Mms client.
diff --git a/core/java/com/google/android/mms/pdu/PduHeaders.java b/mms-common/java/com/android/common/PduHeaders.java
similarity index 97%
rename from core/java/com/google/android/mms/pdu/PduHeaders.java
rename to mms-common/java/com/android/common/PduHeaders.java
index 4313815..d8f1211 100644
--- a/core/java/com/google/android/mms/pdu/PduHeaders.java
+++ b/mms-common/java/com/android/common/PduHeaders.java
@@ -15,9 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
+package com.android.mmscommon;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -339,7 +337,7 @@
      *          with specified header field. Return 0 if
      *          the value is not set.
      */
-    protected int getOctet(int field) {
+    public int getOctet(int field) {
         Integer octet = (Integer) mHeaderMap.get(field);
         if (null == octet) {
             return 0;
@@ -355,7 +353,7 @@
      * @param field the field
      * @throws InvalidHeaderValueException if the value is invalid.
      */
-    protected void setOctet(int value, int field)
+    public void setOctet(int value, int field)
             throws InvalidHeaderValueException{
         /**
          * Check whether this field can be set for specific
@@ -499,7 +497,7 @@
      * @return the TextString value of the pdu header
      *          with specified header field
      */
-    protected byte[] getTextString(int field) {
+    public byte[] getTextString(int field) {
         return (byte[]) mHeaderMap.get(field);
     }
 
@@ -512,7 +510,7 @@
      *          with specified header field
      * @throws NullPointerException if the value is null.
      */
-    protected void setTextString(byte[] value, int field) {
+    public void setTextString(byte[] value, int field) {
         /**
          * Check whether this field can be set for specific
          * header and check validity of the field.
@@ -548,7 +546,7 @@
      * @return the EncodedStringValue value of the pdu header
      *          with specified header field
      */
-    protected EncodedStringValue getEncodedStringValue(int field) {
+    public EncodedStringValue getEncodedStringValue(int field) {
         return (EncodedStringValue) mHeaderMap.get(field);
     }
 
@@ -559,7 +557,7 @@
      * @return the EncodeStringValue array of the pdu header
      *          with specified header field
      */
-    protected EncodedStringValue[] getEncodedStringValues(int field) {
+    public EncodedStringValue[] getEncodedStringValues(int field) {
         ArrayList<EncodedStringValue> list =
                 (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
         if (null == list) {
@@ -578,7 +576,7 @@
      *          with specified header field
      * @throws NullPointerException if the value is null.
      */
-    protected void setEncodedStringValue(EncodedStringValue value, int field) {
+    public void setEncodedStringValue(EncodedStringValue value, int field) {
         /**
          * Check whether this field can be set for specific
          * header and check validity of the field.
@@ -615,7 +613,7 @@
      *          with specified header field
      * @throws NullPointerException if the value is null.
      */
-    protected void setEncodedStringValues(EncodedStringValue[] value, int field) {
+    public void setEncodedStringValues(EncodedStringValue[] value, int field) {
         /**
          * Check whether this field can be set for specific
          * header and check validity of the field.
@@ -648,7 +646,7 @@
      * @param field the field
      * @throws NullPointerException if the value is null.
      */
-    protected void appendEncodedStringValue(EncodedStringValue value,
+    public void appendEncodedStringValue(EncodedStringValue value,
                                     int field) {
         if (null == value) {
             throw new NullPointerException();
@@ -680,7 +678,7 @@
      *          with specified header field. if return -1, the
      *          field is not existed in pdu header.
      */
-    protected long getLongInteger(int field) {
+    public long getLongInteger(int field) {
         Long longInteger = (Long) mHeaderMap.get(field);
         if (null == longInteger) {
             return -1;
@@ -695,7 +693,7 @@
      * @param value the value
      * @param field the field
      */
-    protected void setLongInteger(long value, int field) {
+    public void setLongInteger(long value, int field) {
         /**
          * Check whether this field can be set for specific
          * header and check validity of the field.
diff --git a/core/java/com/google/android/mms/ContentType.java b/mms-common/java/com/android/common/mms/ContentType.java
similarity index 98%
rename from core/java/com/google/android/mms/ContentType.java
rename to mms-common/java/com/android/common/mms/ContentType.java
index 94bc9fd..0fdb46c 100644
--- a/core/java/com/google/android/mms/ContentType.java
+++ b/mms-common/java/com/android/common/mms/ContentType.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms;
+package com.android.mms.mms;
 
 import java.util.ArrayList;
 
@@ -26,6 +26,7 @@
     public static final String MMS_GENERIC       = "application/vnd.wap.mms-generic";
     public static final String MULTIPART_MIXED   = "application/vnd.wap.multipart.mixed";
     public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related";
+    public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative";
 
     public static final String TEXT_PLAIN        = "text/plain";
     public static final String TEXT_HTML         = "text/html";
diff --git a/core/java/com/google/android/mms/pdu/AcknowledgeInd.java b/mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java
similarity index 94%
rename from core/java/com/google/android/mms/pdu/AcknowledgeInd.java
rename to mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java
index 0e96c60..d1243b2 100644
--- a/core/java/com/google/android/mms/pdu/AcknowledgeInd.java
+++ b/mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 /**
  * M-Acknowledge.ind PDU.
diff --git a/core/java/com/google/android/mms/pdu/Base64.java b/mms-common/java/com/android/common/mms/pdu/Base64.java
similarity index 98%
rename from core/java/com/google/android/mms/pdu/Base64.java
rename to mms-common/java/com/android/common/mms/pdu/Base64.java
index 604bee0..4c95dec 100644
--- a/core/java/com/google/android/mms/pdu/Base64.java
+++ b/mms-common/java/com/android/common/mms/pdu/Base64.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
 public class Base64 {
     /**
diff --git a/core/java/com/google/android/mms/pdu/DeliveryInd.java b/mms-common/java/com/android/common/mms/pdu/DeliveryInd.java
similarity index 94%
rename from core/java/com/google/android/mms/pdu/DeliveryInd.java
rename to mms-common/java/com/android/common/mms/pdu/DeliveryInd.java
index dafa8d1..e83729b 100644
--- a/core/java/com/google/android/mms/pdu/DeliveryInd.java
+++ b/mms-common/java/com/android/common/mms/pdu/DeliveryInd.java
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 /**
  * M-Delivery.Ind Pdu.
diff --git a/core/java/com/google/android/mms/pdu/GenericPdu.java b/mms-common/java/com/android/common/mms/pdu/GenericPdu.java
similarity index 93%
rename from core/java/com/google/android/mms/pdu/GenericPdu.java
rename to mms-common/java/com/android/common/mms/pdu/GenericPdu.java
index 705de6a..c38e502 100644
--- a/core/java/com/google/android/mms/pdu/GenericPdu.java
+++ b/mms-common/java/com/android/common/mms/pdu/GenericPdu.java
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 public class GenericPdu {
     /**
diff --git a/core/java/com/google/android/mms/pdu/MultimediaMessagePdu.java b/mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java
similarity index 94%
rename from core/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
rename to mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java
index 5a85e0e..04fde2d 100644
--- a/core/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
+++ b/mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 /**
  * Multimedia message PDU.
diff --git a/core/java/com/google/android/mms/pdu/NotificationInd.java b/mms-common/java/com/android/common/mms/pdu/NotificationInd.java
similarity index 97%
rename from core/java/com/google/android/mms/pdu/NotificationInd.java
rename to mms-common/java/com/android/common/mms/pdu/NotificationInd.java
index c56cba6..24f17b09 100644
--- a/core/java/com/google/android/mms/pdu/NotificationInd.java
+++ b/mms-common/java/com/android/common/mms/pdu/NotificationInd.java
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 /**
  * M-Notification.ind PDU.
diff --git a/core/java/com/google/android/mms/pdu/NotifyRespInd.java b/mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java
similarity index 95%
rename from core/java/com/google/android/mms/pdu/NotifyRespInd.java
rename to mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java
index 2cc2fce..c2e2b26 100644
--- a/core/java/com/google/android/mms/pdu/NotifyRespInd.java
+++ b/mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 /**
  * M-NofifyResp.ind PDU.
diff --git a/core/java/com/google/android/mms/pdu/PduBody.java b/mms-common/java/com/android/common/mms/pdu/PduBody.java
similarity index 98%
rename from core/java/com/google/android/mms/pdu/PduBody.java
rename to mms-common/java/com/android/common/mms/pdu/PduBody.java
index fa0416c..cc28d80 100644
--- a/core/java/com/google/android/mms/pdu/PduBody.java
+++ b/mms-common/java/com/android/common/mms/pdu/PduBody.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
 import java.util.HashMap;
 import java.util.Map;
diff --git a/core/java/com/google/android/mms/pdu/PduComposer.java b/mms-common/java/com/android/common/mms/pdu/PduComposer.java
similarity index 99%
rename from core/java/com/google/android/mms/pdu/PduComposer.java
rename to mms-common/java/com/android/common/mms/pdu/PduComposer.java
index 8940945..bb3116d 100644
--- a/core/java/com/google/android/mms/pdu/PduComposer.java
+++ b/mms-common/java/com/android/common/mms/pdu/PduComposer.java
@@ -15,7 +15,10 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
+
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.PduHeaders;
 
 import android.content.ContentResolver;
 import android.content.Context;
diff --git a/core/java/com/google/android/mms/pdu/PduContentTypes.java b/mms-common/java/com/android/common/mms/pdu/PduContentTypes.java
similarity index 98%
rename from core/java/com/google/android/mms/pdu/PduContentTypes.java
rename to mms-common/java/com/android/common/mms/pdu/PduContentTypes.java
index 7799e0e..3f971fd 100644
--- a/core/java/com/google/android/mms/pdu/PduContentTypes.java
+++ b/mms-common/java/com/android/common/mms/pdu/PduContentTypes.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
 public class PduContentTypes {
     /**
diff --git a/core/java/com/google/android/mms/pdu/PduParser.java b/mms-common/java/com/android/common/mms/pdu/PduParser.java
similarity index 98%
rename from core/java/com/google/android/mms/pdu/PduParser.java
rename to mms-common/java/com/android/common/mms/pdu/PduParser.java
index d465c5a..9253f83 100644
--- a/core/java/com/google/android/mms/pdu/PduParser.java
+++ b/mms-common/java/com/android/common/mms/pdu/PduParser.java
@@ -15,10 +15,13 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.ContentType;
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.ContentType;
+import com.android.mmscommon.CharacterSets;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 import android.util.Config;
 import android.util.Log;
@@ -157,9 +160,11 @@
                 }
                 String ctTypeStr = new String(contentType);
                 if (ctTypeStr.equals(ContentType.MULTIPART_MIXED)
-                        || ctTypeStr.equals(ContentType.MULTIPART_RELATED)) {
+                        || ctTypeStr.equals(ContentType.MULTIPART_RELATED)
+                        || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
                     // The MMS content type must be "application/vnd.wap.multipart.mixed"
                     // or "application/vnd.wap.multipart.related"
+                    // or "application/vnd.wap.multipart.alternative"
                     return retrieveConf;
                 }
                 return null;
diff --git a/core/java/com/google/android/mms/pdu/PduPart.java b/mms-common/java/com/android/common/mms/pdu/PduPart.java
similarity index 99%
rename from core/java/com/google/android/mms/pdu/PduPart.java
rename to mms-common/java/com/android/common/mms/pdu/PduPart.java
index b43e388..7d51b86 100644
--- a/core/java/com/google/android/mms/pdu/PduPart.java
+++ b/mms-common/java/com/android/common/mms/pdu/PduPart.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
 import android.net.Uri;
 
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/mms-common/java/com/android/common/mms/pdu/PduPersister.java
similarity index 96%
rename from core/java/com/google/android/mms/pdu/PduPersister.java
rename to mms-common/java/com/android/common/mms/pdu/PduPersister.java
index 1f754bc..46f28c7 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/mms-common/java/com/android/common/mms/pdu/PduPersister.java
@@ -15,14 +15,19 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.ContentType;
-import com.google.android.mms.InvalidHeaderValueException;
-import com.google.android.mms.MmsException;
-import com.google.android.mms.util.PduCache;
-import com.google.android.mms.util.PduCacheEntry;
-import com.google.android.mms.util.SqliteWrapper;
+import com.android.mmscommon.mms.pdu.PduPersister;
+
+import com.android.mmscommon.ContentType;
+import com.android.mmscommon.CharacterSets;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.MmsException;
+import com.android.mmscommon.PduHeaders;
+import com.android.mmscommon.mms.util.PduCache;
+import com.android.mmscommon.mms.util.PduCacheEntry;
+import android.database.sqlite.SqliteWrapper;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -31,13 +36,13 @@
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.net.Uri;
-import android.provider.Telephony;
-import android.provider.Telephony.Mms;
-import android.provider.Telephony.MmsSms;
-import android.provider.Telephony.Threads;
-import android.provider.Telephony.Mms.Addr;
-import android.provider.Telephony.Mms.Part;
-import android.provider.Telephony.MmsSms.PendingMessages;
+import com.android.mmscommon.telephony.TelephonyProvider;
+import com.android.mmscommon.telephony.TelephonyProvider.Mms;
+import com.android.mmscommon.telephony.TelephonyProvider.MmsSms;
+import com.android.mmscommon.telephony.TelephonyProvider.Threads;
+import com.android.mmscommon.telephony.TelephonyProvider.Mms.Addr;
+import com.android.mmscommon.telephony.TelephonyProvider.Mms.Part;
+import com.android.mmscommon.telephony.TelephonyProvider.MmsSms.PendingMessages;
 import android.text.TextUtils;
 import android.util.Config;
 import android.util.Log;
@@ -55,7 +60,6 @@
 import java.util.Set;
 import java.util.Map.Entry;
 
-import com.google.android.mms.pdu.EncodedStringValue;
 
 /**
  * This class is the high-level manager of PDU storage.
@@ -422,7 +426,8 @@
                     // Store simple string values directly in the database instead of an
                     // external file.  This makes the text searchable and retrieval slightly
                     // faster.
-                    if ("text/plain".equals(type) || "application/smil".equals(type)) {
+                    if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type)
+                            || ContentType.TEXT_HTML.equals(type)) {
                         String text = c.getString(PART_COLUMN_TEXT);
                         if (text == null) {
                             text = "";
@@ -738,9 +743,11 @@
 
         try {
             byte[] data = part.getData();
-            if ("text/plain".equals(contentType) || "application/smil".equals(contentType)) {
+            if (ContentType.TEXT_PLAIN.equals(contentType)
+                    || ContentType.APP_SMIL.equals(contentType)
+                    || ContentType.TEXT_HTML.equals(contentType)) {
                 ContentValues cv = new ContentValues();
-                cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString());
+                cv.put(TelephonyProvider.Mms.Part.TEXT, new EncodedStringValue(data).getString());
                 if (mContentResolver.update(uri, cv, null, null) != 1) {
                     throw new MmsException("unable to update " + uri.toString());
                 }
diff --git a/core/java/com/google/android/mms/pdu/QuotedPrintable.java b/mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java
similarity index 97%
rename from core/java/com/google/android/mms/pdu/QuotedPrintable.java
rename to mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java
index a34ed12..e9da7df 100644
--- a/core/java/com/google/android/mms/pdu/QuotedPrintable.java
+++ b/mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
 import java.io.ByteArrayOutputStream;
 
diff --git a/core/java/com/google/android/mms/pdu/ReadOrigInd.java b/mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java
similarity index 95%
rename from core/java/com/google/android/mms/pdu/ReadOrigInd.java
rename to mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java
index 1bfc0bb..9678784 100644
--- a/core/java/com/google/android/mms/pdu/ReadOrigInd.java
+++ b/mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 public class ReadOrigInd extends GenericPdu {
     /**
diff --git a/core/java/com/google/android/mms/pdu/ReadRecInd.java b/mms-common/java/com/android/common/mms/pdu/ReadRecInd.java
similarity index 95%
rename from core/java/com/google/android/mms/pdu/ReadRecInd.java
rename to mms-common/java/com/android/common/mms/pdu/ReadRecInd.java
index 880e3ac..c1efbbc 100644
--- a/core/java/com/google/android/mms/pdu/ReadRecInd.java
+++ b/mms-common/java/com/android/common/mms/pdu/ReadRecInd.java
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 public class ReadRecInd extends GenericPdu {
     /**
diff --git a/core/java/com/google/android/mms/pdu/RetrieveConf.java b/mms-common/java/com/android/common/mms/pdu/RetrieveConf.java
similarity index 97%
rename from core/java/com/google/android/mms/pdu/RetrieveConf.java
rename to mms-common/java/com/android/common/mms/pdu/RetrieveConf.java
index 98e67c0..442949e 100644
--- a/core/java/com/google/android/mms/pdu/RetrieveConf.java
+++ b/mms-common/java/com/android/common/mms/pdu/RetrieveConf.java
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 /**
  * M-Retrive.conf Pdu.
diff --git a/core/java/com/google/android/mms/pdu/SendConf.java b/mms-common/java/com/android/common/mms/pdu/SendConf.java
similarity index 94%
rename from core/java/com/google/android/mms/pdu/SendConf.java
rename to mms-common/java/com/android/common/mms/pdu/SendConf.java
index 0568fe79..0a57b6b 100644
--- a/core/java/com/google/android/mms/pdu/SendConf.java
+++ b/mms-common/java/com/android/common/mms/pdu/SendConf.java
@@ -15,9 +15,11 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 public class SendConf extends GenericPdu {
     /**
diff --git a/core/java/com/google/android/mms/pdu/SendReq.java b/mms-common/java/com/android/common/mms/pdu/SendReq.java
similarity index 97%
rename from core/java/com/google/android/mms/pdu/SendReq.java
rename to mms-common/java/com/android/common/mms/pdu/SendReq.java
index 597cd00..5da4719 100644
--- a/core/java/com/google/android/mms/pdu/SendReq.java
+++ b/mms-common/java/com/android/common/mms/pdu/SendReq.java
@@ -15,11 +15,13 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.pdu;
+package com.android.mmscommon.mms.pdu;
 
 import android.util.Log;
 
-import com.google.android.mms.InvalidHeaderValueException;
+import com.android.mmscommon.EncodedStringValue;
+import com.android.mmscommon.InvalidHeaderValueException;
+import com.android.mmscommon.PduHeaders;
 
 public class SendReq extends MultimediaMessagePdu {
     private static final String TAG = "SendReq";
diff --git a/mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java b/mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java
new file mode 100644
index 0000000..0237bc2
--- /dev/null
+++ b/mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java
@@ -0,0 +1,1790 @@
+/*
+ * Copyright (C) 2006 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.mmscommon.telephony;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.telephony.SmsMessage;
+import android.text.TextUtils;
+import android.util.Config;
+import android.util.Log;
+
+import com.android.common.Patterns;
+import android.database.sqlite.SqliteWrapper;
+
+/**
+ * The Telephony provider contains data related to phone operation.
+ *
+ * @hide
+ */
+
+// This is a copy of the private TelephoneProvider.java file found in:
+//      com.android.providers.telephony
+// TODO: keep these files in sync.
+
+public final class TelephonyProvider {
+    private static final String TAG = "Telephony";
+    private static final boolean DEBUG = true;
+    private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+
+//    public static final Pattern EMAIL_ADDRESS
+//        = Pattern.compile(
+//            "[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}" +
+//            "\\@" +
+//            "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" +
+//            "(" +
+//                "\\." +
+//                "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" +
+//            ")+"
+//        );
+//
+//    /**
+//     * This pattern is intended for searching for things that look like they
+//     * might be phone numbers in arbitrary text, not for validating whether
+//     * something is in fact a phone number.  It will miss many things that
+//     * are legitimate phone numbers.
+//     *
+//     * <p> The pattern matches the following:
+//     * <ul>
+//     * <li>Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes
+//     * may follow.
+//     * <li>Optionally, sets of digits in parentheses, separated by spaces, dots, or dashes.
+//     * <li>A string starting and ending with a digit, containing digits, spaces, dots, and/or dashes.
+//     * </ul>
+//     */
+//    public static final Pattern PHONE
+//        = Pattern.compile(                                  // sdd = space, dot, or dash
+//                "(\\+[0-9]+[\\- \\.]*)?"                    // +<digits><sdd>*
+//                + "(\\([0-9]+\\)[\\- \\.]*)?"               // (<digits>)<sdd>*
+//                + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit>
+
+    // Constructor
+    public TelephonyProvider() {
+    }
+
+    /**
+     * Base columns for tables that contain text based SMSs.
+     */
+    public interface TextBasedSmsColumns {
+        /**
+         * The type of the message
+         * <P>Type: INTEGER</P>
+         */
+        public static final String TYPE = "type";
+
+        public static final int MESSAGE_TYPE_ALL    = 0;
+        public static final int MESSAGE_TYPE_INBOX  = 1;
+        public static final int MESSAGE_TYPE_SENT   = 2;
+        public static final int MESSAGE_TYPE_DRAFT  = 3;
+        public static final int MESSAGE_TYPE_OUTBOX = 4;
+        public static final int MESSAGE_TYPE_FAILED = 5; // for failed outgoing messages
+        public static final int MESSAGE_TYPE_QUEUED = 6; // for messages to send later
+
+
+        /**
+         * The thread ID of the message
+         * <P>Type: INTEGER</P>
+         */
+        public static final String THREAD_ID = "thread_id";
+
+        /**
+         * The address of the other party
+         * <P>Type: TEXT</P>
+         */
+        public static final String ADDRESS = "address";
+
+        /**
+         * The person ID of the sender
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String PERSON_ID = "person";
+
+        /**
+         * The date the message was sent
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DATE = "date";
+
+        /**
+         * Has the message been read
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String READ = "read";
+
+        /**
+         * The TP-Status value for the message, or -1 if no status has
+         * been received
+         */
+        public static final String STATUS = "status";
+
+        public static final int STATUS_NONE = -1;
+        public static final int STATUS_COMPLETE = 0;
+        public static final int STATUS_PENDING = 64;
+        public static final int STATUS_FAILED = 128;
+
+        /**
+         * The subject of the message, if present
+         * <P>Type: TEXT</P>
+         */
+        public static final String SUBJECT = "subject";
+
+        /**
+         * The body of the message
+         * <P>Type: TEXT</P>
+         */
+        public static final String BODY = "body";
+
+        /**
+         * The id of the sender of the conversation, if present
+         * <P>Type: INTEGER (reference to item in content://contacts/people)</P>
+         */
+        public static final String PERSON = "person";
+
+        /**
+         * The protocol identifier code
+         * <P>Type: INTEGER</P>
+         */
+        public static final String PROTOCOL = "protocol";
+
+        /**
+         * Whether the <code>TP-Reply-Path</code> bit was set on this message
+         * <P>Type: BOOLEAN</P>
+         */
+        public static final String REPLY_PATH_PRESENT = "reply_path_present";
+
+        /**
+         * The service center (SC) through which to send the message, if present
+         * <P>Type: TEXT</P>
+         */
+        public static final String SERVICE_CENTER = "service_center";
+
+        /**
+         * Has the message been locked?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LOCKED = "locked";
+
+        /**
+         * Error code associated with sending or receiving this message
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ERROR_CODE = "error_code";
+}
+
+    /**
+     * Contains all text based SMS messages.
+     */
+    public static final class Sms implements BaseColumns, TextBasedSmsColumns {
+        public static final Cursor query(ContentResolver cr, String[] projection) {
+            return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
+        }
+
+        public static final Cursor query(ContentResolver cr, String[] projection,
+                String where, String orderBy) {
+            return cr.query(CONTENT_URI, projection, where,
+                                         null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        }
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://sms");
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+        /**
+         * Add an SMS to the given URI.
+         *
+         * @param resolver the content resolver to use
+         * @param uri the URI to add the message to
+         * @param address the address of the sender
+         * @param body the body of the message
+         * @param subject the psuedo-subject of the message
+         * @param date the timestamp for the message
+         * @param read true if the message has been read, false if not
+         * @param deliveryReport true if a delivery report was requested, false if not
+         * @return the URI for the new message
+         */
+        public static Uri addMessageToUri(ContentResolver resolver,
+                Uri uri, String address, String body, String subject,
+                Long date, boolean read, boolean deliveryReport) {
+            return addMessageToUri(resolver, uri, address, body, subject,
+                    date, read, deliveryReport, -1L);
+        }
+
+        /**
+         * Add an SMS to the given URI with thread_id specified.
+         *
+         * @param resolver the content resolver to use
+         * @param uri the URI to add the message to
+         * @param address the address of the sender
+         * @param body the body of the message
+         * @param subject the psuedo-subject of the message
+         * @param date the timestamp for the message
+         * @param read true if the message has been read, false if not
+         * @param deliveryReport true if a delivery report was requested, false if not
+         * @param threadId the thread_id of the message
+         * @return the URI for the new message
+         */
+        public static Uri addMessageToUri(ContentResolver resolver,
+                Uri uri, String address, String body, String subject,
+                Long date, boolean read, boolean deliveryReport, long threadId) {
+            ContentValues values = new ContentValues(7);
+
+            values.put(ADDRESS, address);
+            if (date != null) {
+                values.put(DATE, date);
+            }
+            values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0));
+            values.put(SUBJECT, subject);
+            values.put(BODY, body);
+            if (deliveryReport) {
+                values.put(STATUS, STATUS_PENDING);
+            }
+            if (threadId != -1L) {
+                values.put(THREAD_ID, threadId);
+            }
+            return resolver.insert(uri, values);
+        }
+
+        /**
+         * Move a message to the given folder.
+         *
+         * @param context the context to use
+         * @param uri the message to move
+         * @param folder the folder to move to
+         * @return true if the operation succeeded
+         */
+        public static boolean moveMessageToFolder(Context context,
+                Uri uri, int folder, int error) {
+            if (uri == null) {
+                return false;
+            }
+
+            boolean markAsUnread = false;
+            boolean markAsRead = false;
+            switch(folder) {
+            case MESSAGE_TYPE_INBOX:
+            case MESSAGE_TYPE_DRAFT:
+                break;
+            case MESSAGE_TYPE_OUTBOX:
+            case MESSAGE_TYPE_SENT:
+                markAsRead = true;
+                break;
+            case MESSAGE_TYPE_FAILED:
+            case MESSAGE_TYPE_QUEUED:
+                markAsUnread = true;
+                break;
+            default:
+                return false;
+            }
+
+            ContentValues values = new ContentValues(3);
+
+            values.put(TYPE, folder);
+            if (markAsUnread) {
+                values.put(READ, Integer.valueOf(0));
+            } else if (markAsRead) {
+                values.put(READ, Integer.valueOf(1));
+            }
+            values.put(ERROR_CODE, error);
+
+            return 1 == SqliteWrapper.update(context, context.getContentResolver(),
+                            uri, values, null, null);
+        }
+
+        /**
+         * Returns true iff the folder (message type) identifies an
+         * outgoing message.
+         */
+        public static boolean isOutgoingFolder(int messageType) {
+            return  (messageType == MESSAGE_TYPE_FAILED)
+                    || (messageType == MESSAGE_TYPE_OUTBOX)
+                    || (messageType == MESSAGE_TYPE_SENT)
+                    || (messageType == MESSAGE_TYPE_QUEUED);
+        }
+
+        /**
+         * Contains all text based SMS messages in the SMS app's inbox.
+         */
+        public static final class Inbox implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                Uri.parse("content://sms/inbox");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * Add an SMS to the Draft box.
+             *
+             * @param resolver the content resolver to use
+             * @param address the address of the sender
+             * @param body the body of the message
+             * @param subject the psuedo-subject of the message
+             * @param date the timestamp for the message
+             * @param read true if the message has been read, false if not
+             * @return the URI for the new message
+             */
+            public static Uri addMessage(ContentResolver resolver,
+                    String address, String body, String subject, Long date,
+                    boolean read) {
+                return addMessageToUri(resolver, CONTENT_URI, address, body,
+                        subject, date, read, false);
+            }
+        }
+
+        /**
+         * Contains all sent text based SMS messages in the SMS app's.
+         */
+        public static final class Sent implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                    Uri.parse("content://sms/sent");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * Add an SMS to the Draft box.
+             *
+             * @param resolver the content resolver to use
+             * @param address the address of the sender
+             * @param body the body of the message
+             * @param subject the psuedo-subject of the message
+             * @param date the timestamp for the message
+             * @return the URI for the new message
+             */
+            public static Uri addMessage(ContentResolver resolver,
+                    String address, String body, String subject, Long date) {
+                return addMessageToUri(resolver, CONTENT_URI, address, body,
+                        subject, date, true, false);
+            }
+        }
+
+        /**
+         * Contains all sent text based SMS messages in the SMS app's.
+         */
+        public static final class Draft implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                    Uri.parse("content://sms/draft");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * Add an SMS to the Draft box.
+             *
+             * @param resolver the content resolver to use
+             * @param address the address of the sender
+             * @param body the body of the message
+             * @param subject the psuedo-subject of the message
+             * @param date the timestamp for the message
+             * @return the URI for the new message
+             */
+            public static Uri addMessage(ContentResolver resolver,
+                    String address, String body, String subject, Long date) {
+                return addMessageToUri(resolver, CONTENT_URI, address, body,
+                        subject, date, true, false);
+            }
+
+            /**
+             * Save over an existing draft message.
+             *
+             * @param resolver the content resolver to use
+             * @param uri of existing message
+             * @param body the new body for the draft message
+             * @return true is successful, false otherwise
+             */
+            public static boolean saveMessage(ContentResolver resolver,
+                    Uri uri, String body) {
+                ContentValues values = new ContentValues(2);
+                values.put(BODY, body);
+                values.put(DATE, System.currentTimeMillis());
+                return resolver.update(uri, values, null, null) == 1;
+            }
+        }
+
+        /**
+         * Contains all pending outgoing text based SMS messages.
+         */
+        public static final class Outbox implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                Uri.parse("content://sms/outbox");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * Add an SMS to the Out box.
+             *
+             * @param resolver the content resolver to use
+             * @param address the address of the sender
+             * @param body the body of the message
+             * @param subject the psuedo-subject of the message
+             * @param date the timestamp for the message
+             * @param deliveryReport whether a delivery report was requested for the message
+             * @return the URI for the new message
+             */
+            public static Uri addMessage(ContentResolver resolver,
+                    String address, String body, String subject, Long date,
+                    boolean deliveryReport, long threadId) {
+                return addMessageToUri(resolver, CONTENT_URI, address, body,
+                        subject, date, true, deliveryReport, threadId);
+            }
+        }
+
+        /**
+         * Contains all sent text-based SMS messages in the SMS app's.
+         */
+        public static final class Conversations
+                implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                Uri.parse("content://sms/conversations");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * The first 45 characters of the body of the message
+             * <P>Type: TEXT</P>
+             */
+            public static final String SNIPPET = "snippet";
+
+            /**
+             * The number of messages in the conversation
+             * <P>Type: INTEGER</P>
+             */
+            public static final String MESSAGE_COUNT = "msg_count";
+        }
+
+        /**
+         * Contains info about SMS related Intents that are broadcast.
+         */
+        public static final class Intents {
+            /**
+             * Set by BroadcastReceiver. Indicates the message was handled
+             * successfully.
+             */
+            public static final int RESULT_SMS_HANDLED = 1;
+
+            /**
+             * Set by BroadcastReceiver. Indicates a generic error while
+             * processing the message.
+             */
+            public static final int RESULT_SMS_GENERIC_ERROR = 2;
+
+            /**
+             * Set by BroadcastReceiver. Indicates insufficient memory to store
+             * the message.
+             */
+            public static final int RESULT_SMS_OUT_OF_MEMORY = 3;
+
+            /**
+             * Set by BroadcastReceiver. Indicates the message, while
+             * possibly valid, is of a format or encoding that is not
+             * supported.
+             */
+            public static final int RESULT_SMS_UNSUPPORTED = 4;
+
+            /**
+             * Broadcast Action: A new text based SMS message has been received
+             * by the device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>pdus</em> - An Object[] od byte[]s containing the PDUs
+             *   that make up the message.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            public static final String SMS_RECEIVED_ACTION =
+                    "android.provider.Telephony.SMS_RECEIVED";
+
+            /**
+             * Broadcast Action: A new data based SMS message has been received
+             * by the device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>pdus</em> - An Object[] od byte[]s containing the PDUs
+             *   that make up the message.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            public static final String DATA_SMS_RECEIVED_ACTION =
+                    "android.intent.action.DATA_SMS_RECEIVED";
+
+            /**
+             * Broadcast Action: A new WAP PUSH message has been received by the
+             * device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>transactionId (Integer)</em> - The WAP transaction
+             *    ID</li>
+             *   <li><em>pduType (Integer)</em> - The WAP PDU type</li>
+             *   <li><em>header (byte[])</em> - The header of the message</li>
+             *   <li><em>data (byte[])</em> - The data payload of the message</li>
+             * </ul>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            public static final String WAP_PUSH_RECEIVED_ACTION =
+                    "android.provider.Telephony.WAP_PUSH_RECEIVED";
+
+            /**
+             * Broadcast Action: The SIM storage for SMS messages is full.  If
+             * space is not freed, messages targeted for the SIM (class 2) may
+             * not be saved.
+             */
+            public static final String SIM_FULL_ACTION =
+                "android.provider.Telephony.SIM_FULL";
+
+            /**
+             * Broadcast Action: An incoming SMS has been rejected by the
+             * telephony framework.  This intent is sent in lieu of any
+             * of the RECEIVED_ACTION intents.  The intent will have the
+             * following extra value:</p>
+             *
+             * <ul>
+             *   <li><em>result</em> - An int result code, eg,
+             *   <code>{@link #RESULT_SMS_OUT_OF_MEMORY}</code>,
+             *   indicating the error returned to the network.</li>
+             * </ul>
+
+             */
+            public static final String SMS_REJECTED_ACTION =
+                "android.provider.Telephony.SMS_REJECTED";
+
+            /**
+             * Broadcast Action: The phone service state has changed. The intent will have the following
+             * extra values:</p>
+             * <ul>
+             *   <li><em>state</em> - An int with one of the following values:
+             *          {@link android.telephony.ServiceState#STATE_IN_SERVICE},
+             *          {@link android.telephony.ServiceState#STATE_OUT_OF_SERVICE},
+             *          {@link android.telephony.ServiceState#STATE_EMERGENCY_ONLY}
+             *          or {@link android.telephony.ServiceState#STATE_POWER_OFF}
+             *   <li><em>roaming</em> - A boolean value indicating whether the phone is roaming.</li>
+             *   <li><em>operator-alpha-long</em> - The carrier name as a string.</li>
+             *   <li><em>operator-alpha-short</em> - A potentially shortened version of the carrier name,
+             *          as a string.</li>
+             *   <li><em>operator-numeric</em> - A number representing the carrier, as a string. This is
+             *          a five or six digit number consisting of the MCC (Mobile Country Code, 3 digits)
+             *          and MNC (Mobile Network code, 2-3 digits).</li>
+             *   <li><em>manual</em> - A boolean, where true indicates that the user has chosen to select
+             *          the network manually, and false indicates that network selection is handled by the
+             *          phone.</li>
+             * </ul>
+             *
+             * <p class="note">
+             * Requires the READ_PHONE_STATE permission.
+             *
+             * <p class="note">This is a protected intent that can only be sent
+             * by the system.
+             */
+            public static final String ACTION_SERVICE_STATE_CHANGED =
+                "android.intent.action.SERVICE_STATE";
+
+            /**
+             * Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a
+             * {@link #DATA_SMS_RECEIVED_ACTION} intent.
+             *
+             * @param intent the intent to read from
+             * @return an array of SmsMessages for the PDUs
+             */
+            public static final SmsMessage[] getMessagesFromIntent(
+                    Intent intent) {
+                Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
+                byte[][] pduObjs = new byte[messages.length][];
+
+                for (int i = 0; i < messages.length; i++) {
+                    pduObjs[i] = (byte[]) messages[i];
+                }
+                byte[][] pdus = new byte[pduObjs.length][];
+                int pduCount = pdus.length;
+                SmsMessage[] msgs = new SmsMessage[pduCount];
+                for (int i = 0; i < pduCount; i++) {
+                    pdus[i] = pduObjs[i];
+                    msgs[i] = SmsMessage.createFromPdu(pdus[i]);
+                }
+                return msgs;
+            }
+        }
+    }
+
+    /**
+     * Base columns for tables that contain MMSs.
+     */
+    public interface BaseMmsColumns extends BaseColumns {
+
+        public static final int MESSAGE_BOX_ALL    = 0;
+        public static final int MESSAGE_BOX_INBOX  = 1;
+        public static final int MESSAGE_BOX_SENT   = 2;
+        public static final int MESSAGE_BOX_DRAFTS = 3;
+        public static final int MESSAGE_BOX_OUTBOX = 4;
+
+        /**
+         * The date the message was sent.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DATE = "date";
+
+        /**
+         * The box which the message belong to, for example, MESSAGE_BOX_INBOX.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_BOX = "msg_box";
+
+        /**
+         * Has the message been read.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String READ = "read";
+
+        /**
+         * The Message-ID of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MESSAGE_ID = "m_id";
+
+        /**
+         * The subject of the message, if present.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SUBJECT = "sub";
+
+        /**
+         * The character set of the subject, if present.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String SUBJECT_CHARSET = "sub_cs";
+
+        /**
+         * The Content-Type of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CONTENT_TYPE = "ct_t";
+
+        /**
+         * The Content-Location of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CONTENT_LOCATION = "ct_l";
+
+        /**
+         * The address of the sender.
+         * <P>Type: TEXT</P>
+         */
+        public static final String FROM = "from";
+
+        /**
+         * The address of the recipients.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TO = "to";
+
+        /**
+         * The address of the cc. recipients.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CC = "cc";
+
+        /**
+         * The address of the bcc. recipients.
+         * <P>Type: TEXT</P>
+         */
+        public static final String BCC = "bcc";
+
+        /**
+         * The expiry time of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String EXPIRY = "exp";
+
+        /**
+         * The class of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MESSAGE_CLASS = "m_cls";
+
+        /**
+         * The type of the message defined by MMS spec.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_TYPE = "m_type";
+
+        /**
+         * The version of specification that this message conform.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MMS_VERSION = "v";
+
+        /**
+         * The size of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_SIZE = "m_size";
+
+        /**
+         * The priority of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PRIORITY = "pri";
+
+        /**
+         * The read-report of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String READ_REPORT = "rr";
+
+        /**
+         * Whether the report is allowed.
+         * <P>Type: TEXT</P>
+         */
+        public static final String REPORT_ALLOWED = "rpt_a";
+
+        /**
+         * The response-status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String RESPONSE_STATUS = "resp_st";
+
+        /**
+         * The status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String STATUS = "st";
+
+        /**
+         * The transaction-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TRANSACTION_ID = "tr_id";
+
+        /**
+         * The retrieve-status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String RETRIEVE_STATUS = "retr_st";
+
+        /**
+         * The retrieve-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RETRIEVE_TEXT = "retr_txt";
+
+        /**
+         * The character set of the retrieve-text.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RETRIEVE_TEXT_CHARSET = "retr_txt_cs";
+
+        /**
+         * The read-status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String READ_STATUS = "read_status";
+
+        /**
+         * The content-class of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CONTENT_CLASS = "ct_cls";
+
+        /**
+         * The delivery-report of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DELIVERY_REPORT = "d_rpt";
+
+        /**
+         * The delivery-time-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DELIVERY_TIME_TOKEN = "d_tm_tok";
+
+        /**
+         * The delivery-time of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DELIVERY_TIME = "d_tm";
+
+        /**
+         * The response-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RESPONSE_TEXT = "resp_txt";
+
+        /**
+         * The sender-visibility of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SENDER_VISIBILITY = "s_vis";
+
+        /**
+         * The reply-charging of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String REPLY_CHARGING = "r_chg";
+
+        /**
+         * The reply-charging-deadline-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String REPLY_CHARGING_DEADLINE_TOKEN = "r_chg_dl_tok";
+
+        /**
+         * The reply-charging-deadline of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String REPLY_CHARGING_DEADLINE = "r_chg_dl";
+
+        /**
+         * The reply-charging-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String REPLY_CHARGING_ID = "r_chg_id";
+
+        /**
+         * The reply-charging-size of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String REPLY_CHARGING_SIZE = "r_chg_sz";
+
+        /**
+         * The previously-sent-by of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PREVIOUSLY_SENT_BY = "p_s_by";
+
+        /**
+         * The previously-sent-date of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String PREVIOUSLY_SENT_DATE = "p_s_d";
+
+        /**
+         * The store of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STORE = "store";
+
+        /**
+         * The mm-state of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MM_STATE = "mm_st";
+
+        /**
+         * The mm-flags-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MM_FLAGS_TOKEN = "mm_flg_tok";
+
+        /**
+         * The mm-flags of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MM_FLAGS = "mm_flg";
+
+        /**
+         * The store-status of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STORE_STATUS = "store_st";
+
+        /**
+         * The store-status-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STORE_STATUS_TEXT = "store_st_txt";
+
+        /**
+         * The stored of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STORED = "stored";
+
+        /**
+         * The totals of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TOTALS = "totals";
+
+        /**
+         * The mbox-totals of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MBOX_TOTALS = "mb_t";
+
+        /**
+         * The mbox-totals-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MBOX_TOTALS_TOKEN = "mb_t_tok";
+
+        /**
+         * The quotas of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String QUOTAS = "qt";
+
+        /**
+         * The mbox-quotas of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MBOX_QUOTAS = "mb_qt";
+
+        /**
+         * The mbox-quotas-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MBOX_QUOTAS_TOKEN = "mb_qt_tok";
+
+        /**
+         * The message-count of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_COUNT = "m_cnt";
+
+        /**
+         * The start of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String START = "start";
+
+        /**
+         * The distribution-indicator of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String DISTRIBUTION_INDICATOR = "d_ind";
+
+        /**
+         * The element-descriptor of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ELEMENT_DESCRIPTOR = "e_des";
+
+        /**
+         * The limit of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String LIMIT = "limit";
+
+        /**
+         * The recommended-retrieval-mode of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String RECOMMENDED_RETRIEVAL_MODE = "r_r_mod";
+
+        /**
+         * The recommended-retrieval-mode-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RECOMMENDED_RETRIEVAL_MODE_TEXT = "r_r_mod_txt";
+
+        /**
+         * The status-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STATUS_TEXT = "st_txt";
+
+        /**
+         * The applic-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String APPLIC_ID = "apl_id";
+
+        /**
+         * The reply-applic-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String REPLY_APPLIC_ID = "r_apl_id";
+
+        /**
+         * The aux-applic-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String AUX_APPLIC_ID = "aux_apl_id";
+
+        /**
+         * The drm-content of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String DRM_CONTENT = "drm_c";
+
+        /**
+         * The adaptation-allowed of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ADAPTATION_ALLOWED = "adp_a";
+
+        /**
+         * The replace-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String REPLACE_ID = "repl_id";
+
+        /**
+         * The cancel-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CANCEL_ID = "cl_id";
+
+        /**
+         * The cancel-status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CANCEL_STATUS = "cl_st";
+
+        /**
+         * The thread ID of the message
+         * <P>Type: INTEGER</P>
+         */
+        public static final String THREAD_ID = "thread_id";
+
+        /**
+         * Has the message been locked?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LOCKED = "locked";
+    }
+
+    /**
+     * Columns for the "canonical_addresses" table used by MMS and
+     * SMS."
+     */
+    public interface CanonicalAddressesColumns extends BaseColumns {
+        /**
+         * An address used in MMS or SMS.  Email addresses are
+         * converted to lower case and are compared by string
+         * equality.  Other addresses are compared using
+         * PHONE_NUMBERS_EQUAL.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ADDRESS = "address";
+    }
+
+    /**
+     * Columns for the "threads" table used by MMS and SMS.
+     */
+    public interface ThreadsColumns extends BaseColumns {
+        /**
+         * The date at which the thread was created.
+         *
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DATE = "date";
+
+        /**
+         * A string encoding of the recipient IDs of the recipients of
+         * the message, in numerical order and separated by spaces.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RECIPIENT_IDS = "recipient_ids";
+
+        /**
+         * The message count of the thread.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_COUNT = "message_count";
+        /**
+         * Indicates whether all messages of the thread have been read.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String READ = "read";
+        /**
+         * The snippet of the latest message in the thread.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SNIPPET = "snippet";
+        /**
+         * The charset of the snippet.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String SNIPPET_CHARSET = "snippet_cs";
+        /**
+         * Type of the thread, either Threads.COMMON_THREAD or
+         * Threads.BROADCAST_THREAD.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String TYPE = "type";
+        /**
+         * Indicates whether there is a transmission error in the thread.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ERROR = "error";
+        /**
+         * Indicates whether this thread contains any attachments.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String HAS_ATTACHMENT = "has_attachment";
+    }
+
+    /**
+     * Helper functions for the "threads" table used by MMS and SMS.
+     */
+    public static final class Threads implements ThreadsColumns {
+        private static final String[] ID_PROJECTION = { BaseColumns._ID };
+        private static final String STANDARD_ENCODING = "UTF-8";
+        private static final Uri THREAD_ID_CONTENT_URI = Uri.parse(
+                "content://mms-sms/threadID");
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(
+                MmsSms.CONTENT_URI, "conversations");
+        public static final Uri OBSOLETE_THREADS_URI = Uri.withAppendedPath(
+                CONTENT_URI, "obsolete");
+
+        public static final int COMMON_THREAD    = 0;
+        public static final int BROADCAST_THREAD = 1;
+
+        // No one should construct an instance of this class.
+        private Threads() {
+        }
+
+        /**
+         * This is a single-recipient version of
+         * getOrCreateThreadId.  It's convenient for use with SMS
+         * messages.
+         */
+        public static long getOrCreateThreadId(Context context, String recipient) {
+            Set<String> recipients = new HashSet<String>();
+
+            recipients.add(recipient);
+            return getOrCreateThreadId(context, recipients);
+        }
+
+        /**
+         * Given the recipients list and subject of an unsaved message,
+         * return its thread ID.  If the message starts a new thread,
+         * allocate a new thread ID.  Otherwise, use the appropriate
+         * existing thread ID.
+         *
+         * Find the thread ID of the same set of recipients (in
+         * any order, without any additions). If one
+         * is found, return it.  Otherwise, return a unique thread ID.
+         */
+        public static long getOrCreateThreadId(
+                Context context, Set<String> recipients) {
+            Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon();
+
+            for (String recipient : recipients) {
+                if (Mms.isEmailAddress(recipient)) {
+                    recipient = Mms.extractAddrSpec(recipient);
+                }
+
+                uriBuilder.appendQueryParameter("recipient", recipient);
+            }
+
+            Uri uri = uriBuilder.build();
+            if (DEBUG) {
+                Log.v(TAG, "getOrCreateThreadId uri: " + uri);
+            }
+            Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
+                    uri, ID_PROJECTION, null, null, null);
+            if (DEBUG) {
+                Log.v(TAG, "getOrCreateThreadId cursor cnt: " + cursor.getCount());
+            }
+            if (cursor != null) {
+                try {
+                    if (cursor.moveToFirst()) {
+                        return cursor.getLong(0);
+                    } else {
+                        Log.e(TAG, "getOrCreateThreadId returned no rows!");
+                    }
+                } finally {
+                    cursor.close();
+                }
+            }
+
+            Log.e(TAG, "getOrCreateThreadId failed with uri " + uri.toString());
+            throw new IllegalArgumentException("Unable to find or allocate a thread ID.");
+        }
+    }
+
+    /**
+     * Contains all MMS messages.
+     */
+    public static final class Mms implements BaseMmsColumns {
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://mms");
+
+        public static final Uri REPORT_REQUEST_URI = Uri.withAppendedPath(
+                                            CONTENT_URI, "report-request");
+
+        public static final Uri REPORT_STATUS_URI = Uri.withAppendedPath(
+                                            CONTENT_URI, "report-status");
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+        /**
+         * mailbox         =       name-addr
+         * name-addr       =       [display-name] angle-addr
+         * angle-addr      =       [CFWS] "<" addr-spec ">" [CFWS]
+         */
+        public static final Pattern NAME_ADDR_EMAIL_PATTERN =
+                Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*");
+
+        /**
+         * quoted-string   =       [CFWS]
+         *                         DQUOTE *([FWS] qcontent) [FWS] DQUOTE
+         *                         [CFWS]
+         */
+        public static final Pattern QUOTED_STRING_PATTERN =
+                Pattern.compile("\\s*\"([^\"]*)\"\\s*");
+
+        public static final Cursor query(
+                ContentResolver cr, String[] projection) {
+            return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
+        }
+
+        public static final Cursor query(
+                ContentResolver cr, String[] projection,
+                String where, String orderBy) {
+            return cr.query(CONTENT_URI, projection,
+                    where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        }
+
+        public static final String getMessageBoxName(int msgBox) {
+            switch (msgBox) {
+                case MESSAGE_BOX_ALL:
+                    return "all";
+                case MESSAGE_BOX_INBOX:
+                    return "inbox";
+                case MESSAGE_BOX_SENT:
+                    return "sent";
+                case MESSAGE_BOX_DRAFTS:
+                    return "drafts";
+                case MESSAGE_BOX_OUTBOX:
+                    return "outbox";
+                default:
+                    throw new IllegalArgumentException("Invalid message box: " + msgBox);
+            }
+        }
+
+        public static String extractAddrSpec(String address) {
+            Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address);
+
+            if (match.matches()) {
+                return match.group(2);
+            }
+            return address;
+        }
+
+        /**
+         * Returns true if the address is an email address
+         *
+         * @param address the input address to be tested
+         * @return true if address is an email address
+         */
+        public static boolean isEmailAddress(String address) {
+            if (TextUtils.isEmpty(address)) {
+                return false;
+            }
+
+            String s = extractAddrSpec(address);
+            Matcher match = Patterns.EMAIL_ADDRESS.matcher(s);
+            return match.matches();
+        }
+
+        /**
+         * Returns true if the number is a Phone number
+         *
+         * @param number the input number to be tested
+         * @return true if number is a Phone number
+         */
+        public static boolean isPhoneNumber(String number) {
+            if (TextUtils.isEmpty(number)) {
+                return false;
+            }
+
+            Matcher match = Patterns.PHONE.matcher(number);
+            return match.matches();
+        }
+
+        /**
+         * Contains all MMS messages in the MMS app's inbox.
+         */
+        public static final class Inbox implements BaseMmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri
+                    CONTENT_URI = Uri.parse("content://mms/inbox");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+        }
+
+        /**
+         * Contains all MMS messages in the MMS app's sent box.
+         */
+        public static final class Sent implements BaseMmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri
+                    CONTENT_URI = Uri.parse("content://mms/sent");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+        }
+
+        /**
+         * Contains all MMS messages in the MMS app's drafts box.
+         */
+        public static final class Draft implements BaseMmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri
+                    CONTENT_URI = Uri.parse("content://mms/drafts");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+        }
+
+        /**
+         * Contains all MMS messages in the MMS app's outbox.
+         */
+        public static final class Outbox implements BaseMmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri
+                    CONTENT_URI = Uri.parse("content://mms/outbox");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+        }
+
+        public static final class Addr implements BaseColumns {
+            /**
+             * The ID of MM which this address entry belongs to.
+             */
+            public static final String MSG_ID = "msg_id";
+
+            /**
+             * The ID of contact entry in Phone Book.
+             */
+            public static final String CONTACT_ID = "contact_id";
+
+            /**
+             * The address text.
+             */
+            public static final String ADDRESS = "address";
+
+            /**
+             * Type of address, must be one of PduHeaders.BCC,
+             * PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO.
+             */
+            public static final String TYPE = "type";
+
+            /**
+             * Character set of this entry.
+             */
+            public static final String CHARSET = "charset";
+        }
+
+        public static final class Part implements BaseColumns {
+            /**
+             * The identifier of the message which this part belongs to.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String MSG_ID = "mid";
+
+            /**
+             * The order of the part.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String SEQ = "seq";
+
+            /**
+             * The content type of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String CONTENT_TYPE = "ct";
+
+            /**
+             * The name of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String NAME = "name";
+
+            /**
+             * The charset of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String CHARSET = "chset";
+
+            /**
+             * The file name of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String FILENAME = "fn";
+
+            /**
+             * The content disposition of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String CONTENT_DISPOSITION = "cd";
+
+            /**
+             * The content ID of the part.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String CONTENT_ID = "cid";
+
+            /**
+             * The content location of the part.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String CONTENT_LOCATION = "cl";
+
+            /**
+             * The start of content-type of the message.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String CT_START = "ctt_s";
+
+            /**
+             * The type of content-type of the message.
+             * <P>Type: TEXT</P>
+             */
+            public static final String CT_TYPE = "ctt_t";
+
+            /**
+             * The location(on filesystem) of the binary data of the part.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String _DATA = "_data";
+
+            public static final String TEXT = "text";
+
+        }
+
+        public static final class Rate {
+            public static final Uri CONTENT_URI = Uri.withAppendedPath(
+                    Mms.CONTENT_URI, "rate");
+            /**
+             * When a message was successfully sent.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String SENT_TIME = "sent_time";
+        }
+
+        public static final class ScrapSpace {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI = Uri.parse("content://mms/scrapSpace");
+
+            /**
+             * This is the scrap file we use to store the media attachment when the user
+             * chooses to capture a photo to be attached . We pass {#link@Uri} to the Camera app,
+             * which streams the captured image to the uri. Internally we write the media content
+             * to this file. It's named '.temp.jpg' so Gallery won't pick it up.
+             */
+            public static final String SCRAP_FILE_PATH = "/sdcard/mms/scrapSpace/.temp.jpg";
+        }
+
+        public static final class Intents {
+            private Intents() {
+                // Non-instantiatable.
+            }
+
+            /**
+             * The extra field to store the contents of the Intent,
+             * which should be an array of Uri.
+             */
+            public static final String EXTRA_CONTENTS = "contents";
+            /**
+             * The extra field to store the type of the contents,
+             * which should be an array of String.
+             */
+            public static final String EXTRA_TYPES    = "types";
+            /**
+             * The extra field to store the 'Cc' addresses.
+             */
+            public static final String EXTRA_CC       = "cc";
+            /**
+             * The extra field to store the 'Bcc' addresses;
+             */
+            public static final String EXTRA_BCC      = "bcc";
+            /**
+             * The extra field to store the 'Subject'.
+             */
+            public static final String EXTRA_SUBJECT  = "subject";
+            /**
+             * Indicates that the contents of specified URIs were changed.
+             * The application which is showing or caching these contents
+             * should be updated.
+             */
+            public static final String
+            CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED";
+            /**
+             * An extra field which stores the URI of deleted contents.
+             */
+            public static final String DELETED_CONTENTS = "deleted_contents";
+        }
+    }
+
+    /**
+     * Contains all MMS and SMS messages.
+     */
+    public static final class MmsSms implements BaseColumns {
+        /**
+         * The column to distinguish SMS &amp; MMS messages in query results.
+         */
+        public static final String TYPE_DISCRIMINATOR_COLUMN =
+                "transport_type";
+
+        public static final Uri CONTENT_URI = Uri.parse("content://mms-sms/");
+
+        public static final Uri CONTENT_CONVERSATIONS_URI = Uri.parse(
+                "content://mms-sms/conversations");
+
+        public static final Uri CONTENT_FILTER_BYPHONE_URI = Uri.parse(
+                "content://mms-sms/messages/byphone");
+
+        public static final Uri CONTENT_UNDELIVERED_URI = Uri.parse(
+                "content://mms-sms/undelivered");
+
+        public static final Uri CONTENT_DRAFT_URI = Uri.parse(
+                "content://mms-sms/draft");
+
+        public static final Uri CONTENT_LOCKED_URI = Uri.parse(
+                "content://mms-sms/locked");
+
+        /***
+         * Pass in a query parameter called "pattern" which is the text
+         * to search for.
+         * The sort order is fixed to be thread_id ASC,date DESC.
+         */
+        public static final Uri SEARCH_URI = Uri.parse(
+                "content://mms-sms/search");
+
+        // Constants for message protocol types.
+        public static final int SMS_PROTO = 0;
+        public static final int MMS_PROTO = 1;
+
+        // Constants for error types of pending messages.
+        public static final int NO_ERROR                      = 0;
+        public static final int ERR_TYPE_GENERIC              = 1;
+        public static final int ERR_TYPE_SMS_PROTO_TRANSIENT  = 2;
+        public static final int ERR_TYPE_MMS_PROTO_TRANSIENT  = 3;
+        public static final int ERR_TYPE_TRANSPORT_FAILURE    = 4;
+        public static final int ERR_TYPE_GENERIC_PERMANENT    = 10;
+        public static final int ERR_TYPE_SMS_PROTO_PERMANENT  = 11;
+        public static final int ERR_TYPE_MMS_PROTO_PERMANENT  = 12;
+
+        public static final class PendingMessages implements BaseColumns {
+            public static final Uri CONTENT_URI = Uri.withAppendedPath(
+                    MmsSms.CONTENT_URI, "pending");
+            /**
+             * The type of transport protocol(MMS or SMS).
+             * <P>Type: INTEGER</P>
+             */
+            public static final String PROTO_TYPE = "proto_type";
+            /**
+             * The ID of the message to be sent or downloaded.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String MSG_ID = "msg_id";
+            /**
+             * The type of the message to be sent or downloaded.
+             * This field is only valid for MM. For SM, its value is always
+             * set to 0.
+             */
+            public static final String MSG_TYPE = "msg_type";
+            /**
+             * The type of the error code.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String ERROR_TYPE = "err_type";
+            /**
+             * The error code of sending/retrieving process.
+             * <P>Type:  INTEGER</P>
+             */
+            public static final String ERROR_CODE = "err_code";
+            /**
+             * How many times we tried to send or download the message.
+             * <P>Type:  INTEGER</P>
+             */
+            public static final String RETRY_INDEX = "retry_index";
+            /**
+             * The time to do next retry.
+             */
+            public static final String DUE_TIME = "due_time";
+            /**
+             * The time we last tried to send or download the message.
+             */
+            public static final String LAST_TRY = "last_try";
+        }
+    }
+
+    public static final class Carriers implements BaseColumns {
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://telephony/carriers");
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "name ASC";
+
+        public static final String NAME = "name";
+
+        public static final String APN = "apn";
+
+        public static final String PROXY = "proxy";
+
+        public static final String PORT = "port";
+
+        public static final String MMSPROXY = "mmsproxy";
+
+        public static final String MMSPORT = "mmsport";
+
+        public static final String SERVER = "server";
+
+        public static final String USER = "user";
+
+        public static final String PASSWORD = "password";
+
+        public static final String MMSC = "mmsc";
+
+        public static final String MCC = "mcc";
+
+        public static final String MNC = "mnc";
+
+        public static final String NUMERIC = "numeric";
+
+        public static final String AUTH_TYPE = "authtype";
+
+        public static final String TYPE = "type";
+
+        public static final String CURRENT = "current";
+    }
+
+    public static final class Intents {
+        private Intents() {
+            // Not instantiable
+        }
+
+        /**
+         * Broadcast Action: A "secret code" has been entered in the dialer. Secret codes are
+         * of the form *#*#<code>#*#*. The intent will have the data URI:</p>
+         *
+         * <p><code>android_secret_code://&lt;code&gt;</code></p>
+         */
+        public static final String SECRET_CODE_ACTION =
+                "android.provider.Telephony.SECRET_CODE";
+
+        /**
+         * Broadcast Action: The Service Provider string(s) have been updated.  Activities or
+         * services that use these strings should update their display.
+         * The intent will have the following extra values:</p>
+         * <ul>
+         *   <li><em>showPlmn</em> - Boolean that indicates whether the PLMN should be shown.</li>
+         *   <li><em>plmn</em> - The operator name of the registered network, as a string.</li>
+         *   <li><em>showSpn</em> - Boolean that indicates whether the SPN should be shown.</li>
+         *   <li><em>spn</em> - The service provider name, as a string.</li>
+         * </ul>
+         * Note that <em>showPlmn</em> may indicate that <em>plmn</em> should be displayed, even
+         * though the value for <em>plmn</em> is null.  This can happen, for example, if the phone
+         * has not registered to a network yet.  In this case the receiver may substitute an
+         * appropriate placeholder string (eg, "No service").
+         *
+         * It is recommended to display <em>plmn</em> before / above <em>spn</em> if
+         * both are displayed.
+         *
+         * <p>Note this is a protected intent that can only be sent
+         * by the system.
+         */
+        public static final String SPN_STRINGS_UPDATED_ACTION =
+                "android.provider.Telephony.SPN_STRINGS_UPDATED";
+
+        public static final String EXTRA_SHOW_PLMN  = "showPlmn";
+        public static final String EXTRA_PLMN       = "plmn";
+        public static final String EXTRA_SHOW_SPN   = "showSpn";
+        public static final String EXTRA_SPN        = "spn";
+    }
+}
diff --git a/core/java/com/google/android/mms/util/AbstractCache.java b/mms-common/java/com/android/common/mms/util/AbstractCache.java
similarity index 98%
rename from core/java/com/google/android/mms/util/AbstractCache.java
rename to mms-common/java/com/android/common/mms/util/AbstractCache.java
index 670439c..10a6fce 100644
--- a/core/java/com/google/android/mms/util/AbstractCache.java
+++ b/mms-common/java/com/android/common/mms/util/AbstractCache.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.util;
+package com.android.mmscommon.mms.util;
 
 import android.util.Config;
 import android.util.Log;
diff --git a/core/java/com/google/android/mms/util/PduCache.java b/mms-common/java/com/android/common/mms/util/PduCache.java
similarity index 98%
rename from core/java/com/google/android/mms/util/PduCache.java
rename to mms-common/java/com/android/common/mms/util/PduCache.java
index 7c3fad7..ca5432f 100644
--- a/core/java/com/google/android/mms/util/PduCache.java
+++ b/mms-common/java/com/android/common/mms/util/PduCache.java
@@ -15,12 +15,12 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.util;
+package com.android.mmscommon.mms.util;
 
 import android.content.ContentUris;
 import android.content.UriMatcher;
 import android.net.Uri;
-import android.provider.Telephony.Mms;
+import com.android.mmscommon.telephony.TelephonyProvider.Mms;
 import android.util.Config;
 import android.util.Log;
 
diff --git a/core/java/com/google/android/mms/util/PduCacheEntry.java b/mms-common/java/com/android/common/mms/util/PduCacheEntry.java
similarity index 92%
rename from core/java/com/google/android/mms/util/PduCacheEntry.java
rename to mms-common/java/com/android/common/mms/util/PduCacheEntry.java
index 8b41386..aed741d 100644
--- a/core/java/com/google/android/mms/util/PduCacheEntry.java
+++ b/mms-common/java/com/android/common/mms/util/PduCacheEntry.java
@@ -15,9 +15,9 @@
  * limitations under the License.
  */
 
-package com.google.android.mms.util;
+package com.android.mmscommon.mms.util;
 
-import com.google.android.mms.pdu.GenericPdu;
+import com.android.mmscommon.mms.pdu.GenericPdu;
 
 public final class PduCacheEntry {
     private final GenericPdu mPdu;
diff --git a/preloaded-classes b/preloaded-classes
index d108883..0c84904 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -686,10 +686,6 @@
 com.google.android.gles_jni.EGLDisplayImpl
 com.google.android.gles_jni.EGLImpl
 com.google.android.gles_jni.GLImpl
-com.google.android.mms.ContentType
-com.google.android.mms.pdu.CharacterSets
-com.google.android.mms.pdu.PduPart
-com.google.android.mms.pdu.PduPersister
 com.ibm.icu4jni.charset.CharsetDecoderICU
 com.ibm.icu4jni.charset.CharsetEncoderICU
 com.ibm.icu4jni.charset.CharsetICU
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 4417d7b..aa4956f 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -828,12 +828,15 @@
                     info.getExtraInfo());
         }
 
-        NetworkStateTracker newNet = tryFailover(prevNetType);
-        if (newNet != null) {
-            NetworkInfo switchTo = newNet.getNetworkInfo();
-            intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
-        } else {
-            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+        NetworkStateTracker newNet = null;
+        if (mNetAttributes[prevNetType].isDefault()) {
+            newNet = tryFailover(prevNetType);
+            if (newNet != null) {
+                NetworkInfo switchTo = newNet.getNetworkInfo();
+                intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
+            } else {
+                intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+            }
         }
         // do this before we broadcast the change
         handleConnectivityChange();
@@ -848,7 +851,7 @@
         }
     }
 
-    // returns -1 if no failover available
+    // returns null if no failover available
     private NetworkStateTracker tryFailover(int prevNetType) {
         /*
          * If this is a default network, check if other defaults are available
@@ -970,13 +973,17 @@
             info.setFailover(false);
         }
 
-        NetworkStateTracker newNet = tryFailover(info.getType());
-        if (newNet != null) {
-            NetworkInfo switchTo = newNet.getNetworkInfo();
-            intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
-        } else {
-            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+        NetworkStateTracker newNet = null;
+        if (mNetAttributes[info.getType()].isDefault()) {
+            newNet = tryFailover(info.getType());
+            if (newNet != null) {
+                NetworkInfo switchTo = newNet.getNetworkInfo();
+                intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
+            } else {
+                intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+            }
         }
+
         // do this before we broadcast the change
         handleConnectivityChange();
 
@@ -1280,9 +1287,16 @@
                     info = (NetworkInfo) msg.obj;
                     int type = info.getType();
                     NetworkInfo.State state = info.getState();
-                    if (mNetAttributes[type].mLastState == state) {
+                    // only do this optimization for wifi.  It going into scan mode for location
+                    // services generates alot of noise.  Meanwhile the mms apn won't send out
+                    // subsequent notifications when on default cellular because it never
+                    // disconnects..  so only do this to wifi notifications.  Fixed better when the
+                    // APN notifications are standardized.
+                    if (mNetAttributes[type].mLastState == state &&
+                            mNetAttributes[type].mRadio == ConnectivityManager.TYPE_WIFI) {
                         if (DBG) {
-                            // TODO - remove this after we validate the dropping doesn't break anything
+                            // TODO - remove this after we validate the dropping doesn't break
+                            // anything
                             Log.d(TAG, "Dropping ConnectivityChange for " +
                                     info.getTypeName() + ": " +
                                     state + "/" + info.getDetailedState());
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 7b8645f..17a3ab8 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -41,8 +41,10 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.util.Log;
 import android.util.Xml;
+import android.view.WindowManagerPolicy;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -161,30 +163,35 @@
         return null;
     }
     
-    ActiveAdmin getActiveAdminForCallerLocked(ComponentName who)
-            throws SecurityException {
-        ActiveAdmin admin = mAdminMap.get(who);
-        if (admin != null && admin.getUid() == Binder.getCallingUid()) {
-            if (who != null) {
-                if (!who.getPackageName().equals(admin.info.getActivityInfo().packageName)
-                        || !who.getClassName().equals(admin.info.getActivityInfo().name)) {
-                    throw new SecurityException("Current admin is not " + who);
-                }
-            }
-            return admin;
-        }
-        throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
-    }
-    
     ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
             throws SecurityException {
-        ActiveAdmin admin = getActiveAdminForCallerLocked(who);
-        if (!admin.info.usesPolicy(reqPolicy)) {
-            throw new SecurityException("Admin " + admin.info.getComponent()
-                    + " did not specify uses-policy for: "
-                    + admin.info.getTagForPolicy(reqPolicy));
+        final int callingUid = Binder.getCallingUid();
+        if (who != null) {
+            ActiveAdmin admin = mAdminMap.get(who);
+            if (admin == null) {
+                throw new SecurityException("No active admin " + who);
+            }
+            if (admin.getUid() != callingUid) {
+                throw new SecurityException("Admin " + who + " is not owned by uid "
+                        + Binder.getCallingUid());
+            }
+            if (!admin.info.usesPolicy(reqPolicy)) {
+                throw new SecurityException("Admin " + admin.info.getComponent()
+                        + " did not specify uses-policy for: "
+                        + admin.info.getTagForPolicy(reqPolicy));
+            }
+            return admin;
+        } else {
+            final int N = mAdminList.size();
+            for (int i=0; i<N; i++) {
+                ActiveAdmin admin = mAdminList.get(i);
+                if (admin.getUid() == callingUid && admin.info.usesPolicy(reqPolicy)) {
+                    return admin;
+                }
+            }
+            throw new SecurityException("No active admin owned by uid "
+                    + Binder.getCallingUid() + " for policy #" + reqPolicy);
         }
-        return admin;
     }
     
     void sendAdminCommandLocked(ActiveAdmin admin, String action) {
@@ -346,7 +353,7 @@
             // Ignore
         }
 
-        long timeMs = getMaximumTimeToLock();
+        long timeMs = getMaximumTimeToLock(null);
         if (timeMs <= 0) {
             timeMs = Integer.MAX_VALUE;
         }
@@ -355,7 +362,6 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Failure talking with power manager", e);
         }
-        
     }
 
     public void systemReady() {
@@ -443,10 +449,16 @@
         }
     }
     
-    public int getPasswordMode() {
+    public int getPasswordMode(ComponentName who) {
         synchronized (this) {
-            final int N = mAdminList.size();
             int mode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+            
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.passwordMode : mode;
+            }
+            
+            final int N = mAdminList.size();
             for  (int i=0; i<N; i++) {
                 ActiveAdmin admin = mAdminList.get(i);
                 if (mode < admin.passwordMode) {
@@ -457,7 +469,7 @@
         }
     }
     
-    public void setMinimumPasswordLength(ComponentName who, int length) {
+    public void setPasswordMinimumLength(ComponentName who, int length) {
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -471,10 +483,16 @@
         }
     }
     
-    public int getMinimumPasswordLength() {
+    public int getPasswordMinimumLength(ComponentName who) {
         synchronized (this) {
-            final int N = mAdminList.size();
             int length = 0;
+            
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.minimumPasswordLength : length;
+            }
+            
+            final int N = mAdminList.size();
             for  (int i=0; i<N; i++) {
                 ActiveAdmin admin = mAdminList.get(i);
                 if (length < admin.minimumPasswordLength) {
@@ -491,8 +509,8 @@
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(null,
                     DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
-            return mActivePasswordMode >= getPasswordMode()
-                    && mActivePasswordLength >= getMinimumPasswordLength();
+            return mActivePasswordMode >= getPasswordMode(null)
+                    && mActivePasswordLength >= getPasswordMinimumLength(null);
         }
     }
     
@@ -521,10 +539,16 @@
         }
     }
     
-    public int getMaximumFailedPasswordsForWipe() {
+    public int getMaximumFailedPasswordsForWipe(ComponentName who) {
         synchronized (this) {
-            final int N = mAdminList.size();
             int count = 0;
+            
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.maximumFailedPasswordsForWipe : count;
+            }
+            
+            final int N = mAdminList.size();
             for  (int i=0; i<N; i++) {
                 ActiveAdmin admin = mAdminList.get(i);
                 if (count == 0) {
@@ -545,8 +569,8 @@
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(null,
                     DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
-            mode = getPasswordMode();
-            if (password.length() < getMinimumPasswordLength()) {
+            mode = getPasswordMode(null);
+            if (password.length() < getPasswordMinimumLength(null)) {
                 return false;
             }
         }
@@ -577,9 +601,12 @@
                 long ident = Binder.clearCallingIdentity();
                 try {
                     saveSettingsLocked();
+                    
+                    timeMs = getMaximumTimeToLock(null);
                     if (timeMs <= 0) {
                         timeMs = Integer.MAX_VALUE;
                     }
+                    
                     try {
                         getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
                     } catch (RemoteException e) {
@@ -592,10 +619,16 @@
         }
     }
     
-    public long getMaximumTimeToLock() {
+    public long getMaximumTimeToLock(ComponentName who) {
         synchronized (this) {
-            final int N = mAdminList.size();
             long time = 0;
+            
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.maximumTimeToUnlock : time;
+            }
+            
+            final int N = mAdminList.size();
             for  (int i=0; i<N; i++) {
                 ActiveAdmin admin = mAdminList.get(i);
                 if (time == 0) {
@@ -615,7 +648,14 @@
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(null,
                     DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
-            // STOPSHIP need to implement.
+            long ident = Binder.clearCallingIdentity();
+            try {
+                mIPowerManager.goToSleepWithReason(SystemClock.uptimeMillis(),
+                        WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN);
+            } catch (RemoteException e) {
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
     }
     
@@ -702,7 +742,7 @@
             try {
                 mFailedPasswordAttempts++;
                 saveSettingsLocked();
-                int max = getMaximumFailedPasswordsForWipe();
+                int max = getMaximumFailedPasswordsForWipe(null);
                 if (max > 0 && mFailedPasswordAttempts >= max) {
                     wipeDataLocked(0);
                 }
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index a935131..9d69564 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -103,6 +103,7 @@
         // Retain only relevant bits
         int headsetState = newState & SUPPORTED_HEADSETS;
         int newOrOld = headsetState | mHeadsetState;
+        int delay = 0;
         // reject all suspect transitions: only accept state changes from:
         // - a: 0 heaset to 1 headset
         // - b: 1 headset to 0 headset
@@ -117,21 +118,25 @@
         if (headsetState == 0) {
             Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
             mContext.sendBroadcast(intent);
-
             // It can take hundreds of ms flush the audio pipeline after
             // apps pause audio playback, but audio route changes are
             // immediate, so delay the route change by 1000ms.
             // This could be improved once the audio sub-system provides an
             // interface to clear the audio pipeline.
-            mWakeLock.acquire();
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
-                                                               mHeadsetState,
-                                                               mPrevHeadsetState,
-                                                               mHeadsetName),
-                                        1000);
+            delay = 1000;
         } else {
-            sendIntents(mHeadsetState, mPrevHeadsetState, mHeadsetName);
+            // Insert the same delay for headset connection so that the connection event is not
+            // broadcast before the disconnection event in case of fast removal/insertion
+            if (mHandler.hasMessages(0)) {
+                delay = 1000;
+            }
         }
+        mWakeLock.acquire();
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
+                                                           mHeadsetState,
+                                                           mPrevHeadsetState,
+                                                           mHeadsetName),
+                                    delay);
     }
 
     private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 0b7cfae..05cea46 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -206,7 +206,7 @@
             if (mUmsEnabled) {
                 if (newUmsNotifyEnabled) {
                     Intent intent = new Intent();
-                    intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
+                    intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
                     PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
                     setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
                             com.android.internal.R.string.usb_storage_stop_notification_message,
@@ -866,7 +866,7 @@
 
         if (mUmsActiveNotify) {
             Intent intent = new Intent();
-            intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
+            intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
             PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
             setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
                     com.android.internal.R.string.usb_storage_stop_notification_message,
@@ -1104,7 +1104,6 @@
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission");
         }
-        mConnector.doCommand(String.format("destroy_asec %s", id));
         String cmd = String.format("mount_asec %s %s %d",
                                    id, key, ownerUid);
         mConnector.doCommand(cmd);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1cf3bad..cd4ae4cc 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -731,20 +731,32 @@
         }
     }
 
-    void cleanupInstallFailedPackage(PackageSetting pkgSettings) {
+    void cleanupInstallFailedPackage(PackageSetting ps) {
+        Log.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
         if (mInstaller != null) {
-            boolean useSecureFS = useEncryptedFilesystemForPackage(pkgSettings.pkg);
-            int retCode = mInstaller.remove(pkgSettings.name, useSecureFS);
+            boolean useSecureFS = useEncryptedFilesystemForPackage(ps.pkg);
+            int retCode = mInstaller.remove(ps.name, useSecureFS);
             if (retCode < 0) {
                 Log.w(TAG, "Couldn't remove app data directory for package: "
-                           + pkgSettings.name + ", retcode=" + retCode);
+                           + ps.name + ", retcode=" + retCode);
             }
         } else {
             //for emulator
-            File dataDir = new File(pkgSettings.pkg.applicationInfo.dataDir);
+            PackageParser.Package pkg = mPackages.get(ps.name);
+            File dataDir = new File(pkg.applicationInfo.dataDir);
             dataDir.delete();
         }
-        mSettings.removePackageLP(pkgSettings.name);
+        if (ps.codePath != null) {
+            if (!ps.codePath.delete()) {
+                Log.w(TAG, "Unable to remove old code file: " + ps.codePath);
+            }
+        }
+        if (ps.resourcePath != null) {
+            if (!ps.resourcePath.delete() && !ps.resourcePath.equals(ps.codePath)) {
+                Log.w(TAG, "Unable to remove old code file: " + ps.resourcePath);
+            }
+        }
+        mSettings.removePackageLP(ps.name);
     }
 
     void readPermissions() {
@@ -4357,18 +4369,13 @@
         String idxStr = "";
         int idx = 1;
         if (oldCodePath != null) {
-            int eidx = -1;
-            if (suffix != null) {
-                eidx = oldCodePath.indexOf(suffix);
+            String subStr = oldCodePath;
+            if (subStr.startsWith(prefix)) {
+                subStr = subStr.substring(prefix.length());
             }
-            if (eidx == -1) {
-                eidx = oldCodePath.length();
+            if (subStr.endsWith(suffix)) {
+                subStr = subStr.substring(0, subStr.length() - suffix.length());
             }
-            int sidx = oldCodePath.indexOf(prefix);
-            if (sidx == -1) {
-                sidx = 0;
-            }
-            String subStr = oldCodePath.substring(sidx + prefix.length(), eidx);
             if (subStr != null) {
                 if (subStr.startsWith("-")) {
                     subStr = subStr.substring(1);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index f106fc3..e14a973 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1101,11 +1101,10 @@
         // 0 was to turn it off, and we can't strip that, because keyguard needs to come
         // on, so have to run the queue then.
         if (index == 2) {
-            // Also, while we're collapsing them, if it's going to be an "off," and one
-            // is off because of user, then use that, regardless of whether it's the first
-            // or second one.
-            if (!on && why == WindowManagerPolicy.OFF_BECAUSE_OF_USER) {
-                mBroadcastWhy[0] = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
+            // While we're collapsing them, if it's going off, and the new reason
+            // is more significant than the first, then use the new one.
+            if (!on && mBroadcastWhy[0] > why) {
+                mBroadcastWhy[0] = why;
             }
             mBroadcastQueue[0] = on ? 1 : 0;
             mBroadcastQueue[1] = -1;
@@ -2012,6 +2011,10 @@
                 }
             }
         }
+
+        if (mPolicy != null) {
+            mPolicy.userActivity();
+        }
     }
 
     private int getAutoBrightnessValue(int sensorValue, int[] values) {
@@ -2136,9 +2139,18 @@
      */
     public void goToSleep(long time)
     {
+        goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+    }
+
+    /**
+     * The user requested that we go to sleep (probably with the power button).
+     * This overrides all wake locks that are held.
+     */
+    public void goToSleepWithReason(long time, int reason)
+    {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
         synchronized (mLocks) {
-            goToSleepLocked(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+            goToSleepLocked(time, reason);
         }
     }
 
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index d5de1f0..7951fb7 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -923,7 +923,7 @@
                 String value = field.value();
                 if (value != null) {
                     if (field != config.eap) {
-                        value = convertToQuotedString(value);
+                        value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
                     }
                     if (!WifiNative.setNetworkVariableCommand(
                                 netId,
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 9443a95..f2347ed 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -739,6 +739,8 @@
         
         // Black list this "bad" AP, this will cause an attempt to connect to another
         blacklistAp(ap.bssid);
+        // Initiate an association to an alternate AP
+        mWifiStateTracker.reassociate();
     }
 
     private void blacklistAp(String bssid) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 1d14e5e..88aadbd 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -2385,7 +2385,8 @@
                     // to provide the correct semantics while starting.
                     final int mask =
                         WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                        | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
                     WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs;
                     sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask);
                 }
diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java
index 17f3467..57c75e0 100644
--- a/services/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/java/com/android/server/am/AppNotRespondingDialog.java
@@ -113,7 +113,8 @@
                         ProcessRecord app = mProc;
 
                         if (msg.what == WAIT_AND_REPORT) {
-                            appErrorIntent = mService.createAppErrorIntentLocked(app, 0, null);
+                            appErrorIntent = mService.createAppErrorIntentLocked(app,
+                                    System.currentTimeMillis(), null);
                         }
 
                         app.notResponding = false;
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index fffc3cb..917fbfe 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -20,9 +20,12 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -96,6 +99,12 @@
      */
     private boolean mDataRoaming = false;
 
+    /**
+     * Mark when service state is in emergency call only mode
+     */
+    private boolean mEmergencyOnly = false;
+    private boolean mNewEmergencyOnly = false;
+
     private RegistrantList gprsAttachedRegistrants = new RegistrantList();
     private RegistrantList gprsDetachedRegistrants = new RegistrantList();
     private RegistrantList psRestrictEnabledRegistrants = new RegistrantList();
@@ -160,6 +169,16 @@
 
     static final int MAX_NUM_DATA_STATE_READS = 15;
 
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) {
+                // update emergency string whenever locale changed
+                updateSpnDisplay();
+            }
+        }
+    };
+
     private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
         @Override
         public void onChange(boolean selfChange) {
@@ -205,6 +224,11 @@
                 mAutoTimeObserver);
         setSignalStrengthDefaultValues();
         mNeedToRegForSimLoaded = true;
+
+        // Monitor locale change
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
+        phone.getContext().registerReceiver(mIntentReceiver, filter);
     }
 
     public void dispose() {
@@ -559,13 +583,20 @@
         String spn = phone.mSIMRecords.getServiceProviderName();
         String plmn = ss.getOperatorAlphaLong();
 
+        // For emergency calls only, pass the EmergencyCallsOnly string via EXTRA_PLMN
+        if (mEmergencyOnly && cm.getRadioState().isOn()) {
+            plmn = Resources.getSystem().
+                getText(com.android.internal.R.string.emergency_calls_only).toString();
+        }
+
         if (rule != curSpnRule
                 || !TextUtils.equals(spn, curSpn)
                 || !TextUtils.equals(plmn, curPlmn)) {
-            boolean showSpn =
-                (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN;
+            boolean showSpn = mEmergencyOnly
+                || (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN;
             boolean showPlmn =
                 (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN;
+
             Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
             intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn);
@@ -574,6 +605,7 @@
             intent.putExtra(Intents.EXTRA_PLMN, plmn);
             phone.getContext().sendStickyBroadcast(intent);
         }
+
         curSpnRule = rule;
         curSpn = spn;
         curPlmn = plmn;
@@ -639,6 +671,13 @@
 
                     mGsmRoaming = regCodeIsRoaming(regState);
                     newSS.setState (regCodeToServiceState(regState));
+
+                    if (regState == 10 || regState == 12 || regState == 13 || regState == 14) {
+                        mNewEmergencyOnly = true;
+                    } else {
+                        mNewEmergencyOnly = false;
+                    }
+
                     // LAC and CID are -1 if not avail
                     newCellLoc.setLacAndCid(lac, cid);
                 break;
@@ -848,6 +887,8 @@
 
         boolean hasLocationChanged = !newCellLoc.equals(cellLoc);
 
+        boolean hasEmergencyOnlyChanged = mNewEmergencyOnly != mEmergencyOnly;
+
         ServiceState tss;
         tss = ss;
         ss = newSS;
@@ -859,6 +900,8 @@
         cellLoc = newCellLoc;
         newCellLoc = tcl;
 
+        mEmergencyOnly = mNewEmergencyOnly;
+
 
         // Add an event log when network type switched
         // TODO: we may add filtering to reduce the event logged,
@@ -958,10 +1001,13 @@
             phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
                 ss.getRoaming() ? "true" : "false");
 
-            updateSpnDisplay();
             phone.notifyServiceStateChanged(ss);
         }
 
+        if (hasChanged || hasEmergencyOnlyChanged) {
+            updateSpnDisplay();
+        }
+
         if (hasGprsAttached) {
             gprsAttachedRegistrants.notifyRegistrants();
         }
@@ -1208,6 +1254,10 @@
             case 2: // 2 is "searching"
             case 3: // 3 is "registration denied"
             case 4: // 4 is "unknown" no vaild in current baseband
+            case 10:// same as 0, but indicates that emergency call is possible.
+            case 12:// same as 2, but indicates that emergency call is possible.
+            case 13:// same as 3, but indicates that emergency call is possible.
+            case 14:// same as 4, but indicates that emergency call is possible.
                 return ServiceState.STATE_OUT_OF_SERVICE;
 
             case 1:
diff --git a/tests/CoreTests/android/Android.mk b/tests/CoreTests/android/Android.mk
index e6b5c45..012e5eb 100644
--- a/tests/CoreTests/android/Android.mk
+++ b/tests/CoreTests/android/Android.mk
@@ -5,8 +5,6 @@
 
 LOCAL_SRC_FILES := \
 	$(call all-subdir-java-files)
-LOCAL_SRC_FILES += \
-	$(call all-java-files-under, ../com)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 1e322bd..0928d2b 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -119,6 +119,26 @@
 
     private final WifiStateTracker mWifiStateTracker;
 
+    /**
+     * This indicates the supplicant connection for the monitor is closed
+     */
+    private static final String monitorSocketClosed = "connection closed";
+
+    /**
+     * This indicates a read error on the monitor socket conenction
+     */
+    private static final String wpaRecvError = "recv error";
+
+    /**
+     * Tracks consecutive receive errors
+     */
+    private int mRecvErrors = 0;
+
+    /**
+     * Max errors before we close supplicant connection
+     */
+    private static final int MAX_RECV_ERRORS    = 10;
+
     public WifiMonitor(WifiStateTracker tracker) {
         mWifiStateTracker = tracker;
     }
@@ -151,16 +171,13 @@
             for (;;) {
                 String eventStr = WifiNative.waitForEvent();
 
-                if (eventStr == null) {
-                    continue;
-                }
-
                 // Skip logging the common but mostly uninteresting scan-results event
                 if (Config.LOGD && eventStr.indexOf(scanResultsEvent) == -1) {
                     Log.v(TAG, "Event [" + eventStr + "]");
                 }
                 if (!eventStr.startsWith(eventPrefix)) {
-                    if (eventStr.startsWith(wpaEventPrefix) && 0 < eventStr.indexOf(passwordKeyMayBeIncorrectEvent)) {
+                    if (eventStr.startsWith(wpaEventPrefix) &&
+                            0 < eventStr.indexOf(passwordKeyMayBeIncorrectEvent)) {
                         handlePasswordKeyMayBeIncorrect();
                     }
                     continue;
@@ -216,12 +233,38 @@
                 } else if (event == DRIVER_STATE) {
                     handleDriverEvent(eventData);
                 } else if (event == TERMINATING) {
+                    /**
+                     * If monitor socket is closed, we have already
+                     * stopped the supplicant, simply exit the monitor thread
+                     */
+                    if (eventData.startsWith(monitorSocketClosed)) {
+                        if (Config.LOGD) {
+                            Log.d(TAG, "Monitor socket is closed, exiting thread");
+                        }
+                        break;
+                    }
+
+                    /**
+                     * Close the supplicant connection if we see
+                     * too many recv errors
+                     */
+                    if (eventData.startsWith(wpaRecvError)) {
+                        if (++mRecvErrors > MAX_RECV_ERRORS) {
+                            if (Config.LOGD) {
+                                Log.d(TAG, "too many recv errors, closing connection");
+                            }
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    // notify and exit
                     mWifiStateTracker.notifySupplicantLost();
-                    // If supplicant is gone, exit the thread
                     break;
                 } else {
                     handleEvent(event, eventData);
                 }
+                mRecvErrors = 0;
             }
         }
 
@@ -293,6 +336,7 @@
         private void handleSupplicantStateChange(String dataString) {
             String[] dataTokens = dataString.split(" ");
 
+            String BSSID = null;
             int networkId = -1;
             int newState  = -1;
             for (String token : dataTokens) {
@@ -301,6 +345,11 @@
                     continue;
                 }
 
+                if (nameValue[0].equals("BSSID")) {
+                    BSSID = nameValue[1];
+                    continue;
+                }
+
                 int value;
                 try {
                     value = Integer.parseInt(nameValue[1]);
@@ -328,7 +377,7 @@
             if (newSupplicantState == SupplicantState.INVALID) {
                 Log.w(TAG, "Invalid supplicant state: " + newState);
             }
-            mWifiStateTracker.notifyStateChange(networkId, newSupplicantState);
+            mWifiStateTracker.notifyStateChange(networkId, BSSID, newSupplicantState);
         }
     }
 
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 2668fe0..cb615d2 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -304,11 +304,13 @@
      * thread.
      */
     private static class SupplicantStateChangeResult {
-        SupplicantStateChangeResult(int networkId, SupplicantState state) {
+        SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state) {
             this.state = state;
+            this.BSSID = BSSID;
             this.networkId = networkId;
         }
         int networkId;
+        String BSSID;
         SupplicantState state;
     }
 
@@ -512,10 +514,10 @@
      * @param networkId the configured network on which the state change occurred
      * @param newState the new {@code SupplicantState}
      */
-    void notifyStateChange(int networkId, SupplicantState newState) {
+    void notifyStateChange(int networkId, String BSSID, SupplicantState newState) {
         Message msg = Message.obtain(
             this, EVENT_SUPPLICANT_STATE_CHANGED,
-            new SupplicantStateChangeResult(networkId, newState));
+            new SupplicantStateChangeResult(networkId, BSSID, newState));
         msg.sendToTarget();
     }
 
@@ -884,6 +886,13 @@
 
                 int networkId = supplicantStateResult.networkId;
 
+                /**
+                 * The SupplicantState BSSID value is valid in ASSOCIATING state only.
+                 * The NetworkState BSSID value comes upon a successful connection.
+                 */
+                if (supplicantStateResult.state == SupplicantState.ASSOCIATING) {
+                    mLastBssid = supplicantStateResult.BSSID;
+                }
                 /*
                  * If we get disconnect or inactive we need to start our
                  * watchdog timer to start a scan
@@ -928,6 +937,7 @@
                     setSupplicantState(newState);
                     if (newState == SupplicantState.DORMANT) {
                         DetailedState newDetailedState;
+                        Message reconnectMsg = obtainMessage(EVENT_DEFERRED_RECONNECT, mLastBssid);
                         if (mIsScanOnly || mRunState == RUN_STATE_STOPPING) {
                             newDetailedState = DetailedState.IDLE;
                         } else {
@@ -942,7 +952,7 @@
                          * milliseconds.
                          */
                         if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly && networkId != -1) {
-                            sendEmptyMessageDelayed(EVENT_DEFERRED_RECONNECT, RECONNECT_DELAY_MSECS);
+                            sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS);
                         } else if (mRunState == RUN_STATE_STOPPING) {
                             synchronized (this) {
                                 WifiNative.stopDriverCommand();
@@ -1104,15 +1114,19 @@
                 break;
 
             case EVENT_DEFERRED_RECONNECT:
-                /*
+                String BSSID = msg.obj.toString();
+                /**
                  * If we've exceeded the maximum number of retries for reconnecting
-                 * to a given network, disable the network so that the supplicant
-                 * will try some other network, if any is available.
-                 * TODO: network ID may have changed since we stored it.
+                 * to a given network, blacklist the BSSID to allow a connection attempt on
+                 * an alternate BSSID if available
                  */
                 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
                     if (++mReconnectCount > getMaxDhcpRetries()) {
-                        mWM.disableNetwork(mLastNetworkId);
+                        if (LOCAL_LOGD) {
+                            Log.d(TAG, "Failed reconnect count: " +
+                                    mReconnectCount + " Blacklisting " + BSSID);
+                        }
+                        addToBlacklist(BSSID);
                     }
                     synchronized(this) {
                         WifiNative.reconnectCommand();
@@ -1684,6 +1698,10 @@
         mNumScansSinceNetworkStateChange = 0;
     }
     
+    public synchronized boolean reassociate() {
+        return WifiNative.reassociateCommand();
+    }
+
     public synchronized boolean addToBlacklist(String bssid) {
         return WifiNative.addToBlacklistCommand(bssid);
     }