Merge "Preview seperating RenderScript into RenderScript and RenderScriptGL"
diff --git a/Android.mk b/Android.mk
index ab1e7ea..ec6f96b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -111,8 +111,8 @@
 	core/java/android/net/INetworkManagementEventObserver.aidl \
 	core/java/android/os/ICheckinService.aidl \
 	core/java/android/os/IMessenger.aidl \
-	core/java/android/os/IMountService.aidl \
-	core/java/android/os/IMountServiceListener.aidl \
+	core/java/android/os/storage/IMountService.aidl \
+	core/java/android/os/storage/IMountServiceListener.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/INetStatService.aidl \
 	core/java/android/os/IParentalControlCallback.aidl \
diff --git a/api/current.xml b/api/current.xml
index c54fc98..a8f7109 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -29824,6 +29824,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="java.lang.Cloneable">
+</implements>
 <implements name="java.lang.Comparable">
 </implements>
 <implements name="android.os.Parcelable">
@@ -29874,6 +29876,17 @@
 <parameter name="in" type="android.os.Parcel">
 </parameter>
 </constructor>
+<method name="clone"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="compareTo"
  return="int"
  abstract="false"
@@ -31727,6 +31740,17 @@
  visibility="public"
 >
 </field>
+<field name="SYNC_EXTRAS_DO_NOT_RETRY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;do_not_retry&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SYNC_EXTRAS_EXPEDITED"
  type="java.lang.String"
  transient="false"
@@ -31749,6 +31773,28 @@
  visibility="public"
 >
 </field>
+<field name="SYNC_EXTRAS_IGNORE_BACKOFF"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;ignore_backoff&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SYNC_EXTRAS_IGNORE_SETTINGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;ignore_settings&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SYNC_EXTRAS_INITIALIZE"
  type="java.lang.String"
  transient="false"
@@ -32739,6 +32785,30 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
 <method name="getFileStreamPath"
  return="java.io.File"
  abstract="true"
@@ -34247,6 +34317,30 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
 <method name="getFileStreamPath"
  return="java.io.File"
  abstract="false"
@@ -41850,17 +41944,6 @@
  visibility="public"
 >
 </field>
-<field name="resourceDirs"
- type="java.lang.String[]"
- transient="false"
- volatile="false"
- value="null"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="sharedLibraryFiles"
  type="java.lang.String[]"
  transient="false"
@@ -77568,32 +77651,6 @@
 <parameter name="enabledOnly" type="boolean">
 </parameter>
 </method>
-<method name="installGeocodeProvider"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="provider" type="android.location.LocationManager.GeocodeProvider">
-</parameter>
-</method>
-<method name="installLocationProvider"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="provider" type="android.location.LocationProviderImpl">
-</parameter>
-</method>
 <method name="isProviderEnabled"
  return="boolean"
  abstract="false"
@@ -77685,19 +77742,6 @@
 <parameter name="intent" type="android.app.PendingIntent">
 </parameter>
 </method>
-<method name="reportLocation"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="location" type="android.location.Location">
-</parameter>
-</method>
 <method name="requestLocationUpdates"
  return="void"
  abstract="false"
@@ -77890,62 +77934,6 @@
 >
 </field>
 </class>
-<interface name="LocationManager.GeocodeProvider"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getFromLocation"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="latitude" type="double">
-</parameter>
-<parameter name="longitude" type="double">
-</parameter>
-<parameter name="maxResults" type="int">
-</parameter>
-<parameter name="params" type="android.location.GeocoderParams">
-</parameter>
-<parameter name="addrs" type="java.util.List&lt;android.location.Address&gt;">
-</parameter>
-</method>
-<method name="getFromLocationName"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="locationName" type="java.lang.String">
-</parameter>
-<parameter name="lowerLeftLatitude" type="double">
-</parameter>
-<parameter name="lowerLeftLongitude" type="double">
-</parameter>
-<parameter name="upperRightLatitude" type="double">
-</parameter>
-<parameter name="upperRightLongitude" type="double">
-</parameter>
-<parameter name="maxResults" type="int">
-</parameter>
-<parameter name="params" type="android.location.GeocoderParams">
-</parameter>
-<parameter name="addrs" type="java.util.List&lt;android.location.Address&gt;">
-</parameter>
-</method>
-</interface>
 <class name="LocationProvider"
  extends="java.lang.Object"
  abstract="true"
@@ -78111,166 +78099,6 @@
 >
 </field>
 </class>
-<class name="LocationProviderImpl"
- extends="android.location.LocationProvider"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="LocationProviderImpl"
- type="android.location.LocationProviderImpl"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-</constructor>
-<method name="addListener"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uid" type="int">
-</parameter>
-</method>
-<method name="disable"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="enable"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="enableLocationTracking"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enable" type="boolean">
-</parameter>
-</method>
-<method name="getStatus"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="extras" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="getStatusUpdateTime"
- return="long"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeListener"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uid" type="int">
-</parameter>
-</method>
-<method name="sendExtraCommand"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="command" type="java.lang.String">
-</parameter>
-<parameter name="extras" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="setMinTime"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="minTime" type="long">
-</parameter>
-</method>
-<method name="updateLocation"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="location" type="android.location.Location">
-</parameter>
-</method>
-<method name="updateNetworkState"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="state" type="int">
-</parameter>
-<parameter name="info" type="android.net.NetworkInfo">
-</parameter>
-</method>
-</class>
 </package>
 <package name="android.media"
 >
@@ -83248,6 +83076,25 @@
 <parameter name="mimeType" type="java.lang.String">
 </parameter>
 </method>
+<method name="scanFile"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="paths" type="java.lang.String[]">
+</parameter>
+<parameter name="mimeTypes" type="java.lang.String[]">
+</parameter>
+<parameter name="callback" type="android.media.MediaScannerConnection.ScanResultListener">
+</parameter>
+</method>
 </class>
 <interface name="MediaScannerConnection.MediaScannerConnectionClient"
  abstract="true"
@@ -83256,6 +83103,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.media.MediaScannerConnection.ScanResultListener">
+</implements>
 <method name="onMediaScannerConnected"
  return="void"
  abstract="true"
@@ -83283,6 +83132,29 @@
 </parameter>
 </method>
 </interface>
+<interface name="MediaScannerConnection.ScanResultListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onScanCompleted"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+</interface>
 <class name="Ringtone"
  extends="java.lang.Object"
  abstract="false"
@@ -112890,6 +112762,19 @@
  visibility="public"
 >
 </method>
+<method name="getExternalStoragePublicDirectory"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
 <method name="getExternalStorageState"
  return="java.lang.String"
  abstract="false"
@@ -112912,6 +112797,96 @@
  visibility="public"
 >
 </method>
+<field name="DIRECTORY_ALARMS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_DCIM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_DOWNLOADS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_MOVIES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_MUSIC"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_NOTIFICATIONS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_PICTURES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_PODCASTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_RINGTONES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="MEDIA_BAD_REMOVAL"
  type="java.lang.String"
  transient="false"
@@ -117866,6 +117841,218 @@
 </method>
 </class>
 </package>
+<package name="android.os.storage"
+>
+<class name="StorageEventListener"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StorageEventListener"
+ type="android.os.storage.StorageEventListener"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onStorageStateChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="oldState" type="java.lang.String">
+</parameter>
+<parameter name="newState" type="java.lang.String">
+</parameter>
+</method>
+<method name="onUsbMassStorageConnectionChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="connected" type="boolean">
+</parameter>
+</method>
+</class>
+<class name="StorageManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="disableUsbMassStorage"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="enableUsbMassStorage"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isUsbMassStorageConnected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isUsbMassStorageEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="registerListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.os.storage.StorageEventListener">
+</parameter>
+</method>
+<method name="unregisterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.os.storage.StorageEventListener">
+</parameter>
+</method>
+</class>
+<class name="StorageResultCode"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StorageResultCode"
+ type="android.os.storage.StorageResultCode"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="OperationFailedInternalError"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedMediaBlank"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedMediaCorrupt"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedNoMedia"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedVolumeNotMounted"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationSucceeded"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+</package>
 <package name="android.preference"
 >
 <class name="CheckBoxPreference"
@@ -136521,126 +136708,6 @@
 </method>
 </interface>
 </package>
-<package name="android.storage"
->
-<interface name="StorageEventListener"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onMediaInserted"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="label" type="java.lang.String">
-</parameter>
-<parameter name="path" type="java.lang.String">
-</parameter>
-<parameter name="major" type="int">
-</parameter>
-<parameter name="minor" type="int">
-</parameter>
-</method>
-<method name="onMediaRemoved"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="label" type="java.lang.String">
-</parameter>
-<parameter name="path" type="java.lang.String">
-</parameter>
-<parameter name="major" type="int">
-</parameter>
-<parameter name="minor" type="int">
-</parameter>
-<parameter name="clean" type="boolean">
-</parameter>
-</method>
-<method name="onShareAvailabilityChanged"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="method" type="java.lang.String">
-</parameter>
-<parameter name="available" type="boolean">
-</parameter>
-</method>
-<method name="onVolumeStateChanged"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="label" type="java.lang.String">
-</parameter>
-<parameter name="path" type="java.lang.String">
-</parameter>
-<parameter name="oldState" type="java.lang.String">
-</parameter>
-<parameter name="newState" type="java.lang.String">
-</parameter>
-</method>
-</interface>
-<class name="StorageManager"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="registerListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.storage.StorageEventListener">
-</parameter>
-</method>
-<method name="unregisterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.storage.StorageEventListener">
-</parameter>
-</method>
-</class>
-</package>
 <package name="android.telephony"
 >
 <class name="CellLocation"
@@ -144371,6 +144438,30 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
 <method name="getFileStreamPath"
  return="java.io.File"
  abstract="false"
@@ -181421,6 +181512,17 @@
 <parameter name="t" type="android.view.animation.Transformation">
 </parameter>
 </method>
+<method name="cancel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="computeDurationHint"
  return="long"
  abstract="false"
diff --git a/common/java/com/android/common/Patterns.java b/common/java/com/android/common/Patterns.java
index 24a18c0..71c3a5e 100644
--- a/common/java/com/android/common/Patterns.java
+++ b/common/java/com/android/common/Patterns.java
@@ -25,87 +25,87 @@
 public class Patterns {
     /**
      *  Regular expression pattern to match all IANA top-level domains.
-     *  List accurate as of 2007/06/15.  List taken from:
+     *  List accurate as of 2010/02/05.  List taken from:
      *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-     *  This pattern is auto-generated by //device/tools/make-iana-tld-pattern.py
+     *  This pattern is auto-generated by development/tools/make-iana-tld-pattern.py
      */
-    public static final Pattern TOP_LEVEL_DOMAIN
-        = Pattern.compile(
-                "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
-                + "|(biz|b[abdefghijmnorstvwyz])"
-                + "|(cat|com|coop|c[acdfghiklmnoruvxyz])"
-                + "|d[ejkmoz]"
-                + "|(edu|e[cegrstu])"
-                + "|f[ijkmor]"
-                + "|(gov|g[abdefghilmnpqrstuwy])"
-                + "|h[kmnrtu]"
-                + "|(info|int|i[delmnoqrst])"
-                + "|(jobs|j[emop])"
-                + "|k[eghimnrwyz]"
-                + "|l[abcikrstuvy]"
-                + "|(mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
-                + "|(name|net|n[acefgilopruz])"
-                + "|(org|om)"
-                + "|(pro|p[aefghklmnrstwy])"
-                + "|qa"
-                + "|r[eouw]"
-                + "|s[abcdeghijklmnortuvyz]"
-                + "|(tel|travel|t[cdfghjklmnoprtvwz])"
-                + "|u[agkmsyz]"
-                + "|v[aceginu]"
-                + "|w[fs]"
-                + "|y[etu]"
-                + "|z[amw])");
+    public static final Pattern TOP_LEVEL_DOMAIN = Pattern.compile(
+        "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+        + "|(biz|b[abdefghijmnorstvwyz])"
+        + "|(cat|com|coop|c[acdfghiklmnoruvxyz])"
+        + "|d[ejkmoz]"
+        + "|(edu|e[cegrstu])"
+        + "|f[ijkmor]"
+        + "|(gov|g[abdefghilmnpqrstuwy])"
+        + "|h[kmnrtu]"
+        + "|(info|int|i[delmnoqrst])"
+        + "|(jobs|j[emop])"
+        + "|k[eghimnprwyz]"
+        + "|l[abcikrstuvy]"
+        + "|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])"
+        + "|(name|net|n[acefgilopruz])"
+        + "|(org|om)"
+        + "|(pro|p[aefghklmnrstwy])"
+        + "|qa"
+        + "|r[eosuw]"
+        + "|s[abcdeghijklmnortuvyz]"
+        + "|(tel|travel|t[cdfghjklmnoprtvwz])"
+        + "|u[agksyz]"
+        + "|v[aceginu]"
+        + "|w[fs]"
+        + "|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)"
+        + "|y[etu]"
+        + "|z[amw])");
 
     /**
      *  Regular expression pattern to match RFC 1738 URLs
-     *  List accurate as of 2007/06/15.  List taken from:
+     *  List accurate as of 2010/02/05.  List taken from:
      *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-     *  This pattern is auto-generated by //device/tools/make-iana-tld-pattern.py
+     *  This pattern is auto-generated by development/tools/make-iana-tld-pattern.py
      */
-    public static final Pattern WEB_URL
-        = Pattern.compile(
-            "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
-            + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
-            + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
-            + "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+"   // named host
-            + "(?:"   // plus top level domain
-            + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
-            + "|(?:biz|b[abdefghijmnorstvwyz])"
-            + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
-            + "|d[ejkmoz]"
-            + "|(?:edu|e[cegrstu])"
-            + "|f[ijkmor]"
-            + "|(?:gov|g[abdefghilmnpqrstuwy])"
-            + "|h[kmnrtu]"
-            + "|(?:info|int|i[delmnoqrst])"
-            + "|(?:jobs|j[emop])"
-            + "|k[eghimnrwyz]"
-            + "|l[abcikrstuvy]"
-            + "|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
-            + "|(?:name|net|n[acefgilopruz])"
-            + "|(?:org|om)"
-            + "|(?:pro|p[aefghklmnrstwy])"
-            + "|qa"
-            + "|r[eouw]"
-            + "|s[abcdeghijklmnortuvyz]"
-            + "|(?:tel|travel|t[cdfghjklmnoprtvwz])"
-            + "|u[agkmsyz]"
-            + "|v[aceginu]"
-            + "|w[fs]"
-            + "|y[etu]"
-            + "|z[amw]))"
-            + "|(?:(?:25[0-5]|2[0-4]" // or ip address
-            + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
-            + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
-            + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
-            + "|[1-9][0-9]|[0-9])))"
-            + "(?:\\:\\d{1,5})?)" // plus option port number
-            + "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
-            + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
-            + "(?:\\b|$)"); // and finally, a word boundary or end of
-                            // input.  This is to stop foo.sure from
-                            // matching as foo.su
+    public static final Pattern WEB_URL = Pattern.compile(
+        "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+        + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+        + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+        + "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+"   // named host
+        + "(?:"   // plus top level domain
+        + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+        + "|(?:biz|b[abdefghijmnorstvwyz])"
+        + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
+        + "|d[ejkmoz]"
+        + "|(?:edu|e[cegrstu])"
+        + "|f[ijkmor]"
+        + "|(?:gov|g[abdefghilmnpqrstuwy])"
+        + "|h[kmnrtu]"
+        + "|(?:info|int|i[delmnoqrst])"
+        + "|(?:jobs|j[emop])"
+        + "|k[eghimnprwyz]"
+        + "|l[abcikrstuvy]"
+        + "|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])"
+        + "|(?:name|net|n[acefgilopruz])"
+        + "|(?:org|om)"
+        + "|(?:pro|p[aefghklmnrstwy])"
+        + "|qa"
+        + "|r[eosuw]"
+        + "|s[abcdeghijklmnortuvyz]"
+        + "|(?:tel|travel|t[cdfghjklmnoprtvwz])"
+        + "|u[agksyz]"
+        + "|v[aceginu]"
+        + "|w[fs]"
+        + "|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)"
+        + "|y[etu]"
+        + "|z[amw]))"
+        + "|(?:(?:25[0-5]|2[0-4]" // or ip address
+        + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
+        + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
+        + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+        + "|[1-9][0-9]|[0-9])))"
+        + "(?:\\:\\d{1,5})?)" // plus option port number
+        + "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
+        + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
+        + "(?:\\b|$)"); // and finally, a word boundary or end of
+                        // input.  This is to stop foo.sure from
+                        // matching as foo.su
 
     public static final Pattern IP_ADDRESS
         = Pattern.compile(
diff --git a/common/tests/src/com/android/common/PatternsTest.java b/common/tests/src/com/android/common/PatternsTest.java
index 7fabe5e..635601e 100644
--- a/common/tests/src/com/android/common/PatternsTest.java
+++ b/common/tests/src/com/android/common/PatternsTest.java
@@ -31,6 +31,20 @@
         t = Patterns.TOP_LEVEL_DOMAIN.matcher("com").matches();
         assertTrue("Missed valid TLD", t);
 
+        // One of the new top level domain.
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("me").matches();
+        assertTrue("Missed valid TLD", t);
+
+        // One of the new top level test domain.
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("xn--0zwm56d").matches();
+        assertTrue("Missed valid TLD", t);
+
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("mem").matches();
+        assertFalse("Matched invalid TLD!", t);
+
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("xn").matches();
+        assertFalse("Matched invalid TLD!", t);
+
         t = Patterns.TOP_LEVEL_DOMAIN.matcher("xer").matches();
         assertFalse("Matched invalid TLD!", t);
     }
@@ -42,6 +56,18 @@
         t = Patterns.WEB_URL.matcher("http://www.google.com").matches();
         assertTrue("Valid URL", t);
 
+        // Google in one of the new top level domain.
+        t = Patterns.WEB_URL.matcher("http://www.google.me").matches();
+        assertTrue("Valid URL", t);
+        t = Patterns.WEB_URL.matcher("google.me").matches();
+        assertTrue("Valid URL", t);
+
+        // Test url in Chinese: http://xn--fsqu00a.xn--0zwm56d
+        t = Patterns.WEB_URL.matcher("http://xn--fsqu00a.xn--0zwm56d").matches();
+        assertTrue("Valid URL", t);
+        t = Patterns.WEB_URL.matcher("xn--fsqu00a.xn--0zwm56d").matches();
+        assertTrue("Valid URL", t);
+
         t = Patterns.WEB_URL.matcher("ftp://www.example.com").matches();
         assertFalse("Matched invalid protocol", t);
 
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 19e741a..be15ac9 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -27,6 +27,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.Parcelable;
+import android.os.Build;
 import android.util.Log;
 
 import java.io.IOException;
@@ -837,14 +838,13 @@
     private void ensureNotOnMainThread() {
         final Looper looper = Looper.myLooper();
         if (looper != null && looper == mContext.getMainLooper()) {
-            // We really want to throw an exception here, but GTalkService exercises this
-            // path quite a bit and needs some serious rewrite in order to work properly.
-            //noinspection ThrowableInstanceNeverThrow
-//            Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
-//                    new Exception());
-            // TODO remove the log and throw this exception when the callers are fixed
-//            throw new IllegalStateException(
-//                    "calling this from your main thread can lead to deadlock");
+            final IllegalStateException exception = new IllegalStateException(
+                    "calling this from your main thread can lead to deadlock");
+            Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
+                    exception);
+            if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO) {
+                throw exception;
+            }
         }
     }
 
@@ -909,7 +909,9 @@
 
         private Bundle internalGetResult(Long timeout, TimeUnit unit)
                 throws OperationCanceledException, IOException, AuthenticatorException {
-            ensureNotOnMainThread();
+            if (!isDone()) {
+                ensureNotOnMainThread();
+            }
             try {
                 if (timeout == null) {
                     return get();
@@ -1075,7 +1077,9 @@
 
         private T internalGetResult(Long timeout, TimeUnit unit)
                 throws OperationCanceledException, IOException, AuthenticatorException {
-            ensureNotOnMainThread();
+            if (!isDone()) {
+                ensureNotOnMainThread();
+            }
             try {
                 if (timeout == null) {
                     return get();
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index e3ccd00..7850124 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -1788,7 +1788,7 @@
         if (!permissionGranted && isDebuggableMonkeyBuild) {
             // TODO: Skip this check when running automated tests. Replace this
             // with a more general solution.
-            Log.w(TAG, "no credentials permission for usage of " + account + ", "
+            Log.d(TAG, "no credentials permission for usage of " + account + ", "
                     + authTokenType + " by uid " + Binder.getCallingUid()
                     + " but ignoring since this is a monkey build");
             return true;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5f89496..9b9cbd5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -84,7 +84,7 @@
 import android.os.StatFs;
 import android.os.Vibrator;
 import android.os.FileUtils.FileStatus;
-import android.storage.StorageManager;
+import android.os.storage.StorageManager;
 import android.telephony.TelephonyManager;
 import android.text.ClipboardManager;
 import android.util.AndroidRuntimeException;
@@ -197,9 +197,9 @@
     private File mDatabasesDir;
     private File mPreferencesDir;
     private File mFilesDir;
-    
-
     private File mCacheDir;
+    private File mExternalFilesDir;
+    private File mExternalCacheDir;
     
     private static long sInstanceCount = 0;
 
@@ -438,6 +438,38 @@
     }
     
     @Override
+    public File getExternalFilesDir(String type) {
+        synchronized (mSync) {
+            if (mExternalFilesDir == null) {
+                mExternalFilesDir = Environment.getExternalStorageAppFilesDirectory(
+                        getPackageName());
+            }
+            if (!mExternalFilesDir.exists()) {
+                try {
+                    (new File(Environment.getExternalStorageAndroidDataDir(),
+                            ".nomedia")).createNewFile();
+                } catch (IOException e) {
+                }
+                if (!mExternalFilesDir.mkdirs()) {
+                    Log.w(TAG, "Unable to create external files directory");
+                    return null;
+                }
+            }
+            if (type == null) {
+                return mExternalFilesDir;
+            }
+            File dir = new File(mExternalFilesDir, type);
+            if (!dir.exists()) {
+                if (!dir.mkdirs()) {
+                    Log.w(TAG, "Unable to create external media directory " + dir);
+                    return null;
+                }
+            }
+            return dir;
+        }
+    }
+    
+    @Override
     public File getCacheDir() {
         synchronized (mSync) {
             if (mCacheDir == null) {
@@ -457,7 +489,28 @@
         return mCacheDir;
     }
     
-
+    @Override
+    public File getExternalCacheDir() {
+        synchronized (mSync) {
+            if (mExternalCacheDir == null) {
+                mExternalCacheDir = Environment.getExternalStorageAppCacheDirectory(
+                        getPackageName());
+            }
+            if (!mExternalCacheDir.exists()) {
+                try {
+                    (new File(Environment.getExternalStorageAndroidDataDir(),
+                            ".nomedia")).createNewFile();
+                } catch (IOException e) {
+                }
+                if (!mExternalCacheDir.mkdirs()) {
+                    Log.w(TAG, "Unable to create external cache directory");
+                    return null;
+                }
+            }
+            return mExternalCacheDir;
+        }
+    }
+    
     @Override
     public File getFileStreamPath(String name) {
         return makeFilename(getFilesDir(), name);
@@ -2166,7 +2219,7 @@
                             filter, null, null, null);
                     // Register for events related to sdcard installation.
                     IntentFilter sdFilter = new IntentFilter();
-                    sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+                    sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
                     mContext.registerReceiverInternal(sPackageRemovedReceiver,
                             sdFilter, null, null, null);
                 }
@@ -2189,7 +2242,7 @@
                 String pkgList[] = null;
                 String action = intent.getAction();
                 boolean immediateGc = false;
-                if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     immediateGc = true;
                 } else {
diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java
index ecbad01..af9c379 100644
--- a/core/java/android/app/DeviceAdmin.java
+++ b/core/java/android/app/DeviceAdmin.java
@@ -40,7 +40,7 @@
  * to the device administrator, as parsed by the {@link DeviceAdminInfo} class.
  * A typical file would be:</p>
  * 
- * {@sample development/samples/ApiDemos/res/xml/sample_device_admin.xml meta_data}
+ * {@sample development/samples/ApiDemos/res/xml/device_admin_sample.xml meta_data}
  */
 public class DeviceAdmin extends BroadcastReceiver {
     private static String TAG = "DevicePolicy";
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
index 08cdd05..847e879 100644
--- a/core/java/android/app/DevicePolicyManager.java
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -328,14 +328,20 @@
     }
 
     /**
-     * Set the maximum number of failed password attempts that are allowed
-     * before the device wipes its data.  This is convenience for implementing
-     * the corresponding functionality with a combination of watching failed
-     * password attempts and calling {@link #wipeData} upon reaching a certain
-     * count, and as such requires that you request both
-     * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and
+     * Setting this to a value greater than zero enables a built-in policy
+     * that will perform a device wipe after too many incorrect
+     * device-unlock passwords have been entered.  This built-in policy combines
+     * watching for failed passwords and wiping the device, and requires
+     * that you request both {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and
      * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}}.
      * 
+     * <p>To implement any other policy (e.g. wiping data for a particular
+     * application only, erasing or revoking credentials, or reporting the
+     * failure to a server), you should implement
+     * {@link DeviceAdmin#onPasswordFailed(Context, android.content.Intent)}
+     * instead.  Do not use this API, because if the maximum count is reached,
+     * the device will be wiped immediately, and your callback will not be invoked.
+     * 
      * @param admin Which {@link DeviceAdmin} this request is associated with.
      * @param num The number of failed password attempts at which point the
      * device will wipe its data.
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 804c8eb..3fd36a37 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -42,7 +42,7 @@
      * {@link #onStartCommand(Intent, int, int)} will return
      * {@link Service#START_REDELIVER_INTENT} instead of
      * {@link Service#START_NOT_STICKY}, so that if this service's process
-     * is called while it is executing the Intent in
+     * is killed while it is executing the Intent in
      * {@link #onHandleIntent(Intent)}, then when later restarted the same Intent
      * will be re-delivered to it, to retry its execution.
      */
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 0455202..c4ba05d 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -30,7 +30,7 @@
  * name inside of that package.
  * 
  */
-public final class ComponentName implements Parcelable, Comparable<ComponentName> {
+public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
     private final String mPackage;
     private final String mClass;
 
@@ -76,6 +76,10 @@
         mClass = cls.getName();
     }
 
+    public ComponentName clone() {
+        return new ComponentName(mPackage, mClass);
+    }
+
     /**
      * Return the package name of this component.
      */
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index b5587ed..b33be23 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -42,7 +42,6 @@
 import java.io.OutputStream;
 import java.util.List;
 import java.util.ArrayList;
-import java.util.Collection;
 
 
 /**
@@ -62,7 +61,31 @@
      */
     @Deprecated
     public static final String SYNC_EXTRAS_FORCE = "force";
+
+    /**
+     * If this extra is set to true then the sync settings (like getSyncAutomatically())
+     * are ignored by the sync scheduler.
+     */
+    public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings";
+
+    /**
+     * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries)
+     * are ignored by the sync scheduler. If this request fails and gets rescheduled then the
+     * retries will still honor the backoff.
+     */
+    public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff";
+
+    /**
+     * If this extra is set to true then the request will not be retried if it fails.
+     */
+    public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry";
+
+    /**
+     * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS}
+     * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF}
+     */
     public static final String SYNC_EXTRAS_MANUAL = "force";
+
     public static final String SYNC_EXTRAS_UPLOAD = "upload";
     public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
     public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
@@ -976,15 +999,38 @@
      * Although these sync are scheduled at the specified frequency, it may take longer for it to
      * actually be started if other syncs are ahead of it in the sync operation queue. This means
      * that the actual start time may drift.
+     * <p>
+     * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY},
+     * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS},
+     * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE},
+     * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true.
+     * If any are supplied then an {@link IllegalArgumentException} will be thrown.
      *
      * @param account the account to specify in the sync
      * @param authority the provider to specify in the sync request
      * @param extras extra parameters to go along with the sync request
      * @param pollFrequency how frequently the sync should be performed, in seconds.
+     * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
+     * are null.
      */
     public static void addPeriodicSync(Account account, String authority, Bundle extras,
             long pollFrequency) {
         validateSyncExtrasBundle(extras);
+        if (account == null) {
+            throw new IllegalArgumentException("account must not be null");
+        }
+        if (authority == null) {
+            throw new IllegalArgumentException("authority must not be null");
+        }
+        if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
+                || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
+                || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
+                || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
+                || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
+                || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
+                || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
+            throw new IllegalArgumentException("illegal extras were set");
+        }
         try {
             getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
         } catch (RemoteException e) {
@@ -1003,6 +1049,12 @@
      */
     public static void removePeriodicSync(Account account, String authority, Bundle extras) {
         validateSyncExtrasBundle(extras);
+        if (account == null) {
+            throw new IllegalArgumentException("account must not be null");
+        }
+        if (authority == null) {
+            throw new IllegalArgumentException("authority must not be null");
+        }
         try {
             getContentService().removePeriodicSync(account, authority, extras);
         } catch (RemoteException e) {
@@ -1018,6 +1070,12 @@
      * @return a list of PeriodicSync objects. This list may be empty but will never be null.
      */
     public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) {
+        if (account == null) {
+            throw new IllegalArgumentException("account must not be null");
+        }
+        if (authority == null) {
+            throw new IllegalArgumentException("authority must not be null");
+        }
         try {
             return getContentService().getPeriodicSyncs(account, authority);
         } catch (RemoteException e) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5aefe4c..672e5f7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -25,6 +25,7 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.media.MediaScannerConnection.ScanResultListener;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -137,7 +138,28 @@
     
     /**
      * Return the context of the single, global Application object of the
-     * current process.
+     * current process.  This generally should only be used if you need a
+     * Context whose lifecycle is separate from the current context, that is
+     * tied to the lifetime of the process rather than the current component.
+     * 
+     * <p>Consider for example how this interacts with
+     * {@ #registerReceiver(BroadcastReceiver, IntentFilter)}:
+     * <ul>
+     * <li> <p>If used from an Activity context, the receiver is being registered
+     * within that activity.  This means that you are expected to unregister
+     * before the activity is done being destroyed; in fact if you do not do
+     * so, the framework will clean up your leaked registration as it removes
+     * the activity and log an error.  Thus, if you use the Activity context
+     * to register a receiver that is static (global to the process, not
+     * associated with an Activity instance) then that registration will be
+     * removed on you at whatever point the activity you used is destroyed.
+     * <li> <p>If used from the Context returned here, the receiver is being
+     * registered with the global state associated with your application.  Thus
+     * it will never be unregistered for you.  This is necessary if the receiver
+     * is associated with static data, not a particular component.  However
+     * using the ApplicationContext elsewhere can easily lead to serious leaks
+     * if you forget to unregister, unbind, etc.
+     * </ul>
      */
     public abstract Context getApplicationContext();
 
@@ -393,11 +415,84 @@
     public abstract File getFilesDir();
     
     /**
+     * Returns the absolute path to the directory on the external filesystem
+     * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
+     * Environment.getExternalStorageDirectory()} where the application can
+     * place persistent files it owns.  These files are private to the
+     * applications, and not typically visible to the user as media.
+     * 
+     * <p>This is like {@link #getFilesDir()} in that these
+     * files will be deleted when the application is uninstalled, however there
+     * are some important differences:
+     * 
+     * <ul>
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.  See the
+     * APIs on {@link android.os.Environment} for information in the storage state.
+     * <li>There is no security enforced with these files.  All applications
+     * can read and write files placed here.
+     * </ul>
+     * 
+     * <p>Here is an example of typical code to manipulate a file in
+     * an application's private storage:</p>
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * private_file}
+     *
+     * <p>If you install a non-null <var>type</var> to this function, the returned
+     * file will be a path to a sub-directory of the given type.  Though these files
+     * are not automatically scanned by the media scanner, you can explicitly
+     * add them to the media database with
+     * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[],
+     *      ScanResultListener) MediaScannerConnection.scanFile}.
+     * Note that this is not the same as
+     * {@link android.os.Environment#getExternalStoragePublicDirectory
+     * Environment.getExternalStoragePublicDirectory()}, which provides
+     * directories of media shared by all applications.  The
+     * directories returned here are
+     * owned by the application, and its contents will be removed when the
+     * application is uninstalled.  Unlike
+     * {@link android.os.Environment#getExternalStoragePublicDirectory
+     * Environment.getExternalStoragePublicDirectory()}, the directory
+     * returned here will be automatically created for you.
+     * 
+     * <p>Here is an example of typical code to manipulate a picture in
+     * an application's private storage and add it to the media database:</p>
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * private_picture}
+     * 
+     * @param type The type of files directory to return.  May be null for
+     * the root of the files directory or one of
+     * the following Environment constants for a subdirectory:
+     * {@link android.os.Environment#DIRECTORY_MUSIC},
+     * {@link android.os.Environment#DIRECTORY_PODCASTS},
+     * {@link android.os.Environment#DIRECTORY_RINGTONES},
+     * {@link android.os.Environment#DIRECTORY_ALARMS},
+     * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+     * {@link android.os.Environment#DIRECTORY_PICTURES}, or
+     * {@link android.os.Environment#DIRECTORY_MOVIES}.
+     * 
+     * @return Returns the path of the directory holding application files
+     * on external storage.  Returns null if external storage is not currently
+     * mounted so it could not ensure the path exists; you will need to call
+     * this method again when it is available.
+     *
+     * @see #getFilesDir
+     */
+    public abstract File getExternalFilesDir(String type);
+    
+    /**
      * Returns the absolute path to the application specific cache directory 
      * on the filesystem. These files will be ones that get deleted first when the
-     * device runs low on storage
+     * device runs low on storage.
      * There is no guarantee when these files will be deleted.
-     *
+     * 
+     * <strong>Note: you should not <em>rely</em> on the system deleting these
+     * files for you; you should always have a reasonable maximum, such as 1 MB,
+     * for the amount of space you consume with cache files, and prune those
+     * files when exceeding that space.</strong>
+     * 
      * @return Returns the path of the directory holding application cache files.
      *
      * @see #openFileOutput
@@ -407,6 +502,37 @@
     public abstract File getCacheDir();
 
     /**
+     * Returns the absolute path to the directory on the external filesystem
+     * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
+     * Environment.getExternalStorageDirectory()} where the application can
+     * place cache files it owns.
+     * 
+     * <p>This is like {@link #getCacheDir()} in that these
+     * files will be deleted when the application is uninstalled, however there
+     * are some important differences:
+     * 
+     * <ul>
+     * <li>The platform does not monitor the space available in external storage,
+     * and thus will not automatically delete these files.  Note that you should
+     * be managing the maximum space you will use for these anyway, just like
+     * with {@link #getCacheDir()}.
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.  See the
+     * APIs on {@link android.os.Environment} for information in the storage state.
+     * <li>There is no security enforced with these files.  All applications
+     * can read and write files placed here.
+     * </ul>
+     *
+     * @return Returns the path of the directory holding application cache files
+     * on external storage.  Returns null if external storage is not currently
+     * mounted so it could not ensure the path exists; you will need to call
+     * this method again when it is available.
+     *
+     * @see #getCacheDir
+     */
+    public abstract File getExternalCacheDir();
+    
+    /**
      * Returns an array of strings naming the private files associated with
      * this Context's application package.
      *
@@ -1110,7 +1236,7 @@
      * @see #SENSOR_SERVICE
      * @see android.hardware.SensorManager
      * @see #STORAGE_SERVICE
-     * @see android.storage.StorageManager
+     * @see android.os.storage.StorageManager
      * @see #VIBRATOR_SERVICE
      * @see android.os.Vibrator
      * @see #CONNECTIVITY_SERVICE
@@ -1243,11 +1369,11 @@
     
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
-     * android.storage.StorageManager} for accesssing system storage
+     * android.os.storage.StorageManager} for accesssing system storage
      * functions.
      *
      * @see #getSystemService
-     * @see android.storage.StorageManager
+     * @see android.os.storage.StorageManager
      */
     public static final String STORAGE_SERVICE = "storage";
 
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 1b34320..a447108 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -179,11 +179,21 @@
     }
     
     @Override
+    public File getExternalFilesDir(String type) {
+        return mBase.getExternalFilesDir(type);
+    }
+    
+    @Override
     public File getCacheDir() {
         return mBase.getCacheDir();
     }
 
     @Override
+    public File getExternalCacheDir() {
+        return mBase.getExternalCacheDir();
+    }
+
+    @Override
     public File getDir(String name, int mode) {
         return mBase.getDir(name, mode);
     }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e957e20..c32999f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1381,8 +1381,8 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MEDIA_RESOURCES_AVAILABLE =
-        "android.intent.action.MEDIA_RESOURCES_AVAILABILE";
+    public static final String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE =
+        "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
 
     /**
      * Broadcast Action: Resources for a set of packages are currently
@@ -1406,8 +1406,8 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MEDIA_RESOURCES_UNAVAILABLE =
-        "android.intent.action.MEDIA_RESOURCES_UNAVAILABILE";
+    public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE =
+        "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABILE";
 
     /**
      * Broadcast Action:  The current system wallpaper has changed.  See
@@ -2198,8 +2198,8 @@
 
     /**
      * This field is part of
-     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE},
-     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE}
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
      * and contains a string array of all of the components that have changed.
      * @hide
      */
@@ -2208,8 +2208,8 @@
 
     /**
      * This field is part of
-     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE},
-     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE}
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
      * and contains an integer array of uids of all of the components
      * that have changed.
      * @hide
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 619c7d5..ebb95e87 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -124,7 +124,7 @@
 
     private Context mContext;
 
-    private volatile Account[] mAccounts = null;
+    private volatile Account[] mAccounts = INITIAL_ACCOUNTS_ARRAY;
 
     volatile private PowerManager.WakeLock mSyncWakeLock;
     volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
@@ -186,9 +186,11 @@
         }
     };
 
+    private static final Account[] INITIAL_ACCOUNTS_ARRAY = new Account[0];
+
     public void onAccountsUpdated(Account[] accounts) {
         // remember if this was the first time this was called after an update
-        final boolean justBootedUp = mAccounts == null;
+        final boolean justBootedUp = mAccounts == INITIAL_ACCOUNTS_ARRAY;
         mAccounts = accounts;
 
         // if a sync is in progress yet it is no longer in the accounts list,
@@ -486,11 +488,6 @@
             Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) {
         boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
 
-        if (mAccounts == null) {
-            Log.e(TAG, "scheduleSync: the accounts aren't known yet, this should never happen");
-            return;
-        }
-
         if (!isSyncEnabled()) {
             if (isLoggable) {
                 Log.v(TAG, "not syncing because sync is disabled");
@@ -515,13 +512,6 @@
             // if the accounts aren't configured yet then we can't support an account-less
             // sync request
             accounts = mAccounts;
-            if (accounts == null) {
-                // not ready yet
-                if (isLoggable) {
-                    Log.v(TAG, "scheduleSync: no accounts yet, dropping");
-                }
-                return;
-            }
             if (accounts.length == 0) {
                 if (isLoggable) {
                     Log.v(TAG, "scheduleSync: no accounts configured, dropping");
@@ -532,6 +522,12 @@
 
         final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
         final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
+        if (manualSync) {
+            extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
+            extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
+        }
+        final boolean ignoreSettings =
+                extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
 
         int source;
         if (uploadOnly) {
@@ -590,7 +586,7 @@
                     final boolean syncAutomatically = masterSyncAutomatically
                             && mSyncStorageEngine.getSyncAutomatically(account, authority);
                     boolean syncAllowed =
-                            manualSync || (backgroundDataUsageAllowed && syncAutomatically);
+                            ignoreSettings || (backgroundDataUsageAllowed && syncAutomatically);
                     if (!syncAllowed) {
                         if (isLoggable) {
                             Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
@@ -797,21 +793,29 @@
             Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
         }
 
+        operation = new SyncOperation(operation);
+
+        // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
+        // request. Retries of the request will always honor the backoff, so clear the
+        // flag in case we retry this request.
+        if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
+            operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
+        }
+
         // If this sync aborted because the internal sync loop retried too many times then
         //   don't reschedule. Otherwise we risk getting into a retry loop.
         // If the operation succeeded to some extent then retry immediately.
         // If this was a two-way sync then retry soft errors with an exponential backoff.
         // If this was an upward sync then schedule a two-way sync immediately.
         // Otherwise do not reschedule.
-        if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)) {
-            Log.d(TAG, "not retrying sync operation because it is a manual sync: "
+        if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) {
+            Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
                     + operation);
         } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)) {
-            final SyncOperation newSyncOperation = new SyncOperation(operation);
-            newSyncOperation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
+            operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
             Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
                     + "encountered an error: " + operation);
-            scheduleSyncOperation(newSyncOperation);
+            scheduleSyncOperation(operation);
         } else if (syncResult.tooManyRetries) {
             Log.d(TAG, "not retrying sync operation because it retried too many times: "
                     + operation);
@@ -820,13 +824,13 @@
                 Log.d(TAG, "retrying sync operation because even though it had an error "
                         + "it achieved some success");
             }
-            scheduleSyncOperation(new SyncOperation(operation));
+            scheduleSyncOperation(operation);
         } else if (syncResult.hasSoftError()) {
             if (isLoggable) {
                 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
                         + operation);
             }
-            scheduleSyncOperation(new SyncOperation(operation));
+            scheduleSyncOperation(operation);
         } else {
             Log.d(TAG, "not retrying sync operation because the error is a hard error: "
                     + operation);
@@ -942,10 +946,10 @@
 
         final Account[] accounts = mAccounts;
         pw.print("accounts: ");
-        if (accounts != null) {
+        if (accounts != INITIAL_ACCOUNTS_ARRAY) {
             pw.println(accounts.length);
         } else {
-            pw.println("none");
+            pw.println("not known yet");
         }
         final long now = SystemClock.elapsedRealtime();
         pw.print("now: "); pw.print(now);
@@ -1448,7 +1452,7 @@
             }
         }
 
-        private boolean isSyncAllowed(Account account, String authority, boolean manualSync,
+        private boolean isSyncAllowed(Account account, String authority, boolean ignoreSettings,
                 boolean backgroundDataUsageAllowed) {
             Account[] accounts = mAccounts;
 
@@ -1458,7 +1462,7 @@
             }
 
             // skip the sync if the account of this operation no longer exists
-            if (accounts == null || !ArrayUtils.contains(accounts, account)) {
+            if (!ArrayUtils.contains(accounts, account)) {
                 return false;
             }
 
@@ -1466,7 +1470,7 @@
             final boolean syncAutomatically =
                     mSyncStorageEngine.getSyncAutomatically(account, authority)
                             && mSyncStorageEngine.getMasterSyncAutomatically();
-            if (!(manualSync || (backgroundDataUsageAllowed && syncAutomatically))) {
+            if (!(ignoreSettings || (backgroundDataUsageAllowed && syncAutomatically))) {
                 return false;
             }
 
@@ -1525,7 +1529,7 @@
             // If the accounts aren't known yet then we aren't ready to run. We will be kicked
             // when the account lookup request does complete.
             Account[] accounts = mAccounts;
-            if (accounts == null) {
+            if (accounts == INITIAL_ACCOUNTS_ARRAY) {
                 if (isLoggable) {
                     Log.v(TAG, "runStateIdle: accounts not known, skipping");
                 }
@@ -1553,8 +1557,9 @@
                     // from the queue now
                     mSyncQueue.remove(op);
 
-                    if (!isSyncAllowed(op.account, op.authority,
-                            op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false),
+                    final boolean ignoreSettings = op.extras
+                            .getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
+                    if (!isSyncAllowed(op.account, op.authority, ignoreSettings,
                             backgroundDataUsageAllowed)) {
                         continue;
                     }
@@ -1609,7 +1614,7 @@
             Bundle bestExtras = null;
             ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
             for (SyncStorageEngine.AuthorityInfo info : infos) {
-                if (!isSyncAllowed(info.account, info.authority, false /* manualSync */,
+                if (!isSyncAllowed(info.account, info.authority, false /* ignoreSettings */,
                         backgroundDataUsageAllowed)) {
                     continue;
                 }
@@ -1881,7 +1886,6 @@
             // in each of these cases the sync loop will be kicked, which will cause this
             // method to be called again
             if (!mDataConnectionIsConnected) return;
-            if (mAccounts == null) return;
             if (mStorageIsLow) return;
 
             final long now = SystemClock.elapsedRealtime();
@@ -1895,7 +1899,7 @@
             if (activeSyncContext == null) {
                 synchronized (mSyncQueue) {
                     Pair<SyncOperation, Long> candidate = bestSyncOperationCandidate();
-                    alarmTime = candidate != null ? candidate.second : 0;
+                    alarmTime = candidate != null ? candidate.second : null;
                 }
             } else {
                 final long notificationTime =
@@ -2040,18 +2044,4 @@
                     resultMessage, downstreamActivity, upstreamActivity);
         }
     }
-
-    public static long runTimeWithBackoffs(SyncStorageEngine syncStorageEngine,
-            Account account, String authority, boolean isManualSync, long runTime) {
-        // if this is a manual sync, the run time is unchanged
-        // otherwise, the run time is the max of the backoffs and the run time.
-        if (isManualSync) {
-            return runTime;
-        }
-
-        Pair<Long, Long> backoff = syncStorageEngine.getBackoff(account, authority);
-        long delayUntilTime = syncStorageEngine.getDelayUntilTime(account, authority);
-
-        return Math.max(Math.max(runTime, delayUntilTime), backoff != null ? backoff.first : 0);
-    }
 }
diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java
index 2d6e833..4599165 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -26,6 +26,9 @@
         this.extras = new Bundle(extras);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_UPLOAD);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_MANUAL);
+        removeFalseExtra(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS);
+        removeFalseExtra(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
+        removeFalseExtra(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 2eead3a..bb21488 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -113,10 +113,14 @@
         SyncOperation best = null;
         long bestRunTime = 0;
         for (SyncOperation op : mOperationsMap.values()) {
-            long opRunTime = SyncManager.runTimeWithBackoffs(mSyncStorageEngine, op.account,
-                    op.authority,
-                    op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false),
-                    op.earliestRunTime);
+            long opRunTime = op.earliestRunTime;
+            if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
+                Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(op.account, op.authority);
+                long delayUntil = mSyncStorageEngine.getDelayUntilTime(op.account, op.authority);
+                opRunTime = Math.max(
+                        Math.max(opRunTime, delayUntil),
+                        backoff != null ? backoff.first : 0);
+            }
             // if the expedited state of both ops are the same then compare their runtime.
             // Otherwise the candidate is only better than the current best if the candidate
             // is expedited.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 808c839..6591313 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -270,6 +270,8 @@
      * Full paths to the locations of extra resource packages this application
      * uses. This field is only used if there are extra resource packages,
      * otherwise it is null.
+     * 
+     * {@hide}
      */
     public String[] resourceDirs;
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 54db5e0..2c8c112 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -305,4 +305,5 @@
      */
     void updateExternalMediaStatus(boolean mounted);
 
+    String nextPackageToClean(String lastPackage);
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 745628a..fca8588 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -437,6 +437,15 @@
     public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
 
     /**
+     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+     * the new package couldn't be installed in the specified install
+     * location.
+     * @hide
+     */
+    public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
+
+    /**
      * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
      * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
      * if the parser was given a path that is not a file, or does not end with the expected
@@ -613,6 +622,13 @@
     public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
 
     /**
+     * Action to external storage service to clean out removed apps.
+     * @hide
+     */
+    public static final String ACTION_CLEAN_EXTERNAL_STORAGE
+            = "android.content.pm.CLEAN_EXTERNAL_STORAGE";
+    
+    /**
      * Determines best place to install an application: either SD or internal FLASH.
      * Tweak the algorithm for best results.
      * @param appInfo ApplicationInfo object of the package to install.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bac55cc..b31df32 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2138,7 +2138,7 @@
                     havePerm = true;
                 }
                 if (writePermission != null) {
-                    writePermission = readPermission.intern();
+                    writePermission = writePermission.intern();
                     havePerm = true;
                 }
 
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 7362394..a885820 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -117,8 +117,8 @@
         mContext.registerReceiver(receiver, intentFilter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(receiver, sdFilter);
     }
 
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 540f4445..5d1e7cf 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1880,8 +1880,7 @@
                  */
                 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
                         getPath() + "; i.e., NO space for this sql statement in cache: " +
-                        sql + ". Make sure your sql " +
-                        "statements are using prepared-sql-statement syntax with '?' for " +
+                        sql + ". Please change your sql statements to use '?' for " +
                         "bindargs, instead of using actual values");
                 
                 /* increment the number of times this warnings has been printed.
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
old mode 100755
new mode 100644
index b0c3909..28a86b8
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -620,7 +620,10 @@
         if (mBuffer == null || mKeyboardChanged) {
             if (mBuffer == null || mKeyboardChanged &&
                     (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
-                mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
+                // Make sure our bitmap is at least 1x1
+                final int width = Math.max(1, getWidth());
+                final int height = Math.max(1, getHeight());
+                mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                 mCanvas = new Canvas(mBuffer);
             }
             invalidateAllKeys();
@@ -739,6 +742,10 @@
             final Key key = keys[nearestKeyIndices[i]];
             int dist = 0;
             boolean isInside = key.isInside(x,y);
+            if (isInside) {
+                primaryIndex = nearestKeyIndices[i];
+            }
+
             if (((mProximityCorrectOn 
                     && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold) 
                     || isInside)
@@ -767,10 +774,6 @@
                     }
                 }
             }
-            
-            if (isInside) {
-                primaryIndex = nearestKeyIndices[i];
-            }
         }
         if (primaryIndex == NOT_A_KEY) {
             primaryIndex = closestKey;
diff --git a/core/java/android/os/Base64Utils.java b/core/java/android/os/Base64Utils.java
deleted file mode 100644
index 684a469..0000000
--- a/core/java/android/os/Base64Utils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 android.os;
-
-/**
- * {@hide}
- */
-public class Base64Utils
-{
-    // TODO add encode api here if possible
-    
-    public static byte [] decodeBase64(String data) {
-        return decodeBase64Native(data);
-    }
-    private static native byte[] decodeBase64Native(String data);
-}
-
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 9491bd4..a9831aa 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,7 +18,7 @@
 
 import java.io.File;
 
-import android.os.IMountService;
+import android.os.storage.IMountService;
 
 /**
  * Provides access to environment variables.
@@ -91,6 +91,14 @@
     private static final File EXTERNAL_STORAGE_DIRECTORY
             = getDirectory("EXTERNAL_STORAGE", "/sdcard");
 
+    private static final File EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY
+            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+                    "Android"), "data");
+
+    private static final File EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY
+            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+                    "Android"), "media");
+
     private static final File DOWNLOAD_CACHE_DIRECTORY
             = getDirectory("DOWNLOAD_CACHE", "/cache");
 
@@ -102,13 +110,183 @@
     }
 
     /**
-     * Gets the Android external storage directory.
+     * Gets the Android external storage directory.  This directory may not
+     * currently be accessible if it has been mounted by the user on their
+     * computer, has been removed from the device, or some other problem has
+     * happened.  You can determine its current state with
+     * {@link #getExternalStorageState()}.
+     * 
+     * <p>Here is an example of typical code to monitor the state of
+     * external storage:</p>
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * monitor_storage}
      */
     public static File getExternalStorageDirectory() {
         return EXTERNAL_STORAGE_DIRECTORY;
     }
 
     /**
+     * Standard directory in which to place any audio files that should be
+     * in the regular list of music for the user.
+     * This may be combined with
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_MUSIC = "Music";
+    
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of podcasts that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_PODCASTS = "Podcasts";
+    
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of ringtones that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
+     * {@link #DIRECTORY_ALARMS} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_RINGTONES = "Ringtones";
+    
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of alarms that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * and {@link #DIRECTORY_RINGTONES} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_ALARMS = "Alarms";
+    
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of notifications that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_NOTIFICATIONS = "Notifications";
+    
+    /**
+     * Standard directory in which to place pictures that are available to
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, as the media scanner will find and collect pictures
+     * in any directory.
+     */
+    public static String DIRECTORY_PICTURES = "Pictures";
+    
+    /**
+     * Standard directory in which to place movies that are available to
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, as the media scanner will find and collect movies
+     * in any directory.
+     */
+    public static String DIRECTORY_MOVIES = "Movies";
+    
+    /**
+     * Standard directory in which to place files that have been downloaded by
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, you are free to download files anywhere in your own
+     * private directories.
+     */
+    public static String DIRECTORY_DOWNLOADS = "Downloads";
+    
+    /**
+     * The traditional location for pictures and videos when mounting the
+     * device as a camera.  Note that this is primarily a convention for the
+     * top-level public directory, as this convention makes no sense elsewhere.
+     */
+    public static String DIRECTORY_DCIM = "DCIM";
+    
+    /**
+     * Get a top-level public external storage directory for placing files of
+     * a particular type.  This is where the user will typically place and
+     * manage their own files, so you should be careful about what you put here
+     * to ensure you don't erase their files or get in the way of their own
+     * organization.
+     * 
+     * <p>Here is an example of typical code to manipulate a picture on
+     * the public external storage:</p>
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * public_picture}
+     * 
+     * @param type The type of storage directory to return.  Should be one of
+     * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
+     * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
+     * {@link #DIRECTORY_DCIM}.  May not be null.
+     * 
+     * @return Returns the File path for the directory.  Note that this
+     * directory may not yet exist, so you must make sure it exists before
+     * using it such as with {@link File#mkdirs File.mkdirs()}.
+     */
+    public static File getExternalStoragePublicDirectory(String type) {
+        return new File(getExternalStorageDirectory(), type);
+    }
+
+    /**
+     * Returns the path for android-specific data on the SD card.
+     * @hide
+     */
+    public static File getExternalStorageAndroidDataDir() {
+        return EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY;
+    }
+    
+    /**
+     * Generates the raw path to an application's data
+     * @hide
+     */
+    public static File getExternalStorageAppDataDirectory(String packageName) {
+        return new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY, packageName);
+    }
+    
+    /**
+     * Generates the raw path to an application's media
+     * @hide
+     */
+    public static File getExternalStorageAppMediaDirectory(String packageName) {
+        return new File(EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY, packageName);
+    }
+    
+    /**
+     * Generates the path to an application's files.
+     * @hide
+     */
+    public static File getExternalStorageAppFilesDirectory(String packageName) {
+        return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
+                packageName), "files");
+    }
+    
+    /**
+     * Generates the path to an application's cache.
+     * @hide
+     */
+    public static File getExternalStorageAppCacheDirectory(String packageName) {
+        return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
+                packageName), "cache");
+    }
+    
+    /**
      * Gets the Android Download/Cache content directory.
      */
     public static File getDownloadCacheDirectory() {
@@ -173,6 +351,8 @@
      * Gets the current state of the external storage device.
      * Note: This call should be deprecated as it doesn't support
      * multiple volumes.
+     * 
+     * <p>See {@link #getExternalStorageDirectory()} for an example of its use.
      */
     public static String getExternalStorageState() {
         try {
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index 3457815..7e99f38 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -52,73 +52,75 @@
 
     public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE 
             | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
-	    | DELETE_SELF | MOVE_SELF;
+            | DELETE_SELF | MOVE_SELF;
 
     private static final String LOG_TAG = "FileObserver";
 
     private static class ObserverThread extends Thread {
-	private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
-	private int m_fd;
+        private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
+        private int m_fd;
 
-	public ObserverThread() {
-	    super("FileObserver");
-	    m_fd = init();
-	}
+        public ObserverThread() {
+            super("FileObserver");
+            m_fd = init();
+        }
 
-	public void run() {
-	    observe(m_fd);
-	}
+        public void run() {
+            observe(m_fd);
+        }
 
-	public int startWatching(String path, int mask, FileObserver observer) {
-	    int wfd = startWatching(m_fd, path, mask);
+        public int startWatching(String path, int mask, FileObserver observer) {
+            int wfd = startWatching(m_fd, path, mask);
 
-	    Integer i = new Integer(wfd);
-	    if (wfd >= 0) {
-		synchronized (m_observers) {
-		    m_observers.put(i, new WeakReference(observer));
-		}
-	    }
+            Integer i = new Integer(wfd);
+            if (wfd >= 0) {
+                synchronized (m_observers) {
+                    m_observers.put(i, new WeakReference(observer));
+                }
+            }
 
-	    return i;
-	}
+            return i;
+        }
 
-	public void stopWatching(int descriptor) {
-	    stopWatching(m_fd, descriptor);
-	}
+        public void stopWatching(int descriptor) {
+            stopWatching(m_fd, descriptor);
+        }
 
-    public void onEvent(int wfd, int mask, String path) {
-        // look up our observer, fixing up the map if necessary...
-        FileObserver observer;
+        public void onEvent(int wfd, int mask, String path) {
+            // look up our observer, fixing up the map if necessary...
+            FileObserver observer = null;
 
-        synchronized (m_observers) {
-            WeakReference weak = m_observers.get(wfd);
-            observer = (FileObserver) weak.get();
-            if (observer == null) {
-                m_observers.remove(wfd);
+            synchronized (m_observers) {
+                WeakReference weak = m_observers.get(wfd);
+                if (weak != null) {  // can happen with lots of events from a dead wfd
+                    observer = (FileObserver) weak.get();
+                    if (observer == null) {
+                        m_observers.remove(wfd);
+                    }
+                }
+            }
+
+            // ...then call out to the observer without the sync lock held
+            if (observer != null) {
+                try {
+                    observer.onEvent(mask, path);
+                } catch (Throwable throwable) {
+                    Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
+                }
             }
         }
 
-        // ...then call out to the observer without the sync lock held
-        if (observer != null) {
-            try {
-                observer.onEvent(mask, path);
-            } catch (Throwable throwable) {
-                Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
-            }
-        }
-    }
-
-	private native int init();
-	private native void observe(int fd);
-	private native int startWatching(int fd, String path, int mask);
-	private native void stopWatching(int fd, int wfd);
+        private native int init();
+        private native void observe(int fd);
+        private native int startWatching(int fd, String path, int mask);
+        private native void stopWatching(int fd, int wfd);
     }
 
     private static ObserverThread s_observerThread;
 
     static {
-	s_observerThread = new ObserverThread();
-	s_observerThread.start();
+        s_observerThread = new ObserverThread();
+        s_observerThread.start();
     }
 
     // instance
@@ -127,30 +129,30 @@
     private int m_mask;
 
     public FileObserver(String path) {
-	this(path, ALL_EVENTS);
+        this(path, ALL_EVENTS);
     }
 
     public FileObserver(String path, int mask) {
-	m_path = path;
-	m_mask = mask;
-	m_descriptor = -1;
+        m_path = path;
+        m_mask = mask;
+        m_descriptor = -1;
     }
 
     protected void finalize() {
-	stopWatching();
+        stopWatching();
     }
 
     public void startWatching() {
-	if (m_descriptor < 0) {
-	    m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
-	}
+        if (m_descriptor < 0) {
+            m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
+        }
     }
 
     public void stopWatching() {
-	if (m_descriptor >= 0) {
-	    s_observerThread.stopWatching(m_descriptor);
-	    m_descriptor = -1;
-	}
+        if (m_descriptor >= 0) {
+            s_observerThread.stopWatching(m_descriptor);
+            m_descriptor = -1;
+        }
     }
 
     public abstract void onEvent(int event, String path);
diff --git a/core/java/android/os/IMountServiceListener.aidl b/core/java/android/os/IMountServiceListener.aidl
deleted file mode 100644
index 3df64b2..0000000
--- a/core/java/android/os/IMountServiceListener.aidl
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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 IMountServiceListener {
-    /**
-     * A sharing method has changed availability state.
-     *
-     * @param method The share method which has changed.
-     * @param available The share availability state.
-     */
-    void onShareAvailabilityChanged(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 onMediaInserted(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 onMediaRemoved(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 onVolumeStateChanged(String label, String path, String oldState, String newState);
-
-}
diff --git a/core/java/android/os/MountServiceListener.java b/core/java/android/os/MountServiceListener.java
deleted file mode 100644
index a68f464..0000000
--- a/core/java/android/os/MountServiceListener.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.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 MountServiceListener {
-    /**
-     * 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/MountServiceResultCode.java b/core/java/android/os/MountServiceResultCode.java
deleted file mode 100644
index e71dbf4..0000000
--- a/core/java/android/os/MountServiceResultCode.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2007 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;
-
-import java.io.IOException;
-
-/**
- * Class that provides access to constants returned from MountService APIs
- *
- * {@hide}
- */
-public class MountServiceResultCode
-{
-    public static final int OperationSucceeded               =  0;
-    public static final int OperationFailedInternalError     = -1;
-    public static final int OperationFailedNoMedia           = -2;
-    public static final int OperationFailedMediaBlank        = -3;
-    public static final int OperationFailedMediaCorrupt      = -4;
-    public static final int OperationFailedVolumeNotMounted  = -5;
-}
diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java
index bc76180..b3df522 100644
--- a/core/java/android/os/Power.java
+++ b/core/java/android/os/Power.java
@@ -18,7 +18,7 @@
 
 import java.io.IOException;
 import android.os.ServiceManager;
-import android.os.IMountService;
+import android.os.storage.IMountService;
 
 /**
  * Class that provides access to some of the power management functions.
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
similarity index 82%
rename from core/java/android/os/IMountService.aidl
rename to core/java/android/os/storage/IMountService.aidl
index a5828f6..84e3f58 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -15,14 +15,15 @@
 ** limitations under the License.
 */
 
-package android.os;
+package android.os.storage;
 
-import android.os.IMountServiceListener;
+import android.os.storage.IMountServiceListener;
 
 /** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
  * In particular, the ordering of the methods below must match the 
  * _TRANSACTION enum in IMountService.cpp
- * @hide
+ * @hide - Applications should use android.os.storage.StorageManager to access
+ * storage functions.
  */
 interface IMountService
 {
@@ -38,31 +39,19 @@
     void unregisterListener(IMountServiceListener listener);
 
     /**
-     * Gets an Array of supported share methods
+     * Returns true if a USB mass storage host is connected
      */
-    String[] getShareMethodList();
+    boolean isUsbMassStorageConnected();
 
     /**
-     * Returns true if the share method is available
+     * Enables / disables USB mass storage.
      */
-    boolean getShareMethodAvailable(String method);
+    int setUsbMassStorageEnabled(boolean enable);
 
     /**
-     * Shares a volume via the specified method
-     * Returns an int consistent with MountServiceResultCode
+     * Returns true if a USB mass storage host is enabled (media is shared)
      */
-    int shareVolume(String path, String method);
-
-    /**
-     * Unshares a volume via the specified method
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int unshareVolume(String path, String method);
-
-    /**
-     * Returns true if the volume is shared via the specified method.
-     */
-    boolean getVolumeShared(String path, String method);
+    boolean isUsbMassStorageEnabled();
 
     /**
      * Mount external storage at given mount point.
diff --git a/core/java/android/os/storage/IMountServiceListener.aidl b/core/java/android/os/storage/IMountServiceListener.aidl
new file mode 100644
index 0000000..883413a
--- /dev/null
+++ b/core/java/android/os/storage/IMountServiceListener.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.storage;
+
+/**
+ * Callback class for receiving events from MountService.
+ *
+ * @hide - Applications should use android.os.storage.IStorageEventListener
+ * for storage event callbacks.
+ */
+interface IMountServiceListener {
+    /**
+     * Detection state of USB Mass Storage has changed
+     *
+     * @param available true if a UMS host is connected.
+     */
+    void onUsbMassStorageConnectionChanged(boolean connected);
+
+    /**
+     * Storage state has changed.
+     *
+     * @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 onStorageStateChanged(String path, String oldState, String newState);
+}
diff --git a/core/java/android/os/storage/MountServiceListener.java b/core/java/android/os/storage/MountServiceListener.java
new file mode 100644
index 0000000..bebb3f6
--- /dev/null
+++ b/core/java/android/os/storage/MountServiceListener.java
@@ -0,0 +1,44 @@
+/*
+ * 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.storage;
+
+/**
+ * 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 MountServiceListener {
+    /**
+     * USB Mass storage connection state has changed.
+     *
+     * @param connected True if UMS is connected.
+     */
+    void onUsbMassStorageConnectionChanged(boolean connected) {
+    }
+
+    /**
+     *  Storage state has changed.
+     *
+     * @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 onStorageStateChange(String path, String oldState, String newState) {
+    }
+}
diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java
new file mode 100644
index 0000000..d3d39d6
--- /dev/null
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 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.storage;
+
+/**
+ * Used for receiving notifications from the StorageManager
+ */
+public abstract class StorageEventListener {
+    /**
+     * Called when the detection state of a USB Mass Storage host has changed.
+     * @param connected true if the USB mass storage is connected.
+     */
+    public void onUsbMassStorageConnectionChanged(boolean connected) {
+    }
+
+    /**
+     * Called when storage has changed state
+     * @param path the filesystem path for the storage
+     * @param oldState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
+     * @param newState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
+     */
+    public void onStorageStateChanged(String path, String oldState, String newState) {
+    }
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
new file mode 100644
index 0000000..e421ea5
--- /dev/null
+++ b/core/java/android/os/storage/StorageManager.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2008 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.storage;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * StorageManager is the interface to the systems storage service.
+ * Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
+ * of {@link android.content.Context#STORAGE_SERVICE}.
+ *
+ */
+
+public class StorageManager
+{
+    private static final String TAG = "StorageManager";
+
+    /*
+     * Our internal MountService binder reference
+     */
+    private IMountService mMountService;
+
+    /*
+     * The looper target for callbacks
+     */
+    Looper mTgtLooper;
+
+    /*
+     * Target listener for binder callbacks
+     */
+    private MountServiceBinderListener mBinderListener;
+
+    /*
+     * List of our listeners
+     */
+    private ArrayList<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
+
+    private class MountServiceBinderListener extends IMountServiceListener.Stub {
+        public void onUsbMassStorageConnectionChanged(boolean available) {
+            final int size = mListeners.size();
+            for (int i = 0; i < size; i++) {
+                mListeners.get(i).sendShareAvailabilityChanged(available);
+            }
+        }
+
+        public void onStorageStateChanged(String path, String oldState, String newState) {
+            final int size = mListeners.size();
+            for (int i = 0; i < size; i++) {
+                mListeners.get(i).sendStorageStateChanged(path, oldState, newState);
+            }
+        }
+    }
+
+    /**
+     * Private base class for messages sent between the callback thread
+     * and the target looper handler.
+     */
+    private class StorageEvent {
+        public static final int EVENT_UMS_CONNECTION_CHANGED = 1;
+        public static final int EVENT_STORAGE_STATE_CHANGED   = 2;
+
+        private Message mMessage;
+
+        public StorageEvent(int what) {
+            mMessage = Message.obtain();
+            mMessage.what = what;
+            mMessage.obj = this;
+        }
+
+        public Message getMessage() {
+            return mMessage;
+        }
+    }
+
+    /**
+     * Message sent on a USB mass storage connection change.
+     */
+    private class UmsConnectionChangedStorageEvent extends StorageEvent {
+        public boolean available;
+
+        public UmsConnectionChangedStorageEvent(boolean a) {
+            super(EVENT_UMS_CONNECTION_CHANGED);
+            available = a;
+        }
+    }
+
+    /**
+     * Message sent on volume state change.
+     */
+    private class StorageStateChangedStorageEvent extends StorageEvent {
+        public String path;
+        public String oldState;
+        public String newState;
+
+        public StorageStateChangedStorageEvent(String p, String oldS, String newS) {
+            super(EVENT_STORAGE_STATE_CHANGED);
+            path = p;
+            oldState = oldS;
+            newState = newS;
+        }
+    }
+
+    /**
+     * Private class containing sender and receiver code for StorageEvents.
+     */
+    private class ListenerDelegate {
+        final StorageEventListener mStorageEventListener;
+        private final Handler mHandler;
+
+        ListenerDelegate(StorageEventListener listener) {
+            mStorageEventListener = listener;
+            mHandler = new Handler(mTgtLooper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    StorageEvent e = (StorageEvent) msg.obj;
+
+                    if (msg.what == StorageEvent.EVENT_UMS_CONNECTION_CHANGED) {
+                        UmsConnectionChangedStorageEvent ev = (UmsConnectionChangedStorageEvent) e;
+                        mStorageEventListener.onUsbMassStorageConnectionChanged(ev.available);
+                    } else if (msg.what == StorageEvent.EVENT_STORAGE_STATE_CHANGED) {
+                        StorageStateChangedStorageEvent ev = (StorageStateChangedStorageEvent) e;
+                        mStorageEventListener.onStorageStateChanged(ev.path, ev.oldState, ev.newState);
+                    } else {
+                        Log.e(TAG, "Unsupported event " + msg.what);
+                    }
+                }
+            };
+        }
+
+        StorageEventListener getListener() {
+            return mStorageEventListener;
+        }
+
+        void sendShareAvailabilityChanged(boolean available) {
+            UmsConnectionChangedStorageEvent e = new UmsConnectionChangedStorageEvent(available);
+            mHandler.sendMessage(e.getMessage());
+        }
+
+        void sendStorageStateChanged(String path, String oldState, String newState) {
+            StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);
+            mHandler.sendMessage(e.getMessage());
+        }
+    }
+
+    /**
+     * Constructs a StorageManager object through which an application can
+     * can communicate with the systems mount service.
+     * 
+     * @param tgtLooper The {@android.os.Looper} which events will be received on.
+     *
+     * <p>Applications can get instance of this class by calling
+     * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
+     * of {@link android.content.Context#STORAGE_SERVICE}.
+     *
+     * @hide
+     */
+    public StorageManager(Looper tgtLooper) throws RemoteException {
+        mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
+        if (mMountService == null) {
+            Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
+            return;
+        }
+        mTgtLooper = tgtLooper;
+        mBinderListener = new MountServiceBinderListener();
+        mMountService.registerListener(mBinderListener);
+    }
+
+
+    /**
+     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
+     *
+     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
+     *
+     */
+    public void registerListener(StorageEventListener listener) {
+        if (listener == null) {
+            return;
+        }
+
+        synchronized (mListeners) {
+            mListeners.add(new ListenerDelegate(listener));
+        }
+    }
+
+    /**
+     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
+     *
+     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
+     *
+     */
+    public void unregisterListener(StorageEventListener listener) {
+        if (listener == null) {
+            return;
+        }
+
+        synchronized (mListeners) {
+            final int size = mListeners.size();
+            for (int i=0 ; i<size ; i++) {
+                ListenerDelegate l = mListeners.get(i);
+                if (l.getListener() == listener) {
+                    mListeners.remove(i);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Enables USB Mass Storage (UMS) on the device.
+     * @return an integer value representing the outcome of the operation.
+     * @see android.os.storage.StorageResultCode
+     */
+    public int enableUsbMassStorage() {
+        try {
+            return mMountService.setUsbMassStorageEnabled(true);
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed to enable UMS", ex);
+        }
+        return StorageResultCode.OperationFailedInternalError;
+    }
+
+    /**
+     * Disables USB Mass Storage (UMS) on the device.
+     * @return an integer value representing the outcome of the operation.
+     * @see android.os.storage.StorageResultCode
+     */
+    public int disableUsbMassStorage() {
+        try {
+            return mMountService.setUsbMassStorageEnabled(false);
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed to disable UMS", ex);
+        }
+        return StorageResultCode.OperationFailedInternalError;
+    }
+
+    /**
+     * Query if a USB Mass Storage (UMS) host is connected.
+     * @return true if UMS host is connected.
+     */
+    public boolean isUsbMassStorageConnected() {
+        try {
+            return mMountService.isUsbMassStorageConnected();
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed to get UMS connection state", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Query if a USB Mass Storage (UMS) is enabled on the device.
+     * @return true if UMS host is enabled.
+     */
+    public boolean isUsbMassStorageEnabled() {
+        try {
+            return mMountService.isUsbMassStorageEnabled();
+        } catch (RemoteException rex) {
+            Log.e(TAG, "Failed to get UMS enable state", rex);
+        }
+        return false;
+    }
+}
diff --git a/core/java/android/os/storage/StorageResultCode.java b/core/java/android/os/storage/StorageResultCode.java
new file mode 100644
index 0000000..584f160
--- /dev/null
+++ b/core/java/android/os/storage/StorageResultCode.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 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.storage;
+
+/**
+ * Class that provides access to constants returned from StorageManager
+ * and lower level MountService APIs.
+ */
+public class StorageResultCode
+{
+    /**
+     * Operation succeeded.
+     * @see android.os.storage.StorageManager
+     */
+    public static final int OperationSucceeded               =  0;
+
+    /**
+     * Operation failed: Internal error.
+     * @see android.os.storage.StorageManager
+     */
+    public static final int OperationFailedInternalError     = -1;
+
+    /**
+     * Operation failed: Missing media.
+     * @see android.os.storage.StorageManager
+     */
+    public static final int OperationFailedNoMedia           = -2;
+
+    /**
+     * Operation failed: Media is blank.
+     * @see android.os.storage.StorageManager
+     */
+    public static final int OperationFailedMediaBlank        = -3;
+
+    /**
+     * Operation failed: Media is corrupt.
+     * @see android.os.storage.StorageManager
+     */
+    public static final int OperationFailedMediaCorrupt      = -4;
+
+    /**
+     * Operation failed: Media not mounted.
+     * @see android.os.storage.StorageManager
+     */
+    public static final int OperationFailedVolumeNotMounted  = -5;
+}
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 324fbaa..fbc4a81 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -75,8 +75,8 @@
         mContext.registerReceiver(mPackageChangedReceiver, packageFilter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mPackageChangedReceiver, sdFilter);
     }
 
@@ -96,8 +96,8 @@
             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
                     Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
                     Intent.ACTION_PACKAGE_CHANGED.equals(action) ||
-                    Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) ||
-                    Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                    Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
+                    Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 if (DBG) Log.d(TAG, "Got " + action);
                 // Update list of searchable activities
                 getSearchables().buildSearchableList();
diff --git a/core/java/android/storage/StorageEventListener.java b/core/java/android/storage/StorageEventListener.java
deleted file mode 100644
index cd71090..0000000
--- a/core/java/android/storage/StorageEventListener.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2008 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.storage;
-
-/**
- * Used for receiving notifications from the StorageManager
- */
-public interface StorageEventListener {
-    /**
-     * Called when the ability to share a volume has changed.
-     * @param method the share-method which has changed.
-     * @param available true if the share is available.
-     */
-    public void onShareAvailabilityChanged(String method, boolean available);
-
-    /**
-     * Called when media has been inserted
-     * @param label the system defined label for the volume.
-     * @param path the filesystem path for the volume.
-     * @param major the major number of the device.
-     * @param minor the minor number of the device.
-     */
-    public void onMediaInserted(String label, String path, int major, int minor);
-
-    /**
-     * Called when media has been removed
-     * @param label the system defined label for the volume.
-     * @param path the filesystem path for the volume.
-     * @param major the major number of the device.
-     * @param minor the minor number of the device.
-     * @param clean the media was removed cleanly.
-     */
-    public void onMediaRemoved(String label, String path, int major, int minor, boolean clean);
-
-    /**
-     * Called when a volume has changed state
-     * @param label the system defined label for the volume.
-     * @param path the filesystem path for the volume.
-     * @param oldState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
-     * @param newState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
-     */
-    public void onVolumeStateChanged(String label, String path, String oldState, String newState);
-}
diff --git a/core/java/android/storage/StorageManager.java b/core/java/android/storage/StorageManager.java
deleted file mode 100644
index dd8bd63..0000000
--- a/core/java/android/storage/StorageManager.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2008 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.storage;
-
-import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.Handler;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.IMountService;
-import android.os.IMountServiceListener;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Class that lets you access the device's storage management functions. Get an instance of this
- * class by calling {@link android.content.Context#getSystemService(java.lang.String)
- * Context.getSystemService()} with an argument of {@link android.content.Context#STORAGE_SERVICE}.
- */
-public class StorageManager
-{
-    private static final String TAG = "StorageManager";
-
-    /*
-     * Our internal MountService binder reference
-     */
-    private IMountService mMountService;
-
-    /*
-     * The looper target for callbacks
-     */
-    Looper mTgtLooper;
-
-    /*
-     * Target listener for binder callbacks
-     */
-    private MountServiceBinderListener mBinderListener;
-
-    /*
-     * *static* list of our listeners
-     */
-    static final ArrayList<ListenerDelegate> sListeners = new ArrayList<ListenerDelegate>();
-
-    private class MountServiceBinderListener extends IMountServiceListener.Stub {
-        public void onShareAvailabilityChanged(String method, boolean available) {
-            final int size = sListeners.size();
-            for (int i = 0; i < size; i++) {
-                sListeners.get(i).sendShareAvailabilityChanged(method, available);
-            }
-        }
-
-        public void onMediaInserted(String label, String path, int major, int minor) {
-            final int size = sListeners.size();
-            for (int i = 0; i < size; i++) {
-                sListeners.get(i).sendMediaInserted(label, path, major, minor);
-            }
-        }
-
-        public void onMediaRemoved(String label, String path, int major, int minor, boolean clean) {
-            final int size = sListeners.size();
-            for (int i = 0; i < size; i++) {
-                sListeners.get(i).sendMediaRemoved(label, path, major, minor, clean);
-            }
-        }
-
-        public void onVolumeStateChanged(String label, String path, String oldState, String newState) {
-            final int size = sListeners.size();
-            for (int i = 0; i < size; i++) {
-                sListeners.get(i).sendVolumeStateChanged(label, path, oldState, newState);
-            }
-        }
-    }
-
-    /**
-     * Private base class for messages sent between the callback thread
-     * and the target looper handler
-     */
-    private class StorageEvent {
-        public static final int EVENT_SHARE_AVAILABILITY_CHANGED = 1;
-        public static final int EVENT_MEDIA_INSERTED             = 2;
-        public static final int EVENT_MEDIA_REMOVED              = 3;
-        public static final int EVENT_VOLUME_STATE_CHANGED       = 4;
-
-        private Message mMessage;
-
-        public StorageEvent(int what) {
-            mMessage = Message.obtain();
-            mMessage.what = what;
-            mMessage.obj = this;
-        }
-
-        public Message getMessage() {
-            return mMessage;
-        }
-    }
-
-    /**
-     * Message sent on a share availability change.
-     */
-    private class ShareAvailabilityChangedStorageEvent extends StorageEvent {
-        public String method;
-        public boolean available;
-
-        public ShareAvailabilityChangedStorageEvent(String m, boolean a) {
-            super(EVENT_SHARE_AVAILABILITY_CHANGED);
-            method = m;
-            available = a;
-        }
-    }
-
-    /**
-     * Message sent on media insertion
-     */
-    private class MediaInsertedStorageEvent extends StorageEvent {
-        public String label;
-        public String path;
-        public int major;
-        public int minor;
-
-        public MediaInsertedStorageEvent(String l, String p, int maj, int min) {
-            super(EVENT_MEDIA_INSERTED);
-            label = l;
-            path = p;
-            major = maj;
-            minor = min;
-        }
-    }
-
-    /**
-     * Message sent on media removal
-     */
-    private class MediaRemovedStorageEvent extends StorageEvent {
-        public String label;
-        public String path;
-        public int major;
-        public int minor;
-        public boolean clean;
-
-        public MediaRemovedStorageEvent(String l, String p, int maj, int min, boolean c) {
-            super(EVENT_MEDIA_REMOVED);
-            label = l;
-            path = p;
-            major = maj;
-            minor = min;
-            clean = c;
-        }
-    }
-
-    /**
-     * Message sent on volume state change
-     */
-    private class VolumeStateChangedStorageEvent extends StorageEvent {
-        public String label;
-        public String path;
-        public String oldState;
-        public String newState;
-
-        public VolumeStateChangedStorageEvent(String l, String p, String oldS, String newS) {
-            super(EVENT_VOLUME_STATE_CHANGED);
-            label = l;
-            path = p;
-            oldState = oldS;
-            newState = newS;
-        }
-    }
-
-    /**
-     * Private class containing sender and receiver code for StorageEvents
-     */
-    private class ListenerDelegate {
-        final StorageEventListener mStorageEventListener;
-        private final Handler mHandler;
-
-        ListenerDelegate(StorageEventListener listener) {
-            mStorageEventListener = listener;
-            mHandler = new Handler(mTgtLooper) {
-                @Override
-                public void handleMessage(Message msg) {
-                    StorageEvent e = (StorageEvent) msg.obj;
-
-                    if (msg.what == StorageEvent.EVENT_SHARE_AVAILABILITY_CHANGED) {
-                        ShareAvailabilityChangedStorageEvent ev = (ShareAvailabilityChangedStorageEvent) e;
-                        mStorageEventListener.onShareAvailabilityChanged(ev.method, ev.available);
-                    } else if (msg.what == StorageEvent.EVENT_MEDIA_INSERTED) {
-                        MediaInsertedStorageEvent ev = (MediaInsertedStorageEvent) e;
-                        mStorageEventListener.onMediaInserted(ev.label, ev.path, ev.major, ev.minor);
-                    } else if (msg.what == StorageEvent.EVENT_MEDIA_REMOVED) {
-                        MediaRemovedStorageEvent ev = (MediaRemovedStorageEvent) e;
-                        mStorageEventListener.onMediaRemoved(ev.label, ev.path, ev.major, ev.minor, ev.clean);
-                    } else if (msg.what == StorageEvent.EVENT_VOLUME_STATE_CHANGED) {
-                        VolumeStateChangedStorageEvent ev = (VolumeStateChangedStorageEvent) e;
-                        mStorageEventListener.onVolumeStateChanged(ev.label, ev.path, ev.oldState, ev.newState);
-                    } else {
-                        Log.e(TAG, "Unsupported event " + msg.what);
-                    }
-                }
-            };
-        }
-
-        StorageEventListener getListener() {
-            return mStorageEventListener;
-        }
-
-        void sendShareAvailabilityChanged(String method, boolean available) {
-            ShareAvailabilityChangedStorageEvent e = new ShareAvailabilityChangedStorageEvent(method, available);
-            mHandler.sendMessage(e.getMessage());
-        }
-
-        void sendMediaInserted(String label, String path, int major, int minor) {
-            MediaInsertedStorageEvent e = new MediaInsertedStorageEvent(label, path, major, minor);
-            mHandler.sendMessage(e.getMessage());
-        }
-
-        void sendMediaRemoved(String label, String path, int major, int minor, boolean clean) {
-            MediaRemovedStorageEvent e = new MediaRemovedStorageEvent(label, path, major, minor, clean);
-            mHandler.sendMessage(e.getMessage());
-        }
-
-        void sendVolumeStateChanged(String label, String path, String oldState, String newState) {
-            VolumeStateChangedStorageEvent e = new VolumeStateChangedStorageEvent(label, path, oldState, newState);
-            mHandler.sendMessage(e.getMessage());
-        }
-    }
-
-    /**
-     * {@hide}
-     */
-    public StorageManager(Looper tgtLooper) throws RemoteException {
-        mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
-        mTgtLooper = tgtLooper;
-        mBinderListener = new MountServiceBinderListener();
-        mMountService.registerListener(mBinderListener);
-    }
-
-
-    /**
-     * Registers a {@link android.storage.StorageEventListener StorageEventListener}.
-     *
-     * @param listener A {@link android.storage.StorageEventListener StorageEventListener} object.
-     *
-     */
-    public void registerListener(StorageEventListener listener) {
-        if (listener == null) {
-            return;
-        }
-
-        synchronized (sListeners) {
-            sListeners.add(new ListenerDelegate(listener));
-        }
-    }
-
-    /**
-     * Unregisters a {@link android.storage.StorageEventListener StorageEventListener}.
-     *
-     * @param listener A {@link android.storage.StorageEventListener StorageEventListener} object.
-     *
-     */
-    public void unregisterListener(StorageEventListener listener) {
-        if (listener == null) {
-            return;
-        }
-        synchronized (sListeners) {
-            final int size = sListeners.size();
-            for (int i=0 ; i<size ; i++) {
-                ListenerDelegate l = sListeners.get(i);
-                if (l.getListener() == listener) {
-                    sListeners.remove(i);
-                    break;
-                }
-            }
-        }
-    }
-}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5c465e..889985a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8281,6 +8281,9 @@
      * Cancels any animations for this view.
      */
     public void clearAnimation() {
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.cancel();
+        }
         mCurrentAnimation = null;
     }
 
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 000e4ce..ad98259 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -256,6 +256,27 @@
     }
 
     /**
+     * Cancel the animation. Cancelling an animation invokes the animation
+     * listener, if set, to notify the end of the animation.
+     * 
+     * If you cancel an animation manually, you must call {@link #reset()}
+     * before starting the animation again.
+     * 
+     * @see #reset() 
+     * @see #start() 
+     * @see #startNow() 
+     */
+    public void cancel() {
+        if (mStarted && !mEnded) {
+            if (mListener != null) mListener.onAnimationEnd(this);
+            mEnded = true;
+        }
+        // Make sure we move the animation to the end
+        mStartTime = Long.MIN_VALUE;
+        mMore = mOneMoreTime = false;
+    }
+
+    /**
      * Whether or not the animation has been initialized.
      *
      * @return Has this animation been initialized.
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 6d0be43..9e9cc7e 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -145,6 +145,12 @@
                 break;
         }
 
+        if (KeyEvent.KEYCODE_TAB == keyCode) {
+            if (down) {
+                onEditorAction(EditorInfo.IME_ACTION_NEXT);
+            }
+            return true;
+        }
         Spannable text = (Spannable) getText();
         int oldLength = text.length();
         // Normally the delete key's dom events are sent via onTextChanged.
@@ -810,6 +816,9 @@
         int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
                 | EditorInfo.IME_FLAG_NO_FULLSCREEN;
         switch (type) {
+            case 0: // NORMAL_TEXT_FIELD
+                imeOptions |= EditorInfo.IME_ACTION_GO;
+                break;
             case 1: // TEXT_AREA
                 single = false;
                 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
@@ -819,6 +828,7 @@
                 break;
             case 2: // PASSWORD
                 inPassword = true;
+                imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
             case 3: // SEARCH
                 imeOptions |= EditorInfo.IME_ACTION_SEARCH;
@@ -826,22 +836,25 @@
             case 4: // EMAIL
                 // TYPE_TEXT_VARIATION_WEB_EDIT_TEXT prevents EMAIL_ADDRESS
                 // from working, so exclude it for now.
-                inputType = EditorInfo.TYPE_CLASS_TEXT
-                        | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+                imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
             case 5: // NUMBER
-                inputType = EditorInfo.TYPE_CLASS_NUMBER;
+                inputType |= EditorInfo.TYPE_CLASS_NUMBER;
+                // Number and telephone do not have both a Tab key and an
+                // action, so set the action to NEXT
+                imeOptions |= EditorInfo.IME_ACTION_NEXT;
                 break;
             case 6: // TELEPHONE
-                inputType = EditorInfo.TYPE_CLASS_PHONE;
+                inputType |= EditorInfo.TYPE_CLASS_PHONE;
+                imeOptions |= EditorInfo.IME_ACTION_NEXT;
                 break;
             case 7: // URL
-                // TYPE_TEXT_VARIATION_WEB_EDIT_TEXT prevents URI
-                // from working, so exclude it for now.
-                inputType = EditorInfo.TYPE_CLASS_TEXT
-                        | EditorInfo.TYPE_TEXT_VARIATION_URI;
+                // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
+                // exclude it for now.
+                imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
             default:
+                imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
         }
         setHint(null);
@@ -855,22 +868,6 @@
                     mWebView.requestFormData(name, mNodePointer);
                 }
             }
-            if (type != 3 /* SEARCH */) {
-                int action = mWebView.nativeTextFieldAction();
-                switch (action) {
-                    // Keep in sync with CachedRoot::ImeAction
-                    case 0: // NEXT
-                        imeOptions |= EditorInfo.IME_ACTION_NEXT;
-                        break;
-                    case 1: // GO
-                        imeOptions |= EditorInfo.IME_ACTION_GO;
-                        break;
-                    case -1: // FAILURE
-                    case 2: // DONE
-                        imeOptions |= EditorInfo.IME_ACTION_DONE;
-                        break;
-                }
-            }
         }
         mSingle = single;
         setMaxLength(maxLength);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6627973..8fa8f09 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -26,6 +26,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Interpolator;
 import android.graphics.Picture;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -2972,8 +2973,16 @@
         if (mTitleBar != null) {
             canvas.translate(0, (int) mTitleBar.getHeight());
         }
-        if (mDragTrackerHandler == null || !mDragTrackerHandler.draw(canvas)) {
+        if (mDragTrackerHandler == null) {
             drawContent(canvas);
+        } else {
+            if (!mDragTrackerHandler.draw(canvas)) {
+                // sometimes the tracker doesn't draw, even though its active
+                drawContent(canvas);
+            }
+            if (mDragTrackerHandler.isFinished()) {
+                mDragTrackerHandler = null;
+            }
         }
         canvas.restoreToCount(saveCount);
 
@@ -3370,6 +3379,10 @@
                 text = "";
             }
             mWebTextView.setTextAndKeepSelection(text);
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null && imm.isActive(mWebTextView)) {
+                imm.restartInput(mWebTextView);
+            }
         }
         mWebTextView.requestFocus();
     }
@@ -4066,6 +4079,14 @@
         private final float mMaxDY, mMaxDX;
         private float mCurrStretchY, mCurrStretchX;
         private int mSX, mSY;
+        private Interpolator mInterp;
+        private float[] mXY = new float[2];
+
+        // inner (non-state) classes can't have enums :(
+        private static final int DRAGGING_STATE = 0;
+        private static final int ANIMATING_STATE = 1;
+        private static final int FINISHED_STATE = 2;
+        private int mState;
 
         public DragTrackerHandler(float x, float y, DragTracker proxy) {
             mProxy = proxy;
@@ -4090,6 +4111,7 @@
             mMinDX = -viewLeft;
             mMaxDX = docRight - viewRight;
 
+            mState = DRAGGING_STATE;
             mProxy.onStartDrag(x, y);
 
             // ensure we buildBitmap at least once
@@ -4112,6 +4134,12 @@
             float sy = computeStretch(mStartY - y, mMinDY, mMaxDY);
             float sx = computeStretch(mStartX - x, mMinDX, mMaxDX);
 
+            if ((mSnapScrollMode & SNAP_X) != 0) {
+                sy = 0;
+            } else if ((mSnapScrollMode & SNAP_Y) != 0) {
+                sx = 0;
+            }
+
             if (mCurrStretchX != sx || mCurrStretchY != sy) {
                 mCurrStretchX = sx;
                 mCurrStretchY = sy;
@@ -4126,10 +4154,26 @@
         }
 
         public void stopDrag() {
+            final int DURATION = 200;
+            int now = (int)SystemClock.uptimeMillis();
+            mInterp = new Interpolator(2);
+            mXY[0] = mCurrStretchX;
+            mXY[1] = mCurrStretchY;
+         //   float[] blend = new float[] { 0.5f, 0, 0.75f, 1 };
+            float[] blend = new float[] { 0, 0.5f, 0.75f, 1 };
+            mInterp.setKeyFrame(0, now, mXY, blend);
+            float[] zerozero = new float[] { 0, 0 };
+            mInterp.setKeyFrame(1, now + DURATION, zerozero, null);
+            mState = ANIMATING_STATE;
+
             if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
-                Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag");
+                Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag, starting animation");
             }
-            mProxy.onStopDrag();
+        }
+
+        // Call this after each draw. If it ruturns null, the tracker is done
+        public boolean isFinished() {
+            return mState == FINISHED_STATE;
         }
 
         private int hiddenHeightOfTitleBar() {
@@ -4150,13 +4194,23 @@
             if (mCurrStretchX != 0 || mCurrStretchY != 0) {
                 int sx = getScrollX();
                 int sy = getScrollY() - hiddenHeightOfTitleBar();
-
                 if (mSX != sx || mSY != sy) {
                     buildBitmap(sx, sy);
                     mSX = sx;
                     mSY = sy;
                 }
 
+                if (mState == ANIMATING_STATE) {
+                    Interpolator.Result result = mInterp.timeToValues(mXY);
+                    if (result == Interpolator.Result.FREEZE_END) {
+                        mState = FINISHED_STATE;
+                        return false;
+                    } else {
+                        mProxy.onStretchChange(mXY[0], mXY[1]);
+                        invalidate();
+                        // fall through to the draw
+                    }
+                }
                 int count = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                 canvas.translate(sx, sy);
                 mProxy.onDraw(canvas);
@@ -4600,7 +4654,6 @@
             case MotionEvent.ACTION_UP: {
                 if (mDragTrackerHandler != null) {
                     mDragTrackerHandler.stopDrag();
-                    mDragTrackerHandler = null;
                 }
                 mLastTouchUpTime = eventTime;
                 switch (mTouchMode) {
@@ -4712,7 +4765,6 @@
     private void cancelTouch() {
         if (mDragTrackerHandler != null) {
             mDragTrackerHandler.stopDrag();
-            mDragTrackerHandler = null;
         }
         // we also use mVelocityTracker == null to tell us that we are
         // not "moving around", so we can take the slower/prettier
@@ -6124,6 +6176,9 @@
                     // mContentHeight may not be updated yet
                     y = Math.max(0,
                             (Math.min(maxHeight, y + viewHeight) - viewHeight));
+                    // We need to take into account the visible title height
+                    // when scrolling since y is an absolute view position.
+                    y = Math.max(0, y - getVisibleTitleHeight());
                     scrollTo(x, y);
                     }
                     break;
@@ -6736,8 +6791,6 @@
     private native void     nativeSetFindIsUp();
     private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
-    // Returns a value corresponding to CachedFrame::ImeAction
-    /* package */ native int  nativeTextFieldAction();
     private native int      nativeTextGeneration();
     // Never call this version except by updateCachedTextfield(String) -
     // we always want to pass in our generation number.
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index fd6af05..2f96aef 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1953,6 +1953,7 @@
                 }
             }
         }
+        mLastTouchMode = isInTouchMode ? TOUCH_MODE_ON : TOUCH_MODE_OFF;
     }
 
     @Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 299ed8a..9ef5e0b 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -103,6 +103,18 @@
         mMonthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
         DateFormatSymbols dfs = new DateFormatSymbols();
         String[] months = dfs.getShortMonths();
+
+        /*
+         * If the user is in a locale where the month names are numeric,
+         * use just the number instead of the "month" character for
+         * consistency with the other fields.
+         */
+        if (months[0].startsWith("1")) {
+            for (int i = 0; i < months.length; i++) {
+                months[i] = String.valueOf(i + 1);
+            }
+        }
+
         mMonthPicker.setRange(1, 12, months);
         mMonthPicker.setSpeed(200);
         mMonthPicker.setOnChangeListener(new OnChangedListener() {
diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
index 2b07ae6..7e9bbd1 100644
--- a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
+++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
@@ -24,7 +24,7 @@
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IMountService;
+import android.os.storage.IMountService;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 98fb236..24818a8 100755
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -24,7 +24,6 @@
 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;
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index c110f95..2f48499 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -32,7 +32,7 @@
 import android.os.Power;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.IMountService;
+import android.os.storage.IMountService;
 
 import com.android.internal.telephony.ITelephony;
 import android.util.Log;
diff --git a/core/java/com/android/internal/app/StorageNotification.java b/core/java/com/android/internal/app/StorageNotification.java
new file mode 100644
index 0000000..8876612
--- /dev/null
+++ b/core/java/com/android/internal/app/StorageNotification.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2010 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.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.storage.IMountService;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageResultCode;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class StorageNotification extends StorageEventListener {
+    private static final String TAG = "StorageNotification";
+
+    /**
+     * Binder context for this service
+     */
+    private Context mContext;
+    
+    /**
+     * The notification that is shown when a USB mass storage host
+     * is connected. 
+     * <p>
+     * This is lazily created, so use {@link #setUsbStorageNotification()}.
+     */
+    private Notification mUsbStorageNotification;
+
+    /**
+     * The notification that is shown when the following media events occur:
+     *     - Media is being checked
+     *     - Media is blank (or unknown filesystem)
+     *     - Media is corrupt
+     *     - Media is safe to unmount
+     *     - Media is missing
+     * <p>
+     * This is lazily created, so use {@link #setMediaStorageNotification()}.
+     */
+    private Notification   mMediaStorageNotification;
+    private boolean        mUmsAvailable;
+    private StorageManager mStorageManager;
+
+    public StorageNotification(Context context) {
+        mContext = context;
+
+        mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+        mUmsAvailable = mStorageManager.isUsbMassStorageConnected();
+        Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable,
+                Environment.getExternalStorageState()));
+    }
+
+    /*
+     * @override com.android.os.storage.StorageEventListener
+     */
+    @Override
+    public void onUsbMassStorageConnectionChanged(boolean connected) {
+        mUmsAvailable = connected;
+        /*
+         * Even though we may have a UMS host connected, we the SD card
+         * may not be in a state for export.
+         */
+        String st = Environment.getExternalStorageState();
+
+        Log.i(TAG, String.format("UMS connection changed to %s (media state %s)", connected, st));
+
+        if (connected && (st.equals(
+                Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) {
+            /*
+             * No card or card being checked = don't display
+             */
+            connected = false;
+        }
+        updateUsbMassStorageNotification(connected);
+    }
+
+    /*
+     * @override com.android.os.storage.StorageEventListener
+     */
+    @Override
+    public void onStorageStateChanged(String path, String oldState, String newState) {
+        Log.i(TAG, String.format(
+                "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
+        if (newState.equals(Environment.MEDIA_SHARED)) {
+            /*
+             * Storage is now shared. Modify the UMS notification
+             * for stopping UMS.
+             */
+            Intent intent = new Intent();
+            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,
+                    com.android.internal.R.drawable.stat_sys_warning, false, true, pi);
+        } else if (newState.equals(Environment.MEDIA_CHECKING)) {
+            /*
+             * Storage is now checking. Update media notification and disable
+             * UMS notification.
+             */
+            setMediaStorageNotification(
+                    com.android.internal.R.string.ext_media_checking_notification_title,
+                    com.android.internal.R.string.ext_media_checking_notification_message,
+                    com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null);
+            updateUsbMassStorageNotification(false);
+        } else if (newState.equals(Environment.MEDIA_MOUNTED)) {
+            /*
+             * Storage is now mounted. Dismiss any media notifications,
+             * and enable UMS notification if connected.
+             */
+            setMediaStorageNotification(0, 0, 0, false, false, null);
+            updateUsbMassStorageNotification(mUmsAvailable);
+        } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) {
+            /*
+             * Storage is now unmounted. We may have been unmounted
+             * because the user is enabling/disabling UMS, in which case we don't
+             * want to display the 'safe to unmount' notification.
+             */
+            if (!mStorageManager.isUsbMassStorageEnabled()) {
+                if (oldState.equals(Environment.MEDIA_SHARED)) {
+                    /*
+                     * The unmount was due to UMS being enabled. Dismiss any
+                     * media notifications, and enable UMS notification if connected
+                     */
+                    setMediaStorageNotification(0, 0, 0, false, false, null);
+                    updateUsbMassStorageNotification(mUmsAvailable);
+                } else {
+                    /*
+                     * Show safe to unmount media notification, and enable UMS
+                     * notification if connected.
+                     */
+                    setMediaStorageNotification(
+                            com.android.internal.R.string.ext_media_safe_unmount_notification_title,
+                            com.android.internal.R.string.ext_media_safe_unmount_notification_message,
+                            com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
+                    updateUsbMassStorageNotification(mUmsAvailable);
+                }
+            } else {
+                /*
+                 * The unmount was due to UMS being enabled. Dismiss any
+                 * media notifications, and disable the UMS notification
+                 */
+                setMediaStorageNotification(0, 0, 0, false, false, null);
+                updateUsbMassStorageNotification(false);
+            }
+        } else if (newState.equals(Environment.MEDIA_NOFS)) {
+            /*
+             * Storage has no filesystem. Show blank media notification,
+             * and enable UMS notification if connected.
+             */
+            Intent intent = new Intent();
+            intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
+            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+            setMediaStorageNotification(
+                    com.android.internal.R.string.ext_media_nofs_notification_title,
+                    com.android.internal.R.string.ext_media_nofs_notification_message,
+                    com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
+            updateUsbMassStorageNotification(mUmsAvailable);
+        } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) {
+            /*
+             * Storage is corrupt. Show corrupt media notification,
+             * and enable UMS notification if connected.
+             */
+            Intent intent = new Intent();
+            intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
+            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+            setMediaStorageNotification(
+                    com.android.internal.R.string.ext_media_unmountable_notification_title,
+                    com.android.internal.R.string.ext_media_unmountable_notification_message,
+                    com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); 
+            updateUsbMassStorageNotification(mUmsAvailable);
+        } else if (newState.equals(Environment.MEDIA_REMOVED)) {
+            /*
+             * Storage has been removed. Show nomedia media notification,
+             * and disable UMS notification regardless of connection state.
+             */
+            setMediaStorageNotification(
+                    com.android.internal.R.string.ext_media_nomedia_notification_title,
+                    com.android.internal.R.string.ext_media_nomedia_notification_message,
+                    com.android.internal.R.drawable.stat_notify_sdcard_usb,
+                    true, false, null);
+            updateUsbMassStorageNotification(false);
+        } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) {
+            /*
+             * Storage has been removed unsafely. Show bad removal media notification,
+             * and disable UMS notification regardless of connection state.
+             */
+            setMediaStorageNotification(
+                    com.android.internal.R.string.ext_media_badremoval_notification_title,
+                    com.android.internal.R.string.ext_media_badremoval_notification_message,
+                    com.android.internal.R.drawable.stat_sys_warning,
+                    true, true, null);
+            updateUsbMassStorageNotification(false);
+        } else {
+            Log.w(TAG, String.format("Ignoring unknown state {%s}", newState));
+        }
+    }
+
+    /**
+     * Update the state of the USB mass storage notification
+     */
+    void updateUsbMassStorageNotification(boolean available) {
+
+        if (available) {
+            Intent intent = new Intent();
+            intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+            setUsbStorageNotification(
+                    com.android.internal.R.string.usb_storage_notification_title,
+                    com.android.internal.R.string.usb_storage_notification_message,
+                    com.android.internal.R.drawable.stat_sys_data_usb,
+                    false, true, pi);
+        } else {
+            setUsbStorageNotification(0, 0, 0, false, false, null);
+        }
+    }
+
+    /**
+     * Sets the USB storage notification.
+     */
+    private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
+                                                        PendingIntent pi) {
+
+        if (!visible && mUsbStorageNotification == null) {
+            return;
+        }
+
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (notificationManager == null) {
+            return;
+        }
+        
+        if (visible) {
+            Resources r = Resources.getSystem();
+            CharSequence title = r.getText(titleId);
+            CharSequence message = r.getText(messageId);
+
+            if (mUsbStorageNotification == null) {
+                mUsbStorageNotification = new Notification();
+                mUsbStorageNotification.icon = icon;
+                mUsbStorageNotification.when = 0;
+            }
+
+            if (sound) {
+                mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+            } else {
+                mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+            }
+                
+            mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
+
+            mUsbStorageNotification.tickerText = title;
+            if (pi == null) {
+                Intent intent = new Intent();
+                pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            }
+
+            mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+        }
+    
+        final int notificationId = mUsbStorageNotification.icon;
+        if (visible) {
+            notificationManager.notify(notificationId, mUsbStorageNotification);
+        } else {
+            notificationManager.cancel(notificationId);
+        }
+    }
+
+    private synchronized boolean getMediaStorageNotificationDismissable() {
+        if ((mMediaStorageNotification != null) &&
+            ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
+                    Notification.FLAG_AUTO_CANCEL))
+            return true;
+
+        return false;
+    }
+
+    /**
+     * Sets the media storage notification.
+     */
+    private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
+                                                          boolean dismissable, PendingIntent pi) {
+
+        if (!visible && mMediaStorageNotification == null) {
+            return;
+        }
+
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (notificationManager == null) {
+            return;
+        }
+
+        if (mMediaStorageNotification != null && visible) {
+            /*
+             * Dismiss the previous notification - we're about to
+             * re-use it.
+             */
+            final int notificationId = mMediaStorageNotification.icon;
+            notificationManager.cancel(notificationId);
+        }
+        
+        if (visible) {
+            Resources r = Resources.getSystem();
+            CharSequence title = r.getText(titleId);
+            CharSequence message = r.getText(messageId);
+
+            if (mMediaStorageNotification == null) {
+                mMediaStorageNotification = new Notification();
+                mMediaStorageNotification.when = 0;
+            }
+
+            mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+
+            if (dismissable) {
+                mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
+            } else {
+                mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
+            }
+
+            mMediaStorageNotification.tickerText = title;
+            if (pi == null) {
+                Intent intent = new Intent();
+                pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            }
+
+            mMediaStorageNotification.icon = icon;
+            mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+        }
+    
+        final int notificationId = mMediaStorageNotification.icon;
+        if (visible) {
+            notificationManager.notify(notificationId, mMediaStorageNotification);
+        } else {
+            notificationManager.cancel(notificationId);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
index 2b93dbc..cb268b3 100644
--- a/core/java/com/android/internal/app/TetherActivity.java
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -25,7 +25,6 @@
 import android.net.ConnectivityManager;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IMountService;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
diff --git a/core/java/com/android/internal/app/UsbStorageActivity.java b/core/java/com/android/internal/app/UsbStorageActivity.java
index 34ae2b4..991f04b 100644
--- a/core/java/com/android/internal/app/UsbStorageActivity.java
+++ b/core/java/com/android/internal/app/UsbStorageActivity.java
@@ -25,8 +25,9 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Environment;
-import android.os.IMountService;
-import android.os.MountServiceResultCode;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageResultCode;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -35,6 +36,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 import android.view.View;
+import android.util.Log;
 
 /**
  * This activity is shown to the user for him/her to enable USB mass storage
@@ -42,11 +44,13 @@
  * dialog style. It will be launched from a notification.
  */
 public class UsbStorageActivity extends Activity {
+    private static final String TAG = "UsbStorageActivity";
     private Button mMountButton;
     private Button mUnmountButton;
     private TextView mBanner;
     private TextView mMessage;
     private ImageView mIcon;
+    private StorageManager mStorageManager = null;
 
     /** Used to detect when the USB cable is unplugged, so we can call finish() */
     private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
@@ -57,11 +61,30 @@
             }
         }
     };
+
+    private StorageEventListener mStorageListener = new StorageEventListener() {
+        @Override
+        public void onStorageStateChanged(String path, String oldState, String newState) {
+            if (newState.equals(Environment.MEDIA_SHARED)) {
+                switchDisplay(true);
+            } else {
+                switchDisplay(false);
+            }
+        }
+    };
     
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        if (mStorageManager == null) {
+            mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+            if (mStorageManager == null) {
+                Log.w(TAG, "Failed to get StorageManager");
+            }
+            mStorageManager.registerListener(mStorageListener);
+        }
+
         setTitle(getString(com.android.internal.R.string.usb_storage_activity_title));
 
         setContentView(com.android.internal.R.layout.usb_storage_activity);
@@ -74,9 +97,11 @@
         mMountButton.setOnClickListener(
             new View.OnClickListener() { 
                  public void onClick(View v) {
-                     mountAsUsbStorage();
-                     // TODO: replace with forthcoming MountService callbacks
-                     switchDisplay(true);
+                     int rc = mStorageManager.enableUsbMassStorage();
+                     if (rc != StorageResultCode.OperationSucceeded) {
+                         Log.e(TAG, String.format("UMS enable failed (%d)", rc));
+                         showSharingError();
+                     }
                  }
             });
 
@@ -84,9 +109,11 @@
         mUnmountButton.setOnClickListener(
             new View.OnClickListener() { 
                  public void onClick(View v) {
-                     stopUsbStorage();
-                     // TODO: replace with forthcoming MountService callbacks
-                     switchDisplay(false);
+                     int rc = mStorageManager.disableUsbMassStorage();
+                     if (rc != StorageResultCode.OperationSucceeded) {
+                         Log.e(TAG, String.format("UMS disable failed (%d)", rc));
+                         showStoppingError();
+                     }
                  }
             });
     }
@@ -112,19 +139,11 @@
         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.getVolumeShared(
-                        Environment.getExternalStorageDirectory().getPath(), "ums");
-            }
-        } catch (android.os.RemoteException exc) {
-            // pass
+            switchDisplay(mStorageManager.isUsbMassStorageEnabled());
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed to read UMS enable state", ex);
         }
-        switchDisplay(umsOn);
     }
 
     @Override
@@ -134,42 +153,6 @@
         unregisterReceiver(mBatteryReceiver);
     }
 
-    private void mountAsUsbStorage() {
-        IMountService mountService = IMountService.Stub.asInterface(ServiceManager
-                .getService("mount"));
-        if (mountService == null) {
-            showSharingError();
-            return;
-        }
-
-        try {
-            if (mountService.shareVolume(
-                    Environment.getExternalStorageDirectory().getPath(), "ums") !=
-                            MountServiceResultCode.OperationSucceeded) {
-                showSharingError();
-            }
-        } catch (RemoteException e) {
-            showSharingError();
-        }
-    }
-
-    private void stopUsbStorage() {
-        IMountService mountService = IMountService.Stub.asInterface(ServiceManager
-                .getService("mount"));
-        if (mountService == null) {
-            showStoppingError();
-            return;
-        }
-
-        try {
-            mountService.unshareVolume(
-                    Environment.getExternalStorageDirectory().getPath(), "ums");
-        } catch (RemoteException e) {
-            showStoppingError();
-            return;
-        }
-    }
-
     private void handleBatteryChanged(Intent intent) {
         int pluggedType = intent.getIntExtra("plugged", 0);
         if (pluggedType == 0) {
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
new file mode 100644
index 0000000..51f7f69
--- /dev/null
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import java.util.Locale;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Paint.Align;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.util.Log;
+import com.android.internal.R;
+
+/**
+ * A basic, embed-able keyboard designed for password entry. Allows entry of all Latin-1 characters.
+ *
+ * It has two modes: alpha and numeric. In alpha mode, it allows all Latin-1 characters and enables
+ * an additional keyboard with symbols.  In numeric mode, it shows a 12-key DTMF dialer-like
+ * keypad with alpha characters hints.
+ */
+public class PasswordEntryKeyboard extends Keyboard {
+    private static final String TAG = "PasswordEntryKeyboard";
+    private static final int SHIFT_OFF = 0;
+    private static final int SHIFT_ON = 1;
+    private static final int SHIFT_LOCKED = 2;
+    public static final int KEYCODE_SPACE = ' ';
+
+    private Drawable mShiftIcon;
+    private Drawable mShiftLockIcon;
+    private Drawable mShiftLockPreviewIcon;
+    private Drawable mOldShiftIcon;
+    private Drawable mOldShiftPreviewIcon;
+    private Drawable mSpaceIcon;
+    private Key mShiftKey;
+    private Key mEnterKey;
+    private Key mF1Key;
+    private Key mSpaceKey;
+    private Locale mLocale;
+    private Resources mRes;
+    private int mExtensionResId;
+    private int mShiftState = SHIFT_OFF;
+
+    static int sSpacebarVerticalCorrection;
+
+    public PasswordEntryKeyboard(Context context, int xmlLayoutResId) {
+        this(context, xmlLayoutResId, 0);
+    }
+
+    public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode) {
+        super(context, xmlLayoutResId, mode);
+        final Resources res = context.getResources();
+        mRes = res;
+        mShiftIcon = res.getDrawable(R.drawable.sym_keyboard_shift);
+        mShiftLockIcon = res.getDrawable(R.drawable.sym_keyboard_shift_locked);
+        mShiftLockPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_shift_locked);
+        mShiftLockPreviewIcon.setBounds(0, 0,
+                mShiftLockPreviewIcon.getIntrinsicWidth(),
+                mShiftLockPreviewIcon.getIntrinsicHeight());
+        mSpaceIcon = res.getDrawable(R.drawable.sym_keyboard_space);
+        sSpacebarVerticalCorrection = res.getDimensionPixelOffset(
+                R.dimen.password_keyboard_spacebar_vertical_correction);
+    }
+
+    public PasswordEntryKeyboard(Context context, int layoutTemplateResId,
+            CharSequence characters, int columns, int horizontalPadding) {
+        super(context, layoutTemplateResId, characters, columns, horizontalPadding);
+    }
+
+    @Override
+    protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
+            XmlResourceParser parser) {
+        LatinKey key = new LatinKey(res, parent, x, y, parser);
+        final int code = key.codes[0];
+        if (code >=0 && code != '\n' && (code < 32 || code > 127)) {
+            Log.w(TAG, "Key code for " + key.label + " is not latin-1");
+            key.label = " ";
+            key.setEnabled(false);
+        }
+        switch (key.codes[0]) {
+            case 10:
+                mEnterKey = key;
+                break;
+            case PasswordEntryKeyboardView.KEYCODE_F1:
+                mF1Key = key;
+                break;
+            case 32:
+                mSpaceKey = key;
+                break;
+        }
+        return key;
+    }
+
+    /**
+     * Allows enter key resources to be overridden
+     * @param res resources to grab given items from
+     * @param previewId preview drawable shown on enter key
+     * @param iconId normal drawable shown on enter key
+     * @param labelId string shown on enter key
+     */
+    void setEnterKeyResources(Resources res, int previewId, int iconId, int labelId) {
+        if (mEnterKey != null) {
+            // Reset some of the rarely used attributes.
+            mEnterKey.popupCharacters = null;
+            mEnterKey.popupResId = 0;
+            mEnterKey.text = null;
+
+            mEnterKey.iconPreview = res.getDrawable(previewId);
+            mEnterKey.icon = res.getDrawable(iconId);
+            mEnterKey.label = res.getText(labelId);
+
+            // Set the initial size of the preview icon
+            if (mEnterKey.iconPreview != null) {
+                mEnterKey.iconPreview.setBounds(0, 0,
+                        mEnterKey.iconPreview.getIntrinsicWidth(),
+                        mEnterKey.iconPreview.getIntrinsicHeight());
+            }
+        }
+    }
+
+    /**
+     * Allows shiftlock to be turned on.  See {@link #setShiftLocked(boolean)}
+     *
+     */
+    void enableShiftLock() {
+        int index = getShiftKeyIndex();
+        if (index >= 0) {
+            mShiftKey = getKeys().get(index);
+            if (mShiftKey instanceof LatinKey) {
+                ((LatinKey)mShiftKey).enableShiftLock();
+            }
+            mOldShiftIcon = mShiftKey.icon;
+            mOldShiftPreviewIcon = mShiftKey.iconPreview;
+        }
+    }
+
+    /**
+     * Turn on shift lock. This turns on the LED for this key, if it has one.
+     * It should be followed by a call to {@link KeyboardView#invalidateKey(int)}
+     * or {@link KeyboardView#invalidateAllKeys()}
+     *
+     * @param shiftLocked
+     */
+    void setShiftLocked(boolean shiftLocked) {
+        if (mShiftKey != null) {
+            if (shiftLocked) {
+                mShiftKey.on = true;
+                mShiftKey.icon = mShiftLockIcon;
+                mShiftState = SHIFT_LOCKED;
+            } else {
+                mShiftKey.on = false;
+                mShiftKey.icon = mShiftLockIcon;
+                mShiftState = SHIFT_ON;
+            }
+        }
+    }
+
+    /**
+     * Turn on shift mode. Sets shift mode and turns on icon for shift key.
+     * It should be followed by a call to {@link KeyboardView#invalidateKey(int)}
+     * or {@link KeyboardView#invalidateAllKeys()}
+     *
+     * @param shiftLocked
+     */
+    @Override
+    public boolean setShifted(boolean shiftState) {
+        boolean shiftChanged = false;
+        if (mShiftKey != null) {
+            if (shiftState == false) {
+                shiftChanged = mShiftState != SHIFT_OFF;
+                mShiftState = SHIFT_OFF;
+                mShiftKey.on = false;
+                mShiftKey.icon = mOldShiftIcon;
+            } else if (mShiftState == SHIFT_OFF) {
+                shiftChanged = mShiftState == SHIFT_OFF;
+                mShiftState = SHIFT_ON;
+                mShiftKey.on = false;
+                mShiftKey.icon = mShiftIcon;
+            }
+        } else {
+            return super.setShifted(shiftState);
+        }
+        return shiftChanged;
+    }
+
+    /**
+     * Whether or not keyboard is shifted.
+     * @return true if keyboard state is shifted.
+     */
+    @Override
+    public boolean isShifted() {
+        if (mShiftKey != null) {
+            return mShiftState != SHIFT_OFF;
+        } else {
+            return super.isShifted();
+        }
+    }
+
+    /**
+     * Sets keyboard extension. Keyboard extension is shown when input is detected above keyboard
+     * while keyboard has focus.
+     *
+     * @param resId
+     */
+    public void setExtension(int resId) {
+        mExtensionResId = resId;
+    }
+
+    /**
+     * Get current extesion resource id.
+     *
+     * @return resource id, 0 if not set.
+     */
+    public int getExtension() {
+        return mExtensionResId;
+    }
+
+    private void updateSpaceBarForLocale() {
+        if (mLocale != null) {
+            // Create the graphic for spacebar
+            Bitmap buffer = Bitmap.createBitmap(mSpaceKey.width, mSpaceIcon.getIntrinsicHeight(),
+                    Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(buffer);
+            canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
+            Paint paint = new Paint();
+            paint.setAntiAlias(true);
+            // TODO: Make the text size a customizable attribute
+            paint.setTextSize(22);
+            paint.setTextAlign(Align.CENTER);
+            // Draw a drop shadow for the text
+            paint.setShadowLayer(1f, 0, 0, 0xFF000000);
+            paint.setColor(0x80C0C0C0);
+            canvas.drawText(mLocale.getDisplayLanguage(mLocale),
+                    buffer.getWidth() / 2, - paint.ascent() + 2, paint);
+            int x = (buffer.getWidth() - mSpaceIcon.getIntrinsicWidth()) / 2;
+            int y = buffer.getHeight() - mSpaceIcon.getIntrinsicHeight();
+            mSpaceIcon.setBounds(x, y,
+                    x + mSpaceIcon.getIntrinsicWidth(), y + mSpaceIcon.getIntrinsicHeight());
+            mSpaceIcon.draw(canvas);
+            mSpaceKey.icon = new BitmapDrawable(mRes, buffer);
+            mSpaceKey.repeatable = false;
+        } else {
+            mSpaceKey.icon = mRes.getDrawable(R.drawable.sym_keyboard_space);
+            mSpaceKey.repeatable = true;
+        }
+    }
+
+    public void setLanguage(Locale locale) {
+        if (mLocale != null && mLocale.equals(locale)) return;
+        mLocale = locale;
+        updateSpaceBarForLocale();
+    }
+
+    static class LatinKey extends Keyboard.Key {
+        private boolean mShiftLockEnabled;
+        private boolean mEnabled = true;
+
+        public LatinKey(Resources res, Keyboard.Row parent, int x, int y,
+                XmlResourceParser parser) {
+            super(res, parent, x, y, parser);
+            if (popupCharacters != null && popupCharacters.length() == 0) {
+                // If there is a keyboard with no keys specified in popupCharacters
+                popupResId = 0;
+            }
+        }
+
+        void setEnabled(boolean enabled) {
+            mEnabled = enabled;
+        }
+
+        void enableShiftLock() {
+            mShiftLockEnabled = true;
+        }
+
+        @Override
+        public void onReleased(boolean inside) {
+            if (!mShiftLockEnabled) {
+                super.onReleased(inside);
+            } else {
+                pressed = !pressed;
+            }
+        }
+
+        /**
+         * Overriding this method so that we can reduce the target area for certain keys.
+         */
+        @Override
+        public boolean isInside(int x, int y) {
+            if (!mEnabled) {
+                return false;
+            }
+            final int code = codes[0];
+            if (code == KEYCODE_SHIFT || code == KEYCODE_DELETE) {
+                y -= height / 10;
+                if (code == KEYCODE_SHIFT) x += width / 6;
+                if (code == KEYCODE_DELETE) x -= width / 6;
+            } else if (code == KEYCODE_SPACE) {
+                y += PasswordEntryKeyboard.sSpacebarVerticalCorrection;
+            }
+            return super.isInside(x, y);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
new file mode 100644
index 0000000..b809afc
--- /dev/null
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.content.Context;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.Selection;
+import android.util.Log;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewRoot;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+import com.android.internal.R;
+
+public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
+
+    public static final int KEYBOARD_MODE_ALPHA = 0;
+    public static final int KEYBOARD_MODE_NUMERIC = 1;
+    private static final int KEYBOARD_STATE_NORMAL = 0;
+    private static final int KEYBOARD_STATE_SHIFTED = 1;
+    private static final int KEYBOARD_STATE_CAPSLOCK = 2;
+    private static final String TAG = "PasswordEntryKeyboardHelper";
+    private int mKeyboardMode = KEYBOARD_MODE_ALPHA;
+    private int mKeyboardState = KEYBOARD_STATE_NORMAL;
+    private PasswordEntryKeyboard mQwertyKeyboard;
+    private PasswordEntryKeyboard mQwertyKeyboardShifted;
+    private PasswordEntryKeyboard mSymbolsKeyboard;
+    private PasswordEntryKeyboard mSymbolsKeyboardShifted;
+    private PasswordEntryKeyboard mNumericKeyboard;
+    private Context mContext;
+    private View mTargetView;
+    private KeyboardView mKeyboardView;
+
+    public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) {
+        mContext = context;
+        mTargetView = targetView;
+        mKeyboardView = keyboardView;
+        createKeyboards();
+        mKeyboardView.setOnKeyboardActionListener(this);
+    }
+
+    public boolean isAlpha() {
+        return mKeyboardMode == KEYBOARD_MODE_ALPHA;
+    }
+
+    private void createKeyboards() {
+        mNumericKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_numeric);
+        mQwertyKeyboard = new PasswordEntryKeyboard(mContext,
+                R.xml.password_kbd_qwerty, R.id.mode_normal);
+        mQwertyKeyboard.enableShiftLock();
+
+        mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext,
+                R.xml.password_kbd_qwerty_shifted,
+                R.id.mode_normal);
+        mQwertyKeyboardShifted.enableShiftLock();
+        mQwertyKeyboardShifted.setShifted(true); // always shifted.
+
+        mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_symbols);
+        mSymbolsKeyboard.enableShiftLock();
+
+        mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext,
+                R.xml.password_kbd_symbols_shift);
+        mSymbolsKeyboardShifted.enableShiftLock();
+        mSymbolsKeyboardShifted.setShifted(true); // always shifted
+    }
+
+    public void setKeyboardMode(int mode) {
+        switch (mode) {
+            case KEYBOARD_MODE_ALPHA:
+                mKeyboardView.setKeyboard(mQwertyKeyboard);
+                mKeyboardState = KEYBOARD_STATE_NORMAL;
+                break;
+            case KEYBOARD_MODE_NUMERIC:
+                mKeyboardView.setKeyboard(mNumericKeyboard);
+                mKeyboardState = KEYBOARD_STATE_NORMAL;
+                break;
+        }
+        mKeyboardMode = mode;
+    }
+
+    private void sendKeyEventsToTarget(int keyEventCode) {
+        Handler handler = mTargetView.getHandler();
+        KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.ALPHA).getEvents(
+                new char[] { (char) keyEventCode });
+        if (events != null) {
+            for (KeyEvent event : events) {
+                handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY, event));
+            }
+        }
+    }
+
+    public void sendDownUpKeyEvents(int keyEventCode) {
+        long eventTime = SystemClock.uptimeMillis();
+        Handler handler = mTargetView.getHandler();
+        handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+                new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
+                    KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)));
+        handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+                new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
+                        KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)));
+    }
+
+    public void onKey(int primaryCode, int[] keyCodes) {
+        Log.v(TAG, "Key code = " + Integer.toHexString(primaryCode));
+        if (primaryCode == Keyboard.KEYCODE_DELETE) {
+            handleBackspace();
+        } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
+            handleShift();
+        } else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
+            handleClose();
+            return;
+        } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE && mKeyboardView != null) {
+            handleModeChange();
+        } else {
+            handleCharacter(primaryCode, keyCodes);
+            // Switch back to old keyboard if we're not in capslock mode
+            if (mKeyboardState == KEYBOARD_STATE_SHIFTED) {
+                // skip to the unlocked state
+                mKeyboardState = KEYBOARD_STATE_CAPSLOCK;
+                handleShift();
+            }
+        }
+    }
+
+    private void handleModeChange() {
+        final Keyboard current = mKeyboardView.getKeyboard();
+        Keyboard next = null;
+        if (current == mQwertyKeyboard || current == mQwertyKeyboardShifted) {
+            next = mSymbolsKeyboard;
+        } else if (current == mSymbolsKeyboard || current == mSymbolsKeyboardShifted) {
+            next = mQwertyKeyboard;
+        }
+        if (next != null) {
+            mKeyboardView.setKeyboard(next);
+            mKeyboardState = KEYBOARD_STATE_NORMAL;
+        }
+    }
+
+    private void handleBackspace() {
+        sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+    }
+
+    private void handleShift() {
+        if (mKeyboardView == null) {
+            return;
+        }
+        Keyboard current = mKeyboardView.getKeyboard();
+        PasswordEntryKeyboard next = null;
+        final boolean isAlphaMode = current == mQwertyKeyboard
+                || current == mQwertyKeyboardShifted;
+        if (mKeyboardState == KEYBOARD_STATE_NORMAL) {
+            mKeyboardState = isAlphaMode ? KEYBOARD_STATE_SHIFTED : KEYBOARD_STATE_CAPSLOCK;
+            next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted;
+        } else if (mKeyboardState == KEYBOARD_STATE_SHIFTED) {
+            mKeyboardState = KEYBOARD_STATE_CAPSLOCK;
+            next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted;
+        } else if (mKeyboardState == KEYBOARD_STATE_CAPSLOCK) {
+            mKeyboardState = KEYBOARD_STATE_NORMAL;
+            next = isAlphaMode ? mQwertyKeyboard : mSymbolsKeyboard;
+        }
+        if (next != null) {
+            if (next != current) {
+                mKeyboardView.setKeyboard(next);
+            }
+            next.setShiftLocked(mKeyboardState == KEYBOARD_STATE_CAPSLOCK);
+            mKeyboardView.setShifted(mKeyboardState != KEYBOARD_STATE_NORMAL);
+        }
+    }
+
+    private void handleCharacter(int primaryCode, int[] keyCodes) {
+        // Maybe turn off shift if not in capslock mode.
+        if (mKeyboardView.isShifted() && primaryCode != ' ' && primaryCode != '\n') {
+            primaryCode = Character.toUpperCase(primaryCode);
+        }
+        sendKeyEventsToTarget(primaryCode);
+    }
+
+    private void handleClose() {
+
+    }
+
+    public void onPress(int primaryCode) {
+
+    }
+
+    public void onRelease(int primaryCode) {
+
+    }
+
+    public void onText(CharSequence text) {
+
+    }
+
+    public void swipeDown() {
+
+    }
+
+    public void swipeLeft() {
+
+    }
+
+    public void swipeRight() {
+
+    }
+
+    public void swipeUp() {
+
+    }
+};
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java
new file mode 100644
index 0000000..9b93fc2
--- /dev/null
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.content.Context;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.widget.PopupWindow;
+import com.android.internal.R;
+
+public class PasswordEntryKeyboardView extends KeyboardView {
+
+    public static final int KEYCODE_OPTIONS = -100;
+    static final int KEYCODE_SHIFT_LONGPRESS = -101;
+    static final int KEYCODE_VOICE = -102;
+    static final int KEYCODE_F1 = -103;
+    static final int KEYCODE_NEXT_LANGUAGE = -104;
+
+    private boolean mExtensionVisible;
+    private PasswordEntryKeyboardView mExtension;
+    private PopupWindow mExtensionPopup;
+    private boolean mFirstEvent;
+
+    public PasswordEntryKeyboardView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public PasswordEntryKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent me) {
+        if (((PasswordEntryKeyboard) getKeyboard()).getExtension() == 0) {
+            return super.onTouchEvent(me);
+        }
+        if (me.getY() < 0) {
+            if (mExtensionVisible) {
+                int action = me.getAction();
+                if (mFirstEvent) action = MotionEvent.ACTION_DOWN;
+                mFirstEvent = false;
+                MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
+                        action,
+                        me.getX(), me.getY() + mExtension.getHeight(), me.getMetaState());
+                boolean result = mExtension.onTouchEvent(translated);
+                translated.recycle();
+                if (me.getAction() == MotionEvent.ACTION_UP
+                        || me.getAction() == MotionEvent.ACTION_CANCEL) {
+                    closeExtension();
+                }
+                return result;
+            } else {
+                if (openExtension()) {
+                    MotionEvent cancel = MotionEvent.obtain(me.getDownTime(), me.getEventTime(),
+                            MotionEvent.ACTION_CANCEL, me.getX() - 100, me.getY() - 100, 0);
+                    super.onTouchEvent(cancel);
+                    cancel.recycle();
+                    if (mExtension.getHeight() > 0) {
+                        MotionEvent translated = MotionEvent.obtain(me.getEventTime(),
+                                me.getEventTime(),
+                                MotionEvent.ACTION_DOWN,
+                                me.getX(), me.getY() + mExtension.getHeight(),
+                                me.getMetaState());
+                        mExtension.onTouchEvent(translated);
+                        translated.recycle();
+                    } else {
+                        mFirstEvent = true;
+                    }
+                }
+                return true;
+            }
+        } else if (mExtensionVisible) {
+            closeExtension();
+            // Send a down event into the main keyboard first
+            MotionEvent down = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
+                    MotionEvent.ACTION_DOWN, me.getX(), me.getY(), me.getMetaState());
+            super.onTouchEvent(down);
+            down.recycle();
+            // Send the actual event
+            return super.onTouchEvent(me);
+        } else {
+            return super.onTouchEvent(me);
+        }
+    }
+
+    private boolean openExtension() {
+        if (((PasswordEntryKeyboard) getKeyboard()).getExtension() == 0) return false;
+        makePopupWindow();
+        mExtensionVisible = true;
+        return true;
+    }
+
+    private void makePopupWindow() {
+        if (mExtensionPopup == null) {
+            int[] windowLocation = new int[2];
+            mExtensionPopup = new PopupWindow(getContext());
+            mExtensionPopup.setBackgroundDrawable(null);
+            LayoutInflater li = (LayoutInflater) getContext().getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            mExtension = (PasswordEntryKeyboardView) li.inflate(
+                    R.layout.password_keyboard_input, null);
+            mExtension.setOnKeyboardActionListener(getOnKeyboardActionListener());
+            mExtension.setPopupParent(this);
+            mExtension.setPopupOffset(0, -windowLocation[1]);
+            Keyboard keyboard;
+            mExtension.setKeyboard(keyboard = new PasswordEntryKeyboard(getContext(),
+                    ((PasswordEntryKeyboard) getKeyboard()).getExtension()));
+            mExtensionPopup.setContentView(mExtension);
+            mExtensionPopup.setWidth(getWidth());
+            mExtensionPopup.setHeight(keyboard.getHeight());
+            getLocationInWindow(windowLocation);
+            // TODO: Fix the "- 30".
+            mExtension.setPopupOffset(0, -windowLocation[1] - 30);
+            mExtensionPopup.showAtLocation(this, 0, 0, -keyboard.getHeight()
+                    + windowLocation[1]);
+        } else {
+            mExtension.setVisibility(VISIBLE);
+        }
+    }
+
+    @Override
+    public void closing() {
+        super.closing();
+        if (mExtensionPopup != null && mExtensionPopup.isShowing()) {
+            mExtensionPopup.dismiss();
+            mExtensionPopup = null;
+        }
+    }
+
+    private void closeExtension() {
+        mExtension.setVisibility(INVISIBLE);
+        mExtension.closing();
+        mExtensionVisible = false;
+    }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 7fd58e8..1ffd265 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -70,7 +70,6 @@
 	android_util_Process.cpp \
 	android_util_StringBlock.cpp \
 	android_util_XmlBlock.cpp \
-	android_util_Base64.cpp \
 	android/graphics/Bitmap.cpp \
 	android/graphics/BitmapFactory.cpp \
 	android/graphics/Camera.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 1d22de3..7c8df03 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -153,7 +153,6 @@
 extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
 extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
-extern int register_android_util_Base64(JNIEnv* env);
 extern int register_android_location_GpsLocationProvider(JNIEnv* env);
 extern int register_android_backup_BackupDataInput(JNIEnv *env);
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
@@ -1266,7 +1265,6 @@
     REG_JNI(register_android_server_BluetoothA2dpService),
     REG_JNI(register_android_message_digest_sha1),
     REG_JNI(register_android_ddm_DdmHandleNativeHeap),
-    REG_JNI(register_android_util_Base64),
     REG_JNI(register_android_location_GpsLocationProvider),
     REG_JNI(register_android_backup_BackupDataInput),
     REG_JNI(register_android_backup_BackupDataOutput),
diff --git a/core/jni/android_util_Base64.cpp b/core/jni/android_util_Base64.cpp
deleted file mode 100644
index bc69747..0000000
--- a/core/jni/android_util_Base64.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/* //device/libs/android_runtime/android_util_Base64.cpp
-**
-** Copyright 2008, 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.
-*/
-
-/*********************************************************
-*
-* This code was copied from
-* system/extra/ssh/dropbear-0.49/libtomcrypt/src/misc/base64/base64_decode.c
-*
-*********************************************************/
-
-#define LOG_TAG "Base64"
-
-#include <utils/Log.h>
-
-#include <android_runtime/AndroidRuntime.h>
-
-#include "JNIHelp.h"
-
-#include <sys/errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <signal.h>
-
-namespace android {
-
-static const unsigned char map[256] = {
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
- 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
-255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
-  7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
- 19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
-255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
- 37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
- 49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255 };
-
-/**
-   base64 decode a block of memory
-   @param in       The base64 data to decode
-   @param inlen    The length of the base64 data
-   @param out      [out] The destination of the binary decoded data
-   @param outlen   [in/out] The max size and resulting size of the decoded data
-   @return 0 if successful
-*/
-int base64_decode(const unsigned char *in,  unsigned long inlen, 
-                        unsigned char *out, unsigned long *outlen)
-{
-   unsigned long t, x, y, z;
-   unsigned char c;
-   int           g;
-
-   g = 3;
-   for (x = y = z = t = 0; x < inlen; x++) {
-       c = map[in[x]&0xFF];
-       if (c == 255) continue;
-       /* the final = symbols are read and used to trim the remaining bytes */
-       if (c == 254) { 
-          c = 0; 
-          /* prevent g < 0 which would potentially allow an overflow later */
-          if (--g < 0) {
-             return -3;
-          }
-       } else if (g != 3) {
-          /* we only allow = to be at the end */
-          return -4;
-       }
-
-       t = (t<<6)|c;
-
-       if (++y == 4) {
-          if (z + g > *outlen) { 
-             return -2; 
-          }
-          out[z++] = (unsigned char)((t>>16)&255);
-          if (g > 1) out[z++] = (unsigned char)((t>>8)&255);
-          if (g > 2) out[z++] = (unsigned char)(t&255);
-          y = t = 0;
-       }
-   }
-   if (y != 0) {
-       return -5;
-   }
-   *outlen = z;
-   return 0;
-}
-
-static jbyteArray decodeBase64(JNIEnv *env, jobject jobj, jstring jdata)
-{
-    const char * rawData = env->GetStringUTFChars(jdata, NULL);
-    int stringLength = env->GetStringUTFLength(jdata);
-
-    int resultLength = stringLength / 4 * 3;
-    if (rawData[stringLength-1] == '=') {
-        resultLength -= 1;
-        if (rawData[stringLength-2] == '=') {
-            resultLength -= 1;
-        }
-    }
-
-    jbyteArray byteArray = env->NewByteArray(resultLength);
-    jbyte* byteArrayData = env->GetByteArrayElements(byteArray, NULL);
-
-    unsigned long outlen = resultLength;
-    int result = base64_decode((const unsigned char*)rawData, stringLength, (unsigned char *)byteArrayData, &outlen);
-    if (result != 0)
-        memset((unsigned char *)byteArrayData, -result, resultLength);
-    
-    env->ReleaseStringUTFChars(jdata, rawData);
-    env->ReleaseByteArrayElements(byteArray, byteArrayData, 0);
-
-    return byteArray;
-}
-
-static const JNINativeMethod methods[] = {
-  {"decodeBase64Native", "(Ljava/lang/String;)[B", (void*)decodeBase64 }
-};
-
-static const char* const kBase64PathName = "android/os/Base64Utils";
-
-int register_android_util_Base64(JNIEnv* env)
-{
-    jclass clazz;
-
-    clazz = env->FindClass(kBase64PathName);
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Base64Utils");
-
-    return AndroidRuntime::registerNativeMethods(
-        env, kBase64PathName,
-        methods, NELEM(methods));
-}
-
-}
diff --git a/core/res/res/drawable-en-hdpi/sym_keyboard_delete.png b/core/res/res/drawable-en-hdpi/sym_keyboard_delete.png
new file mode 100755
index 0000000..569369e
--- /dev/null
+++ b/core/res/res/drawable-en-hdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
new file mode 100755
index 0000000..ca76375
--- /dev/null
+++ b/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/core/res/res/drawable-en-mdpi/sym_keyboard_delete.png b/core/res/res/drawable-en-mdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..f1f7c58c
--- /dev/null
+++ b/core/res/res/drawable-en-mdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/core/res/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png
new file mode 100644
index 0000000..3c90839
--- /dev/null
+++ b/core/res/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
new file mode 100644
index 0000000..b6c234c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_off.9.png
new file mode 100644
index 0000000..9f3c087
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_on.9.png
new file mode 100644
index 0000000..4041342
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
new file mode 100644
index 0000000..73a8cd1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
new file mode 100644
index 0000000..8473e8e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
new file mode 100644
index 0000000..f4f59c0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_off.9.png
new file mode 100644
index 0000000..1508653
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_on.9.png
new file mode 100644
index 0000000..66c231a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_off.9.png
new file mode 100644
index 0000000..cdad182
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_on.9.png
new file mode 100644
index 0000000..e95f4cf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/password_field_default.9.png b/core/res/res/drawable-hdpi/password_field_default.9.png
new file mode 100644
index 0000000..2c424f0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/password_field_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_delete.png b/core/res/res/drawable-hdpi/sym_keyboard_delete.png
new file mode 100755
index 0000000..59d78be
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_delete_dim.png b/core/res/res/drawable-hdpi/sym_keyboard_delete_dim.png
new file mode 100644
index 0000000..34b6d1f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_delete_dim.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_delete.png
new file mode 100755
index 0000000..ca76375
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_ok.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_ok.png
new file mode 100644
index 0000000..2d144ec
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_ok.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_return.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_return.png
new file mode 100755
index 0000000..ae57299
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_return.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift.png
new file mode 100755
index 0000000..4db31c8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png
new file mode 100755
index 0000000..3fd5659
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_space.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_space.png
new file mode 100755
index 0000000..98266ee
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_space.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num0_no_plus.png b/core/res/res/drawable-hdpi/sym_keyboard_num0_no_plus.png
new file mode 100644
index 0000000..2aad23c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num0_no_plus.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num1.png b/core/res/res/drawable-hdpi/sym_keyboard_num1.png
new file mode 100755
index 0000000..0fc03ef
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num2.png b/core/res/res/drawable-hdpi/sym_keyboard_num2.png
new file mode 100755
index 0000000..283560b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num3.png b/core/res/res/drawable-hdpi/sym_keyboard_num3.png
new file mode 100755
index 0000000..9a3b329
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num4.png b/core/res/res/drawable-hdpi/sym_keyboard_num4.png
new file mode 100755
index 0000000..f13ff1a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num5.png b/core/res/res/drawable-hdpi/sym_keyboard_num5.png
new file mode 100755
index 0000000..c251329
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num5.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num6.png b/core/res/res/drawable-hdpi/sym_keyboard_num6.png
new file mode 100755
index 0000000..4acba4c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num6.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num7.png b/core/res/res/drawable-hdpi/sym_keyboard_num7.png
new file mode 100755
index 0000000..14931c1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num7.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num8.png b/core/res/res/drawable-hdpi/sym_keyboard_num8.png
new file mode 100755
index 0000000..d4973fd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num8.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num9.png b/core/res/res/drawable-hdpi/sym_keyboard_num9.png
new file mode 100755
index 0000000..49cec66
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_num9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_ok.png b/core/res/res/drawable-hdpi/sym_keyboard_ok.png
new file mode 100644
index 0000000..9105da2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_ok.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_ok_dim.png b/core/res/res/drawable-hdpi/sym_keyboard_ok_dim.png
new file mode 100644
index 0000000..bd419de
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_ok_dim.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_return.png b/core/res/res/drawable-hdpi/sym_keyboard_return.png
new file mode 100755
index 0000000..58505c5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_return.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_shift.png b/core/res/res/drawable-hdpi/sym_keyboard_shift.png
new file mode 100755
index 0000000..8149081
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_shift.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-hdpi/sym_keyboard_shift_locked.png
new file mode 100755
index 0000000..31ca277
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_space.png b/core/res/res/drawable-hdpi/sym_keyboard_space.png
new file mode 100755
index 0000000..3e98b30
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_space.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
new file mode 100644
index 0000000..20f3d50
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
new file mode 100644
index 0000000..d09ce53
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
new file mode 100644
index 0000000..a9e008c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
new file mode 100644
index 0000000..1ed3065
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
new file mode 100644
index 0000000..5710ebf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
new file mode 100644
index 0000000..dd7d89e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_off.9.png
new file mode 100644
index 0000000..77426ef
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_on.9.png
new file mode 100644
index 0000000..e4c9bd5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_off.9.png
new file mode 100644
index 0000000..bb98b01
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_on.9.png
new file mode 100644
index 0000000..3c7dcc80
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png
index 855484a..6a97d5b 100644
--- a/core/res/res/drawable-mdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-mdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/password_field_default.9.png b/core/res/res/drawable-mdpi/password_field_default.9.png
new file mode 100644
index 0000000..3193275
--- /dev/null
+++ b/core/res/res/drawable-mdpi/password_field_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_def_app_icon.png b/core/res/res/drawable-mdpi/sym_def_app_icon.png
index 8be3b54..9777d11 100644
--- a/core/res/res/drawable-mdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-mdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_delete.png b/core/res/res/drawable-mdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..43a033ea
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_delete_dim.png b/core/res/res/drawable-mdpi/sym_keyboard_delete_dim.png
new file mode 100644
index 0000000..25460d8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_delete_dim.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_delete.png
new file mode 100644
index 0000000..1edb10b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_ok.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_ok.png
new file mode 100644
index 0000000..3148836
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_ok.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_return.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_return.png
new file mode 100644
index 0000000..03d9c9b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_return.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift.png
new file mode 100644
index 0000000..97f4661
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png
new file mode 100755
index 0000000..7194b30
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_space.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_space.png
new file mode 100644
index 0000000..739db68
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_space.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num0_no_plus.png b/core/res/res/drawable-mdpi/sym_keyboard_num0_no_plus.png
new file mode 100644
index 0000000..91332b1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num0_no_plus.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num1.png b/core/res/res/drawable-mdpi/sym_keyboard_num1.png
new file mode 100644
index 0000000..aaac11b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num2.png b/core/res/res/drawable-mdpi/sym_keyboard_num2.png
new file mode 100644
index 0000000..4372eb8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num3.png b/core/res/res/drawable-mdpi/sym_keyboard_num3.png
new file mode 100644
index 0000000..6f54c85
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num4.png b/core/res/res/drawable-mdpi/sym_keyboard_num4.png
new file mode 100644
index 0000000..3e50bb9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num5.png b/core/res/res/drawable-mdpi/sym_keyboard_num5.png
new file mode 100644
index 0000000..c39ef44
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num5.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num6.png b/core/res/res/drawable-mdpi/sym_keyboard_num6.png
new file mode 100644
index 0000000..ea88ceb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num6.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num7.png b/core/res/res/drawable-mdpi/sym_keyboard_num7.png
new file mode 100644
index 0000000..ce800ba
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num7.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num8.png b/core/res/res/drawable-mdpi/sym_keyboard_num8.png
new file mode 100644
index 0000000..1a8ff94
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num8.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num9.png b/core/res/res/drawable-mdpi/sym_keyboard_num9.png
new file mode 100644
index 0000000..8b344c0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_num9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_ok.png b/core/res/res/drawable-mdpi/sym_keyboard_ok.png
new file mode 100644
index 0000000..b8b5292
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_ok.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_ok_dim.png b/core/res/res/drawable-mdpi/sym_keyboard_ok_dim.png
new file mode 100644
index 0000000..33ecff5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_ok_dim.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_return.png b/core/res/res/drawable-mdpi/sym_keyboard_return.png
new file mode 100644
index 0000000..17f2574
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_return.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-mdpi/sym_keyboard_shift.png
new file mode 100644
index 0000000..0566e5a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_shift.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png
new file mode 100755
index 0000000..ccaf05d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_space.png b/core/res/res/drawable-mdpi/sym_keyboard_space.png
new file mode 100644
index 0000000..4e6273b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_space.png
Binary files differ
diff --git a/core/res/res/drawable/btn_keyboard_key_fulltrans.xml b/core/res/res/drawable/btn_keyboard_key_fulltrans.xml
new file mode 100644
index 0000000..cfad6e3
--- /dev/null
+++ b/core/res/res/drawable/btn_keyboard_key_fulltrans.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Toggle keys. Use checkable/checked state. -->
+
+    <item android:state_checkable="true" android:state_checked="true"
+          android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed_on" />
+    <item android:state_checkable="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed_off" />
+    <item android:state_checkable="true" android:state_checked="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_normal_on" />
+    <item android:state_checkable="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_normal_off" />
+
+    <!-- Normal keys -->
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+    <item android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+
+</selector>
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index 5bc034b..b089df6 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -16,105 +16,68 @@
 ** limitations under the License.
 */
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:orientation="vertical"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:background="@android:color/background_dark">
-
-    <!-- displays dots as user enters pin -->
-    <LinearLayout android:id="@+id/pinDisplayGroup"
-        android:orientation="horizontal"
-        android:layout_centerInParent="true"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:addStatesFromChildren="true"
-        android:gravity="center_vertical"
-        android:baselineAligned="false"
-        android:paddingRight="0dip"
-        android:layout_marginRight="30dip"
-        android:layout_marginLeft="30dip"
-        android:layout_marginTop="6dip"
-        android:background="@android:drawable/edit_text">
-
-        <EditText android:id="@+id/pinDisplay"
-            android:layout_width="0dip"
-            android:layout_weight="1"
-            android:layout_height="fill_parent"
-            android:maxLines="1"
-            android:background="@null"
-            android:textSize="32sp"
-            android:inputType="textPassword"
-        />
-
-        <ImageButton android:id="@+id/backspace"
-             android:src="@android:drawable/ic_input_delete"
-             android:layout_width="wrap_content"
-             android:layout_height="fill_parent"
-             android:layout_marginTop="2dip"
-             android:layout_marginRight="2dip"
-             android:layout_marginBottom="2dip"
-             android:gravity="center"
-        />
-
-    </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"
-    />
+    android:background="#70000000"
+    android:gravity="center_horizontal">
 
     <LinearLayout
-        android:orientation="horizontal"
-        android:layout_alignParentBottom="true"
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="8dip"
-        android:layout_marginLeft="8dip"
-        android:layout_marginRight="8dip">
-
-        <Button android:id="@+id/ok"
-            android:text="@android:string/ok"
-            android:layout_alignParentBottom="true"
+        android:orientation="horizontal">
+        <!-- "Enter PIN(Password) to unlock" -->
+        <TextView android:id="@+id/enter_password_label"
             android:layout_width="0dip"
             android:layout_height="wrap_content"
-            android:layout_weight="1.0"
-            android:layout_marginBottom="8dip"
-            android:layout_marginRight="8dip"
-            android:textSize="18sp"
-            />
+            android:layout_weight="1"
+            android:orientation="horizontal"
+            android:layout_marginRight="6dip"
+            android:layout_marginLeft="6dip"
+            android:layout_marginTop="10dip"
+            android:layout_marginBottom="10dip"
+            android:gravity="center"
+            android:ellipsize="marquee"
+            android:text="@android:string/keyguard_password_enter_password_code"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+        />
 
-        <Button android:id="@+id/emergencyCall"
-            android:text="@android:string/lockscreen_emergency_call"
-            android:layout_alignParentBottom="true"
-            android:layout_centerHorizontal="true"
+        <!-- Password entry field -->
+        <EditText android:id="@+id/passwordEntry"
             android:layout_width="0dip"
             android:layout_height="wrap_content"
-            android:layout_weight="1.0"
-            android:layout_marginBottom="8dip"
-            android:layout_marginLeft="8dip"
-            android:textSize="18sp"
-            android:drawableLeft="@drawable/ic_emergency"
-            android:drawablePadding="8dip"
+            android:layout_weight="1"
+            android:singleLine="true"
+            android:textStyle="bold"
+            android:inputType="textPassword"
+            android:gravity="center"
+            android:layout_gravity="center"
+            android:textSize="32sp"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:background="@drawable/password_field_default"
+            android:textColor="#ffffffff"
         />
     </LinearLayout>
 
-    <!-- Not currently visible on this screen -->
-    <TextView
-        android:id="@+id/carrier"
-        android:layout_width="wrap_content"
+    <!-- Alphanumeric keyboard -->
+    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+        android:layout_alignParentBottom="true"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_marginTop="6dip"
-        android:layout_alignParentRight="true"
-        android:layout_marginRight="8dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:visibility="gone"
+        android:background="#00000000"
+        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
     />
 
-</RelativeLayout>
+    <!-- emergency call button -->
+    <Button
+        android:id="@+id/emergencyCall"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:drawableLeft="@drawable/ic_emergency"
+        android:drawablePadding="8dip"
+        android:text="@string/lockscreen_emergency_call"
+        style="@style/Widget.Button.Transparent"
+    />
+
+</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index a7814af..9ee8781 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -17,142 +17,77 @@
 */
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:orientation="vertical"
     android:background="#70000000"
     android:gravity="center_horizontal">
 
-    <LinearLayout android:id="@+id/topDisplayGroup"
-        android:layout_width="fill_parent"
+    <!-- "Enter PIN(Password) to unlock" -->
+    <TextView android:id="@+id/enter_password_label"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <RelativeLayout
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content">
-
-            <com.android.internal.widget.DigitalClock android:id="@+id/time"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="6dip"
-                android:layout_marginLeft="6dip"
-                android:layout_alignParentTop="true"
-                android:layout_alignParentLeft="true">
-
-                <TextView android:id="@+id/timeDisplay"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:gravity="bottom"
-                    android:singleLine="true"
-                    android:ellipsize="none"
-                    android:textSize="56sp"
-                    android:textAppearance="?android:attr/textAppearanceMedium"
-                    android:shadowColor="#C0000000"
-                    android:shadowDx="0"
-                    android:shadowDy="0"
-                    android:shadowRadius="3.0"
-                    android:layout_marginBottom="6dip"
-                />
-
-                <TextView android:id="@+id/am_pm"
-                    android:layout_width="wrap_content"
-                    android:layout_height="fill_parent"
-                    android:gravity="bottom"
-                    android:singleLine="true"
-                    android:ellipsize="none"
-                    android:textSize="18sp"
-                    android:layout_marginLeft="4dip"
-                    android:textAppearance="?android:attr/textAppearanceMedium"
-                    android:shadowColor="#C0000000"
-                    android:shadowDx="0"
-                    android:shadowDy="0"
-                    android:shadowRadius="3.0"
-                />
-
-            </com.android.internal.widget.DigitalClock>
-
-            <TextView android:id="@+id/carrier"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentTop="true"
-                android:layout_marginTop="6dip"
-                android:layout_alignParentRight="true"
-                android:layout_marginRight="8dip"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-            />
-
-        </RelativeLayout>
-
-        <!-- password entry -->
-        <LinearLayout
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:layout_marginRight="6dip"
-            android:layout_marginLeft="6dip"
-            android:gravity="center_vertical"
-            android:hint="@android:string/keyguard_password_enter_password_code"
-            android:background="@android:drawable/edit_text">
-
-            <!-- displays dots as user enters pin -->
-            <TextView android:id="@+id/pinDisplay"
-                android:layout_width="0dip"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:maxLines="1"
-                android:textAppearance="?android:attr/textAppearanceLargeInverse"
-                android:textStyle="bold"
-                android:inputType="textPassword"
-            />
-
-            <ImageButton android:id="@+id/backspace"
-                android:src="@android:drawable/ic_input_delete"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginRight="-3dip"
-                android:layout_marginBottom="-3dip"
-            />
-        </LinearLayout>
-
-    </LinearLayout>
-
-    <include
-        android:id="@+id/keyPad"
-        layout="@android:layout/twelve_key_entry"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/topDisplayGroup"
+        android:orientation="horizontal"
+        android:layout_marginRight="6dip"
+        android:layout_marginLeft="6dip"
         android:layout_marginTop="10dip"
+        android:layout_marginBottom="10dip"
+        android:gravity="center"
+        android:text="@android:string/keyguard_password_enter_password_code"
+        android:textAppearance="?android:attr/textAppearanceLarge"
     />
 
-    <!-- spacer below keypad -->
+    <!-- spacer above text entry field -->
     <View
         android:id="@+id/spacerBottom"
         android:layout_width="fill_parent"
         android:layout_height="1dip"
         android:layout_marginTop="6dip"
-        android:layout_above="@id/emergencyCall"
         android:background="@android:drawable/divider_horizontal_dark"
     />
 
-    <!-- The emergency button should take the rest of the space and be centered vertically -->
-    <LinearLayout
-        android:layout_width="fill_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1"
+    <!-- Password entry field -->
+    <EditText android:id="@+id/passwordEntry"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:textStyle="bold"
+        android:inputType="textPassword"
         android:gravity="center"
-        android:orientation="vertical">
+        android:layout_gravity="center"
+        android:textSize="32sp"
+        android:layout_marginTop="15dip"
+        android:layout_marginLeft="30dip"
+        android:layout_marginRight="30dip"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:background="@drawable/password_field_default"
+        android:textColor="#ffffffff"
+    />
 
-        <!-- emergency call button -->
-        <Button
-            android:id="@+id/emergencyCall"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:drawableLeft="@android:drawable/ic_emergency"
-            android:drawablePadding="8dip"
-            android:text="@android:string/lockscreen_emergency_call"
-        />
-    </LinearLayout>
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1" />
 
+    <!-- Alphanumeric keyboard -->
+    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+        android:layout_alignParentBottom="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="#00000000"
+        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+    />
+
+    <!-- emergency call button -->
+    <Button
+        android:id="@+id/emergencyCall"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:drawableLeft="@drawable/ic_emergency"
+        android:drawablePadding="8dip"
+        android:layout_marginTop="20dip"
+        android:layout_marginBottom="20dip"
+        android:text="@string/lockscreen_emergency_call"
+        style="@style/Widget.Button.Transparent"
+    />
 </LinearLayout>
diff --git a/core/res/res/layout/password_keyboard_input.xml b/core/res/res/layout/password_keyboard_input.xml
new file mode 100755
index 0000000..e40b69e
--- /dev/null
+++ b/core/res/res/layout/password_keyboard_input.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<com.android.passwordunlockdemo.LatinKeyboardView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@android:id/keyboardView"
+        android:layout_alignParentBottom="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="#00000000"
+        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        />
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
new file mode 100644
index 0000000..647a562
--- /dev/null
+++ b/core/res/res/values-land/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<resources>
+    <dimen name="password_keyboard_key_height">47dip</dimen>
+    <dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6a3538d..5cea28db 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -4,16 +4,16 @@
 **
 ** Copyright 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 
+** 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 
+**     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 
+** 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.
 */
 -->
@@ -34,4 +34,8 @@
     <dimen name="fastscroll_thumb_width">64dp</dimen>
     <!-- Height of the fastscroll thumb -->
     <dimen name="fastscroll_thumb_height">52dp</dimen>
+    <!-- Default height of a key in the password keyboard -->
+    <dimen name="password_keyboard_key_height">56dip</dimen>
+    <!-- Default correction for the space key in the password keyboard -->
+    <dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4df570c..89cbd08 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1536,6 +1536,14 @@
     <!-- Displayed on lock screen's right tab - turn sound off -->
     <string name="lockscreen_sound_off_label">Sound off</string>
 
+    <!-- Password keyboard strings. Used by LockScreen and Settings --><skip />
+    <!-- Label for "switch to symbols" key.  Must be short to fit on key! -->
+    <string name="password_keyboard_label_symbol_key">\?123</string>
+    <!-- Label for "switch to alphabetic" key.  Must be short to fit on key! -->
+    <string name="password_keyboard_label_alpha_key">ABC</string>
+    <!-- Label for ALT modifier key.  Must be short to fit on key! -->
+    <string name="password_keyboard_label_alt_key">ALT</string>
+
     <!-- A format string for 12-hour time of day, just the hour, not the minute, with lower-case "am" or "pm" (example: "3pm"). -->
     <string name="hour_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="pm">%P</xliff:g>"</string>
 
diff --git a/core/res/res/xml-land/password_kbd_qwerty.xml b/core/res/res/xml-land/password_kbd_qwerty.xml
new file mode 100755
index 0000000..a3d4e88
--- /dev/null
+++ b/core/res/res/xml-land/password_kbd_qwerty.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"/>
+        <Key android:codes="114" android:keyLabel="r"/>
+        <Key android:codes="116" android:keyLabel="t"/>
+        <Key android:codes="121" android:keyLabel="y"/>
+        <Key android:codes="117" android:keyLabel="u"/>
+        <Key android:codes="105" android:keyLabel="i"/>
+        <Key android:codes="111" android:keyLabel="o"/>
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+            android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"/>
+        <Key android:codes="100" android:keyLabel="d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"/>
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"/>
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="-" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:keyLabel="=" />
+        <Key android:codes="46" android:keyLabel="."
+            android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+            android:iconPreview="@drawable/sym_keyboard_feedback_return"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml-land/password_kbd_qwerty_shifted.xml b/core/res/res/xml-land/password_kbd_qwerty_shifted.xml
new file mode 100755
index 0000000..2285d91
--- /dev/null
+++ b/core/res/res/xml-land/password_kbd_qwerty_shifted.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"/>
+        <Key android:codes="114" android:keyLabel="r"/>
+        <Key android:codes="116" android:keyLabel="t"/>
+        <Key android:codes="121" android:keyLabel="y"/>
+        <Key android:codes="117" android:keyLabel="u"/>
+        <Key android:codes="105" android:keyLabel="i"/>
+        <Key android:codes="111" android:keyLabel="o"/>
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+            android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"/>
+        <Key android:codes="100" android:keyLabel="d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"/>
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"/>
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="_" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:keyLabel="+" />
+        <Key android:codes="46" android:keyLabel="."/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+            android:iconPreview="@drawable/sym_keyboard_feedback_return"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml-mdpi/password_kbd_qwerty.xml b/core/res/res/xml-mdpi/password_kbd_qwerty.xml
new file mode 100755
index 0000000..a3d4e88
--- /dev/null
+++ b/core/res/res/xml-mdpi/password_kbd_qwerty.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"/>
+        <Key android:codes="114" android:keyLabel="r"/>
+        <Key android:codes="116" android:keyLabel="t"/>
+        <Key android:codes="121" android:keyLabel="y"/>
+        <Key android:codes="117" android:keyLabel="u"/>
+        <Key android:codes="105" android:keyLabel="i"/>
+        <Key android:codes="111" android:keyLabel="o"/>
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+            android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"/>
+        <Key android:codes="100" android:keyLabel="d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"/>
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"/>
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="-" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:keyLabel="=" />
+        <Key android:codes="46" android:keyLabel="."
+            android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+            android:iconPreview="@drawable/sym_keyboard_feedback_return"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml b/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml
new file mode 100755
index 0000000..2285d91
--- /dev/null
+++ b/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"/>
+        <Key android:codes="114" android:keyLabel="r"/>
+        <Key android:codes="116" android:keyLabel="t"/>
+        <Key android:codes="121" android:keyLabel="y"/>
+        <Key android:codes="117" android:keyLabel="u"/>
+        <Key android:codes="105" android:keyLabel="i"/>
+        <Key android:codes="111" android:keyLabel="o"/>
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+            android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"/>
+        <Key android:codes="100" android:keyLabel="d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"/>
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"/>
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="_" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:keyLabel="+" />
+        <Key android:codes="46" android:keyLabel="."/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+            android:iconPreview="@drawable/sym_keyboard_feedback_return"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml/password_kbd_extension.xml b/core/res/res/xml/password_kbd_extension.xml
new file mode 100755
index 0000000..354594e
--- /dev/null
+++ b/core/res/res/xml/password_kbd_extension.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row android:rowEdgeFlags="top">
+        <Key android:keyLabel="!" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\@"/>
+        <Key android:keyLabel="\#"/>
+        <Key android:keyLabel="&amp;"/>
+        <Key android:keyLabel="-"/>
+        <Key android:keyLabel="\'"/>
+        <Key android:keyLabel=":"/>
+        <Key android:keyLabel="&quot;"/>
+        <Key android:keyLabel="/"/>
+        <Key android:keyLabel="\?" android:keyEdgeFlags="right"
+        />
+    </Row>
+
+    <Row android:rowEdgeFlags="bottom">
+        <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"
+        />
+        <Key android:codes="50" android:keyLabel="2"
+        />
+        <Key android:codes="51" android:keyLabel="3"
+        />
+        <Key android:codes="52" android:keyLabel="4"
+        />
+        <Key android:codes="53" android:keyLabel="5"
+        />
+        <Key android:codes="54" android:keyLabel="6"/>
+        <Key android:codes="55" android:keyLabel="7"
+        />
+        <Key android:codes="56" android:keyLabel="8"/>
+        <Key android:codes="57" android:keyLabel="9"/>
+        <Key android:codes="48" android:keyLabel="0"
+                android:keyEdgeFlags="right"/>
+    </Row>
+</Keyboard>
diff --git a/core/res/res/xml/password_kbd_numeric.xml b/core/res/res/xml/password_kbd_numeric.xml
new file mode 100755
index 0000000..e3f1612
--- /dev/null
+++ b/core/res/res/xml/password_kbd_numeric.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="33.33%p"
+    android:horizontalGap="2px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row>
+        <Key android:codes="49" android:keyIcon="@drawable/sym_keyboard_num1" android:keyEdgeFlags="left"/>
+        <Key android:codes="50" android:keyIcon="@drawable/sym_keyboard_num2"/>
+        <Key android:codes="51" android:keyIcon="@drawable/sym_keyboard_num3"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="52" android:keyIcon="@drawable/sym_keyboard_num4" android:keyEdgeFlags="left"/>
+        <Key android:codes="53" android:keyIcon="@drawable/sym_keyboard_num5"/>
+        <Key android:codes="54" android:keyIcon="@drawable/sym_keyboard_num6"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="55" android:keyIcon="@drawable/sym_keyboard_num7" android:keyEdgeFlags="left"/>
+        <Key android:codes="56" android:keyIcon="@drawable/sym_keyboard_num8"/>
+        <Key android:codes="57" android:keyIcon="@drawable/sym_keyboard_num9"/>
+    </Row>
+
+    <Row android:rowEdgeFlags="bottom">
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"/>
+        <Key android:codes="48" android:keyIcon="@drawable/sym_keyboard_num0_no_plus"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
diff --git a/core/res/res/xml/password_kbd_popup_template.xml b/core/res/res/xml/password_kbd_popup_template.xml
new file mode 100644
index 0000000..5ddfd3e
--- /dev/null
+++ b/core/res/res/xml/password_kbd_popup_template.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+</Keyboard>
diff --git a/core/res/res/xml/password_kbd_qwerty.xml b/core/res/res/xml/password_kbd_qwerty.xml
new file mode 100755
index 0000000..d4a454b
--- /dev/null
+++ b/core/res/res/xml/password_kbd_qwerty.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row android:rowEdgeFlags="top">
+        <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/>
+        <Key android:codes="50" android:keyLabel="2"/>
+        <Key android:codes="51" android:keyLabel="3"/>
+        <Key android:codes="52" android:keyLabel="4"/>
+        <Key android:codes="53" android:keyLabel="5"/>
+        <Key android:codes="54" android:keyLabel="6"/>
+        <Key android:codes="55" android:keyLabel="7"/>
+        <Key android:codes="56" android:keyLabel="8"/>
+        <Key android:codes="57" android:keyLabel="9"/>
+        <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"/>
+        <Key android:codes="114" android:keyLabel="r"/>
+        <Key android:codes="116" android:keyLabel="t"/>
+        <Key android:codes="121" android:keyLabel="y"/>
+        <Key android:codes="117" android:keyLabel="u"/>
+        <Key android:codes="105" android:keyLabel="i"/>
+        <Key android:codes="111" android:keyLabel="o"/>
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+            android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"/>
+        <Key android:codes="100" android:keyLabel="d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"/>
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"/>
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="-" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:keyLabel="=" />
+        <Key android:codes="46" android:keyLabel="."
+            android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+            android:iconPreview="@drawable/sym_keyboard_feedback_ok"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml/password_kbd_qwerty_shifted.xml b/core/res/res/xml/password_kbd_qwerty_shifted.xml
new file mode 100755
index 0000000..f341d9e
--- /dev/null
+++ b/core/res/res/xml/password_kbd_qwerty_shifted.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row android:rowEdgeFlags="top">
+        <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/>
+        <Key android:codes="35" android:keyLabel="\#"/>
+        <Key android:codes="36" android:keyLabel="$"/>
+        <Key android:codes="37" android:keyLabel="%"/>
+        <Key android:codes="38" android:keyLabel="&amp;"/>
+        <Key android:codes="42" android:keyLabel="*"/>
+        <Key android:codes="45" android:keyLabel="-"/>
+        <Key android:keyLabel="+"/>
+        <Key android:codes="40" android:keyLabel="("/>
+        <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"/>
+        <Key android:codes="114" android:keyLabel="r"/>
+        <Key android:codes="116" android:keyLabel="t"/>
+        <Key android:codes="121" android:keyLabel="y"/>
+        <Key android:codes="117" android:keyLabel="u"/>
+        <Key android:codes="105" android:keyLabel="i"/>
+        <Key android:codes="111" android:keyLabel="o"/>
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+            android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"/>
+        <Key android:codes="100" android:keyLabel="d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"/>
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"/>
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="_" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:keyLabel="+" />
+        <Key android:codes="46" android:keyLabel="."/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+            android:iconPreview="@drawable/sym_keyboard_feedback_ok"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml/password_kbd_symbols.xml b/core/res/res/xml/password_kbd_symbols.xml
new file mode 100755
index 0000000..c97e6ae
--- /dev/null
+++ b/core/res/res/xml/password_kbd_symbols.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row>
+        <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/>
+        <Key android:codes="50" android:keyLabel="2"/>
+        <Key android:codes="51" android:keyLabel="3"/>
+        <Key android:codes="52" android:keyLabel="4"/>
+        <Key android:codes="53" android:keyLabel="5"/>
+        <Key android:codes="54" android:keyLabel="6"/>
+        <Key android:codes="55" android:keyLabel="7"/>
+        <Key android:codes="56" android:keyLabel="8"/>
+        <Key android:codes="57" android:keyLabel="9"/>
+        <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/>
+        <Key android:codes="35" android:keyLabel="\#"/>
+        <Key android:codes="36" android:keyLabel="$"/>
+        <Key android:codes="37" android:keyLabel="%"/>
+        <Key android:codes="38" android:keyLabel="&amp;"/>
+        <Key android:codes="42" android:keyLabel="*"/>
+        <Key android:codes="45" android:keyLabel="-"/>
+        <Key android:keyLabel="+"/>
+        <Key android:codes="40" android:keyLabel="("
+                android:popupKeyboard="@xml/password_kbd_popup_template"
+                android:popupCharacters="[{&lt;"
+        />
+        <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right"
+                android:popupKeyboard="@xml/password_kbd_popup_template"
+                android:popupCharacters="]}&gt;"
+        />
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyLabel="@string/password_keyboard_label_alt_key"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="33" android:keyLabel="!"/>
+        <Key android:codes="34" android:keyLabel="&quot;"/>
+        <Key android:codes="39" android:keyLabel="\'"/>
+        <Key android:codes="58" android:keyLabel=":"/>
+        <Key android:codes="59" android:keyLabel=";"/>
+        <Key android:codes="47" android:keyLabel="/" />
+        <Key android:codes="63" android:keyLabel="\?"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row  android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_alpha_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:keyWidth="40%p"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:isRepeatable="true"/>
+        <Key android:keyLabel="." android:keyWidth="10%p" />
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_ok"
+        />
+    </Row>
+</Keyboard>
diff --git a/core/res/res/xml/password_kbd_symbols_shift.xml b/core/res/res/xml/password_kbd_symbols_shift.xml
new file mode 100755
index 0000000..97ec3c5f
--- /dev/null
+++ b/core/res/res/xml/password_kbd_symbols_shift.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height"
+    >
+
+    <Row>
+        <Key android:keyLabel="~" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="`"/>
+        <Key android:keyLabel="|"/>
+        <Key android:keyLabel="•"/>
+        <Key android:keyLabel="√"/>
+        <Key android:keyLabel="π"/>
+        <Key android:keyLabel="÷"/>
+        <Key android:keyLabel="×"/>
+        <Key android:keyLabel="{"/>
+        <Key android:keyLabel="}" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:keyLabel="¥" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="£"/>
+        <Key android:keyLabel="¢"/>
+        <Key android:keyLabel="€"/>
+        <Key android:keyLabel="°"/>
+        <Key android:keyLabel="^"/>
+        <Key android:keyLabel="_"/>
+        <Key android:keyLabel="="/>
+        <Key android:keyLabel="["/>
+        <Key android:keyLabel="]" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyLabel="@string/password_keyboard_label_alt_key"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="™"/>
+        <Key android:keyLabel="®"/>
+        <Key android:keyLabel="©"/>
+        <Key android:keyLabel="¶"/>
+        <Key android:keyLabel="\\"/>
+        <Key android:keyLabel="&lt;"/>
+        <Key android:keyLabel="&gt;"/>
+        <Key android:codes="-5"
+            android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"
+        />
+    </Row>
+
+    <Row android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_alpha_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="„" android:keyWidth="10%p" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:keyWidth="40%p"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:isRepeatable="true"/>
+        <Key android:keyLabel="…" android:keyWidth="10%p" />
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_ok"
+        />
+    </Row>
+</Keyboard>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 8d7e187..6bc6f2e 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -74,6 +74,10 @@
     <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_TRANSITION_TYPES" />
     <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_NOTIFICATION_TYPES" />
 
+    <!-- package manager test permissions -->
+    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+
     <application android:theme="@style/Theme">
         <uses-library android:name="android.test.runner" />
         <activity android:name="StubTestBrowserActivity" android:label="Stubbed Test Browser">
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
index fad4349..fb5a36f 100644
--- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -16,6 +16,7 @@
 
 package android.database;
 
+import dalvik.annotation.BrokenTest;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -373,7 +374,9 @@
         c.close();
     }
     
-    @LargeTest
+    //@LargeTest
+    @BrokenTest("Consistently times out")
+    @Suppress
     public void testLoadingThread() throws Exception {
         mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
         
@@ -398,7 +401,9 @@
         c.close();
     } 
     
-    @LargeTest
+    //@LargeTest
+    @BrokenTest("Consistently times out")
+    @Suppress
     public void testLoadingThreadClose() throws Exception {
         mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
         
@@ -450,9 +455,11 @@
         mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
         
         final int count = 36799; 
+        mDatabase.execSQL("BEGIN Transaction;");
         for (int i = 0; i < count; i++) {
             mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
         }
+        mDatabase.execSQL("COMMIT;");
 
         Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
         assertNotNull(c);
@@ -484,9 +491,11 @@
 
         // if cursor window size changed, adjust this value too  
         final int count = 600; // more than two fillWindow needed
+        mDatabase.execSQL("BEGIN Transaction;");
         for (int i = 0; i < count; i++) {
             mDatabase.execSQL(sql.toString());
         }
+        mDatabase.execSQL("COMMIT;");
 
         Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
         assertNotNull(c);
@@ -513,6 +522,7 @@
 
         // if cursor window size changed, adjust this value too  
         final int count = 600;
+        mDatabase.execSQL("BEGIN Transaction;");
         for (int i = 0; i < count; i++) {
             StringBuilder sql = new StringBuilder(2100);
             sql.append("INSERT INTO test (txt, data) VALUES ('");
@@ -522,6 +532,7 @@
             sql.append("');");
             mDatabase.execSQL(sql.toString());
         }
+        mDatabase.execSQL("COMMIT;");
 
         Cursor c = mDatabase.query("test", new String[]{"txt", "data"}, null, null, null, null, null);
         assertNotNull(c);
diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
old mode 100755
new mode 100644
index 61460b1..a935f16
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackLegacy.ttf b/data/fonts/DroidSansFallbackLegacy.ttf
new file mode 100644
index 0000000..61460b1
--- /dev/null
+++ b/data/fonts/DroidSansFallbackLegacy.ttf
Binary files differ
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 113c452..022b849 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -54,6 +54,7 @@
     METADATA_KEY_VIDEO_WIDTH     = 20,
     METADATA_KEY_WRITER          = 21,
     METADATA_KEY_MIMETYPE        = 22,
+    METADATA_KEY_DISC_NUMBER     = 23,
     // Add more here...
 };
 
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 3b21468..8f423f7 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -59,6 +59,7 @@
     kKeyAlbumArtMIME      = 'alAM',  // cstring
     kKeyAuthor            = 'auth',  // cstring
     kKeyCDTrackNumber     = 'cdtr',  // cstring
+    kKeyDiscNumber        = 'dnum',  // cstring
     kKeyDate              = 'date',  // cstring
     kKeyWriter            = 'writ',  // cstring
 };
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 965b7dd..2d6152e 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -39,7 +39,6 @@
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
-#include <ui/DisplayInfo.h>
 
 #include <pixelflinger/pixelflinger.h>
 #include <GLES/gl.h>
@@ -350,8 +349,8 @@
     mServerCblk->connected |= 1<<dpy;
     display_cblk_t* dcblk = mServerCblk->displays + dpy;
     memset(dcblk, 0, sizeof(display_cblk_t));
-    dcblk->w            = w;
-    dcblk->h            = h;
+    dcblk->w            = plane.getWidth();
+    dcblk->h            = plane.getHeight();
     dcblk->format       = f;
     dcblk->orientation  = ISurfaceComposer::eOrientationDefault;
     dcblk->xdpi         = hw.getDpiX();
@@ -621,14 +620,8 @@
             const DisplayHardware& hw(plane.displayHardware());
             volatile display_cblk_t* dcblk = mServerCblk->displays + dpy;
             dcblk->orientation = orientation;
-            if (orientation & eOrientationSwapMask) {
-                // 90 or 270 degrees orientation
-                dcblk->w = hw.getHeight();
-                dcblk->h = hw.getWidth();
-            } else {
-                dcblk->w = hw.getWidth();
-                dcblk->h = hw.getHeight();
-            }
+            dcblk->w = plane.getWidth();
+            dcblk->h = plane.getHeight();
 
             mVisibleRegionsDirty = true;
             mDirtyRegion.set(hw.bounds());
@@ -1795,13 +1788,47 @@
     return mHw ? true : false;
 }
 
-void GraphicPlane::setDisplayHardware(DisplayHardware *hw) {
-    mHw = hw;
+int GraphicPlane::getWidth() const {
+    return mWidth;
 }
 
-void GraphicPlane::setTransform(const Transform& tr) {
-    mTransform = tr;
-    mGlobalTransform = mOrientationTransform * mTransform;
+int GraphicPlane::getHeight() const {
+    return mHeight;
+}
+
+void GraphicPlane::setDisplayHardware(DisplayHardware *hw)
+{
+    mHw = hw;
+
+    // initialize the display orientation transform.
+    // it's a constant that should come from the display driver.
+    int displayOrientation = ISurfaceComposer::eOrientationDefault;
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get("ro.sf.hwrotation", property, NULL) > 0) {
+        //displayOrientation
+        switch (atoi(property)) {
+        case 90:
+            displayOrientation = ISurfaceComposer::eOrientation90;
+            break;
+        case 270:
+            displayOrientation = ISurfaceComposer::eOrientation270;
+            break;
+        }
+    }
+
+    const float w = hw->getWidth();
+    const float h = hw->getHeight();
+    GraphicPlane::orientationToTransfrom(displayOrientation, w, h,
+            &mDisplayTransform);
+    if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) {
+        mDisplayWidth = h;
+        mDisplayHeight = w;
+    } else {
+        mDisplayWidth = w;
+        mDisplayHeight = h;
+    }
+
+    setOrientation(ISurfaceComposer::eOrientationDefault);
 }
 
 status_t GraphicPlane::orientationToTransfrom(
@@ -1810,8 +1837,9 @@
     float a, b, c, d, x, y;
     switch (orientation) {
     case ISurfaceComposer::eOrientationDefault:
-        a=1; b=0; c=0; d=1; x=0; y=0;
-        break;
+        // make sure the default orientation is optimal
+        tr->reset();
+        return NO_ERROR;
     case ISurfaceComposer::eOrientation90:
         a=0; b=-1; c=1; d=0; x=w; y=0;
         break;
@@ -1831,20 +1859,16 @@
 
 status_t GraphicPlane::setOrientation(int orientation)
 {
-    const DisplayHardware& hw(displayHardware());
-    const float w = hw.getWidth();
-    const float h = hw.getHeight();
-
-    if (orientation == ISurfaceComposer::eOrientationDefault) {
-        // make sure the default orientation is optimal
-        mOrientationTransform.reset();
-        mOrientation = orientation;
-        mGlobalTransform = mTransform;
-        return NO_ERROR;
-    }
-
     // If the rotation can be handled in hardware, this is where
     // the magic should happen.
+
+    const DisplayHardware& hw(displayHardware());
+    const float w = mDisplayWidth;
+    const float h = mDisplayHeight;
+    mWidth = int(w);
+    mHeight = int(h);
+
+    Transform orientationTransform;
     if (UNLIKELY(orientation == 42)) {
         float a, b, c, d, x, y;
         const float r = (3.14159265f / 180.0f) * 42.0f;
@@ -1853,14 +1877,18 @@
         a=co; b=-si; c=si; d=co;
         x = si*(h*0.5f) + (1-co)*(w*0.5f);
         y =-si*(w*0.5f) + (1-co)*(h*0.5f);
-        mOrientationTransform.set(a, b, c, d);
-        mOrientationTransform.set(x, y);
+        orientationTransform.set(a, b, c, d);
+        orientationTransform.set(x, y);
     } else {
         GraphicPlane::orientationToTransfrom(orientation, w, h,
-                &mOrientationTransform);
+                &orientationTransform);
+        if (orientation & ISurfaceComposer::eOrientationSwapMask) {
+            mWidth = int(h);
+            mHeight = int(w);
+        }
     }
     mOrientation = orientation;
-    mGlobalTransform = mOrientationTransform * mTransform;
+    mGlobalTransform = mDisplayTransform * orientationTransform;
     return NO_ERROR;
 }
 
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index c0ab73d..2b7820c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -116,9 +116,10 @@
         bool                    initialized() const;
 
         void                    setDisplayHardware(DisplayHardware *);
-        void                    setTransform(const Transform& tr);
         status_t                setOrientation(int orientation);
         int                     getOrientation() const { return mOrientation; }
+        int                     getWidth() const;
+        int                     getHeight() const;
 
         const DisplayHardware&  displayHardware() const;
         const Transform&        transform() const;
@@ -129,10 +130,13 @@
         GraphicPlane            operator = (const GraphicPlane&);
 
         DisplayHardware*        mHw;
-        Transform               mTransform;
-        Transform               mOrientationTransform;
         Transform               mGlobalTransform;
+        Transform               mDisplayTransform;
         int                     mOrientation;
+        float                   mDisplayWidth;
+        float                   mDisplayHeight;
+        int                     mWidth;
+        int                     mHeight;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1fac07c..628cb6b7 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -81,10 +81,6 @@
     void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime);
     void clearTestProviderStatus(String provider);
 
-    /* for installing external Location Providers */
-    void installLocationProvider(String name, ILocationProvider provider);
-    void installGeocodeProvider(IGeocodeProvider provider);
-
     // for NI support
     boolean sendNiResponse(int notifId, int userResponse);
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 6d7a23d..9027fc2 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -104,48 +104,6 @@
      */
     public static final String KEY_LOCATION_CHANGED = "location";
 
-    public interface GeocodeProvider {
-        String getFromLocation(double latitude, double longitude, int maxResults,
-            GeocoderParams params, List<Address> addrs);
-
-        String getFromLocationName(String locationName,
-            double lowerLeftLatitude, double lowerLeftLongitude,
-            double upperRightLatitude, double upperRightLongitude, int maxResults,
-            GeocoderParams params, List<Address> addrs);
-    }
-
-    private static final class GeocodeProviderProxy extends IGeocodeProvider.Stub {
-        private GeocodeProvider mProvider;
-
-        GeocodeProviderProxy(GeocodeProvider provider) {
-            mProvider = provider;
-        }
-
-        /**
-         * This method is overridden to implement the
-         * {@link Geocoder#getFromLocation(double, double, int)} method.
-         * Classes implementing this method should not hold a reference to the params parameter.
-         */
-        public String getFromLocation(double latitude, double longitude, int maxResults,
-                GeocoderParams params, List<Address> addrs) {
-            return mProvider.getFromLocation(latitude, longitude, maxResults, params, addrs);
-        }
-
-        /**
-         * This method is overridden to implement the
-         * {@link Geocoder#getFromLocationName(String, int, double, double, double, double)} method.
-         * Classes implementing this method should not hold a reference to the params parameter.
-         */
-        public String getFromLocationName(String locationName,
-                double lowerLeftLatitude, double lowerLeftLongitude,
-                double upperRightLatitude, double upperRightLongitude, int maxResults,
-                GeocoderParams params, List<Address> addrs) {
-            return mProvider.getFromLocationName(locationName, lowerLeftLatitude,
-                    lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
-                    maxResults, params, addrs);
-        }
-    }
-
     // Map from LocationListeners to their associated ListenerTransport objects
     private HashMap<LocationListener,ListenerTransport> mListeners =
         new HashMap<LocationListener,ListenerTransport>();
@@ -1395,75 +1353,6 @@
             return false;
         }
     }
-
-    /**
-     * Installs a location provider.
-     *
-     * @param name of the location provider
-     * @param provider Binder interface for the location provider
-     *
-     * @return true if the command succeeds.
-     *
-     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
-     *
-     * {@hide}
-     */
-    public boolean installLocationProvider(String name, ILocationProvider provider) {
-        try {
-            mService.installLocationProvider(name, provider);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in installLocationProvider: ", e);
-            return false;
-        }
-    }
-
-    /**
-     * Installs a location provider.
-     *
-     * @param provider implementation of the location provider
-     *
-     * @return true if the command succeeds.
-     *
-     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
-     */
-    public boolean installLocationProvider(LocationProviderImpl provider) {
-        return installLocationProvider(provider.getName(), provider.getInterface());
-    }
-
-    /**
-     * Installs a geocoder server.
-     *
-     * @param provider Binder interface for the geocoder provider
-     *
-     * @return true if the command succeeds.
-     *
-     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
-     */
-    public boolean installGeocodeProvider(GeocodeProvider provider) {
-        try {
-            mService.installGeocodeProvider(new GeocodeProviderProxy(provider));
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in setGeocodeProvider: ", e);
-            return false;
-        }
-    }
-
-    /**
-     * Used by location providers to report new locations.
-     *
-     * @param location new Location to report
-     *
-     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
-     */
-    public void reportLocation(Location location) {
-        try {
-            mService.reportLocation(location);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in reportLocation: ", e);
-        }
-    }
     
     /**
      * Used by NetInitiatedActivity to report user response
diff --git a/location/java/android/location/LocationProviderImpl.java b/location/java/android/location/LocationProviderImpl.java
deleted file mode 100644
index 7148a02..0000000
--- a/location/java/android/location/LocationProviderImpl.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.location;
-
-import android.net.NetworkInfo;
-import android.os.Bundle;
-
-/**
- * An abstract superclass for location providers that are implemented
- * outside of the core android platform.
- * A LocationProviderImpl can be installed using the
- * {@link LocationManager#installLocationProvider(LocationProviderImpl)} method.
- * Installing a location provider requires the
- * android.permission.INSTALL_LOCATION_PROVIDER permission.
- */
-public abstract class LocationProviderImpl extends LocationProvider {
-
-    private ILocationProvider.Stub mProvider = new ILocationProvider.Stub() {
-
-        public boolean requiresNetwork() {
-            return LocationProviderImpl.this.requiresNetwork();
-        }
-
-        public boolean requiresSatellite() {
-            return LocationProviderImpl.this.requiresSatellite();
-        }
-
-        public boolean requiresCell() {
-            return LocationProviderImpl.this.requiresCell();
-        }
-
-        public boolean hasMonetaryCost() {
-            return LocationProviderImpl.this.hasMonetaryCost();
-        }
-
-        public boolean supportsAltitude() {
-            return LocationProviderImpl.this.supportsAltitude();
-        }
-
-        public boolean supportsSpeed() {
-            return LocationProviderImpl.this.supportsSpeed();
-        }
-
-        public boolean supportsBearing() {
-            return LocationProviderImpl.this.supportsBearing();
-        }
-
-        public int getPowerRequirement() {
-            return LocationProviderImpl.this.getPowerRequirement();
-        }
-
-        public int getAccuracy() {
-            return LocationProviderImpl.this.getAccuracy();
-        }
-
-        public void enable() {
-            LocationProviderImpl.this.enable();
-        }
-
-        public void disable() {
-            LocationProviderImpl.this.disable();
-        }
-
-        public int getStatus(Bundle extras) {
-            return LocationProviderImpl.this.getStatus(extras);
-        }
-
-        public long getStatusUpdateTime() {
-            return LocationProviderImpl.this.getStatusUpdateTime();
-        }
-
-        public void enableLocationTracking(boolean enable) {
-            LocationProviderImpl.this.enableLocationTracking(enable);
-        }
-
-        public void setMinTime(long minTime) {
-            LocationProviderImpl.this.setMinTime(minTime);
-        }
-
-        public void updateNetworkState(int state, NetworkInfo info) {
-            LocationProviderImpl.this.updateNetworkState(state, info);
-        }
-
-        public void updateLocation(Location location) {
-            LocationProviderImpl.this.updateLocation(location);
-        }
-
-        public boolean sendExtraCommand(String command, Bundle extras) {
-            return LocationProviderImpl.this.sendExtraCommand(command, extras);
-        }
-
-        public void addListener(int uid) {
-            LocationProviderImpl.this.addListener(uid);
-        }
-
-        public void removeListener(int uid) {
-            LocationProviderImpl.this.removeListener(uid);
-        }
-    };
-
-    public LocationProviderImpl(String name) {
-        super(name);
-    }
-
-    /**
-     * {@hide}
-     */
-    /* package */ ILocationProvider getInterface() {
-        return mProvider;
-    }
-
-    /**
-     * Enables the location provider
-     */
-    public abstract void enable();
-
-    /**
-     * Disables the location provider
-     */
-    public abstract void disable();
-
-    /**
-     * Returns a information on the status of this provider.
-     * {@link #OUT_OF_SERVICE} is returned if the provider is
-     * out of service, and this is not expected to change in the near
-     * future; {@link #TEMPORARILY_UNAVAILABLE} is returned if
-     * the provider is temporarily unavailable but is expected to be
-     * available shortly; and {@link #AVAILABLE} is returned
-     * if the provider is currently available.
-     *
-     * <p> If extras is non-null, additional status information may be
-     * added to it in the form of provider-specific key/value pairs.
-     */
-    public abstract int getStatus(Bundle extras);
-
-    /**
-     * Returns the time at which the status was last updated. It is the
-     * responsibility of the provider to appropriately set this value using
-     * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
-     * there is a status update that it wishes to broadcast to all its
-     * listeners. The provider should be careful not to broadcast
-     * the same status again.
-     *
-     * @return time of last status update in millis since last reboot
-     */
-    public abstract long getStatusUpdateTime();
-
-    /**
-     * Notifies the location provider that clients are listening for locations.
-     * Called with enable set to true when the first client is added and
-     * called with enable set to false when the last client is removed.
-     * This allows the provider to prepare for receiving locations,
-     * and to shut down when no clients are remaining.
-     *
-     * @param enable true if location tracking should be enabled.
-     */
-    public abstract void enableLocationTracking(boolean enable);
-
-    /**
-     * Notifies the location provider of the smallest minimum time between updates amongst
-     * all clients that are listening for locations.  This allows the provider to reduce
-     * the frequency of updates to match the requested frequency.
-     *
-     * @param minTime the smallest minTime value over all listeners for this provider.
-     */
-    public abstract void setMinTime(long minTime);
-
-    /**
-     * Updates the network state for the given provider. This function must
-     * be overwritten if {@link #requiresNetwork} returns true. The state is
-     * {@link #TEMPORARILY_UNAVAILABLE} (disconnected), OR {@link #AVAILABLE}
-     * (connected or connecting).
-     *
-     * @param state data state
-     */
-    public abstract void updateNetworkState(int state, NetworkInfo info);
-
-    /**
-     * Informs the provider when a new location has been computed by a different
-     * location provider.  This is intended to be used as aiding data for the
-     * receiving provider.
-     *
-     * @param location new location from other location provider
-     */
-    public abstract void updateLocation(Location location);
-
-    /**
-     * Implements addditional location provider specific additional commands.
-     *
-     * @param command name of the command to send to the provider.
-     * @param extras optional arguments for the command (or null).
-     * The provider may optionally fill the extras Bundle with results from the command.
-     *
-     * @return true if the command succeeds.
-     */
-    public abstract boolean sendExtraCommand(String command, Bundle extras);
-
-    /**
-     * Notifies the location provider when a new client is listening for locations.
-     *
-     * @param uid user ID of the new client.
-     */
-    public abstract void addListener(int uid);
-
-    /**
-     * Notifies the location provider when a client is no longer listening for locations.
-     *
-     * @param uid user ID of the client no longer listening.
-     */
-    public abstract void removeListener(int uid);
-}
-
diff --git a/location/java/android/location/provider/GeocodeProvider.java b/location/java/android/location/provider/GeocodeProvider.java
new file mode 100644
index 0000000..86376a7
--- /dev/null
+++ b/location/java/android/location/provider/GeocodeProvider.java
@@ -0,0 +1,83 @@
+/*
+ * 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.location.provider;
+
+import android.os.IBinder;
+
+import android.location.Address;
+import android.location.GeocoderParams;
+import android.location.IGeocodeProvider;
+
+import java.util.List;
+
+/**
+ * An abstract superclass for geocode providers that are implemented
+ * outside of the core android platform.
+ * Geocode providers can be implemented as services and return the result of
+ * {@link GeocodeProvider#getBinder()} in its getBinder() method.
+ *
+ * @hide
+ */
+public abstract class GeocodeProvider {
+
+    private IGeocodeProvider.Stub mProvider = new IGeocodeProvider.Stub() {
+        public String getFromLocation(double latitude, double longitude, int maxResults,
+                GeocoderParams params, List<Address> addrs) {
+            return GeocodeProvider.this.onGetFromLocation(latitude, longitude, maxResults,
+                    params, addrs);
+        }
+
+        public String getFromLocationName(String locationName,
+                double lowerLeftLatitude, double lowerLeftLongitude,
+                double upperRightLatitude, double upperRightLongitude, int maxResults,
+                GeocoderParams params, List<Address> addrs) {
+            return GeocodeProvider.this.onGetFromLocationName(locationName, lowerLeftLatitude,
+                    lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+                    maxResults, params, addrs);
+        }
+    };
+
+    /**
+     * This method is overridden to implement the
+     * {@link Geocoder#getFromLocation(double, double, int)} method.
+     * Classes implementing this method should not hold a reference to the params parameter.
+     */
+    public abstract String onGetFromLocation(double latitude, double longitude, int maxResults,
+            GeocoderParams params, List<Address> addrs);
+
+    /**
+     * This method is overridden to implement the
+     * {@link Geocoder#getFromLocationName(String, int, double, double, double, double)} method.
+     * Classes implementing this method should not hold a reference to the params parameter.
+     */
+    public abstract String onGetFromLocationName(String locationName,
+            double lowerLeftLatitude, double lowerLeftLongitude,
+            double upperRightLatitude, double upperRightLongitude, int maxResults,
+            GeocoderParams params, List<Address> addrs);
+
+    /**
+     * Returns the Binder interface for the geocode provider.
+     * This is intended to be used for the onBind() method of
+     * a service that implements a geocoder service.
+     *
+     * @return the IBinder instance for the provider
+     */
+    public IBinder getBinder() {
+        return mProvider;
+    }
+}
+
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java
new file mode 100644
index 0000000..0d028c0
--- /dev/null
+++ b/location/java/android/location/provider/LocationProvider.java
@@ -0,0 +1,333 @@
+/*
+ * 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.location.provider;
+
+import android.content.Context;
+import android.net.NetworkInfo;
+import android.location.ILocationManager;
+import android.location.ILocationProvider;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * An abstract superclass for location providers that are implemented
+ * outside of the core android platform.
+ * Location providers can be implemented as services and return the result of
+ * {@link LocationProvider#getBinder()} in its getBinder() method.
+ *
+ * @hide
+ */
+public abstract class LocationProvider {
+
+    private static final String TAG = "LocationProvider";
+
+    private ILocationManager mLocationManager;
+
+    private ILocationProvider.Stub mProvider = new ILocationProvider.Stub() {
+
+        public boolean requiresNetwork() {
+            return LocationProvider.this.onRequiresNetwork();
+        }
+
+        public boolean requiresSatellite() {
+            return LocationProvider.this.onRequiresSatellite();
+        }
+
+        public boolean requiresCell() {
+            return LocationProvider.this.onRequiresCell();
+        }
+
+        public boolean hasMonetaryCost() {
+            return LocationProvider.this.onHasMonetaryCost();
+        }
+
+        public boolean supportsAltitude() {
+            return LocationProvider.this.onSupportsAltitude();
+        }
+
+        public boolean supportsSpeed() {
+            return LocationProvider.this.onSupportsSpeed();
+        }
+
+        public boolean supportsBearing() {
+            return LocationProvider.this.onSupportsBearing();
+        }
+
+        public int getPowerRequirement() {
+            return LocationProvider.this.onGetPowerRequirement();
+        }
+
+        public int getAccuracy() {
+            return LocationProvider.this.onGetAccuracy();
+        }
+
+        public void enable() {
+            LocationProvider.this.onEnable();
+        }
+
+        public void disable() {
+            LocationProvider.this.onDisable();
+        }
+
+        public int getStatus(Bundle extras) {
+            return LocationProvider.this.onGetStatus(extras);
+        }
+
+        public long getStatusUpdateTime() {
+            return LocationProvider.this.onGetStatusUpdateTime();
+        }
+
+        public void enableLocationTracking(boolean enable) {
+            LocationProvider.this.onEnableLocationTracking(enable);
+        }
+
+        public void setMinTime(long minTime) {
+            LocationProvider.this.onSetMinTime(minTime);
+        }
+
+        public void updateNetworkState(int state, NetworkInfo info) {
+            LocationProvider.this.onUpdateNetworkState(state, info);
+        }
+
+        public void updateLocation(Location location) {
+            LocationProvider.this.onUpdateLocation(location);
+        }
+
+        public boolean sendExtraCommand(String command, Bundle extras) {
+            return LocationProvider.this.onSendExtraCommand(command, extras);
+        }
+
+        public void addListener(int uid) {
+            LocationProvider.this.onAddListener(uid);
+        }
+
+        public void removeListener(int uid) {
+            LocationProvider.this.onRemoveListener(uid);
+        }
+    };
+
+    public LocationProvider() {
+        IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
+        mLocationManager = ILocationManager.Stub.asInterface(b);
+    }
+
+    /**
+     * {@hide}
+     */
+    /* package */ ILocationProvider getInterface() {
+        return mProvider;
+    }
+
+    /**
+     * Returns the Binder interface for the location provider.
+     * This is intended to be used for the onBind() method of
+     * a service that implements a location provider service.
+     *
+     * @return the IBinder instance for the provider
+     */
+    public IBinder getBinder() {
+        return mProvider;
+    }
+
+    /**
+     * Used by the location provider to report new locations.
+     *
+     * @param location new Location to report
+     *
+     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+     */
+    public void reportLocation(Location location) {
+        try {
+            mLocationManager.reportLocation(location);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in reportLocation: ", e);
+        }
+    }
+
+    /**
+     * Returns true if the provider requires access to a
+     * data network (e.g., the Internet), false otherwise.
+     */
+    public abstract boolean onRequiresNetwork();
+
+    /**
+     * Returns true if the provider requires access to a
+     * satellite-based positioning system (e.g., GPS), false
+     * otherwise.
+     */
+    public abstract boolean onRequiresSatellite();
+
+    /**
+     * Returns true if the provider requires access to an appropriate
+     * cellular network (e.g., to make use of cell tower IDs), false
+     * otherwise.
+     */
+    public abstract boolean onRequiresCell();
+
+    /**
+     * Returns true if the use of this provider may result in a
+     * monetary charge to the user, false if use is free.  It is up to
+     * each provider to give accurate information.
+     */
+    public abstract boolean onHasMonetaryCost();
+
+    /**
+     * Returns true if the provider is able to provide altitude
+     * information, false otherwise.  A provider that reports altitude
+     * under most circumstances but may occassionally not report it
+     * should return true.
+     */
+    public abstract boolean onSupportsAltitude();
+
+    /**
+     * Returns true if the provider is able to provide speed
+     * information, false otherwise.  A provider that reports speed
+     * under most circumstances but may occassionally not report it
+     * should return true.
+     */
+    public abstract boolean onSupportsSpeed();
+
+    /**
+     * Returns true if the provider is able to provide bearing
+     * information, false otherwise.  A provider that reports bearing
+     * under most circumstances but may occassionally not report it
+     * should return true.
+     */
+    public abstract boolean onSupportsBearing();
+
+    /**
+     * Returns the power requirement for this provider.
+     *
+     * @return the power requirement for this provider, as one of the
+     * constants Criteria.POWER_REQUIREMENT_*.
+     */
+    public abstract int onGetPowerRequirement();
+
+    /**
+     * Returns a constant describing horizontal accuracy of this provider.
+     * If the provider returns finer grain or exact location,
+     * {@link Criteria#ACCURACY_FINE} is returned, otherwise if the
+     * location is only approximate then {@link Criteria#ACCURACY_COARSE}
+     * is returned.
+     */
+    public abstract int onGetAccuracy();
+
+    /**
+     * Enables the location provider
+     */
+    public abstract void onEnable();
+
+    /**
+     * Disables the location provider
+     */
+    public abstract void onDisable();
+
+    /**
+     * Returns a information on the status of this provider.
+     * {@link #OUT_OF_SERVICE} is returned if the provider is
+     * out of service, and this is not expected to change in the near
+     * future; {@link #TEMPORARILY_UNAVAILABLE} is returned if
+     * the provider is temporarily unavailable but is expected to be
+     * available shortly; and {@link #AVAILABLE} is returned
+     * if the provider is currently available.
+     *
+     * <p> If extras is non-null, additional status information may be
+     * added to it in the form of provider-specific key/value pairs.
+     */
+    public abstract int onGetStatus(Bundle extras);
+
+    /**
+     * Returns the time at which the status was last updated. It is the
+     * responsibility of the provider to appropriately set this value using
+     * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
+     * there is a status update that it wishes to broadcast to all its
+     * listeners. The provider should be careful not to broadcast
+     * the same status again.
+     *
+     * @return time of last status update in millis since last reboot
+     */
+    public abstract long onGetStatusUpdateTime();
+
+    /**
+     * Notifies the location provider that clients are listening for locations.
+     * Called with enable set to true when the first client is added and
+     * called with enable set to false when the last client is removed.
+     * This allows the provider to prepare for receiving locations,
+     * and to shut down when no clients are remaining.
+     *
+     * @param enable true if location tracking should be enabled.
+     */
+    public abstract void onEnableLocationTracking(boolean enable);
+
+    /**
+     * Notifies the location provider of the smallest minimum time between updates amongst
+     * all clients that are listening for locations.  This allows the provider to reduce
+     * the frequency of updates to match the requested frequency.
+     *
+     * @param minTime the smallest minTime value over all listeners for this provider.
+     */
+    public abstract void onSetMinTime(long minTime);
+
+    /**
+     * Updates the network state for the given provider. This function must
+     * be overwritten if {@link #requiresNetwork} returns true. The state is
+     * {@link #TEMPORARILY_UNAVAILABLE} (disconnected), OR {@link #AVAILABLE}
+     * (connected or connecting).
+     *
+     * @param state data state
+     */
+    public abstract void onUpdateNetworkState(int state, NetworkInfo info);
+
+    /**
+     * Informs the provider when a new location has been computed by a different
+     * location provider.  This is intended to be used as aiding data for the
+     * receiving provider.
+     *
+     * @param location new location from other location provider
+     */
+    public abstract void onUpdateLocation(Location location);
+
+    /**
+     * Implements addditional location provider specific additional commands.
+     *
+     * @param command name of the command to send to the provider.
+     * @param extras optional arguments for the command (or null).
+     * The provider may optionally fill the extras Bundle with results from the command.
+     *
+     * @return true if the command succeeds.
+     */
+    public abstract boolean onSendExtraCommand(String command, Bundle extras);
+
+    /**
+     * Notifies the location provider when a new client is listening for locations.
+     *
+     * @param uid user ID of the new client.
+     */
+    public abstract void onAddListener(int uid);
+
+    /**
+     * Notifies the location provider when a client is no longer listening for locations.
+     *
+     * @param uid user ID of the client no longer listening.
+     */
+    public abstract void onRemoveListener(int uid);
+}
+
diff --git a/location/java/com/android/internal/location/GeocoderProxy.java b/location/java/com/android/internal/location/GeocoderProxy.java
new file mode 100644
index 0000000..b06297b
--- /dev/null
+++ b/location/java/com/android/internal/location/GeocoderProxy.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Address;
+import android.location.GeocoderParams;
+import android.location.IGeocodeProvider;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * A class for proxying IGeocodeProvider implementations.
+ *
+ * {@hide}
+ */
+public class GeocoderProxy {
+
+    private static final String TAG = "GeocoderProxy";
+
+    private final Context mContext;
+    private final Intent mIntent;
+    private final Connection mServiceConnection = new Connection();
+    private IGeocodeProvider mProvider;
+
+    public GeocoderProxy(Context context, String serviceName) {
+        mContext = context;
+        mIntent = new Intent(serviceName);
+        mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    private class Connection implements ServiceConnection {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Log.d(TAG, "onServiceConnected " + className);
+            synchronized (this) {
+                mProvider = IGeocodeProvider.Stub.asInterface(service);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            Log.d(TAG, "onServiceDisconnected " + className);
+            synchronized (this) {
+                mProvider = null;
+            }
+        }
+    }
+
+    public String getFromLocation(double latitude, double longitude, int maxResults,
+            GeocoderParams params, List<Address> addrs) {
+        IGeocodeProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                return provider.getFromLocation(latitude, longitude, maxResults,
+                        params, addrs);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getFromLocation failed", e);
+            }
+        }
+        return "Service not Available";
+    }
+
+    public String getFromLocationName(String locationName,
+            double lowerLeftLatitude, double lowerLeftLongitude,
+            double upperRightLatitude, double upperRightLongitude, int maxResults,
+            GeocoderParams params, List<Address> addrs) {
+        IGeocodeProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                return provider.getFromLocationName(locationName, lowerLeftLatitude,
+                        lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+                        maxResults, params, addrs);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getFromLocationName failed", e);
+            }
+        }
+        return "Service not Available";
+    }
+}
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index 2e0be89..361104f 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -16,155 +16,229 @@
 
 package com.android.internal.location;
 
-import android.location.Address;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
 import android.location.ILocationProvider;
 import android.location.Location;
-import android.location.LocationManager;
 import android.net.NetworkInfo;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Log;
 
-import java.util.List;
-
 /**
- * A class for proxying remote ILocationProvider implementations.
+ * A class for proxying ILocationProvider implementations.
  *
  * {@hide}
  */
-public class LocationProviderProxy implements IBinder.DeathRecipient {
+public class LocationProviderProxy {
 
     private static final String TAG = "LocationProviderProxy";
 
+    private final Context mContext;
     private final String mName;
-    private final ILocationProvider mProvider;
+    private ILocationProvider mProvider;
+    private Intent mIntent;
+    private Handler mHandler;
+    private final Connection mServiceConnection = new Connection();
+
+    // cached values set by the location manager
     private boolean mLocationTracking = false;
     private boolean mEnabled = false;
-    private long mMinTime = 0;
-    private boolean mDead;
+    private long mMinTime = -1;
+    private int mNetworkState;
+    private NetworkInfo mNetworkInfo;
 
-    public LocationProviderProxy(String name, ILocationProvider provider) {
+    // for caching requiresNetwork, requiresSatellite, etc.
+    private DummyLocationProvider mCachedAttributes;
+
+    // constructor for proxying built-in location providers
+    public LocationProviderProxy(Context context, String name, ILocationProvider provider) {
+        mContext = context;
         mName = name;
         mProvider = provider;
-        try {
-            provider.asBinder().linkToDeath(this, 0);
-        } catch (RemoteException e) {
-            Log.e(TAG, "linkToDeath failed", e);
-            mDead = true;
+    }
+
+    // constructor for proxying location providers implemented in a separate service
+    public LocationProviderProxy(Context context, String name, String serviceName,
+            Handler handler) {
+        mContext = context;
+        mName = name;
+        mIntent = new Intent(serviceName);
+        mHandler = handler;
+        mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    private class Connection implements ServiceConnection {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className);
+            synchronized (this) {
+                mProvider = ILocationProvider.Stub.asInterface(service);
+                if (mProvider != null) {
+                    mHandler.post(mServiceConnectedTask);
+                }
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className);
+            synchronized (this) {
+                mProvider = null;
+            }
         }
     }
 
-    public void unlinkProvider() {
-        if (mProvider != null) {
-            mProvider.asBinder().unlinkToDeath(this, 0);
+    private Runnable mServiceConnectedTask = new Runnable() {
+        public void run() {
+            ILocationProvider provider;
+            synchronized (mServiceConnection) {
+                provider = mProvider;
+                if (provider == null) {
+                    return;
+                }
+            }
+
+            if (mCachedAttributes == null) {
+                try {
+                    mCachedAttributes = new DummyLocationProvider(mName);
+                    mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
+                    mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
+                    mCachedAttributes.setRequiresCell(provider.requiresCell());
+                    mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
+                    mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
+                    mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
+                    mCachedAttributes.setSupportsBearing(provider.supportsBearing());
+                    mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
+                    mCachedAttributes.setAccuracy(provider.getAccuracy());
+                } catch (RemoteException e) {
+                    mCachedAttributes = null;
+                }
+            }
+
+            // resend previous values from the location manager if the service has restarted
+            try {
+                if (mEnabled) {
+                    provider.enable();
+                }
+                if (mLocationTracking) {
+                    provider.enableLocationTracking(true);
+                }
+                if (mMinTime >= 0) {
+                    provider.setMinTime(mMinTime);
+                }
+                if (mNetworkInfo != null) {
+                    provider.updateNetworkState(mNetworkState, mNetworkInfo);
+                }
+            } catch (RemoteException e) {
+            }
         }
-    }
+    };
 
     public String getName() {
         return mName;
     }
 
-    public boolean isDead() {
-        return mDead;
-    }
-
     public boolean requiresNetwork() {
-        try {
-            return mProvider.requiresNetwork();
-        } catch (RemoteException e) {
-            Log.e(TAG, "requiresNetwork failed", e);
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.requiresNetwork();
+        } else {
             return false;
         }
     }
 
     public boolean requiresSatellite() {
-        try {
-            return mProvider.requiresSatellite();
-        } catch (RemoteException e) {
-            Log.e(TAG, "requiresSatellite failed", e);
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.requiresSatellite();
+        } else {
             return false;
         }
     }
 
     public boolean requiresCell() {
-        try {
-            return mProvider.requiresCell();
-        } catch (RemoteException e) {
-            Log.e(TAG, "requiresCell failed", e);
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.requiresCell();
+        } else {
             return false;
         }
     }
 
     public boolean hasMonetaryCost() {
-        try {
-            return mProvider.hasMonetaryCost();
-        } catch (RemoteException e) {
-            Log.e(TAG, "hasMonetaryCost failed", e);
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.hasMonetaryCost();
+        } else {
             return false;
         }
     }
 
     public boolean supportsAltitude() {
-        try {
-            return mProvider.supportsAltitude();
-        } catch (RemoteException e) {
-            Log.e(TAG, "supportsAltitude failed", e);
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.supportsAltitude();
+        } else {
             return false;
         }
     }
 
     public boolean supportsSpeed() {
-        try {
-            return mProvider.supportsSpeed();
-        } catch (RemoteException e) {
-            Log.e(TAG, "supportsSpeed failed", e);
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.supportsSpeed();
+        } else {
             return false;
         }
     }
 
      public boolean supportsBearing() {
-        try {
-            return mProvider.supportsBearing();
-        } catch (RemoteException e) {
-            Log.e(TAG, "supportsBearing failed", e);
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.supportsBearing();
+        } else {
             return false;
         }
     }
 
     public int getPowerRequirement() {
-        try {
-            return mProvider.getPowerRequirement();
-        } catch (RemoteException e) {
-            Log.e(TAG, "getPowerRequirement failed", e);
-            return 0;
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.getPowerRequirement();
+        } else {
+            return -1;
         }
     }
 
     public int getAccuracy() {
-        try {
-            return mProvider.getAccuracy();
-        } catch (RemoteException e) {
-            Log.e(TAG, "getAccuracy failed", e);
-            return 0;
+        if (mCachedAttributes != null) {
+            return mCachedAttributes.getAccuracy();
+        } else {
+            return -1;
         }
     }
 
     public void enable() {
-        try {
-            mProvider.enable();
-            mEnabled = true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "enable failed", e);
+        mEnabled = true;
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                provider.enable();
+            } catch (RemoteException e) {
+            }
         }
     }
 
     public void disable() {
-        try {
-            mProvider.disable();
-            mEnabled = false;
-        } catch (RemoteException e) {
-            Log.e(TAG, "disable failed", e);
+        mEnabled = false;
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                provider.disable();
+            } catch (RemoteException e) {
+            }
         }
     }
 
@@ -173,22 +247,32 @@
     }
 
     public int getStatus(Bundle extras) {
-        try {
-            return mProvider.getStatus(extras);
-        } catch (RemoteException e) {
-            Log.e(TAG, "getStatus failed", e);
-            return 0;
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
         }
+        if (provider != null) {
+            try {
+                return provider.getStatus(extras);
+            } catch (RemoteException e) {
+            }
+        }
+        return 0;
     }
 
     public long getStatusUpdateTime() {
-        try {
-            return mProvider.getStatusUpdateTime();
-        } catch (RemoteException e) {
-            Log.e(TAG, "getStatusUpdateTime failed", e);
-            return 0;
+         ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
         }
-    }
+        if (provider != null) {
+            try {
+                return provider.getStatusUpdateTime();
+            } catch (RemoteException e) {
+            }
+        }
+        return 0;
+     }
 
     public boolean isLocationTracking() {
         return mLocationTracking;
@@ -196,10 +280,18 @@
 
     public void enableLocationTracking(boolean enable) {
         mLocationTracking = enable;
-        try {
-            mProvider.enableLocationTracking(enable);
-        } catch (RemoteException e) {
-            Log.e(TAG, "enableLocationTracking failed", e);
+        if (!enable) {
+            mMinTime = -1;
+        }
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                provider.enableLocationTracking(enable);
+            } catch (RemoteException e) {
+            }
         }
     }
 
@@ -208,58 +300,84 @@
     }
 
     public void setMinTime(long minTime) {
-        mMinTime = minTime;
-        try {
-            mProvider.setMinTime(minTime);
-        } catch (RemoteException e) {
-            Log.e(TAG, "setMinTime failed", e);
+       mMinTime = minTime;
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                provider.setMinTime(minTime);
+            } catch (RemoteException e) {
+            }
         }
     }
 
     public void updateNetworkState(int state, NetworkInfo info) {
-        try {
-            mProvider.updateNetworkState(state, info);
-        } catch (RemoteException e) {
-            Log.e(TAG, "updateNetworkState failed", e);
+        mNetworkState = state;
+        mNetworkInfo = info;
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                provider.updateNetworkState(state, info);
+            } catch (RemoteException e) {
+            }
         }
     }
 
     public void updateLocation(Location location) {
-        try {
-            mProvider.updateLocation(location);
-        } catch (RemoteException e) {
-            Log.e(TAG, "updateLocation failed", e);
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                provider.updateLocation(location);
+            } catch (RemoteException e) {
+            }
         }
     }
 
     public boolean sendExtraCommand(String command, Bundle extras) {
-        try {
-            return mProvider.sendExtraCommand(command, extras);
-        } catch (RemoteException e) {
-            Log.e(TAG, "sendExtraCommand failed", e);
-            return false;
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
         }
+        if (provider != null) {
+            try {
+                provider.sendExtraCommand(command, extras);
+            } catch (RemoteException e) {
+            }
+        }
+        return false;
     }
 
     public void addListener(int uid) {
-        try {
-            mProvider.addListener(uid);
-        } catch (RemoteException e) {
-            Log.e(TAG, "addListener failed", e);
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
+        }
+        if (provider != null) {
+            try {
+                provider.addListener(uid);
+            } catch (RemoteException e) {
+            }
         }
     }
 
     public void removeListener(int uid) {
-        try {
-            mProvider.removeListener(uid);
-        } catch (RemoteException e) {
-            Log.e(TAG, "removeListener failed", e);
+        ILocationProvider provider;
+        synchronized (mServiceConnection) {
+            provider = mProvider;
         }
-    }
-
-    public void binderDied() {
-        Log.w(TAG, "Location Provider " + mName + " died");
-        mDead = true;
-        mProvider.asBinder().unlinkToDeath(this, 0);
+        if (provider != null) {
+            try {
+                provider.removeListener(uid);
+            } catch (RemoteException e) {
+            }
+        }
     }
 }
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index d2596b8..65b67a1 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -57,17 +57,32 @@
     };
 
     /**
+     * Interface for notifying clients of the result of scanning a
+     * requested media file.
+     */
+    public interface ScanResultListener {
+        /**
+         * Called to notify the client when the media scanner has finished
+         * scanning a file.
+         * @param path the path to the file that has been scanned.
+         * @param uri the Uri for the file if the scanning operation succeeded 
+         * and the file was added to the media database, or null if scanning failed. 
+         */    
+        public void onScanCompleted(String path, Uri uri);
+    }
+    
+    /**
      * An interface for notifying clients of MediaScannerConnection
      * when a connection to the MediaScanner service has been established
      * and when the scanning of a file has completed.
      */
-    public interface MediaScannerConnectionClient {
+    public interface MediaScannerConnectionClient extends ScanResultListener {
         /**
          * Called to notify the client when a connection to the 
          * MediaScanner service has been established.
          */    
         public void onMediaScannerConnected();
-
+        
         /**
          * Called to notify the client when the media scanner has finished
          * scanning a file.
@@ -136,11 +151,12 @@
 
     /**
      * Requests the media scanner to scan a file.
+     * Success or failure of the scanning operation cannot be determined until 
+     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
+     * 
      * @param path the path to the file to be scanned.
      * @param mimeType  an optional mimeType for the file.
      * If mimeType is null, then the mimeType will be inferred from the file extension.
-     * Success or failure of the scanning operation cannot be determined until 
-     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
      */
      public void scanFile(String path, String mimeType) {
         synchronized (this) {
@@ -159,7 +175,67 @@
             }
         }
     }
-
+    
+    static class ClientProxy implements MediaScannerConnectionClient {
+        final String[] mPaths;
+        final String[] mMimeTypes;
+        final ScanResultListener mClient;
+        MediaScannerConnection mConnection;
+        int mNextPath;
+        
+        ClientProxy(String[] paths, String[] mimeTypes, ScanResultListener client) {
+            mPaths = paths;
+            mMimeTypes = mimeTypes;
+            mClient = client;
+        }
+        
+        public void onMediaScannerConnected() {
+            scanNextPath();
+        }
+        
+        public void onScanCompleted(String path, Uri uri) {
+            if (mClient != null) {
+                mClient.onScanCompleted(path, uri);
+            }
+            scanNextPath();
+        }
+        
+        void scanNextPath() {
+            if (mNextPath >= mPaths.length) {
+                mConnection.disconnect();
+                return;
+            }
+            String mimeType = mMimeTypes != null ? mMimeTypes[mNextPath] : null;
+            mConnection.scanFile(mPaths[mNextPath], mimeType);
+            mNextPath++;
+        }
+    }
+    
+    /**
+     * Convenience for constructing a {@link MediaScannerConnection}, calling
+     * {@link #connect} on it, and calling {@link #scanFile} with the given
+     * <var>path</var> and <var>mimeType</var> when the connection is
+     * established.
+     * @param context The caller's Context, required for establishing a connection to
+     * the media scanner service.
+     * Success or failure of the scanning operation cannot be determined until 
+     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
+     * @param paths Array of paths to be scanned.
+     * @param mimeTypes Optional array of MIME types for each path.
+     * If mimeType is null, then the mimeType will be inferred from the file extension.
+     * @param callback Optional callback through which you can receive the
+     * scanned URI and MIME type; If null, the file will be scanned but
+     * you will not get a result back.
+     * @see scanFile(String, String)
+     */
+    public static void scanFile(Context context, String[] paths, String[] mimeTypes,
+            ScanResultListener callback) {
+        ClientProxy client = new ClientProxy(paths, mimeTypes, callback);
+        MediaScannerConnection connection = new MediaScannerConnection(context, client);
+        client.mConnection = connection;
+        connection.connect();
+    }
+     
     /**
      * Part of the ServiceConnection interface.  Do not call.
      */
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index d51ab30..12872bc 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -54,12 +54,20 @@
 LOCAL_SHARED_LIBRARIES += libdl
 endif
 
-LOCAL_C_INCLUDES := external/tremor/Tremor                              \
+LOCAL_C_INCLUDES :=                                                     \
 	$(JNI_H_INCLUDE)                                                \
 	$(call include-path-for, graphics corecg)                       \
 	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
 	$(TOP)/frameworks/base/media/libstagefright/include
 
+ifeq ($(TARGET_ARCH),arm)
+    LOCAL_C_INCLUDES += \
+        $(TOP)/external/tremolo/Tremolo
+else
+    LOCAL_C_INCLUDES += \
+        $(TOP)/external/tremor/Tremor
+endif
+
 LOCAL_MODULE:= libmediaplayerservice
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index f42d55b..1bfcf65 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -49,30 +49,11 @@
 }
 
 status_t StagefrightPlayer::prepare() {
-    LOGV("prepare");
-
-    int32_t width, height;
-    if (mPlayer->getVideoDimensions(&width, &height) != OK) {
-        width = height = 0;
-    }
-
-    sendEvent(MEDIA_SET_VIDEO_SIZE, width, height);
-
-    return OK;
+    return mPlayer->prepare();
 }
 
 status_t StagefrightPlayer::prepareAsync() {
-    LOGV("prepareAsync");
-
-    status_t err = prepare();
-
-    if (err != OK) {
-        return err;
-    }
-
-    sendEvent(MEDIA_PREPARED);
-
-    return OK;
+    return mPlayer->prepareAsync();
 }
 
 status_t StagefrightPlayer::start() {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index b25ce67..58eb12e 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -48,8 +48,15 @@
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
         $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
-        $(TOP)/external/opencore/android \
+        $(TOP)/external/opencore/android
+
+ifeq ($(TARGET_ARCH),arm)
+    LOCAL_C_INCLUDES += \
+        $(TOP)/external/tremolo/Tremolo
+else
+    LOCAL_C_INCLUDES += \
         $(TOP)/external/tremor/Tremor
+endif
 
 LOCAL_SHARED_LIBRARIES := \
         libbinder         \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 2b403f8..a13b242 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -37,21 +37,23 @@
 namespace android {
 
 struct AwesomeEvent : public TimedEventQueue::Event {
-    AwesomeEvent(AwesomePlayer *player, int32_t code)
+    AwesomeEvent(
+            AwesomePlayer *player,
+            void (AwesomePlayer::*method)())
         : mPlayer(player),
-          mCode(code) {
+          mMethod(method) {
     }
 
 protected:
     virtual ~AwesomeEvent() {}
 
     virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
-        mPlayer->onEvent(mCode);
+        (mPlayer->*mMethod)();
     }
 
 private:
     AwesomePlayer *mPlayer;
-    int32_t mCode;
+    void (AwesomePlayer::*mMethod)();
 
     AwesomeEvent(const AwesomeEvent &);
     AwesomeEvent &operator=(const AwesomeEvent &);
@@ -109,19 +111,23 @@
 AwesomePlayer::AwesomePlayer()
     : mTimeSource(NULL),
       mAudioPlayer(NULL),
+      mFlags(0),
       mLastVideoBuffer(NULL),
       mVideoBuffer(NULL) {
     CHECK_EQ(mClient.connect(), OK);
 
     DataSource::RegisterDefaultSniffers();
 
-    mVideoEvent = new AwesomeEvent(this, 0);
+    mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);
     mVideoEventPending = false;
-    mStreamDoneEvent = new AwesomeEvent(this, 1);
+    mStreamDoneEvent = new AwesomeEvent(this, &AwesomePlayer::onStreamDone);
     mStreamDoneEventPending = false;
-    mBufferingEvent = new AwesomeEvent(this, 2);
+    mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate);
     mBufferingEventPending = false;
-    mCheckAudioStatusEvent = new AwesomeEvent(this, 3);
+
+    mCheckAudioStatusEvent = new AwesomeEvent(
+            this, &AwesomePlayer::onCheckAudioStatus);
+
     mAudioStatusEventPending = false;
 
     mQueue.start();
@@ -162,23 +168,17 @@
 
     reset_l();
 
-    sp<DataSource> dataSource = DataSource::CreateFromURI(uri, headers);
+    mUri = uri;
 
-    if (dataSource == NULL) {
-        return UNKNOWN_ERROR;
+    if (headers) {
+        mUriHeaders = *headers;
     }
 
-    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+    // The actual work will be done during preparation in the call to
+    // ::finishSetDataSource_l to avoid blocking the calling thread in
+    // setDataSource for any significant time.
 
-    if (extractor == NULL) {
-        return UNKNOWN_ERROR;
-    }
-
-    if (dataSource->flags() & DataSource::kWantsPrefetching) {
-        mPrefetcher = new Prefetcher;
-    }
-
-    return setDataSource_l(extractor);
+    return OK;
 }
 
 status_t AwesomePlayer::setDataSource(
@@ -237,6 +237,10 @@
 }
 
 void AwesomePlayer::reset_l() {
+    while (mFlags & PREPARING) {
+        mPreparedCondition.wait(mLock);
+    }
+
     cancelPlayerEvents();
 
     mVideoRenderer.clear();
@@ -285,14 +289,17 @@
     mSeekTimeUs = 0;
 
     mPrefetcher.clear();
+
+    mUri.setTo("");
+    mUriHeaders.clear();
 }
 
-void AwesomePlayer::notifyListener_l(int msg, int ext1) {
+void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
     if (mListener != NULL) {
         sp<MediaPlayerBase> listener = mListener.promote();
 
         if (listener != NULL) {
-            listener->sendEvent(msg, ext1);
+            listener->sendEvent(msg, ext1, ext2);
         }
     }
 }
@@ -345,6 +352,14 @@
         return OK;
     }
 
+    if (!(mFlags & PREPARED)) {
+        status_t err = prepare_l();
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
     mFlags |= PLAYING;
     mFlags |= FIRST_FRAME;
 
@@ -623,18 +638,7 @@
     return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
 }
 
-void AwesomePlayer::onEvent(int32_t code) {
-    if (code == 1) {
-        onStreamDone();
-        return;
-    } else if (code == 2) {
-        onBufferingUpdate();
-        return;
-    } else if (code == 3) {
-        onCheckAudioStatus();
-        return;
-    }
-
+void AwesomePlayer::onVideoEvent() {
     Mutex::Autolock autoLock(mLock);
 
     mVideoEventPending = false;
@@ -731,7 +735,7 @@
 
     if (latenessUs > 40000) {
         // We're more than 40ms late.
-        LOGI("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
+        LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
 
         mVideoBuffer->release();
         mVideoBuffer = NULL;
@@ -819,5 +823,131 @@
     postCheckAudioStatusEvent_l();
 }
 
+status_t AwesomePlayer::prepare() {
+    Mutex::Autolock autoLock(mLock);
+    return prepare_l();
+}
+
+status_t AwesomePlayer::prepare_l() {
+    if (mFlags & PREPARED) {
+        return OK;
+    }
+
+    if (mFlags & PREPARING) {
+        return UNKNOWN_ERROR;
+    }
+
+    mIsAsyncPrepare = false;
+    status_t err = prepareAsync_l();
+
+    if (err != OK) {
+        return err;
+    }
+
+    while (mFlags & PREPARING) {
+        mPreparedCondition.wait(mLock);
+    }
+
+    return mPrepareResult;
+}
+
+status_t AwesomePlayer::prepareAsync() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mFlags & PREPARING) {
+        return UNKNOWN_ERROR;  // async prepare already pending
+    }
+
+    mIsAsyncPrepare = true;
+    return prepareAsync_l();
+}
+
+status_t AwesomePlayer::prepareAsync_l() {
+    if (mFlags & PREPARING) {
+        return UNKNOWN_ERROR;  // async prepare already pending
+    }
+
+    mFlags |= PREPARING;
+    mAsyncPrepareEvent = new AwesomeEvent(
+            this, &AwesomePlayer::onPrepareAsyncEvent);
+
+    mQueue.postEvent(mAsyncPrepareEvent);
+
+    return OK;
+}
+
+status_t AwesomePlayer::finishSetDataSource_l() {
+    sp<DataSource> dataSource =
+        DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
+
+    if (dataSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+    if (extractor == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (dataSource->flags() & DataSource::kWantsPrefetching) {
+        mPrefetcher = new Prefetcher;
+    }
+
+    return setDataSource_l(extractor);
+}
+
+void AwesomePlayer::onPrepareAsyncEvent() {
+    {
+        Mutex::Autolock autoLock(mLock);
+
+        if (mUri.size() > 0) {
+            status_t err = finishSetDataSource_l();
+
+            if (err != OK) {
+                if (mIsAsyncPrepare) {
+                    notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+                }
+
+                mPrepareResult = err;
+                mFlags &= ~PREPARING;
+                mAsyncPrepareEvent = NULL;
+                mPreparedCondition.broadcast();
+
+                return;
+            }
+        }
+    }
+
+    sp<Prefetcher> prefetcher;
+
+    {
+        Mutex::Autolock autoLock(mLock);
+        prefetcher = mPrefetcher;
+    }
+
+    if (prefetcher != NULL) {
+        prefetcher->prepare();
+    }
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (mIsAsyncPrepare) {
+        if (mVideoWidth < 0 || mVideoHeight < 0) {
+            notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
+        } else {
+            notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+        }
+
+        notifyListener_l(MEDIA_PREPARED);
+    }
+
+    mPrepareResult = OK;
+    mFlags &= ~PREPARING;
+    mFlags |= PREPARED;
+    mAsyncPrepareEvent = NULL;
+    mPreparedCondition.broadcast();
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index accd94d..79b7674 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -737,6 +737,7 @@
         { kKeyYear, "TYE", "TYER" },
         { kKeyAuthor, "TXT", "TEXT" },
         { kKeyCDTrackNumber, "TRK", "TRCK" },
+        { kKeyDiscNumber, "TPA", "TPOS" },
     };
     static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6274a6c..4458006 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1070,6 +1070,11 @@
             metadataKey = kKeyGenre;
             break;
         }
+        case FOURCC(0xa9, 'g', 'e', 'n'):
+        {
+            metadataKey = kKeyGenre;
+            break;
+        }
         case FOURCC('t', 'r', 'k', 'n'):
         {
             if (size == 16 && flags == 0) {
@@ -1077,11 +1082,22 @@
                 sprintf(tmp, "%d/%d",
                         (int)buffer[size - 5], (int)buffer[size - 3]);
 
-                printf("track: %s\n", tmp);
                 mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
             }
             break;
         }
+        case FOURCC('d', 'i', 's', 'k'):
+        {
+            if (size == 14 && flags == 0) {
+                char tmp[16];
+                sprintf(tmp, "%d/%d",
+                        (int)buffer[size - 3], (int)buffer[size - 1]);
+
+                mFileMetaData->setCString(kKeyDiscNumber, tmp);
+            }
+            break;
+        }
+
         default:
             break;
     }
@@ -1093,11 +1109,25 @@
                     buffer + 8, size - 8);
         } else if (metadataKey == kKeyGenre) {
             if (flags == 0) {
-                // uint8_t
+                // uint8_t genre code, iTunes genre codes are
+                // the standard id3 codes, except they start
+                // at 1 instead of 0 (e.g. Pop is 14, not 13)
+                // We use standard id3 numbering, so subtract 1.
+                int genrecode = (int)buffer[size - 1];
+                genrecode--;
+                if (genrecode < 0) {
+                    genrecode = 255; // reserved for 'unknown genre'
+                }
                 char genre[10];
-                sprintf(genre, "%d", (int)buffer[size - 1]);
+                sprintf(genre, "%d", genrecode);
 
                 mFileMetaData->setCString(metadataKey, genre);
+            } else if (flags == 1) {
+                // custom genre string
+                buffer[size] = '\0';
+
+                mFileMetaData->setCString(
+                        metadataKey, (const char *)buffer + 8);
             }
         } else {
             buffer[size] = '\0';
@@ -1198,7 +1228,7 @@
     CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
 
     if (prevSampleRate != sampleRate) {
-        LOGW("mpeg4 audio sample rate different from previous setting. "
+        LOGV("mpeg4 audio sample rate different from previous setting. "
              "was: %d, now: %d", prevSampleRate, sampleRate);
     }
 
@@ -1208,7 +1238,7 @@
     CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount));
 
     if (prevChannelCount != numChannels) {
-        LOGW("mpeg4 audio channel count different from previous setting. "
+        LOGV("mpeg4 audio channel count different from previous setting. "
              "was: %d, now: %d", prevChannelCount, numChannels);
     }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 0355a82..c6c6f21 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -139,6 +139,7 @@
 
 #define CODEC_LOGI(x, ...) LOGI("[%s] "x, mComponentName, ##__VA_ARGS__)
 #define CODEC_LOGV(x, ...) LOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
+#define CODEC_LOGE(x, ...) LOGE("[%s] "x, mComponentName, ##__VA_ARGS__)
 
 struct OMXCodecObserver : public BnOMXObserver {
     OMXCodecObserver() {
@@ -1284,7 +1285,8 @@
                 CHECK_EQ(err, OK);
 
                 buffers->removeAt(i);
-            } else if (mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
+            } else if (mState != ERROR
+                    && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
                 CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
                 drainInputBuffer(&buffers->editItemAt(i));
             }
@@ -1930,10 +1932,17 @@
         srcLength = srcBuffer->range_length();
 
         if (info->mSize < srcLength) {
-            LOGE("info->mSize = %d, srcLength = %d",
+            CODEC_LOGE(
+                 "Codec's input buffers are too small to accomodate "
+                 "buffer read from source (info->mSize = %d, srcLength = %d)",
                  info->mSize, srcLength);
+
+            srcBuffer->release();
+            srcBuffer = NULL;
+
+            setState(ERROR);
+            return;
         }
-        CHECK(info->mSize >= srcLength);
         memcpy(info->mData,
                (const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(),
                srcLength);
@@ -2250,7 +2259,7 @@
 }
 
 status_t OMXCodec::stop() {
-    CODEC_LOGV("stop");
+    CODEC_LOGV("stop mState=%d", mState);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -2309,6 +2318,8 @@
 
     mSource->stop();
 
+    CODEC_LOGV("stopped");
+
     return OK;
 }
 
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
index 862998a..835e167 100644
--- a/media/libstagefright/Prefetcher.cpp
+++ b/media/libstagefright/Prefetcher.cpp
@@ -171,7 +171,7 @@
     }
 }
 
-int64_t Prefetcher::getCachedDurationUs() {
+int64_t Prefetcher::getCachedDurationUs(bool *noMoreData) {
     Mutex::Autolock autoLock(mLock);
 
     int64_t minCacheDurationUs = -1;
@@ -197,9 +197,25 @@
         }
     }
 
+    if (noMoreData) {
+        *noMoreData = minCacheDurationUs < 0;
+    }
+
     return minCacheDurationUs < 0 ? 0 : minCacheDurationUs;
 }
 
+status_t Prefetcher::prepare() {
+    // Buffer about 2 secs worth of data on prepare.
+
+    int64_t duration;
+    bool noMoreData;
+    do {
+        duration = getCachedDurationUs(&noMoreData);
+    } while (!noMoreData && duration < 2000000);
+
+    return OK;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 PrefetchedSource::PrefetchedSource(
@@ -232,15 +248,6 @@
 
     mStarted = true;
 
-    for (;;) {
-        // Buffer 2 secs on startup.
-        if (mReachedEOS || mCacheDurationUs > 2000000) {
-            break;
-        }
-
-        mCondition.wait(mLock);
-    }
-
     return OK;
 }
 
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 3451383..22f701c 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -191,6 +191,7 @@
         };
         static const KeyMap kKeyMap[] = {
             { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER },
+            { "discnumber", METADATA_KEY_DISC_NUMBER },
             { "album", METADATA_KEY_ALBUM },
             { "artist", METADATA_KEY_ARTIST },
             { "composer", METADATA_KEY_COMPOSER },
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index dfba74f..f617fe8 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -305,6 +305,7 @@
     static const Map kMap[] = {
         { kKeyMIMEType, METADATA_KEY_MIMETYPE },
         { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER },
+        { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER },
         { kKeyAlbum, METADATA_KEY_ALBUM },
         { kKeyArtist, METADATA_KEY_ARTIST },
         { kKeyAuthor, METADATA_KEY_AUTHOR },
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 6d64717..16a2a10 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -24,6 +24,7 @@
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/Utils.h>
 #include <utils/String8.h>
+#include <byteswap.h>
 
 namespace android {
 
@@ -273,7 +274,9 @@
         String8 *s) {
     size_t utf8len = 0;
     for (size_t i = 0; i < size; ++i) {
-        if (data[i] < 0x80) {
+        if (data[i] == '\0') {
+            break;
+        } else if (data[i] < 0x80) {
             ++utf8len;
         } else {
             utf8len += 2;
@@ -290,7 +293,9 @@
     char *tmp = new char[utf8len];
     char *ptr = tmp;
     for (size_t i = 0; i < size; ++i) {
-        if (data[i] < 0x80) {
+        if (data[i] == '\0') {
+            break;
+        } else if (data[i] < 0x80) {
             *ptr++ = data[i];
         } else if (data[i] < 0xc0) {
             *ptr++ = 0xc2;
@@ -324,7 +329,7 @@
             return;
         }
 
-        id->setTo((const char *)mFrameData, mFrameSize);
+        convertISO8859ToString8(mFrameData, mFrameSize, id);
         return;
     }
 
@@ -336,7 +341,26 @@
     } else {
         // UCS-2
         // API wants number of characters, not number of bytes...
-        id->setTo((const char16_t *)(mFrameData + 1), n / 2);
+        int len = n / 2;
+        const char16_t *framedata = (const char16_t *) (mFrameData + 1);
+        char16_t *framedatacopy = NULL;
+        if (*framedata == 0xfffe) {
+            // endianness marker doesn't match host endianness, convert
+            framedatacopy = new char16_t[len];
+            for (int i = 0; i < len; i++) {
+                framedatacopy[i] = bswap_16(framedata[i]);
+            }
+            framedata = framedatacopy;
+        }
+        // If the string starts with an endianness marker, skip it
+        if (*framedata == 0xfeff) {
+            framedata++;
+            len--;
+        }
+        id->setTo(framedata, len);
+        if (framedatacopy != NULL) {
+            delete[] framedatacopy;
+        }
     }
 }
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 75e71e6..a19784b 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -57,6 +57,11 @@
 
     void reset();
 
+    status_t prepare();
+    status_t prepare_l();
+    status_t prepareAsync();
+    status_t prepareAsync_l();
+
     status_t play();
     status_t pause();
 
@@ -80,6 +85,8 @@
         PLAYING     = 1,
         LOOPING     = 2,
         FIRST_FRAME = 4,
+        PREPARING   = 8,
+        PREPARED    = 16,
     };
 
     mutable Mutex mLock;
@@ -93,6 +100,9 @@
 
     TimeSource *mTimeSource;
 
+    String8 mUri;
+    KeyedVector<String8, String8> mUriHeaders;
+
     sp<MediaSource> mVideoSource;
     sp<AwesomeRenderer> mVideoRenderer;
 
@@ -121,6 +131,11 @@
     sp<TimedEventQueue::Event> mCheckAudioStatusEvent;
     bool mAudioStatusEventPending;
 
+    sp<TimedEventQueue::Event> mAsyncPrepareEvent;
+    Condition mPreparedCondition;
+    bool mIsAsyncPrepare;
+    status_t mPrepareResult;
+
     void postVideoEvent_l(int64_t delayUs = -1);
     void postBufferingEvent_l();
     void postStreamDoneEvent_l();
@@ -143,13 +158,15 @@
     status_t setAudioSource(sp<MediaSource> source);
     status_t setVideoSource(sp<MediaSource> source);
 
-    void onEvent(int32_t code);
     void onStreamDone();
 
-    void notifyListener_l(int msg, int ext1 = 0);
+    void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0);
 
+    void onVideoEvent();
     void onBufferingUpdate();
     void onCheckAudioStatus();
+    void onPrepareAsyncEvent();
+    status_t finishSetDataSource_l();
 
     AwesomePlayer(const AwesomePlayer &);
     AwesomePlayer &operator=(const AwesomePlayer &);
diff --git a/media/libstagefright/include/Prefetcher.h b/media/libstagefright/include/Prefetcher.h
index 7a97785..d227864 100644
--- a/media/libstagefright/include/Prefetcher.h
+++ b/media/libstagefright/include/Prefetcher.h
@@ -34,7 +34,9 @@
     // that will benefit from prefetching/caching the original one.
     sp<MediaSource> addSource(const sp<MediaSource> &source);
 
-    int64_t getCachedDurationUs();
+    int64_t getCachedDurationUs(bool *noMoreData = NULL);
+
+    status_t prepare();
 
 protected:
     virtual ~Prefetcher();
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 70c6a58..2458d2a 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -129,11 +129,16 @@
             sendCommand(OMX_CommandStateSet, OMX_StateIdle);
             OMX_ERRORTYPE err;
             while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone
-                   && state != OMX_StateIdle) {
+                   && state != OMX_StateIdle
+                   && state != OMX_StateInvalid) {
                 usleep(100000);
             }
             CHECK_EQ(err, OMX_ErrorNone);
 
+            if (state == OMX_StateInvalid) {
+                break;
+            }
+
             // fall through
         }
 
@@ -146,7 +151,8 @@
 
             OMX_ERRORTYPE err;
             while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone
-                   && state != OMX_StateLoaded) {
+                   && state != OMX_StateLoaded
+                   && state != OMX_StateInvalid) {
                 LOGV("waiting for Loaded state...");
                 usleep(100000);
             }
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index 3d72017..5ec72df 100755
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -5,9 +5,9 @@
     <uses-permission android:name="android.permission.ASEC_CREATE"/>
     <uses-permission android:name="android.permission.ASEC_DESTROY"/>
     <uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
-    <application android:process="def.container.service"
-                 android:label="@string/service_name">
+    <application android:label="@string/service_name">
 
         <service android:name=".DefaultContainerService"
                  android:enabled="true"
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index d23b7d0..c418ccb 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -3,15 +3,19 @@
 import com.android.internal.app.IMediaContainerService;
 
 import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Debug;
+import android.os.Environment;
 import android.os.IBinder;
-import android.os.IMountService;
-import android.os.MountServiceResultCode;
+import android.os.storage.IMountService;
+import android.os.storage.StorageResultCode;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.app.IntentService;
 import android.app.Service;
 import android.util.Log;
 
@@ -25,7 +29,6 @@
 
 import android.os.FileUtils;
 
-
 /*
  * This service copies a downloaded apk to a file passed in as
  * a ParcelFileDescriptor or to a newly created container specified
@@ -33,7 +36,7 @@
  * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER
  * permission to access apks downloaded via the download manager.
  */
-public class DefaultContainerService extends Service {
+public class DefaultContainerService extends IntentService {
     private static final String TAG = "DefContainer";
     private static final boolean localLOGV = false;
 
@@ -78,6 +81,40 @@
         }
     };
 
+    public DefaultContainerService() {
+        super("DefaultContainerService");
+        setIntentRedelivery(true);
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {
+            IPackageManager pm = IPackageManager.Stub.asInterface(
+                    ServiceManager.getService("package"));
+            String pkg = null;
+            try {
+                while ((pkg=pm.nextPackageToClean(pkg)) != null) {
+                    eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg));
+                    eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    void eraseFiles(File path) {
+        if (path.isDirectory()) {
+            String[] files = path.list();
+            if (files != null) {
+                for (String file : files) {
+                    eraseFiles(new File(path, file));
+                }
+            }
+        }
+        //Log.i(TAG, "Deleting: " + path);
+        path.delete();
+    }
+    
     public IBinder onBind(Intent intent) {
         return mBinder;
     }
@@ -158,12 +195,12 @@
             int rc = mountService.createSecureContainer(
                     containerId, mbLen, "vfat", sdEncKey, ownerUid);
 
-            if (rc != MountServiceResultCode.OperationSucceeded) {
+            if (rc != StorageResultCode.OperationSucceeded) {
                 Log.e(TAG, String.format("Container creation failed (%d)", rc));
 
                 // XXX: This destroy should not be necessary
                 rc = mountService.destroySecureContainer(containerId);
-                if (rc != MountServiceResultCode.OperationSucceeded) {
+                if (rc != StorageResultCode.OperationSucceeded) {
                     Log.e(TAG, String.format("Container creation-cleanup failed (%d)", rc));
                     return null;
                 }
@@ -171,7 +208,7 @@
                 // XXX: Does this ever actually succeed?
                 rc = mountService.createSecureContainer(
                         containerId, mbLen, "vfat", sdEncKey, ownerUid);
-                if (rc != MountServiceResultCode.OperationSucceeded) {
+                if (rc != StorageResultCode.OperationSucceeded) {
                     Log.e(TAG, String.format("Container creation retry failed (%d)", rc));
                 }
             }
@@ -226,7 +263,7 @@
     private String mountSdDir(String containerId, String key) {
         try {
             int rc = getMountService().mountSecureContainer(containerId, key, Process.myUid());
-            if (rc == MountServiceResultCode.OperationSucceeded) {
+            if (rc == StorageResultCode.OperationSucceeded) {
                 return getMountService().getSecureContainerPath(containerId);
             } else {
                 Log.e(TAG, String.format("Failed to mount id %s with rc %d ", containerId, rc));
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index b7eea2e..bca736a2e 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -133,6 +133,7 @@
     private HashMap<String, ITtsCallback> mCallbacksMap;
 
     private Boolean mIsSpeaking;
+    private Boolean mSynthBusy;
     private ArrayList<SpeechItem> mSpeechQueue;
     private HashMap<String, SoundResource> mEarcons;
     private HashMap<String, SoundResource> mUtterances;
@@ -168,6 +169,7 @@
 
         mSelf = this;
         mIsSpeaking = false;
+        mSynthBusy = false;
 
         mEarcons = new HashMap<String, SoundResource>();
         mUtterances = new HashMap<String, SoundResource>();
@@ -733,10 +735,11 @@
                 try {
                     synthAvailable = synthesizerLock.tryLock();
                     if (!synthAvailable) {
+                        mSynthBusy = true;
                         Thread.sleep(100);
                         Thread synth = (new Thread(new SynthThread()));
-                        //synth.setPriority(Thread.MIN_PRIORITY);
                         synth.start();
+                        mSynthBusy = false;
                         return;
                     }
                     int streamType = DEFAULT_STREAM_TYPE;
@@ -821,10 +824,11 @@
                 try {
                     synthAvailable = synthesizerLock.tryLock();
                     if (!synthAvailable) {
+                        mSynthBusy = true;
                         Thread.sleep(100);
                         Thread synth = (new Thread(new SynthThread()));
-                        //synth.setPriority(Thread.MIN_PRIORITY);
                         synth.start();
+                        mSynthBusy = false;
                         return;
                     }
                     String language = "";
@@ -959,6 +963,12 @@
 
     private void processSpeechQueue() {
         boolean speechQueueAvailable = false;
+        synchronized (this) {
+            if (mSynthBusy){
+                // There is already a synth thread waiting to run.
+                return;
+            }
+        }
         try {
             speechQueueAvailable =
                     speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index b56b13b..407983d 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -176,8 +176,8 @@
         context.registerReceiver(broadcastReceiver, packageFilter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(broadcastReceiver, sdFilter);
 
         // boot completed
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index c28cf44..44cc0bb 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -782,7 +782,7 @@
             mContext.registerReceiver(this, filter);
              // Register for events related to sdcard installation.
             IntentFilter sdFilter = new IntentFilter();
-            sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
             mContext.registerReceiver(this, sdFilter);
         }
         
@@ -791,7 +791,7 @@
             synchronized (mLock) {
                 String action = intent.getAction();
                 String pkgList[] = null;
-                if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 } else {
                     Uri data = intent.getData();
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 684f117..f336d1f 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -147,8 +147,8 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
     }
 
@@ -1077,10 +1077,10 @@
             } else {
                 boolean added = false;
                 String pkgList[] = null;
-                if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) {
+                if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     added = true;
-                } if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     added = false;
                 } else  {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2f845e1..27055ed 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -76,6 +76,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
+import java.util.Set;
 
 class BackupManagerService extends IBackupManager.Stub {
     private static final String TAG = "BackupManagerService";
@@ -226,7 +227,9 @@
     private File mEverStored;
     HashSet<String> mEverStoredApps = new HashSet<String>();
 
+    static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;  // increment when the schema changes
     File mTokenFile;
+    Set<String> mAncestralPackages = null;
     long mAncestralToken = 0;
     long mCurrentToken = 0;
 
@@ -500,8 +503,20 @@
         mTokenFile = new File(mBaseStateDir, "ancestral");
         try {
             RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
-            mAncestralToken = tf.readLong();
-            mCurrentToken = tf.readLong();
+            int version = tf.readInt();
+            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
+                mAncestralToken = tf.readLong();
+                mCurrentToken = tf.readLong();
+
+                int numPackages = tf.readInt();
+                if (numPackages >= 0) {
+                    mAncestralPackages = new HashSet<String>();
+                    for (int i = 0; i < numPackages; i++) {
+                        String pkgName = tf.readUTF();
+                        mAncestralPackages.add(pkgName);
+                    }
+                }
+            }
         } catch (IOException e) {
             Log.w(TAG, "Unable to read token file", e);
         }
@@ -565,8 +580,8 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
     }
 
@@ -724,10 +739,10 @@
                 }
                 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
                 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
-            } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) {
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                 added = true;
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            } else if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 added = false;
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
@@ -972,12 +987,31 @@
         }
     }
 
-    // Record the current and ancestral backup tokens persistently
+    // Persistently record the current and ancestral backup tokens as well
+    // as the set of packages with data [supposedly] available in the
+    // ancestral dataset.
     void writeRestoreTokens() {
         try {
             RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
+
+            // First, the version number of this record, for futureproofing
+            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
+
+            // Write the ancestral and current tokens
             af.writeLong(mAncestralToken);
             af.writeLong(mCurrentToken);
+
+            // Now write the set of ancestral packages
+            if (mAncestralPackages == null) {
+                af.writeInt(-1);
+            } else {
+                af.writeInt(mAncestralPackages.size());
+                if (DEBUG) Log.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
+                for (String pkgName : mAncestralPackages) {
+                    af.writeUTF(pkgName);
+                    if (DEBUG) Log.v(TAG, "   " + pkgName);
+                }
+            }
             af.close();
         } catch (IOException e) {
             Log.w(TAG, "Unable to write token file:", e);
@@ -1502,6 +1536,7 @@
              * the user is waiting, after all.
              */
 
+            PackageManagerBackupAgent pmAgent = null;
             int error = -1; // assume error
 
             // build the set of apps to restore
@@ -1562,7 +1597,7 @@
                 }
 
                 // Pull the Package Manager metadata from the restore set first
-                PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                pmAgent = new PackageManagerBackupAgent(
                         mPackageManager, agentPackages);
                 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
 
@@ -1705,8 +1740,10 @@
                 }
 
                 // If this was a restoreAll operation, record that this was our
-                // ancestral dataset
-                if (mTargetPackage == null) {
+                // ancestral dataset, as well as the set of apps that are possibly
+                // restoreable from the dataset
+                if (mTargetPackage == null && pmAgent != null) {
+                    mAncestralPackages = pmAgent.getRestoredPackages();
                     mAncestralToken = mToken;
                     writeRestoreTokens();
                 }
@@ -2436,6 +2473,12 @@
                 }
             }
 
+            pw.println("Ancestral packages: "
+                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
+            for (String pkg : mAncestralPackages) {
+                pw.println("    " + pkg);
+            }
+
             pw.println("Ever backed up: " + mEverStoredApps.size());
             for (String pkg : mEverStoredApps) {
                 pw.println("    " + pkg);
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index e4ee4ae..1625d9f 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -320,14 +320,18 @@
                 }
                 tag = parser.getName();
                 if ("admin".equals(tag)) {
-                    DeviceAdminInfo dai = findAdmin(
-                            ComponentName.unflattenFromString(
-                                    parser.getAttributeValue(null, "name")));
-                    if (dai != null) {
-                        ActiveAdmin ap = new ActiveAdmin(dai);
-                        ap.readFromXml(parser);
-                        mAdminMap.put(ap.info.getComponent(), ap);
-                        mAdminList.add(ap);
+                    String name = parser.getAttributeValue(null, "name");
+                    try {
+                        DeviceAdminInfo dai = findAdmin(
+                                ComponentName.unflattenFromString(name));
+                        if (dai != null) {
+                            ActiveAdmin ap = new ActiveAdmin(dai);
+                            ap.readFromXml(parser);
+                            mAdminMap.put(ap.info.getComponent(), ap);
+                            mAdminList.add(ap);
+                        }
+                    } catch (RuntimeException e) {
+                        Log.w(TAG, "Failed loading admin " + name, e);
                     }
                 } else if ("failed-password-attempts".equals(tag)) {
                     mFailedPasswordAttempts = Integer.parseInt(
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index dc942a2..5e96a2f 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -344,8 +344,8 @@
                 if (pkg != null) {
                     pkgList = new String[] { pkg };
                 }
-            } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) ||
-                    Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
+                    Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
             if (pkgList == null || pkgList.length == 0) {
@@ -448,8 +448,8 @@
         mContext.registerReceiver(mBroadcastReceiver, packageFilt);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
 
         IntentFilter screenOnOffFilt = new IntentFilter();
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 3a42b37..fff6c54 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -29,16 +29,18 @@
 
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentQueryMap;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.location.Address;
 import android.location.GeocoderParams;
-import android.location.IGeocodeProvider;
 import android.location.IGpsStatusListener;
 import android.location.IGpsStatusProvider;
 import android.location.ILocationListener;
@@ -64,10 +66,11 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 
+import com.android.internal.location.GeocoderProxy;
 import com.android.internal.location.GpsLocationProvider;
+import com.android.internal.location.GpsNetInitiatedHandler;
 import com.android.internal.location.LocationProviderProxy;
 import com.android.internal.location.MockProvider;
-import com.android.internal.location.GpsNetInitiatedHandler;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -105,7 +108,7 @@
     private static boolean sProvidersLoaded = false;
 
     private final Context mContext;
-    private IGeocodeProvider mGeocodeProvider;
+    private GeocoderProxy mGeocodeProvider;
     private IGpsStatusProvider mGpsStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
     private LocationWorkerHandler mLocationHandler;
@@ -415,7 +418,6 @@
 
     private void removeProvider(LocationProviderProxy provider) {
         mProviders.remove(provider);
-        provider.unlinkProvider();
         mProvidersByName.remove(provider.getName());
     }
 
@@ -446,11 +448,28 @@
             GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
             mGpsStatusProvider = provider.getGpsStatusProvider();
             mNetInitiatedListener = provider.getNetInitiatedListener();
-            LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
+            LocationProviderProxy proxy =
+                    new LocationProviderProxy(mContext, LocationManager.GPS_PROVIDER, provider);
             addProvider(proxy);
             mGpsLocationProvider = proxy;
         }
 
+        // initialize external network location and geocoder services
+        Resources resources = mContext.getResources();
+        String serviceName = resources.getString(
+                com.android.internal.R.string.config_networkLocationProvider);
+        if (serviceName != null) {
+            mNetworkLocationProvider =
+                new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
+                        serviceName, mLocationHandler);
+            addProvider(mNetworkLocationProvider);
+        }
+
+        serviceName = resources.getString(com.android.internal.R.string.config_geocodeProvider);
+        if (serviceName != null) {
+            mGeocodeProvider = new GeocoderProxy(mContext, serviceName);
+        }
+
         updateProvidersLocked();
     }
 
@@ -484,7 +503,7 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         mContext.registerReceiver(mBroadcastReceiver, intentFilter);
-        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
 
         // listen for settings changes
@@ -507,45 +526,6 @@
         Looper.loop();
     }
 
-    public void installLocationProvider(String name, ILocationProvider provider) {
-        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
-        }
-
-        synchronized (mLock) {
-            // check to see if we are reinstalling a dead provider
-            LocationProviderProxy oldProvider = mProvidersByName.get(name);
-            if (oldProvider != null) {
-                if (oldProvider.isDead()) {
-                    Log.d(TAG, "replacing dead provider");
-                    removeProvider(oldProvider);
-                } else {
-                    throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
-                }
-            }
-
-            LocationProviderProxy proxy = new LocationProviderProxy(name, provider);
-            addProvider(proxy);
-            updateProvidersLocked();
-            if (LocationManager.NETWORK_PROVIDER.equals(name)) {
-                mNetworkLocationProvider = proxy;
-            }
-
-            // notify provider of current network state
-            proxy.updateNetworkState(mNetworkState, null);
-        }
-    }
-
-    public void installGeocodeProvider(IGeocodeProvider provider) {
-        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
-        }
-
-        mGeocodeProvider = provider;
-    }
-
     private boolean isAllowedBySettingsLocked(String provider) {
         if (mEnabledProviders.contains(provider)) {
             return true;
@@ -1313,7 +1293,7 @@
      }
 
     /**
-     * @return null if the provider does not exits
+     * @return null if the provider does not exist
      * @throws SecurityException if the provider is not allowed to be
      * accessed by the caller
      */
@@ -1332,7 +1312,7 @@
 
     private Bundle _getProviderInfoLocked(String provider) {
         LocationProviderProxy p = mProvidersByName.get(provider);
-        if (p == null) {
+        if (p == null || !p.isEnabled()) {
             return null;
         }
 
@@ -1552,10 +1532,10 @@
 
             if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
-                    || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                 synchronized (mLock) {
                     int uidList[] = null;
-                    if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                    if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                         uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                     } else {
                         uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)};
@@ -1618,7 +1598,7 @@
                 synchronized (mLock) {
                     for (int i = mProviders.size() - 1; i >= 0; i--) {
                         LocationProviderProxy provider = mProviders.get(i);
-                        if (provider.requiresNetwork()) {
+                        if (provider.isEnabled() && provider.requiresNetwork()) {
                             provider.updateNetworkState(mNetworkState, info);
                         }
                     }
@@ -1669,13 +1649,8 @@
     public String getFromLocation(double latitude, double longitude, int maxResults,
             GeocoderParams params, List<Address> addrs) {
         if (mGeocodeProvider != null) {
-            try {
-                return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
-                        params, addrs);
-            } catch (RemoteException e) {
-                Log.e(TAG, "getFromLocation failed", e);
-                mGeocodeProvider = null;
-            }
+            return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
+                    params, addrs);
         }
         return null;
     }
@@ -1687,14 +1662,9 @@
             GeocoderParams params, List<Address> addrs) {
 
         if (mGeocodeProvider != null) {
-            try {
-                return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
-                        lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
-                        maxResults, params, addrs);
-            } catch (RemoteException e) {
-                Log.e(TAG, "getFromLocationName failed", e);
-                mGeocodeProvider = null;
-            }
+            return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
+                    lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+                    maxResults, params, addrs);
         }
         return null;
     }
@@ -1737,7 +1707,7 @@
             if (mProvidersByName.get(name) != null) {
                 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
             }
-            addProvider(new LocationProviderProxy(name, provider));
+            addProvider(new LocationProviderProxy(mContext, name, provider));
             mMockProviders.put(name, provider);
             mLastKnownLocation.put(name, null);
             updateProvidersLocked();
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 6382646..8d45033 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -23,13 +23,14 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.Uri;
-import android.os.IMountService;
-import android.os.IMountServiceListener;
-import android.os.MountServiceResultCode;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.os.storage.StorageResultCode;
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.Environment;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
 import android.os.Handler;
@@ -41,11 +42,14 @@
 import java.io.FileReader;
 
 /**
- * MountService implements an to the mount service daemon
- * @hide
+ * MountService implements back-end services for platform storage
+ * management.
+ * @hide - Applications should use android.os.storage.StorageManager
+ * to access the MountService.
  */
 class MountService extends IMountService.Stub
         implements INativeDaemonConnectorCallbacks {
+    private static final boolean LOCAL_LOGD = false;
     
     private static final String TAG = "MountService";
 
@@ -109,45 +113,35 @@
     private PackageManagerService                 mPms;
     private boolean                               mUmsEnabling;
     private ArrayList<MountServiceBinderListener> mListeners;
+    private boolean                               mBooted;
+    private boolean                               mReady;
 
-    /**
-     * Constructs a new MountService instance
-     * 
-     * @param context  Binder context for this service
-     */
-    public MountService(Context context) {
-        mContext = context;
-
-        // XXX: This will go away soon in favor of IMountServiceObserver
-        mPms = (PackageManagerService) ServiceManager.getService("package");
-
-        // Register a BOOT_COMPLETED handler so that we can start
-        // our NativeDaemonConnector. We defer the startup so that we don't
-        // start processing events before we ought-to
-        mContext.registerReceiver(mBroadcastReceiver,
-                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
-
-        mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
-        mListeners = new ArrayList<MountServiceBinderListener>();
+    private void waitForReady() {
+        while (mReady == false) {
+            for (int retries = 5; retries > 0; retries--) {
+                if (mReady) {
+                    return;
+                }
+                SystemClock.sleep(1000);
+            }
+            Log.w(TAG, "Waiting too long for mReady!");
+        }
     }
   
-    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
             if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
-                /*
-                 * Vold does not run in the simulator, so fake out a mounted
-                 * event to trigger MediaScanner
-                 */
-                if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
-                    updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED);
-                    return;
-                }
+                mBooted = true;
 
-                Thread thread = new Thread(
-                        mConnector, NativeDaemonConnector.class.getName());
-                thread.start();
+                String path = Environment.getExternalStorageDirectory().getPath();
+                if (getVolumeState(path).equals(Environment.MEDIA_UNMOUNTED)) {
+                    int rc = doMountVolume(path);
+                    if (rc != StorageResultCode.OperationSucceeded) {
+                        Log.e(TAG, String.format("Boot-time mount failed (%d)", rc));
+                    }
+                }
             }
         }
     };
@@ -161,7 +155,7 @@
         }
 
         public void binderDied() {
-            Log.d(TAG, "An IMountServiceListener has died!");
+            if (LOCAL_LOGD) Log.d(TAG, "An IMountServiceListener has died!");
             synchronized(mListeners) {
                 mListeners.remove(this);
                 mListener.asBinder().unlinkToDeath(this, 0);
@@ -169,7 +163,7 @@
         }
     }
 
-    int doShareUnshareVolume(String path, String method, boolean enable) {
+    private int doShareUnshareVolume(String path, String method, boolean enable) {
         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
         // TODO: Add support for multiple share methods
@@ -182,9 +176,13 @@
          */
         String vs = getVolumeState(path);
         if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
-            mUmsEnabling = enable; // Supress unmounted events
-            unmountVolume(path);
-            mUmsEnabling = false; // Unsupress unmounted events
+            mUmsEnabling = enable; // Override for isUsbMassStorageEnabled()
+            int rc = doUnmountVolume(path);
+            mUmsEnabling = false; // Clear override
+            if (rc != StorageResultCode.OperationSucceeded) {
+                Log.e(TAG, String.format("Failed to unmount before enabling UMS (%d)", rc));
+                return rc;
+            }
         }
 
         try {
@@ -192,14 +190,14 @@
                     "volume %sshare %s %s", (enable ? "" : "un"), path, method));
         } catch (NativeDaemonConnectorException e) {
             Log.e(TAG, "Failed to share/unshare", e);
-            return MountServiceResultCode.OperationFailedInternalError;
+            return StorageResultCode.OperationFailedInternalError;
         }
 
         /*
          * If we disabled UMS then mount the volume
          */
         if (!enable) {
-            if (mountVolume(path) != MountServiceResultCode.OperationSucceeded) {
+            if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
                 Log.e(TAG, String.format(
                         "Failed to remount %s after disabling share method %s", path, method));
                 /*
@@ -210,15 +208,19 @@
             }
         }
 
-        return MountServiceResultCode.OperationSucceeded;
+        return StorageResultCode.OperationSucceeded;
     }
 
-    void updatePublicVolumeState(String path, String state) {
+    private void updatePublicVolumeState(String path, String state) {
         if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
             Log.w(TAG, "Multiple volumes not currently supported");
             return;
         }
-        Log.i(TAG, "State for {" + path + "} = {" + state + "}");
+
+        if (mLegacyState.equals(state)) {
+            Log.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
+            return;
+        }
 
         String oldState = mLegacyState;
         mLegacyState = state;
@@ -227,7 +229,7 @@
             for (int i = mListeners.size() -1; i >= 0; i--) {
                 MountServiceBinderListener bl = mListeners.get(i);
                 try {
-                    bl.mListener.onVolumeStateChanged("", path, oldState, state);
+                    bl.mListener.onStorageStateChanged(path, oldState, state);
                 } catch (RemoteException rex) {
                     Log.e(TAG, "Listener dead");
                     mListeners.remove(i);
@@ -270,11 +272,7 @@
                         if (st == VolumeState.NoMedia) {
                             state = Environment.MEDIA_REMOVED;
                         } else if (st == VolumeState.Idle) {
-                            state = null;
-                            int rc = mountVolume(path);
-                            if (rc != MountServiceResultCode.OperationSucceeded) {
-                                Log.e(TAG, String.format("Connection-mount failed (%d)", rc));
-                            }
+                            state = Environment.MEDIA_UNMOUNTED;
                         } else if (st == VolumeState.Mounted) {
                             state = Environment.MEDIA_MOUNTED;
                             Log.i(TAG, "Media already mounted on daemon connection");
@@ -294,23 +292,26 @@
                 }
 
                 try {
-                    boolean avail = getShareMethodAvailable("ums");
+                    boolean avail = doGetShareMethodAvailable("ums");
                     notifyShareAvailabilityChange("ums", avail);
                 } catch (Exception ex) {
                     Log.w(TAG, "Failed to get share availability");
                 }
+                /*
+                 * Now that we've done our initialization, release 
+                 * the hounds!
+                 */
+                mReady = true;
             }
         }.start();
     }
 
     /**
-     *
      * Callback from NativeDaemonConnector
      */
     public boolean onEvent(int code, String raw, String[] cooked) {
         Intent in = null;
 
-        // Log.d(TAG, "event {" + raw + "}");
         if (code == VoldResponseCode.VolumeStateChange) {
             /*
              * One of the volumes we're managing has changed state.
@@ -347,34 +348,12 @@
                 Log.e(TAG, "Failed to parse major/minor", ex);
             }
 
-            synchronized (mListeners) {
-                for (int i = mListeners.size() -1; i >= 0; i--) {
-                    MountServiceBinderListener bl = mListeners.get(i);
-                    try {
-                        if (code == VoldResponseCode.VolumeDiskInserted) {
-                            bl.mListener.onMediaInserted(label, path, major, minor);
-                        } else if (code == VoldResponseCode.VolumeDiskRemoved) {
-                            bl.mListener.onMediaRemoved(label, path, major, minor, true);
-                        } else if (code == VoldResponseCode.VolumeBadRemoval) {
-                            bl.mListener.onMediaRemoved(label, path, major, minor, false);
-                        } else {
-                            Log.e(TAG, String.format("Unknown code {%d}", code));
-                        }
-                    } catch (RemoteException rex) {
-                        Log.e(TAG, "Listener dead");
-                        mListeners.remove(i);
-                    } catch (Exception ex) {
-                        Log.e(TAG, "Listener failed", ex);
-                    }
-                }
-            }
-
             if (code == VoldResponseCode.VolumeDiskInserted) {
                 new Thread() {
                     public void run() {
                         try {
                             int rc;
-                            if ((rc = mountVolume(path)) != MountServiceResultCode.OperationSucceeded) {
+                            if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
                                 Log.w(TAG, String.format("Insertion mount failed (%d)", rc));
                             }
                         } catch (Exception ex) {
@@ -417,7 +396,7 @@
        return true;
     }
 
-    void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
+    private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
         String vs = getVolumeState(path);
 
         Intent in = null;
@@ -471,7 +450,144 @@
         }
     }
 
-    void notifyShareAvailabilityChange(String method, final boolean avail) {
+    private boolean doGetShareMethodAvailable(String method) {
+        ArrayList<String> rsp = mConnector.doCommand("share status " + method);
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code;
+            try {
+                code = Integer.parseInt(tok[0]);
+            } catch (NumberFormatException nfe) {
+                Log.e(TAG, String.format("Error parsing code %s", tok[0]));
+                return false;
+            }
+            if (code == VoldResponseCode.ShareStatusResult) {
+                if (tok[2].equals("available"))
+                    return true;
+                return false;
+            } else {
+                Log.e(TAG, String.format("Unexpected response code %d", code));
+                return false;
+            }
+        }
+        Log.e(TAG, "Got an empty response");
+        return false;
+    }
+
+    private int doMountVolume(String path) {
+        int rc = StorageResultCode.OperationSucceeded;
+
+        try {
+            mConnector.doCommand(String.format("volume mount %s", path));
+        } catch (NativeDaemonConnectorException e) {
+            /*
+             * Mount failed for some reason
+             */
+            Intent in = null;
+            int code = e.getCode();
+            if (code == VoldResponseCode.OpFailedNoMedia) {
+                /*
+                 * Attempt to mount but no media inserted
+                 */
+                rc = StorageResultCode.OperationFailedNoMedia;
+            } else if (code == VoldResponseCode.OpFailedMediaBlank) {
+                /*
+                 * Media is blank or does not contain a supported filesystem
+                 */
+                updatePublicVolumeState(path, Environment.MEDIA_NOFS);
+                in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
+                rc = StorageResultCode.OperationFailedMediaBlank;
+            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
+                /*
+                 * Volume consistency check failed
+                 */
+                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
+                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
+                rc = StorageResultCode.OperationFailedMediaCorrupt;
+            } else {
+                rc = StorageResultCode.OperationFailedInternalError;
+            }
+
+            /*
+             * Send broadcast intent (if required for the failure)
+             */
+            if (in != null) {
+                mContext.sendBroadcast(in);
+            }
+        }
+
+        return rc;
+    }
+
+    private int doUnmountVolume(String path) {
+        if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
+            return VoldResponseCode.OpFailedVolNotMounted;
+        }
+
+        // Notify PackageManager of potential media removal and deal with
+        // return code later on. The caller of this api should be aware or have been
+        // notified that the applications installed on the media will be killed.
+        mPms.updateExternalMediaStatus(false);
+        try {
+            mConnector.doCommand(String.format("volume unmount %s", path));
+            return StorageResultCode.OperationSucceeded;
+        } catch (NativeDaemonConnectorException e) {
+            // Don't worry about mismatch in PackageManager since the
+            // call back will handle the status changes any way.
+            int code = e.getCode();
+            if (code == VoldResponseCode.OpFailedVolNotMounted) {
+                return StorageResultCode.OperationFailedVolumeNotMounted;
+            } else {
+                return StorageResultCode.OperationFailedInternalError;
+            }
+        }
+    }
+
+    private int doFormatVolume(String path) {
+        try {
+            String cmd = String.format("volume format %s", path);
+            mConnector.doCommand(cmd);
+            return StorageResultCode.OperationSucceeded;
+        } catch (NativeDaemonConnectorException e) {
+            int code = e.getCode();
+            if (code == VoldResponseCode.OpFailedNoMedia) {
+                return StorageResultCode.OperationFailedNoMedia;
+            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
+                return StorageResultCode.OperationFailedMediaCorrupt;
+            } else {
+                return StorageResultCode.OperationFailedInternalError;
+            }
+        }
+    }
+
+    private boolean doGetVolumeShared(String path, String method) {
+        String cmd = String.format("volume shared %s %s", path, method);
+        ArrayList<String> rsp = mConnector.doCommand(cmd);
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code;
+            try {
+                code = Integer.parseInt(tok[0]);
+            } catch (NumberFormatException nfe) {
+                Log.e(TAG, String.format("Error parsing code %s", tok[0]));
+                return false;
+            }
+            if (code == VoldResponseCode.ShareEnabledResult) {
+                if (tok[2].equals("enabled"))
+                    return true;
+                return false;
+            } else {
+                Log.e(TAG, String.format("Unexpected response code %d", code));
+                return false;
+            }
+        }
+        Log.e(TAG, "Got an empty response");
+        return false;
+    }
+
+    private void notifyShareAvailabilityChange(String method, final boolean avail) {
         if (!method.equals("ums")) {
            Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
            return;
@@ -481,7 +597,7 @@
             for (int i = mListeners.size() -1; i >= 0; i--) {
                 MountServiceBinderListener bl = mListeners.get(i);
                 try {
-                    bl.mListener.onShareAvailabilityChanged(method, avail);
+                    bl.mListener.onUsbMassStorageConnectionChanged(avail);
                 } catch (RemoteException rex) {
                     Log.e(TAG, "Listener dead");
                     mListeners.remove(i);
@@ -491,22 +607,55 @@
             }
         }
 
-        Intent intent;
-        if (avail) {
-            intent = new Intent(Intent.ACTION_UMS_CONNECTED);
-        } else {
-            intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
+        if (mBooted == true) {
+            Intent intent;
+            if (avail) {
+                intent = new Intent(Intent.ACTION_UMS_CONNECTED);
+            } else {
+                intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
+            }
+            mContext.sendBroadcast(intent);
         }
-        mContext.sendBroadcast(intent);
     }
 
-    void validatePermission(String perm) {
+    private void validatePermission(String perm) {
         if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException(String.format("Requires %s permission", perm));
         }
     }
 
     /**
+     * Constructs a new MountService instance
+     *
+     * @param context  Binder context for this service
+     */
+    public MountService(Context context) {
+        mContext = context;
+
+        /*
+         * Vold does not run in the simulator, so fake out a mounted
+         * event to trigger MediaScanner
+         */
+        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
+            updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED);
+            return;
+        }
+
+        // XXX: This will go away soon in favor of IMountServiceObserver
+        mPms = (PackageManagerService) ServiceManager.getService("package");
+
+        mContext.registerReceiver(mBroadcastReceiver,
+                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+
+        mListeners = new ArrayList<MountServiceBinderListener>();
+
+        mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
+        mReady = false;
+        Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
+        thread.start();
+    }
+
+    /**
      * Exposed API calls below here
      */
 
@@ -549,7 +698,7 @@
              * the UMS host could have dirty FAT cache entries
              * yet to flush.
              */
-            if (unshareVolume(path, "ums") != MountServiceResultCode.OperationSucceeded) {
+            if (setUsbMassStorageEnabled(false) != StorageResultCode.OperationSucceeded) {
                 Log.e(TAG, "UMS disable on shutdown failed");
             }
         } else if (state.equals(Environment.MEDIA_CHECKING)) {
@@ -577,75 +726,30 @@
             /*
              * If the media is mounted, then gracefully unmount it.
              */
-            if (unmountVolume(path) != MountServiceResultCode.OperationSucceeded) {
+            if (doUnmountVolume(path) != StorageResultCode.OperationSucceeded) {
                 Log.e(TAG, "Failed to unmount media for shutdown");
             }
         }
     }
 
-    public String[] getShareMethodList() {
-        String[] rdata = new String[1];
-        rdata[0] = "ums";
-        return rdata;
-    }
+    public boolean isUsbMassStorageConnected() {
+        waitForReady();
 
-    public boolean getShareMethodAvailable(String method) {
-        ArrayList<String> rsp = mConnector.doCommand("share status " + method);
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code;
-            try {
-                code = Integer.parseInt(tok[0]);
-            } catch (NumberFormatException nfe) {
-                Log.e(TAG, String.format("Error parsing code %s", tok[0]));
-                return false;
-            }
-            if (code == VoldResponseCode.ShareStatusResult) {
-                if (tok[2].equals("available"))
-                    return true;
-                return false;
-            } else {
-                Log.e(TAG, String.format("Unexpected response code %d", code));
-                return false;
-            }
+        if (mUmsEnabling) {
+            return true;
         }
-        Log.e(TAG, "Got an empty response");
-        return false;
+        return doGetShareMethodAvailable("ums");
     }
 
-    public int shareVolume(String path, String method) {
-        return doShareUnshareVolume(path, method, true);
+    public int setUsbMassStorageEnabled(boolean enable) {
+        waitForReady();
+
+        return doShareUnshareVolume(Environment.getExternalStorageDirectory().getPath(), "ums", enable);
     }
 
-    public int unshareVolume(String path, String method) {
-        return doShareUnshareVolume(path, method, false);
-    }
-
-    public boolean getVolumeShared(String path, String method) {
-        String cmd = String.format("volume shared %s %s", path, method);
-        ArrayList<String> rsp = mConnector.doCommand(cmd);
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code;
-            try {
-                code = Integer.parseInt(tok[0]);
-            } catch (NumberFormatException nfe) {
-                Log.e(TAG, String.format("Error parsing code %s", tok[0]));
-                return false;
-            }
-            if (code == VoldResponseCode.ShareEnabledResult) {
-                if (tok[2].equals("enabled"))
-                    return true;
-                return false;
-            } else {
-                Log.e(TAG, String.format("Unexpected response code %d", code));
-                return false;
-            }
-        }
-        Log.e(TAG, "Got an empty response");
-        return false;
+    public boolean isUsbMassStorageEnabled() {
+        waitForReady();
+        return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
     }
     
     /**
@@ -664,103 +768,38 @@
         return mLegacyState;
     }
 
-    
-    /**
-     * Attempt to mount external media
-     */
     public int mountVolume(String path) {
         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
-        int rc = MountServiceResultCode.OperationSucceeded;
-
-        try {
-            mConnector.doCommand(String.format("volume mount %s", path));
-        } catch (NativeDaemonConnectorException e) {
-            /*
-             * Mount failed for some reason
-             */
-            Intent in = null;
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedNoMedia) {
-                /*
-                 * Attempt to mount but no media inserted
-                 */
-                rc = MountServiceResultCode.OperationFailedNoMedia;
-            } else if (code == VoldResponseCode.OpFailedMediaBlank) {
-                /*
-                 * Media is blank or does not contain a supported filesystem
-                 */
-                updatePublicVolumeState(path, Environment.MEDIA_NOFS);
-                in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
-                rc = MountServiceResultCode.OperationFailedMediaBlank;
-            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
-                /*
-                 * Volume consistency check failed
-                 */
-                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
-                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
-                rc = MountServiceResultCode.OperationFailedMediaCorrupt;
-            } else {
-                rc = MountServiceResultCode.OperationFailedInternalError;
-            }
-
-            /*
-             * Send broadcast intent (if required for the failure)
-             */
-            if (in != null) {
-                mContext.sendBroadcast(in);
-            }
-        }
-
-        return rc;
+        waitForReady();
+        return doMountVolume(path);
     }
 
-    /**
-     * Attempt to unmount external media to prepare for eject
-     */
     public int unmountVolume(String path) {
         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        waitForReady();
 
-        try {
-            mConnector.doCommand(String.format("volume unmount %s", path));
-            return MountServiceResultCode.OperationSucceeded;
-        } catch (NativeDaemonConnectorException e) {
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedVolNotMounted) {
-                return MountServiceResultCode.OperationFailedVolumeNotMounted;
-            } else {
-                return MountServiceResultCode.OperationFailedInternalError;
-            }
-        }
+        return doUnmountVolume(path);
     }
 
-    /**
-     * Synchronously formats a volume
-     *
-     * @param path The volume path to format
-     * @return Error code from MountServiceResultCode
-     */
     public int formatVolume(String path) {
         validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+        waitForReady();
 
-        try {
-            String cmd = String.format("volume format %s", path);
-            mConnector.doCommand(cmd);
-            return MountServiceResultCode.OperationSucceeded;
-        } catch (NativeDaemonConnectorException e) {
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedNoMedia) {
-                return MountServiceResultCode.OperationFailedNoMedia;
-            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
-                return MountServiceResultCode.OperationFailedMediaCorrupt;
-            } else {
-                return MountServiceResultCode.OperationFailedInternalError;
-            }
+        return doFormatVolume(path);
+    }
+
+    private void warnOnNotMounted() {
+        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+            Log.w(TAG, "getSecureContainerList() called when storage not mounted");
         }
     }
 
     public String[] getSecureContainerList() {
         validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        waitForReady();
+        warnOnNotMounted();
+
         try {
             return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
         } catch (NativeDaemonConnectorException e) {
@@ -771,82 +810,96 @@
     public int createSecureContainer(String id, int sizeMb, String fstype,
                                     String key, int ownerUid) {
         validatePermission(android.Manifest.permission.ASEC_CREATE);
+        waitForReady();
+        warnOnNotMounted();
 
-        int rc = MountServiceResultCode.OperationSucceeded;
+        int rc = StorageResultCode.OperationSucceeded;
         String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
         try {
             mConnector.doCommand(cmd);
         } catch (NativeDaemonConnectorException e) {
-            rc = MountServiceResultCode.OperationFailedInternalError;
+            rc = StorageResultCode.OperationFailedInternalError;
         }
         return rc;
     }
 
     public int finalizeSecureContainer(String id) {
         validatePermission(android.Manifest.permission.ASEC_CREATE);
+        warnOnNotMounted();
 
-        int rc = MountServiceResultCode.OperationSucceeded;
+        int rc = StorageResultCode.OperationSucceeded;
         try {
             mConnector.doCommand(String.format("asec finalize %s", id));
         } catch (NativeDaemonConnectorException e) {
-            rc = MountServiceResultCode.OperationFailedInternalError;
+            rc = StorageResultCode.OperationFailedInternalError;
         }
         return rc;
     }
 
     public int destroySecureContainer(String id) {
         validatePermission(android.Manifest.permission.ASEC_DESTROY);
+        waitForReady();
+        warnOnNotMounted();
 
-        int rc = MountServiceResultCode.OperationSucceeded;
+        int rc = StorageResultCode.OperationSucceeded;
         try {
             mConnector.doCommand(String.format("asec destroy %s", id));
         } catch (NativeDaemonConnectorException e) {
-            rc = MountServiceResultCode.OperationFailedInternalError;
+            rc = StorageResultCode.OperationFailedInternalError;
         }
         return rc;
     }
    
     public int mountSecureContainer(String id, String key, int ownerUid) {
         validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+        waitForReady();
+        warnOnNotMounted();
 
-        int rc = MountServiceResultCode.OperationSucceeded;
+        int rc = StorageResultCode.OperationSucceeded;
         String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
         try {
             mConnector.doCommand(cmd);
         } catch (NativeDaemonConnectorException e) {
-            rc = MountServiceResultCode.OperationFailedInternalError;
+            rc = StorageResultCode.OperationFailedInternalError;
         }
         return rc;
     }
 
     public int unmountSecureContainer(String id) {
         validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+        waitForReady();
+        warnOnNotMounted();
 
-        int rc = MountServiceResultCode.OperationSucceeded;
+        int rc = StorageResultCode.OperationSucceeded;
         String cmd = String.format("asec unmount %s", id);
         try {
             mConnector.doCommand(cmd);
         } catch (NativeDaemonConnectorException e) {
-            rc = MountServiceResultCode.OperationFailedInternalError;
+            rc = StorageResultCode.OperationFailedInternalError;
         }
         return rc;
     }
 
     public int renameSecureContainer(String oldId, String newId) {
         validatePermission(android.Manifest.permission.ASEC_RENAME);
+        waitForReady();
+        warnOnNotMounted();
 
-        int rc = MountServiceResultCode.OperationSucceeded;
+        int rc = StorageResultCode.OperationSucceeded;
         String cmd = String.format("asec rename %s %s", oldId, newId);
         try {
             mConnector.doCommand(cmd);
         } catch (NativeDaemonConnectorException e) {
-            rc = MountServiceResultCode.OperationFailedInternalError;
+            rc = StorageResultCode.OperationFailedInternalError;
         }
         return rc;
     }
 
     public String getSecureContainerPath(String id) {
         validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        waitForReady();
+        warnOnNotMounted();
+
         ArrayList<String> rsp = mConnector.doCommand("asec path " + id);
 
         for (String line : rsp) {
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 9a066d3..cf083c0 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -21,7 +21,6 @@
 import android.os.Environment;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.util.Config;
 import android.util.Log;
 
 import java.io.IOException;
@@ -41,6 +40,7 @@
  * protocol.
  */
 final class NativeDaemonConnector implements Runnable {
+    private static final boolean LOCAL_LOGD = false;
 
     private BlockingQueue<String> mResponseQueue;
     private OutputStream          mOutputStream;
@@ -110,7 +110,7 @@
                 for (int i = 0; i < count; i++) {
                     if (buffer[i] == 0) {
                         String event = new String(buffer, start, i - start);
-//                        Log.d(TAG, "Got packet {" + event + "}");
+                        if (LOCAL_LOGD) Log.d(TAG, String.format("RCV <- {%s}", event));
 
                         String[] tokens = event.split(" ");
                         try {
@@ -177,7 +177,7 @@
      */
     private void sendCommand(String command, String argument) {
         synchronized (this) {
-             Log.d(TAG, "sendCommand {" + command + "} {" + argument + "}");
+            if (LOCAL_LOGD) Log.d(TAG, String.format("SND -> {%s} {%s}", command, argument));
             if (mOutputStream == null) {
                 Log.e(TAG, "No connection to daemon", new IllegalStateException());
             } else {
@@ -210,7 +210,7 @@
         while (!complete) {
             try {
                 String line = mResponseQueue.take();
-                Log.d(TAG, String.format("RSP -> {%s}", line));
+                if (LOCAL_LOGD) Log.d(TAG, String.format("RSP <- {%s}", line));
                 String[] tokens = line.split(" ");
                 try {
                     code = Integer.parseInt(tokens[0]);
@@ -250,13 +250,18 @@
         String[] rdata = new String[rsp.size()-1];
         int idx = 0;
 
-        for (String line : rsp) {
+        for (int i = 0; i < rsp.size(); i++) {
+            String line = rsp.get(i);
             try {
                 String[] tok = line.split(" ");
                 int code = Integer.parseInt(tok[0]);
                 if (code == expectedResponseCode) {
                     rdata[idx++] = line.substring(tok[0].length() + 1);
                 } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+                    if (LOCAL_LOGD) Log.d(TAG, String.format("List terminated with {%s}", line));
+                    if (i != rsp.size()) {
+                        Log.w(TAG, String.format("Recv'd %d lines after list term", (rsp.size()-i)));
+                    }
                     return rdata;
                 } else {
                     throw new NativeDaemonConnectorException(
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 02a0401..ad8ab84 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -50,6 +50,7 @@
 import android.os.Power;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.storage.StorageManager;
 import android.os.SystemProperties;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -330,9 +331,9 @@
                 updateAdbNotification();
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
-                    || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                 String pkgList[] = null;
-                if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 } else {
                     Uri uri = intent.getData();
@@ -406,6 +407,10 @@
         mSound.setUsesWakeLock(context);
         mToastQueue = new ArrayList<ToastRecord>();
         mHandler = new WorkerHandler();
+
+        StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+        sm.registerListener(new com.android.internal.app.StorageNotification(context));
+
         mStatusBarService = statusBar;
         statusBar.setNotificationCallbacks(mNotificationCallbacks);
 
@@ -440,7 +445,7 @@
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         mContext.registerReceiver(mIntentReceiver, filter);
-        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mIntentReceiver, sdFilter);
 
         SettingsObserver observer = new SettingsObserver(mHandler);
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index dbd1826..9551db5 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -40,6 +40,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * We back up the signatures of each package so that during a system restore,
@@ -100,6 +101,20 @@
 
         return mRestoredSignatures.get(packageName);
     }
+
+    public Set<String> getRestoredPackages() {
+        if (mRestoredSignatures == null) {
+            Log.w(TAG, "getRestoredPackages() before metadata read!");
+            return null;
+        }
+
+        // This is technically the set of packages on the originating handset
+        // that had backup agents at all, not limited to the set of packages
+        // that had actually contributed a restore dataset, but it's a
+        // close enough approximation for our purposes and does not require any
+        // additional involvement by the transport to obtain.
+        return mRestoredSignatures.keySet();
+    }
     
     // The backed up data is the signature block for each app, keyed by
     // the package name.
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 387f139..b1e5d32 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -74,7 +74,7 @@
 import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.Handler;
-import android.os.MountServiceResultCode;
+import android.os.storage.StorageResultCode;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.ServiceManager;
@@ -148,6 +148,10 @@
     static final int SCAN_NEW_INSTALL = 1<<4;
     static final int SCAN_NO_PATHS = 1<<5;
 
+    static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+            "com.android.defcontainer",
+            "com.android.defcontainer.DefaultContainerService");
+    
     final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
             Process.THREAD_PRIORITY_BACKGROUND);
     final PackageHandler mHandler;
@@ -298,6 +302,7 @@
     static final int END_COPY = 4;
     static final int INIT_COPY = 5;
     static final int MCS_UNBIND = 6;
+    static final int START_CLEANING_PACKAGE = 7;
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
     private ServiceConnection mDefContainerConn = new ServiceConnection() {
@@ -328,9 +333,7 @@
                 case INIT_COPY: {
                     InstallArgs args = (InstallArgs) msg.obj;
                     args.createCopyFile();
-                    Intent service = new Intent().setComponent(new ComponentName(
-                            "com.android.defcontainer",
-                            "com.android.defcontainer.DefaultContainerService"));
+                    Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
                     if (mContainerService != null) {
                         // No need to add to pending list. Use remote stub directly
                         handleStartCopy(args);
@@ -405,6 +408,15 @@
                     }
                     break;
                 }
+                case START_CLEANING_PACKAGE: {
+                    String packageName = (String)msg.obj;
+                    synchronized (mPackages) {
+                        if (!mSettings.mPackagesToBeCleaned.contains(packageName)) {
+                            mSettings.mPackagesToBeCleaned.add(packageName);
+                        }
+                    }
+                    startCleaningPackages();
+                } break;
             }
         }
 
@@ -2111,6 +2123,12 @@
             File file = new File(dir, files[i]);
             PackageParser.Package pkg = scanPackageLI(file,
                     flags|PackageParser.PARSE_MUST_BE_APK, scanMode);
+            // Don't mess around with apps in system partition.
+            if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0) {
+                // Delete the apk
+                Log.w(TAG, "Cleaning up failed install of " + file);
+                file.delete();
+            }
         }
     }
 
@@ -2353,6 +2371,13 @@
     private PackageParser.Package scanPackageLI(
         PackageParser.Package pkg, int parseFlags, int scanMode) {
         File scanFile = new File(pkg.mScanPath);
+        if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
+                pkg.applicationInfo.publicSourceDir == null) {
+            // Bail out. The resource and code paths haven't been set.
+            Log.w(TAG, " Code and resource paths haven't been set correctly");
+            mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK;
+            return null;
+        }
         mScanningPath = scanFile;
         if (pkg == null) {
             mLastScanError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
@@ -2810,6 +2835,9 @@
             mSettings.insertPackageSettingLP(pkgSetting, pkg);
             // Add the new setting to mPackages
             mPackages.put(pkg.applicationInfo.packageName, pkg);
+            // Make sure we don't accidentally delete its data.
+            mSettings.mPackagesToBeCleaned.remove(pkgName);
+            
             int N = pkg.providers.size();
             StringBuilder r = null;
             int i;
@@ -4005,7 +4033,47 @@
             }
         }
     }
+    
+    public String nextPackageToClean(String lastPackage) {
+        synchronized (mPackages) {
+            if (!mMediaMounted) {
+                // If the external storage is no longer mounted at this point,
+                // the caller may not have been able to delete all of this
+                // packages files and can not delete any more.  Bail.
+                return null;
+            }
+            if (lastPackage != null) {
+                mSettings.mPackagesToBeCleaned.remove(lastPackage);
+            }
+            return mSettings.mPackagesToBeCleaned.size() > 0
+                    ? mSettings.mPackagesToBeCleaned.get(0) : null;
+        }
+    }
 
+    void schedulePackageCleaning(String packageName) {
+        mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName));
+    }
+    
+    void startCleaningPackages() {
+        synchronized (mPackages) {
+            if (!mMediaMounted) {
+                return;
+            }
+            if (mSettings.mPackagesToBeCleaned.size() <= 0) {
+                return;
+            }
+        }
+        Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);
+        intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
+        IActivityManager am = ActivityManagerNative.getDefault();
+        if (am != null) {
+            try {
+                am.startService(null, intent, null);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+    
     private final class AppDirObserver extends FileObserver {
         public AppDirObserver(String path, int mask, boolean isrom) {
             super(path, mask);
@@ -4959,69 +5027,82 @@
         File tmpPackageFile = new File(args.getCodePath());
         boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
         boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0);
-        boolean replacingExistingPackage = false;
+        boolean replace = false;
         int scanMode = SCAN_MONITOR | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE
                 | (newInstall ? SCAN_NEW_INSTALL : 0);
         // Result object to be returned
         res.returnCode = PackageManager.INSTALL_SUCCEEDED;
 
-        main_flow: try {
-            // Retrieve PackageSettings and parse package
-            int parseFlags = PackageParser.PARSE_CHATTY |
-                    (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |
-                    (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
-            parseFlags |= mDefParseFlags;
-            PackageParser pp = new PackageParser(tmpPackageFile.getPath());
-            pp.setSeparateProcesses(mSeparateProcesses);
-            final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
-                    null, mMetrics, parseFlags);
-            if (pkg == null) {
-                res.returnCode = pp.getParseError();
-                break main_flow;
+        // Retrieve PackageSettings and parse package
+        int parseFlags = PackageParser.PARSE_CHATTY |
+        (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |
+        (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
+        parseFlags |= mDefParseFlags;
+        PackageParser pp = new PackageParser(tmpPackageFile.getPath());
+        pp.setSeparateProcesses(mSeparateProcesses);
+        final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
+                null, mMetrics, parseFlags);
+        if (pkg == null) {
+            res.returnCode = pp.getParseError();
+            return;
+        }
+        String pkgName = res.name = pkg.packageName;
+        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+            if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
+                res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
+                return;
             }
-            String pkgName = res.name = pkg.packageName;
-            if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
-                if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
-                    res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
-                    break main_flow;
+        }
+        if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {
+            res.returnCode = pp.getParseError();
+            return;
+        }
+        // Some preinstall checks
+        if (forwardLocked && onSd) {
+            // Make sure forward locked apps can only be installed
+            // on internal storage
+            Log.w(TAG, "Cannot install protected apps on sdcard");
+            res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+            return;
+        }
+        // Get rid of all references to package scan path via parser.
+        pp = null;
+        String oldCodePath = null;
+        boolean systemApp = false;
+        synchronized (mPackages) {
+            // Check if installing already existing package
+            if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0
+                    && mPackages.containsKey(pkgName)) {
+                replace = true;
+            }
+            PackageSetting ps = mSettings.mPackages.get(pkgName);
+            if (ps != null) {
+                oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
+                if (ps.pkg != null && ps.pkg.applicationInfo != null) {
+                    systemApp = (ps.pkg.applicationInfo.flags &
+                            ApplicationInfo.FLAG_SYSTEM) != 0;
                 }
             }
-            if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {
-                res.returnCode = pp.getParseError();
-                break main_flow;
-            }
+        }
 
-            // Get rid of all references to package scan path via parser.
-            pp = null;
-            String oldCodePath = null;
-            synchronized (mPackages) {
-                //check if installing already existing package
-                if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0
-                        && mPackages.containsKey(pkgName)) {
-                    replacingExistingPackage = true;
-                }
-                PackageSetting ps = mSettings.mPackages.get(pkgName);
-                if (ps != null) {
-                    oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
-                }
-            }
-
-            if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
-                res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                break main_flow;
-            }
-            // Set application objects path explicitly after the rename
-            setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
-            if(replacingExistingPackage) {
-                replacePackageLI(pkg, parseFlags, scanMode,
-                        installerPackageName, res);
-            } else {
-                installNewPackageLI(pkg, parseFlags, scanMode,
-                        installerPackageName,res);
-            }
-        } finally {
-            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-            }
+        if (systemApp && onSd) {
+            // Disable updates to system apps on sdcard
+            Log.w(TAG, "Cannot install updates to system apps on sdcard");
+            res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+            return;
+        }
+        if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
+            res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            return;
+        }
+        // Set application objects path explicitly after the rename
+        setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
+        if(replace) {
+            replacePackageLI(pkg, parseFlags, scanMode,
+                    installerPackageName, res);
+        } else {
+            installNewPackageLI(pkg, parseFlags, scanMode,
+                    installerPackageName,res);
         }
     }
 
@@ -5198,7 +5279,6 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-
     private boolean deletePackageX(String packageName, boolean sendBroadCast,
                                    boolean deleteCodeAndResources, int flags) {
         PackageRemovedInfo info = new PackageRemovedInfo();
@@ -5290,6 +5370,7 @@
                 File dataDir = new File(pkg.applicationInfo.dataDir);
                 dataDir.delete();
             }
+            schedulePackageCleaning(packageName);
             synchronized (mPackages) {
                 if (outInfo != null) {
                     outInfo.removedUid = mSettings.removePackageLP(packageName);
@@ -5302,7 +5383,7 @@
                 mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
             }
             // Save settings now
-            mSettings.writeLP ();
+            mSettings.writeLP();
         }
     }
 
@@ -6791,6 +6872,10 @@
         final HashMap<String, BasePermission> mPermissionTrees =
                 new HashMap<String, BasePermission>();
 
+        // Packages that have been uninstalled and still need their external
+        // storage data deleted.
+        final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+        
         private final StringBuilder mReadMessages = new StringBuilder();
 
         private static final class PendingPackage extends PackageSettingBase {
@@ -7354,6 +7439,14 @@
                     serializer.endTag(null, "shared-user");
                 }
 
+                if (mPackagesToBeCleaned.size() > 0) {
+                    for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
+                        serializer.startTag(null, "cleaning-package");
+                        serializer.attribute(null, "name", mPackagesToBeCleaned.get(i));
+                        serializer.endTag(null, "cleaning-package");
+                    }
+                }
+                
                 serializer.endTag(null, "packages");
 
                 serializer.endDocument();
@@ -7386,7 +7479,7 @@
         }
 
         void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg)
-        throws java.io.IOException {
+                throws java.io.IOException {
             serializer.startTag(null, "updated-package");
             serializer.attribute(null, "name", pkg.name);
             serializer.attribute(null, "codePath", pkg.codePathString);
@@ -7424,7 +7517,7 @@
         }
 
         void writePackage(XmlSerializer serializer, final PackageSetting pkg)
-        throws java.io.IOException {
+                throws java.io.IOException {
             serializer.startTag(null, "package");
             serializer.attribute(null, "name", pkg.name);
             serializer.attribute(null, "codePath", pkg.codePathString);
@@ -7614,6 +7707,11 @@
                         readPreferredActivitiesLP(parser);
                     } else if(tagName.equals("updated-package")) {
                         readDisabledSysPackageLP(parser);
+                    } else if (tagName.equals("cleaning-package")) {
+                        String name = parser.getAttributeValue(null, "name");
+                        if (name != null) {
+                            mPackagesToBeCleaned.add(name);
+                        }
                     } else {
                         Log.w(TAG, "Unknown element under <packages>: "
                               + parser.getName());
@@ -7739,7 +7837,7 @@
         }
 
         private void readDisabledSysPackageLP(XmlPullParser parser)
-        throws XmlPullParserException, IOException {
+                throws XmlPullParserException, IOException {
             String name = parser.getAttributeValue(null, "name");
             String codePathStr = parser.getAttributeValue(null, "codePath");
             String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
@@ -8269,17 +8367,17 @@
 
         int rc = mountService.createSecureContainer(
                 pkgName, mbLen, "vfat", sdEncKey, Process.SYSTEM_UID);
-        if (rc != MountServiceResultCode.OperationSucceeded) {
+        if (rc != StorageResultCode.OperationSucceeded) {
             Log.e(TAG, String.format("Failed to create container (%d)", rc));
 
             rc = mountService.destroySecureContainer(pkgName);
-            if (rc != MountServiceResultCode.OperationSucceeded) {
+            if (rc != StorageResultCode.OperationSucceeded) {
                 Log.e(TAG, String.format("Failed to cleanup container (%d)", rc));
                 return null;
             }
             rc = mountService.createSecureContainer(
                     pkgName, mbLen, "vfat", sdEncKey, Process.SYSTEM_UID);
-            if (rc != MountServiceResultCode.OperationSucceeded) {
+            if (rc != StorageResultCode.OperationSucceeded) {
                 Log.e(TAG, String.format("Failed to create container (2nd try) (%d)", rc));
                 return null;
             }
@@ -8299,7 +8397,7 @@
 
        int rc = getMountService().mountSecureContainer(pkgName, sdEncKey, ownerUid);
 
-       if (rc != MountServiceResultCode.OperationSucceeded) {
+       if (rc != StorageResultCode.OperationSucceeded) {
            Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " rc : " + rc);
            return null;
        }
@@ -8310,7 +8408,7 @@
    private boolean unMountSdDir(String pkgName) {
        // STOPSHIP unmount directory
        int rc = getMountService().unmountSecureContainer(pkgName);
-       if (rc != MountServiceResultCode.OperationSucceeded) {
+       if (rc != StorageResultCode.OperationSucceeded) {
            Log.e(TAG, "Failed to unmount : " + pkgName + " with rc " + rc);
            return false;
        }
@@ -8334,7 +8432,7 @@
 
     private boolean finalizeSdDir(String pkgName) {
         int rc = getMountService().finalizeSecureContainer(pkgName);
-        if (rc != MountServiceResultCode.OperationSucceeded) {
+        if (rc != StorageResultCode.OperationSucceeded) {
             Log.i(TAG, "Failed to finalize container for pkg : " + pkgName);
             return false;
         }
@@ -8343,7 +8441,7 @@
 
     private boolean destroySdDir(String pkgName) {
         int rc = getMountService().destroySecureContainer(pkgName);
-        if (rc != MountServiceResultCode.OperationSucceeded) {
+        if (rc != StorageResultCode.OperationSucceeded) {
             Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
             return false;
         }
@@ -8409,19 +8507,21 @@
    }
 
    public void updateExternalMediaStatus(final boolean mediaStatus) {
-       final boolean DEBUG = true;
-       if (DEBUG) Log.i(TAG, "updateExterMediaStatus::");
-       if (mediaStatus == mMediaMounted) {
-           return;
-       }
-       mMediaMounted = mediaStatus;
-        // Queue up an async operation since the package installation may take a little while.
-       mHandler.post(new Runnable() {
-           public void run() {
-               mHandler.removeCallbacks(this);
-               updateExternalMediaStatusInner(mediaStatus);
+       synchronized (mPackages) {
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
+                   mediaStatus+", mMediaMounted=" + mMediaMounted);
+           if (mediaStatus == mMediaMounted) {
+               return;
            }
-       });
+           mMediaMounted = mediaStatus;
+            // Queue up an async operation since the package installation may take a little while.
+           mHandler.post(new Runnable() {
+               public void run() {
+                   mHandler.removeCallbacks(this);
+                   updateExternalMediaStatusInner(mediaStatus);
+               }
+           });
+       }
    }
 
    void updateExternalMediaStatusInner(boolean mediaStatus) {
@@ -8432,9 +8532,6 @@
        HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
        int uidList[] = new int[list.length];
        int num = 0;
-       for (int i = 0; i < uidList.length; i++) {
-           uidList[i] = Process.LAST_APPLICATION_UID;
-       }
        synchronized (mPackages) {
            Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
            for (String cid : list) {
@@ -8456,24 +8553,35 @@
                processCids.put(args, ps.codePathString);
                int uid = ps.userId;
                if (uid != -1) {
-                   int idx = Arrays.binarySearch(uidList, uid);
-                   if (idx < 0) {
-                       uidList[-idx] = uid;
-                       num++;
-                   }
+                   uidList[num++] = uid;
                }
            }
        }
-       int uidArr[] = uidList;
-       if ((num > 0) && (num < uidList.length)) {
+       int uidArr[] = null;
+       if (num > 0) {
+           // Sort uid list
+           Arrays.sort(uidList, 0, num);
+           // Throw away duplicates
            uidArr = new int[num];
-           for (int i = 0; i < num; i++) {
-               uidArr[i] = uidList[i];
+           uidArr[0] = uidList[0];
+           int di = 0;
+           for (int i = 1; i < num; i++) {
+               if (uidList[i-1] != uidList[i]) {
+                   uidArr[di++] = uidList[i];
+               }
+           }
+           if (true) {
+               for (int j = 0; j < num; j++) {
+                   Log.i(TAG, "uidArr[" + j + "]=" + uidArr[j]);
+               }
            }
        }
        if (mediaStatus) {
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
            loadMediaPackages(processCids, uidArr);
+           startCleaningPackages();
        } else {
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
            unloadMediaPackages(processCids, uidArr);
        }
    }
@@ -8486,9 +8594,11 @@
            Bundle extras = new Bundle();
            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
                    pkgList.toArray(new String[size]));
-           extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
-           String action = mediaStatus ? Intent.ACTION_MEDIA_RESOURCES_AVAILABLE
-                   : Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE;
+           if (uidArr != null) {
+               extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
+           }
+           String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
+                   : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
            sendPackageBroadcast(action, null, extras);
        }
    }
@@ -8497,10 +8607,11 @@
        ArrayList<String> pkgList = new ArrayList<String>();
        Set<SdInstallArgs> keys = processCids.keySet();
        for (SdInstallArgs args : keys) {
-           String cid = args.cid;
            String codePath = processCids.get(args);
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install pkg : "
+                   + args.cid + " from " + args.cachePath);
            if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
-               Log.i(TAG, "Failed to install package: " + codePath + " from sdcard");
+               Log.e(TAG, "Failed to install package: " + codePath + " from sdcard");
                continue;
            }
            // Parse package
@@ -8511,7 +8622,8 @@
            final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
                    codePath, mMetrics, parseFlags);
            if (pkg == null) {
-               Log.w(TAG, "Failed to install package : " + cid + " from sd card");
+               Log.e(TAG, "Trying to install pkg : "
+                       + args.cid + " from " + args.cachePath);
                continue;
            }
            setApplicationInfoPaths(pkg, codePath, codePath);
@@ -8532,22 +8644,24 @@
                }
            }
            args.doPostInstall(retCode);
-           pkgList.add(pkg.packageName);
        }
        // Send broadcasts first
-       sendResourcesChangedBroadcast(true, pkgList, uidArr);
-       Runtime.getRuntime().gc();
-       // If something failed do we clean up here or next install?
+       if (pkgList.size() > 0) {
+           sendResourcesChangedBroadcast(true, pkgList, uidArr);
+           Runtime.getRuntime().gc();
+           // If something failed do we clean up here or next install?
+       }
    }
 
    void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+       if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
        ArrayList<String> pkgList = new ArrayList<String>();
+       ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
        Set<SdInstallArgs> keys = processCids.keySet();
        for (SdInstallArgs args : keys) {
            String cid = args.cid;
            String pkgName = args.getPackageName();
-           // STOPSHIP Send broadcast to apps to remove references
-           // STOPSHIP Unmount package
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName);
            // Delete package internally
            PackageRemovedInfo outInfo = new PackageRemovedInfo();
            synchronized (mInstallLock) {
@@ -8557,14 +8671,17 @@
                    pkgList.add(pkgName);
                } else {
                    Log.e(TAG, "Failed to delete pkg  from sdcard : " + pkgName);
+                   failedList.add(args);
                }
            }
        }
        // Send broadcasts
-       sendResourcesChangedBroadcast(false, pkgList, uidArr);
-       Runtime.getRuntime().gc();
+       if (pkgList.size() > 0) {
+           sendResourcesChangedBroadcast(false, pkgList, uidArr);
+           Runtime.getRuntime().gc();
+       }
        // Do clean up. Just unmount
-       for (SdInstallArgs args : keys) {
+       for (SdInstallArgs args : failedList) {
            synchronized (mInstallLock) {
                args.doPostDeleteLI(false);
            }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 1e7dd99..1ccae86 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -41,7 +41,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.IMountService;
+import android.os.storage.IMountService;
 import android.os.IPowerManager;
 import android.os.LocalPowerManager;
 import android.os.Power;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ee1cc8d..6e9c21b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -273,6 +273,17 @@
             }
 
             try {
+                /*
+                 * NotificationManagerService is dependant on MountService,
+                 * (for media / usb notifications) so we must start MountService first.
+                 */
+                Log.i(TAG, "Mount Service");
+                ServiceManager.addService("mount", new MountService(context));
+            } catch (Throwable e) {
+                Log.e(TAG, "Failure starting Mount Service", e);
+            }
+
+            try {
                 Log.i(TAG, "Notification Manager");
                 notification = new NotificationManagerService(context, statusBar, lights);
                 ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification);
@@ -281,14 +292,6 @@
             }
 
             try {
-                // MountService must start after NotificationManagerService
-                Log.i(TAG, "Mount Service");
-                ServiceManager.addService("mount", new MountService(context));
-            } catch (Throwable e) {
-                Log.e(TAG, "Failure starting Mount Service", e);
-            }
-
-            try {
                 Log.i(TAG, "Device Storage Monitor");
                 ServiceManager.addService(DeviceStorageMonitorService.SERVICE,
                         new DeviceStorageMonitorService(context));
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 88aadbd..b57e543 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5465,7 +5465,6 @@
                 curFocus = mCurrentFocus;
                 // cache the paused state at ctor time as well
                 if (theFocus == null || theFocus.mToken == null) {
-                    Log.i(TAG, "focus " + theFocus + " mToken is null at event dispatch!");
                     focusPaused = false;
                 } else {
                     focusPaused = theFocus.mToken.paused;
@@ -5478,7 +5477,7 @@
                         + " fin=" + finished + " gfw=" + gotFirstWindow
                         + " ed=" + eventDispatching + " tts=" + timeToSwitch
                         + " wf=" + wasFrozen + " fp=" + focusPaused
-                        + " mcf=" + mCurrentFocus + "}}";
+                        + " mcf=" + curFocus + "}}";
             }
         };
         private DispatchState mDispatchState = null;
@@ -5651,10 +5650,11 @@
                     synchronized (this) {
                         Log.w(TAG, "Key dispatching timed out sending to " +
                               (targetWin != null ? targetWin.mAttrs.getTitle()
-                              : "<null>"));
+                              : "<null>: no window ready for key dispatch"));
                         // NOSHIP debugging
-                        Log.w(TAG, "Dispatch state: " + mDispatchState);
-                        Log.w(TAG, "Current state:  " + new DispatchState(nextKey, targetWin));
+                        Log.w(TAG, "Previous dispatch state: " + mDispatchState);
+                        Log.w(TAG, "Current dispatch state: " +
+                                new DispatchState(nextKey, targetWin));
                         // END NOSHIP
                         //dump();
                         if (targetWin != null) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5e6881c..98ded37 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -11941,7 +11941,7 @@
                 intent.getAction());
         if (intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
                 || intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
-                || Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())
+                || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())
                 || uidRemoved) {
             if (checkComponentPermission(
                     android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
@@ -11960,7 +11960,7 @@
                 } else {
                     // If resources are unvailble just force stop all
                     // those packages and flush the attribute cache as well.
-                    if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) {
+                    if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
                         String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                         if (list != null && (list.length > 0)) {
                             for (String pkg : list) {
@@ -12146,7 +12146,7 @@
                         skipPackages = new String[] { pkgName };
                     }
                 }
-            } else if (intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(intent.getAction())) {
+            } else if (intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
                 skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
             if (skipPackages != null && (skipPackages.length > 0)) {
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 55041fb..c7c3335 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -1813,14 +1813,14 @@
             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
             filter.addDataScheme("package");
             mContext.registerReceiver(this, filter);
-            IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+            IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
             mContext.registerReceiver(this, sdFilter);
         }
         
         @Override
         public void onReceive(Context context, Intent intent) {
             String pkgList[] = null;
-            if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) {
+            if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             } else {
                 Uri data = intent.getData();
diff --git a/test-runner/android/test/SyncBaseInstrumentation.java b/test-runner/android/test/SyncBaseInstrumentation.java
index e8d72d9..7d418f0 100644
--- a/test-runner/android/test/SyncBaseInstrumentation.java
+++ b/test-runner/android/test/SyncBaseInstrumentation.java
@@ -46,7 +46,7 @@
      */
     protected void syncProvider(Uri uri, String accountName, String authority) throws Exception {
         Bundle extras = new Bundle();
-        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
         Account account = new Account(accountName, "com.google");
 
         ContentResolver.requestSync(account, authority, extras);
diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java
index 57b22f8..ffd757c 100644
--- a/test-runner/android/test/mock/MockContext.java
+++ b/test-runner/android/test/mock/MockContext.java
@@ -158,11 +158,21 @@
     }
 
     @Override
+    public File getExternalFilesDir(String type) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public File getCacheDir() {
         throw new UnsupportedOperationException();
     }
 
     @Override
+    public File getExternalCacheDir() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public File getDir(String name, int mode) {
         throw new UnsupportedOperationException();
     }
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index 70d0e78..2b6b81e 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -28,6 +28,7 @@
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
     <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
     <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 163ddd5..3a4d38c 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -16,6 +16,8 @@
 
 package com.android.unit_tests;
 
+import android.os.storage.IMountService.Stub;
+
 import android.net.Uri;
 import android.os.FileUtils;
 
@@ -34,7 +36,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstallObserver;
-import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
@@ -51,15 +53,27 @@
 import android.util.Log;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.storage.IMountService;
+import android.os.storage.StorageResultCode;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatFs;
 
 public class PackageManagerTests extends AndroidTestCase {
-    private static final boolean localLOGV = false;
+    private static final boolean localLOGV = true;
     public static final String TAG="PackageManagerTests";
-    public final long MAX_WAIT_TIME=60*1000;
-    public final long WAIT_TIME_INCR=10*1000;
+    public final long MAX_WAIT_TIME=120*1000;
+    public final long WAIT_TIME_INCR=20*1000;
+
+    void failStr(String errMsg) {
+        Log.w(TAG, "errMsg="+errMsg);
+        fail(errMsg);
+    }
+    void failStr(Exception e) {
+        Log.w(TAG, "e.getMessage="+e.getMessage());
+        Log.w(TAG, "e="+e);
+    }
 
     @Override
     protected void setUp() throws Exception {
@@ -146,6 +160,7 @@
         try {
             // Wait on observer
             synchronized(observer) {
+                synchronized (receiver) {
                     getPm().installPackage(packageURI, observer, flags, null);
                     long waitTime = 0;
                     while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
@@ -158,7 +173,6 @@
                     if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                         return false;
                     }
-                    synchronized (receiver) {
                     // Verify we received the broadcast
                     waitTime = 0;
                     while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
@@ -262,6 +276,7 @@
         PackageParser.Package pkg = parsePackage(packageURI);
         assertNotNull(pkg);
         InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+        InstallParams ip = null;
         try {
             try {
                 assertTrue(invokeInstallPackage(packageURI, flags,
@@ -271,13 +286,11 @@
             }
             // Verify installed information
             assertInstall(pkg.packageName, flags);
-            return new InstallParams(pkg, outFileName, packageURI);
+            ip = new InstallParams(pkg, outFileName, packageURI);
+            return ip;
         } finally {
             if (cleanUp) {
-                getPm().deletePackage(pkg.packageName, null, 0);
-                if (outFile != null && outFile.exists()) {
-                    outFile.delete();
-                }
+                cleanUpInstall(ip);
             }
         }
     }
@@ -300,28 +313,52 @@
     /* ------------------------- Test replacing packages --------------*/
     class ReplaceReceiver extends GenericReceiver {
         String pkgName;
-        boolean removed = false;
+        final static int INVALID = -1;
+        final static int REMOVED = 1;
+        final static int ADDED = 2;
+        final static int REPLACED = 3;
+        int removed = INVALID;
+        // for updated system apps only
+        boolean update = false;
 
         ReplaceReceiver(String pkgName) {
             this.pkgName = pkgName;
             filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            if (update) {
+                filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            }
+            filter.addDataScheme("package");
             super.setFilter(filter);
         }
 
         public boolean notifyNow(Intent intent) {
             String action = intent.getAction();
-            if (Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
-                    Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
-                Uri data = intent.getData();
-                String installedPkg = data.getEncodedSchemeSpecificPart();
-                if (pkgName.equals(installedPkg)) {
-                    if (removed) {
-                        return true;
-                    } else {
-                        removed = true;
-                    }
+            Uri data = intent.getData();
+            String installedPkg = data.getEncodedSchemeSpecificPart();
+            if (pkgName == null || !pkgName.equals(installedPkg)) {
+                return false;
+            }
+            if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                removed = REMOVED;
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                if (removed != REMOVED) {
+                    return false;
                 }
+                boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                if (!replacing) {
+                    return false;
+                }
+                removed = ADDED;
+                if (!update) {
+                    return true;
+                }
+            } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                if (removed != ADDED) {
+                    return false;
+                }
+                removed = REPLACED;
+                return true;
             }
             return false;
         }
@@ -335,29 +372,26 @@
      */
     public void replaceFromRawResource(int flags) {
         InstallParams ip = installFromRawResource(flags, false);
-        boolean result = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
+        boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
         GenericReceiver receiver;
-        if (result) {
+        if (replace) {
             receiver = new ReplaceReceiver(ip.pkg.packageName);
+            Log.i(TAG, "Creating replaceReceiver");
         } else {
             receiver = new InstallReceiver(ip.pkg.packageName);
         }
         try {
             try {
                 assertEquals(invokeInstallPackage(ip.packageURI, flags,
-                        ip.pkg.packageName, receiver), result);
-                if (result) {
+                        ip.pkg.packageName, receiver), replace);
+                if (replace) {
                     assertInstall(ip.pkg.packageName, flags);
                 }
             } catch (Exception e) {
                 failStr("Failed with exception : " + e);
             }
         } finally {
-            getPm().deletePackage(ip.pkg.packageName, null, 0);
-            File outFile = new File(ip.outFileName);
-            if (outFile != null && outFile.exists()) {
-                outFile.delete();
-            }
+            cleanUpInstall(ip);
         }
     }
 
@@ -376,12 +410,362 @@
         replaceFromRawResource(PackageManager.INSTALL_ON_SDCARD);
     }
 
-    void failStr(String errMsg) {
-        Log.w(TAG, "errMsg="+errMsg);
-        fail(errMsg);
+    @MediumTest
+    public void testReplaceNormalInternal() {
+        replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
     }
-    void failStr(Exception e) {
-        Log.w(TAG, "e.getMessage="+e.getMessage());
-        Log.w(TAG, "e="+e);
+
+    @MediumTest
+    public void testReplaceFwdLockedInternal() {
+        replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING |
+                PackageManager.INSTALL_FORWARD_LOCK);
     }
+
+    @MediumTest
+    public void testReplaceSdcard() {
+        replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING |
+                PackageManager.INSTALL_ON_SDCARD);
+    }
+
+    /* -------------- Delete tests ---*/
+    class DeleteObserver extends IPackageDeleteObserver.Stub {
+
+        public boolean succeeded;
+        private boolean doneFlag = false;
+
+        public boolean isDone() {
+            return doneFlag;
+        }
+
+        public void packageDeleted(boolean succeeded) throws RemoteException {
+            synchronized(this) {
+                this.succeeded = succeeded;
+                doneFlag = true;
+                notifyAll();
+            }
+        }
+    }
+
+    class DeleteReceiver extends GenericReceiver {
+        String pkgName;
+
+        DeleteReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addDataScheme("package");
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            if (!Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                return false;
+            }
+            Uri data = intent.getData();
+            String installedPkg = data.getEncodedSchemeSpecificPart();
+            if (pkgName.equals(installedPkg)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    public boolean invokeDeletePackage(Uri packageURI, int flags,
+            final String pkgName, GenericReceiver receiver) throws Exception {
+        DeleteObserver observer = new DeleteObserver();
+        final boolean received = false;
+        mContext.registerReceiver(receiver, receiver.filter);
+        try {
+            // Wait on observer
+            synchronized(observer) {
+                synchronized (receiver) {
+                    getPm().deletePackage(pkgName, observer, flags);
+                    long waitTime = 0;
+                    while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                        observer.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    }
+                    if(!observer.isDone()) {
+                        throw new Exception("Timed out waiting for packageInstalled callback");
+                    }
+                    // Verify we received the broadcast
+                    waitTime = 0;
+                    while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                        receiver.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    }
+                    if(!receiver.isDone()) {
+                        throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+                    }
+                    return receiver.received;
+                }
+            }
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    public void deleteFromRawResource(int iFlags, int dFlags) {
+        InstallParams ip = installFromRawResource(iFlags, false);
+        boolean retainData = ((dFlags & PackageManager.DONT_DELETE_DATA) != 0);
+        GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
+        DeleteObserver observer = new DeleteObserver();
+        try {
+            assertTrue(invokeDeletePackage(ip.packageURI, dFlags,
+                    ip.pkg.packageName, receiver));
+            ApplicationInfo info = null;
+            Log.i(TAG, "okay4");
+            try {
+            info = getPm().getApplicationInfo(ip.pkg.packageName,
+                    PackageManager.GET_UNINSTALLED_PACKAGES);
+            } catch (NameNotFoundException e) {
+                info = null;
+            }
+            if (retainData) {
+                assertNotNull(info);
+                assertEquals(info.packageName, ip.pkg.packageName);
+                File file = new File(info.dataDir);
+                assertTrue(file.exists());
+            } else {
+                assertNull(info);
+            }
+        } catch (Exception e) {
+            failStr(e);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    @MediumTest
+    public void testDeleteNormalInternal() {
+        deleteFromRawResource(0, 0);
+    }
+
+    @MediumTest
+    public void testDeleteFwdLockedInternal() {
+        deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, 0);
+    }
+
+    @MediumTest
+    public void testDeleteSdcard() {
+        deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, 0);
+    }
+
+    @MediumTest
+    public void testDeleteNormalInternalRetainData() {
+        deleteFromRawResource(0, PackageManager.DONT_DELETE_DATA);
+    }
+
+    @MediumTest
+    public void testDeleteFwdLockedInternalRetainData() {
+        deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DONT_DELETE_DATA);
+    }
+
+    @MediumTest
+    public void testDeleteSdcardRetainData() {
+        deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, PackageManager.DONT_DELETE_DATA);
+    }
+
+    /* sdcard mount/unmount tests ******/
+
+    class SdMountReceiver extends GenericReceiver {
+        String pkgNames[];
+        boolean status = true;
+
+        SdMountReceiver(String[] pkgNames) {
+            this.pkgNames = pkgNames;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            Log.i(TAG, "okay 1");
+            String action = intent.getAction();
+            if (!Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+                return false;
+            }
+            String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            for (String pkg : pkgNames) {
+                boolean found = false;
+                for (String rpkg : rpkgList) {
+                    if (rpkg.equals(pkg)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    status = false;
+                    return true;
+                }
+            }
+            return true;
+        }
+    }
+
+    class SdUnMountReceiver extends GenericReceiver {
+        String pkgNames[];
+        boolean status = true;
+
+        SdUnMountReceiver(String[] pkgNames) {
+            this.pkgNames = pkgNames;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            if (!Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                return false;
+            }
+            String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            for (String pkg : pkgNames) {
+                boolean found = false;
+                for (String rpkg : rpkgList) {
+                    if (rpkg.equals(pkg)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    status = false;
+                    return true;
+                }
+            }
+            return true;
+        }
+    }
+
+    IMountService getMs() {
+        IBinder service = ServiceManager.getService("mount");
+        if (service != null) {
+            return IMountService.Stub.asInterface(service);
+        } else {
+            Log.e(TAG, "Can't get mount service");
+        }
+        return null;
+    }
+
+    boolean getMediaState() {
+        try {
+        String mPath = Environment.getExternalStorageDirectory().toString();
+        String state = getMs().getVolumeState(mPath);
+        return Environment.MEDIA_MOUNTED.equals(state);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    boolean mountMedia() {
+        if (getMediaState()) {
+            return true;
+        }
+        try {
+        String mPath = Environment.getExternalStorageDirectory().toString();
+        int ret = getMs().mountVolume(mPath);
+        return ret == StorageResultCode.OperationSucceeded;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    private boolean unmountMedia() {
+        if (!getMediaState()) {
+            return true;
+        }
+        try {
+        String mPath = Environment.getExternalStorageDirectory().toString();
+        int ret = getMs().unmountVolume(mPath);
+        return ret == StorageResultCode.OperationSucceeded;
+        } catch (RemoteException e) {
+            return true;
+        }
+    }
+
+    /*
+     * Install package on sdcard. Unmount and then mount the media.
+     * (Use PackageManagerService private api for now)
+     * Make sure the installed package is available.
+     * STOPSHIP will uncomment when MountService api's to mount/unmount
+     * are made asynchronous.
+     */
+    public void xxxtestMountSdNormalInternal() {
+        assertTrue(mountFromRawResource());
+    }
+
+    private boolean mountFromRawResource() {
+        // Install pkg on sdcard
+        InstallParams ip = installFromRawResource(PackageManager.INSTALL_ON_SDCARD |
+                PackageManager.INSTALL_REPLACE_EXISTING, false);
+        if (localLOGV) Log.i(TAG, "Installed pkg on sdcard");
+        boolean origState = getMediaState();
+        SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName});
+        try {
+            if (localLOGV) Log.i(TAG, "Unmounting media");
+            // Unmount media
+            assertTrue(unmountMedia());
+            if (localLOGV) Log.i(TAG, "Unmounted media");
+            try {
+                if (localLOGV) Log.i(TAG, "Sleeping for 10 second");
+                Thread.sleep(10*1000);
+            } catch (InterruptedException e) {
+                failStr(e);
+            }
+            // Register receiver here
+            PackageManager pm = getPm();
+            mContext.registerReceiver(receiver, receiver.filter);
+
+            // Wait on receiver
+            synchronized (receiver) {
+                if (localLOGV) Log.i(TAG, "Mounting media");
+                // Mount media again
+                assertTrue(mountMedia());
+                if (localLOGV) Log.i(TAG, "Mounted media");
+                if (localLOGV) Log.i(TAG, "Waiting for notification");
+                long waitTime = 0;
+                // Verify we received the broadcast
+                waitTime = 0;
+                while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                    receiver.wait(WAIT_TIME_INCR);
+                    waitTime += WAIT_TIME_INCR;
+                }
+                if(!receiver.isDone()) {
+                    failStr("Timed out waiting for EXTERNAL_APPLICATIONS notification");
+                }
+                return receiver.received;
+            }
+        } catch (InterruptedException e) {
+            failStr(e);
+            return false;
+        } finally {
+            mContext.unregisterReceiver(receiver);
+            // Restore original media state
+            if (origState) {
+                mountMedia();
+            } else {
+                unmountMedia();
+            }
+            if (localLOGV) Log.i(TAG, "Cleaning up install");
+            cleanUpInstall(ip);
+        }
+    }
+
+    void cleanUpInstall(InstallParams ip) {
+        if (ip == null) {
+            return;
+        }
+        Runtime.getRuntime().gc();
+        Log.i(TAG, "Deleting package : " + ip.pkg.packageName);
+        getPm().deletePackage(ip.pkg.packageName, null, 0);
+        File outFile = new File(ip.outFileName);
+        if (outFile != null && outFile.exists()) {
+            outFile.delete();
+        }
+    }
+    /*
+     * TODO's
+     * check version numbers for upgrades
+     * check permissions of installed packages
+     * how to do tests on updated system apps?
+     * verify updates to system apps cannot be installed on the sdcard.
+     */
 }
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index cbb5203..558b587 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -39,7 +39,7 @@
           mRequireLocalization(false), mPseudolocalize(false),
           mUTF8(false), mEncodingSpecified(false), mValues(false),
           mCompressionMethod(0), mOutputAPKFile(NULL), mManifestPackageNameOverride(NULL),
-          mAssetSourceDir(NULL), mProguardFile(NULL),
+          mAutoAddOverlay(false), mAssetSourceDir(NULL), mProguardFile(NULL),
           mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
           mRClassDir(NULL), mResourceIntermediatesDir(NULL),
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
@@ -90,6 +90,8 @@
     void setOutputAPKFile(const char* val) { mOutputAPKFile = val; }
     const char* getManifestPackageNameOverride() const { return mManifestPackageNameOverride; }
     void setManifestPackageNameOverride(const char * val) { mManifestPackageNameOverride = val; }
+    bool getAutoAddOverlay() { return mAutoAddOverlay; }
+    void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
 
     /*
      * Input options.
@@ -181,6 +183,7 @@
     bool        mJunkPath;
     const char* mOutputAPKFile;
     const char* mManifestPackageNameOverride;
+    bool        mAutoAddOverlay;
     const char* mAssetSourceDir;
     const char* mProguardFile;
     const char* mAndroidManifestFile;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 6675ac2..6d0a351 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -60,6 +60,7 @@
         "        [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n"
         "        [--max-sdk-version VAL] [--app-version VAL] \\\n"
         "        [--app-version-name TEXT] [--custom-package VAL] [--utf16] \\\n"
+        "        [--auto-add-overlay] \\\n"
         "        [-I base-package [-I base-package ...]] \\\n"
         "        [-A asset-source-dir]  [-G class-list-file] [-P public-definitions-file] \\\n"
         "        [-S resource-sources [-S resource-sources ...]] "
@@ -136,6 +137,8 @@
         "       inserts android:versionName in to manifest.\n"
         "   --custom-package\n"
         "       generates R.java into a different package.\n"
+        "   --auto-add-overlay\n"
+        "       Automatically add resources that are only in overlays.\n"
         "   --utf16\n"
         "       changes default encoding for resources to UTF-16.  Only useful when API\n"
         "       level is set to 7 or higher where the default encoding is UTF-8.\n");
@@ -445,6 +448,8 @@
                         goto bail;
                     }
                     bundle.setManifestPackageNameOverride(argv[0]);
+                } else if (strcmp(cp, "-auto-add-overlay") == 0) {
+                    bundle.setAutoAddOverlay(true);
                 } else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index a9cbd11..b682702 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1591,7 +1591,7 @@
                sourcePos.file.striing(), sourcePos.line, String8(type).string());
     }
 #endif
-    if (overlay && !hasBagOrEntry(package, type, name)) {
+    if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) {
         bool canAdd = false;
         sp<Package> p = mPackages.valueFor(package);
         if (p != NULL) {
@@ -3279,12 +3279,13 @@
                                                        const SourcePos& sourcePos,
                                                        const ResTable_config* config,
                                                        bool doSetIndex,
-                                                       bool overlay)
+                                                       bool overlay,
+                                                       bool autoAddOverlay)
 {
     int pos = -1;
     sp<ConfigList> c = mConfigs.valueFor(entry);
     if (c == NULL) {
-        if (overlay == true && mCanAddEntries.indexOf(entry) < 0) {
+        if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
             sourcePos.error("Resource at %s appears in overlay but not"
                             " in the base package; use <add-resource> to add.\n",
                             String8(entry).string());
@@ -3596,7 +3597,7 @@
     if (t == NULL) {
         return NULL;
     }
-    return t->getEntry(name, sourcePos, config, doSetIndex, overlay);
+    return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
 }
 
 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index cfa75a71..60d0901 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -425,7 +425,8 @@
                            const SourcePos& pos,
                            const ResTable_config* config = NULL,
                            bool doSetIndex = false,
-                           bool overlay = false);
+                           bool overlay = false,
+                           bool autoAddOverlay = false);
 
         const SourcePos& getFirstPublicSourcePos() const { return *mFirstPublicSourcePos; }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index b670eee..57b5d4e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -928,6 +928,12 @@
     }
 
     @Override
+    public File getExternalCacheDir() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public ContentResolver getContentResolver() {
         if (mContentResolver == null) {
             mContentResolver = new BridgeContentResolver(this);
@@ -960,6 +966,12 @@
     }
 
     @Override
+    public File getExternalFilesDir(String type) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public String getPackageCodePath() {
         // TODO Auto-generated method stub
         return null;