Merge "Fix ImageProcessing example."
diff --git a/Android.mk b/Android.mk
index ab1e7ea..7bb5b5f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -33,6 +33,9 @@
 # FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
 LOCAL_SRC_FILES := $(call find-other-java-files,$(FRAMEWORKS_BASE_SUBDIRS))
 
+# EventLogTags files.
+LOCAL_SRC_FILES += core/java/android/content/EventLogTags.logtags
+
 # The following filters out code we are temporarily not including at all.
 # TODO: Move AWT and beans (and associated harmony code) back into libcore.
 # TODO: Maybe remove javax.microedition entirely?
@@ -111,8 +114,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 \
@@ -367,6 +370,8 @@
 
 sample_dir := development/samples
 
+# the list here should match the list of samples included in the sdk samples package
+# (see development/build/sdk.atree)
 web_docs_sample_code_flags := \
 		-hdf android.hasSamples 1 \
 		-samplecode $(sample_dir)/ApiDemos \
@@ -377,6 +382,8 @@
 		            resources/samples/BusinessCard "Business Card" \
 		-samplecode $(sample_dir)/ContactManager \
 		            resources/samples/ContactManager "Contact Manager" \
+                -samplecode $(sample_dir)/CubeLiveWallpaper \
+                            resources/samples/CubeLiveWallpaper "Live Wallpaper" \
 		-samplecode $(sample_dir)/Home \
 		            resources/samples/Home "Home" \
 		-samplecode $(sample_dir)/JetBoy \
@@ -387,6 +394,8 @@
 		            resources/samples/MultiResolution "Multiple Resolutions" \
 		-samplecode $(sample_dir)/NotePad \
 		            resources/samples/NotePad "Note Pad" \
+                -samplecode $(sample_dir)/SampleSyncAdapter \
+                            resources/samples/SampleSyncAdapter "Sample Sync Adapter" \
 		-samplecode $(sample_dir)/SearchableDictionary \
 		            resources/samples/SearchableDictionary "Searchable Dictionary" \
 		-samplecode $(sample_dir)/Snake \
diff --git a/api/current.xml b/api/current.xml
index c54fc98..24b9c96 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -4585,6 +4585,17 @@
  visibility="public"
 >
 </field>
+<field name="installLocation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843448"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="interpolator"
  type="int"
  transient="false"
@@ -6818,6 +6829,17 @@
  visibility="public"
 >
 </field>
+<field name="safeMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843449"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="saveEnabled"
  type="int"
  transient="false"
@@ -24886,6 +24908,17 @@
  visibility="public"
 >
 </field>
+<field name="INTENT_ACTION_SEARCH_SETTINGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.search.action.SEARCH_SETTINGS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="INTENT_ACTION_SEARCH_SETTINGS_CHANGED"
  type="java.lang.String"
  transient="false"
@@ -25311,8 +25344,8 @@
  visibility="public"
 >
 </method>
-<method name="getSettingsDescription"
- return="java.lang.String"
+<method name="getSettingsDescriptionId"
+ return="int"
  abstract="false"
  native="false"
  synchronized="false"
@@ -29824,6 +29857,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 +29909,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 +31773,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 +31806,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 +32818,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 +34350,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"
@@ -41760,6 +41887,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_VM_SAFE_MODE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16384"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="className"
  type="java.lang.String"
  transient="false"
@@ -41850,17 +41988,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"
@@ -66191,6 +66318,10 @@
 </parameter>
 <parameter name="format" type="int">
 </parameter>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
 <parameter name="strides" type="int[]">
 </parameter>
 </constructor>
@@ -66204,17 +66335,24 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-<parameter name="offsets" type="int[]">
+<parameter name="rectangle" type="android.graphics.Rect">
 </parameter>
 <parameter name="quality" type="int">
 </parameter>
 <parameter name="stream" type="java.io.OutputStream">
 </parameter>
 </method>
+<method name="getHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getStrides"
  return="int[]"
  abstract="false"
@@ -66226,6 +66364,17 @@
  visibility="public"
 >
 </method>
+<method name="getWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getYuvData"
  return="byte[]"
  abstract="false"
@@ -66248,25 +66397,6 @@
  visibility="public"
 >
 </method>
-<method name="validate"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="format" type="int">
-</parameter>
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-<parameter name="offsets" type="int[]">
-</parameter>
-</method>
 </class>
 </package>
 <package name="android.graphics.drawable"
@@ -77568,32 +77698,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 +77789,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 +77981,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 +78146,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 +83123,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 +83150,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.media.MediaScannerConnection.ScanResultListener">
+</implements>
 <method name="onMediaScannerConnected"
  return="void"
  abstract="true"
@@ -83283,6 +83179,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"
@@ -86773,15 +86692,11 @@
  type="android.net.SSLCertificateSocketFactory"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
-<parameter name="socketReadTimeoutForSslHandshake" type="int">
+<parameter name="handshakeTimeoutMillis" type="int">
 </parameter>
-<exception name="KeyManagementException" type="java.security.KeyManagementException">
-</exception>
-<exception name="NoSuchAlgorithmException" type="java.security.NoSuchAlgorithmException">
-</exception>
 </constructor>
 <method name="createSocket"
  return="java.net.Socket"
@@ -86793,13 +86708,13 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="socket" type="java.net.Socket">
+<parameter name="k" type="java.net.Socket">
 </parameter>
-<parameter name="s" type="java.lang.String">
+<parameter name="host" type="java.lang.String">
 </parameter>
-<parameter name="i" type="int">
+<parameter name="port" type="int">
 </parameter>
-<parameter name="flag" type="boolean">
+<parameter name="close" type="boolean">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -86814,13 +86729,13 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="inaddr" type="java.net.InetAddress">
+<parameter name="addr" type="java.net.InetAddress">
 </parameter>
-<parameter name="i" type="int">
+<parameter name="port" type="int">
 </parameter>
-<parameter name="inaddr2" type="java.net.InetAddress">
+<parameter name="localAddr" type="java.net.InetAddress">
 </parameter>
-<parameter name="j" type="int">
+<parameter name="localPort" type="int">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -86835,9 +86750,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="inaddr" type="java.net.InetAddress">
+<parameter name="addr" type="java.net.InetAddress">
 </parameter>
-<parameter name="i" type="int">
+<parameter name="port" type="int">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -86852,13 +86767,13 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="s" type="java.lang.String">
+<parameter name="host" type="java.lang.String">
 </parameter>
-<parameter name="i" type="int">
+<parameter name="port" type="int">
 </parameter>
-<parameter name="inaddr" type="java.net.InetAddress">
+<parameter name="localAddr" type="java.net.InetAddress">
 </parameter>
-<parameter name="j" type="int">
+<parameter name="localPort" type="int">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -86873,9 +86788,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="s" type="java.lang.String">
+<parameter name="host" type="java.lang.String">
 </parameter>
-<parameter name="i" type="int">
+<parameter name="port" type="int">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -86890,7 +86805,22 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="socketReadTimeoutForSslHandshake" type="int">
+<parameter name="handshakeTimeoutMillis" type="int">
+</parameter>
+</method>
+<method name="getDefault"
+ return="javax.net.SocketFactory"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="handshakeTimeoutMillis" type="int">
+</parameter>
+<parameter name="cache" type="android.net.SSLSessionCache">
 </parameter>
 </method>
 <method name="getDefaultCipherSuites"
@@ -86904,6 +86834,21 @@
  visibility="public"
 >
 </method>
+<method name="getHttpSocketFactory"
+ return="org.apache.http.conn.ssl.SSLSocketFactory"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="handshakeTimeoutMillis" type="int">
+</parameter>
+<parameter name="cache" type="android.net.SSLSessionCache">
+</parameter>
+</method>
 <method name="getSupportedCipherSuites"
  return="java.lang.String[]"
  abstract="false"
@@ -86916,6 +86861,37 @@
 >
 </method>
 </class>
+<class name="SSLSessionCache"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SSLSessionCache"
+ type="android.net.SSLSessionCache"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dir" type="java.io.File">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</constructor>
+<constructor name="SSLSessionCache"
+ type="android.net.SSLSessionCache"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+</class>
 <class name="TrafficStats"
  extends="java.lang.Object"
  abstract="false"
@@ -112890,6 +112866,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 +112901,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 +117945,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"
@@ -135753,6 +136044,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_PARTIAL_RESULTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.speech.extra.PARTIAL_RESULTS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_PROMPT"
  type="java.lang.String"
  transient="false"
@@ -136521,126 +136823,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 +144553,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 +181627,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"
@@ -213935,6 +214152,17 @@
  visibility="public"
 >
 </field>
+<field name="DEBUG_ENABLE_SAFEMODE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 </package>
 <package name="java.awt.font"
diff --git a/common/Android.mk b/common/Android.mk
index 76091eb..5c5b01b 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -19,6 +19,7 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-common
+LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, java)
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/common/java/com/android/common/AndroidHttpClient.java b/common/java/com/android/common/AndroidHttpClient.java
index 99faf6e..4c65eb0 100644
--- a/common/java/com/android/common/AndroidHttpClient.java
+++ b/common/java/com/android/common/AndroidHttpClient.java
@@ -47,8 +47,6 @@
 import org.apache.http.protocol.BasicHttpProcessor;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.BasicHttpContext;
-import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
-import org.apache.harmony.xnet.provider.jsse.SSLContextImpl;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -59,11 +57,11 @@
 import java.net.URI;
 import java.security.KeyManagementException;
 
+import android.content.Context;
 import android.content.ContentResolver;
+import android.net.SSLCertificateSocketFactory;
+import android.net.SSLSessionCache;
 import android.os.Looper;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Log;
 
 /**
@@ -76,11 +74,9 @@
  * To retain cookies, simply add a cookie store to the HttpContext:</p>
  *
  * <pre>context.setAttribute(ClientContext.COOKIE_STORE, cookieStore);</pre>
- * 
- * {@hide}
  */
 public final class AndroidHttpClient implements HttpClient {
-        
+
     // Gzip of data shorter than this probably won't be worthwhile
     public static long DEFAULT_SYNC_MIN_GZIP_BYTES = 256;
 
@@ -101,12 +97,11 @@
     /**
      * Create a new HttpClient with reasonable defaults (which you can update).
      *
-     * @param userAgent to report in your HTTP requests.
-     * @param sessionCache persistent session cache
+     * @param userAgent to report in your HTTP requests
+     * @param context to use for caching SSL sessions (may be null for no caching)
      * @return AndroidHttpClient for you to use for all your requests.
      */
-    public static AndroidHttpClient newInstance(String userAgent,
-            SSLClientSessionCache sessionCache) {
+    public static AndroidHttpClient newInstance(String userAgent, Context context) {
         HttpParams params = new BasicHttpParams();
 
         // Turn off stale checking.  Our connections break all the time anyway,
@@ -122,13 +117,16 @@
         // often wants to re-POST after a redirect, which we must do ourselves.
         HttpClientParams.setRedirecting(params, false);
 
+        // Use a session cache for SSL sockets
+        SSLSessionCache sessionCache = context == null ? null : new SSLSessionCache(context);
+
         // Set the specified user agent and register standard protocols.
         HttpProtocolParams.setUserAgent(params, userAgent);
         SchemeRegistry schemeRegistry = new SchemeRegistry();
         schemeRegistry.register(new Scheme("http",
                 PlainSocketFactory.getSocketFactory(), 80));
         schemeRegistry.register(new Scheme("https",
-                socketFactoryWithCache(sessionCache), 443));
+                SSLCertificateSocketFactory.getHttpSocketFactory(30 * 1000, sessionCache), 443));
 
         ClientConnectionManager manager =
                 new ThreadSafeClientConnManager(params, schemeRegistry);
@@ -139,32 +137,6 @@
     }
 
     /**
-     * Returns a socket factory backed by the given persistent session cache.
-     *
-     * @param sessionCache to retrieve sessions from, null for no cache
-     */
-    private static SSLSocketFactory socketFactoryWithCache(
-            SSLClientSessionCache sessionCache) {
-        if (sessionCache == null) {
-            // Use the default factory which doesn't support persistent
-            // caching.
-            return SSLSocketFactory.getSocketFactory();
-        }
-
-        // Create a new SSL context backed by the cache.
-        // TODO: Keep a weak *identity* hash map of caches to engines. In the
-        // mean time, if we have two engines for the same cache, they'll still
-        // share sessions but will have to do so through the persistent cache.
-        SSLContextImpl sslContext = new SSLContextImpl();
-        try {
-            sslContext.engineInit(null, null, null, sessionCache, null);
-        } catch (KeyManagementException e) {
-            throw new AssertionError(e);
-        }
-        return new SSLSocketFactory(sslContext.engineGetSocketFactory());
-    }
-
-    /**
      * Create a new HttpClient with reasonable defaults (which you can update).
      * @param userAgent to report in your HTTP requests.
      * @return AndroidHttpClient for you to use for all your requests.
@@ -339,9 +311,7 @@
      * Shorter data will not be compressed.
      */
     public static long getMinGzipSize(ContentResolver resolver) {
-        return Settings.Secure.getLong(resolver,
-                                       Settings.Secure.SYNC_MIN_GZIP_BYTES,
-                                       DEFAULT_SYNC_MIN_GZIP_BYTES);
+        return DEFAULT_SYNC_MIN_GZIP_BYTES;  // For now, this is just a constant.
     }
 
     /* cURL logging support. */
@@ -367,15 +337,6 @@
         }
 
         /**
-         * Returns true if auth logging is turned on for this configuration.  Can only be set on
-         * insecure devices.
-         */
-        private boolean isAuthLoggable() {
-            String secure = SystemProperties.get("ro.secure");
-            return "0".equals(secure) && Log.isLoggable(tag + "-auth", level);
-        }
-
-        /**
          * Prints a message using this configuration.
          */
         private void println(String message) {
@@ -421,8 +382,9 @@
             if (configuration != null
                     && configuration.isLoggable()
                     && request instanceof HttpUriRequest) {
-                configuration.println(toCurl((HttpUriRequest) request,
-                        configuration.isAuthLoggable()));
+                // Never print auth token -- we used to check ro.secure=0 to
+                // enable that, but can't do that in unbundled code.
+                configuration.println(toCurl((HttpUriRequest) request, false));
             }
         }
     }
diff --git a/common/java/com/android/common/ArrayListCursor.java b/common/java/com/android/common/ArrayListCursor.java
index cc1fe27..9ad5c36 100644
--- a/common/java/com/android/common/ArrayListCursor.java
+++ b/common/java/com/android/common/ArrayListCursor.java
@@ -115,11 +115,6 @@
     }
 
     @Override
-    public boolean deleteRow() {
-        return false;
-    }
-
-    @Override
     public String[] getColumnNames() {
         return mColumnNames;
     }
diff --git a/common/java/com/android/common/Base64.java b/common/java/com/android/common/Base64.java
index 771875c..772d567 100644
--- a/common/java/com/android/common/Base64.java
+++ b/common/java/com/android/common/Base64.java
@@ -51,6 +51,10 @@
      */
     public static final int WEB_SAFE = 8;
 
+    //  --------------------------------------------------------
+    //  decoding
+    //  --------------------------------------------------------
+
     /**
      * Lookup table for turning bytes into their position in the
      * Base64 alphabet.
@@ -155,18 +159,71 @@
      * incorrect padding
      */
     public static byte[] decode(byte[] input, int offset, int len, int flags) {
-        int p = offset;
         // Allocate space for the most data the input could represent.
         // (It could contain less if it contains whitespace, etc.)
-        byte[] output = new byte[len*3/4];
-        len += offset;
+        DecoderState state = new DecoderState(flags, new byte[len*3/4]);
+
+        if (!decodeInternal(input, offset, len, state, true)) {
+            throw new IllegalArgumentException("bad base-64");
+        }
+
+        // Maybe we got lucky and allocated exactly enough output space.
+        if (state.op == state.output.length) {
+            return state.output;
+        }
+
+        // Need to shorten the array, so allocate a new one of the
+        // right size and copy.
+        byte[] temp = new byte[state.op];
+        System.arraycopy(state.output, 0, temp, 0, state.op);
+        return temp;
+    }
+
+    /* package */ static class DecoderState {
+        public byte[] output;
+        public int op;
+
+        public int state;   // state number (0 to 6)
+        public int value;
+
+        final public int[] alphabet;
+
+        public DecoderState(int flags, byte[] output) {
+            this.output = output;
+
+            alphabet = ((flags & WEB_SAFE) == 0) ? DECODE : DECODE_WEBSAFE;
+            state = 0;
+            value = 0;
+        }
+    }
+
+    /**
+     * Decode another block of input data.
+     *
+     * @param dstate a DecoderState object whose (caller-provided)
+     *        output array is big enough to hold all the decoded data.
+     *        On return, dstate.op will be set to the length of the
+     *        decoded data.
+     * @param finish true if this is the final call to decodeInternal
+     *        with the given DecoderState object.  Will finalize the
+     *        decoder state and include any final bytes in the output.
+     *
+     * @return true if the state machine is still healthy.  false if
+     *         bad base-64 data has been detected in the input stream.
+     */
+
+    /* package */ static boolean decodeInternal(
+        byte[] input, int offset, int len, final DecoderState dstate, boolean finish) {
+        if (dstate.state == 6) return false;
+
+        int state = dstate.state;
+        int value = dstate.value;
+        final int[] decode = dstate.alphabet;
+        final byte[] output = dstate.output;
         int op = 0;
 
-        final int[] decode = ((flags & WEB_SAFE) == 0) ?
-            DECODE : DECODE_WEBSAFE;
-
-        int state = 0;
-        int value = 0;
+        int p = offset;
+        len += offset;
 
         while (p < len) {
 
@@ -207,6 +264,8 @@
             // one more.
             // State 5 is expecting no more data or padding characters
             // in the input.
+            // State 6 is the error state; an error has been detected
+            // in the input and no future input can "fix" it.
 
             int d = decode[input[p++] & 0xff];
 
@@ -216,7 +275,8 @@
                         value = d;
                         ++state;
                     } else if (d != SKIP) {
-                        throw new IllegalArgumentException("bad base-64");
+                        dstate.state = 6;
+                        return false;
                     }
                     break;
 
@@ -225,7 +285,8 @@
                         value = (value << 6) | d;
                         ++state;
                     } else if (d != SKIP) {
-                        throw new IllegalArgumentException("bad base-64");
+                        dstate.state = 6;
+                        return false;
                     }
                     break;
 
@@ -239,7 +300,8 @@
                         output[op++] = (byte) (value >> 4);
                         state = 4;
                     } else if (d != SKIP) {
-                        throw new IllegalArgumentException("bad base-64");
+                        dstate.state = 6;
+                        return false;
                     }
                     break;
 
@@ -260,7 +322,8 @@
                         op += 2;
                         state = 5;
                     } else if (d != SKIP) {
-                        throw new IllegalArgumentException("bad base-64");
+                        dstate.state = 6;
+                        return false;
                     }
                     break;
 
@@ -268,18 +331,30 @@
                     if (d == EQUALS) {
                         ++state;
                     } else if (d != SKIP) {
-                        throw new IllegalArgumentException("bad base-64");
+                        dstate.state = 6;
+                        return false;
                     }
                     break;
 
                 case 5:
                     if (d != SKIP) {
-                        throw new IllegalArgumentException("bad base-64");
+                        dstate.state = 6;
+                        return false;
                     }
                     break;
             }
         }
 
+        if (!finish) {
+            // We're out of input, but a future call could provide
+            // more.  Return the output we've produced on this call
+            // and save the current state of the state machine.
+            dstate.state = state;
+            dstate.value = value;
+            dstate.op = op;
+            return true;
+        }
+
         // Done reading input.  Now figure out where we are left in
         // the state machine and finish up.
 
@@ -290,7 +365,8 @@
             case 1:
                 // Read one extra input byte, which isn't enough to
                 // make another output byte.  Illegal.
-                throw new IllegalArgumentException("bad base-64");
+                dstate.state = 6;
+                return false;
             case 2:
                 // Read two extra input bytes, enough to emit 1 more
                 // output byte.  Fine.
@@ -305,25 +381,22 @@
                 break;
             case 4:
                 // Read one padding '=' when we expected 2.  Illegal.
-                throw new IllegalArgumentException("bad base-64");
+                dstate.state = 6;
+                return false;
             case 5:
                 // Read all the padding '='s we expected and no more.
                 // Fine.
                 break;
         }
 
-        // Maybe we got lucky and allocated exactly enough output space.
-        if (op == output.length) {
-            return output;
-        }
-
-        // Need to shorten the array, so allocate a new one of the
-        // right size and copy.
-        byte[] temp = new byte[op];
-        System.arraycopy(output, 0, temp, 0, op);
-        return temp;
+        dstate.op = op;
+        return true;
     }
 
+    //  --------------------------------------------------------
+    //  encoding
+    //  --------------------------------------------------------
+
     /**
      * Emit a new line every this many output tuples.  Corresponds to
      * a 76-character line length (the maximum allowable according to
@@ -416,17 +489,13 @@
      *               adheres to RFC 2045.
      */
     public static byte[] encode(byte[] input, int offset, int len, int flags) {
-        final boolean do_padding = (flags & NO_PADDING) == 0;
-        final boolean do_newline = (flags & NO_WRAP) == 0;
-        final boolean do_cr = (flags & CRLF) != 0;
-
-        final byte[] encode = ((flags & WEB_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE;
+        EncoderState state = new EncoderState(flags, null);
 
         // Compute the exact length of the array we will produce.
         int output_len = len / 3 * 4;
 
         // Account for the tail of the data and the padding bytes, if any.
-        if (do_padding) {
+        if (state.do_padding) {
             if (len % 3 > 0) {
                 output_len += 4;
             }
@@ -439,26 +508,107 @@
         }
 
         // Account for the newlines, if any.
-        if (do_newline && len > 0) {
-            output_len += (((len-1) / (3 * LINE_GROUPS)) + 1) * (do_cr ? 2 : 1);
+        if (state.do_newline && len > 0) {
+            output_len += (((len-1) / (3 * LINE_GROUPS)) + 1) * (state.do_cr ? 2 : 1);
         }
 
-        int op = 0;
-        byte[] output = new byte[output_len];
+        state.output = new byte[output_len];
+        encodeInternal(input, offset, len, state, true);
 
-        // The main loop, turning 3 input bytes into 4 output bytes on
-        // each iteration.
-        int count = do_newline ? LINE_GROUPS : -1;
+        assert state.op == output_len;
+
+        return state.output;
+    }
+
+    /* package */ static class EncoderState {
+        public byte[] output;
+        public int op;
+
+        final public byte[] tail;
+        public int tailLen;
+        public int count;
+
+        final public boolean do_padding;
+        final public boolean do_newline;
+        final public boolean do_cr;
+        final public byte[] alphabet;
+
+        public EncoderState(int flags, byte[] output) {
+            this.output = output;
+
+            do_padding = (flags & NO_PADDING) == 0;
+            do_newline = (flags & NO_WRAP) == 0;
+            do_cr = (flags & CRLF) != 0;
+            alphabet = ((flags & WEB_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE;
+
+            tail = new byte[2];
+            tailLen = 0;
+
+            count = do_newline ? LINE_GROUPS : -1;
+        }
+    }
+
+    /**
+     * Encode another block of input data.
+     *
+     * @param estate an EncoderState object whose (caller-provided)
+     *        output array is big enough to hold all the encoded data.
+     *        On return, estate.op will be set to the length of the
+     *        encoded data.
+     * @param finish true if this is the final call to encodeInternal
+     *        with the given EncoderState object.  Will finalize the
+     *        encoder state and include any final bytes in the output.
+     */
+    static void encodeInternal(byte[] input, int offset, int len,
+                               final EncoderState estate, boolean finish) {
+        final boolean do_cr = estate.do_cr;
+        final boolean do_newline = estate.do_newline;
+        final boolean do_padding = estate.do_padding;
+        final byte[] output = estate.output;
+
+        int op = 0;
+
         int p = offset;
         len += offset;
-        while (p+3 <= len) {
-            int v = ((input[p++] & 0xff) << 16) |
-                ((input[p++] & 0xff) << 8) |
-                (input[p++] & 0xff);
-            output[op++] = encode[(v >> 18) & 0x3f];
-            output[op++] = encode[(v >> 12) & 0x3f];
-            output[op++] = encode[(v >> 6) & 0x3f];
-            output[op++] = encode[v & 0x3f];
+        int v = -1;
+        int count = estate.count;
+
+        // First we need to concatenate the tail of the previous call
+        // with any input bytes available now and see if we can empty
+        // the tail.
+
+        switch (estate.tailLen) {
+            case 0:
+                // There was no tail.
+                break;
+
+            case 1:
+                if (p+2 <= len) {
+                    // A 1-byte tail with at least 2 bytes of
+                    // input available now.
+                    v = ((estate.tail[0] & 0xff) << 16) |
+                        ((input[p++] & 0xff) << 8) |
+                        (input[p++] & 0xff);
+                    estate.tailLen = 0;
+                };
+                break;
+
+            case 2:
+                if (p+1 <= len) {
+                    // A 2-byte tail with at least 1 byte of input.
+                    v = ((estate.tail[0] & 0xff) << 16) |
+                        ((estate.tail[1] & 0xff) << 8) |
+                        (input[p++] & 0xff);
+                    estate.tailLen = 0;
+                }
+                break;
+        }
+
+        if (v != -1) {
+            output[op++] = estate.alphabet[(v >> 18) & 0x3f];
+            output[op++] = estate.alphabet[(v >> 12) & 0x3f];
+            output[op++] = estate.alphabet[(v >> 6) & 0x3f];
+            output[op++] = estate.alphabet[v & 0x3f];
             if (--count == 0) {
                 if (do_cr) output[op++] = '\r';
                 output[op++] = '\n';
@@ -466,38 +616,82 @@
             }
         }
 
-        // Finish up the tail of the input.
-        if (p == len-1) {
-            int v = (input[p] & 0xff) << 4;
-            output[op++] = encode[(v >> 6) & 0x3f];
-            output[op++] = encode[v & 0x3f];
-            if (do_padding) {
-                output[op++] = '=';
-                output[op++] = '=';
-            }
-            if (do_newline) {
+        // At this point either there is no tail, or there are fewer
+        // than 3 bytes of input available.
+
+        // The main loop, turning 3 input bytes into 4 output bytes on
+        // each iteration.
+        while (p+3 <= len) {
+            v = ((input[p++] & 0xff) << 16) |
+                ((input[p++] & 0xff) << 8) |
+                (input[p++] & 0xff);
+            output[op++] = estate.alphabet[(v >> 18) & 0x3f];
+            output[op++] = estate.alphabet[(v >> 12) & 0x3f];
+            output[op++] = estate.alphabet[(v >> 6) & 0x3f];
+            output[op++] = estate.alphabet[v & 0x3f];
+            if (--count == 0) {
                 if (do_cr) output[op++] = '\r';
                 output[op++] = '\n';
+                count = LINE_GROUPS;
             }
-        } else if (p == len-2) {
-            int v = ((input[p] & 0xff) << 10) | ((input[p+1] & 0xff) << 2);
-            output[op++] = encode[(v >> 12) & 0x3f];
-            output[op++] = encode[(v >> 6) & 0x3f];
-            output[op++] = encode[v & 0x3f];
-            if (do_padding) {
-                output[op++] = '=';
-            }
-            if (do_newline) {
-                if (do_cr) output[op++] = '\r';
-                output[op++] = '\n';
-            }
-        } else if (do_newline && op > 0 && count != LINE_GROUPS) {
-            if (do_cr) output[op++] = '\r';
-            output[op++] = '\n';
         }
 
-        assert op == output.length;
-        return output;
+        if (finish) {
+            // Finish up the tail of the input.  Note that we need to
+            // consume any bytes in estate.tail before any bytes
+            // remaining in input; there should be at most two bytes
+            // total.
+
+            if (p-estate.tailLen == len-1) {
+                int t = 0;
+                v = ((estate.tailLen > 0 ? estate.tail[t++] : input[p++]) & 0xff) << 4;
+                estate.tailLen -= t;
+                output[op++] = estate.alphabet[(v >> 6) & 0x3f];
+                output[op++] = estate.alphabet[v & 0x3f];
+                if (do_padding) {
+                    output[op++] = '=';
+                    output[op++] = '=';
+                }
+                if (do_newline) {
+                    if (do_cr) output[op++] = '\r';
+                    output[op++] = '\n';
+                }
+            } else if (p-estate.tailLen == len-2) {
+                int t = 0;
+                v = (((estate.tailLen > 1 ? estate.tail[t++] : input[p++]) & 0xff) << 10) |
+                    (((estate.tailLen > 0 ? estate.tail[t++] : input[p++]) & 0xff) << 2);
+                estate.tailLen -= t;
+                output[op++] = estate.alphabet[(v >> 12) & 0x3f];
+                output[op++] = estate.alphabet[(v >> 6) & 0x3f];
+                output[op++] = estate.alphabet[v & 0x3f];
+                if (do_padding) {
+                    output[op++] = '=';
+                }
+                if (do_newline) {
+                    if (do_cr) output[op++] = '\r';
+                    output[op++] = '\n';
+                }
+            } else if (do_newline && op > 0 && count != LINE_GROUPS) {
+                if (do_cr) output[op++] = '\r';
+                output[op++] = '\n';
+            }
+
+            assert estate.tailLen == 0;
+            assert p == len;
+        } else {
+            // Save the leftovers in tail to be consumed on the next
+            // call to encodeInternal.
+
+            if (p == len-1) {
+                estate.tail[estate.tailLen++] = input[p];
+            } else if (p == len-2) {
+                estate.tail[estate.tailLen++] = input[p];
+                estate.tail[estate.tailLen++] = input[p+1];
+            }
+        }
+
+        estate.op = op;
+        estate.count = count;
     }
 
     private Base64() { }   // don't instantiate
diff --git a/common/java/com/android/common/Base64OutputStream.java b/common/java/com/android/common/Base64OutputStream.java
new file mode 100644
index 0000000..7c37428
--- /dev/null
+++ b/common/java/com/android/common/Base64OutputStream.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An OutputStream that does either Base64 encoding or decoding on the
+ * data written to it, writing the resulting data to another
+ * OutputStream.
+ */
+public class Base64OutputStream extends FilterOutputStream {
+    private final boolean encode;
+    private final Base64.EncoderState estate;
+    private final Base64.DecoderState dstate;
+
+    private byte[] buffer = null;
+    private int bpos = 0;
+
+    private static byte[] EMPTY = new byte[0];
+
+    /**
+     * Performs Base64 encoding on the data written to the stream,
+     * writing the encoded data to another OutputStream.
+     *
+     * @param out the OutputStream to write the encoded data to
+     * @param flags bit flags for controlling the encoder; see the
+     *        constants in {@link Base64}
+     */
+    public Base64OutputStream(OutputStream out, int flags) {
+        this(out, flags, true);
+    }
+
+    /**
+     * Performs Base64 encoding or decoding on the data written to the
+     * stream, writing the encoded/decoded data to another
+     * OutputStream.
+     *
+     * @param out the OutputStream to write the encoded data to
+     * @param flags bit flags for controlling the encoder; see the
+     *        constants in {@link Base64}
+     * @param encode true to encode, false to decode
+     */
+    public Base64OutputStream(OutputStream out, int flags, boolean encode) {
+        super(out);
+        this.encode = encode;
+        if (encode) {
+            estate = new Base64.EncoderState(flags, null);
+            dstate = null;
+        } else {
+            estate = null;
+            dstate = new Base64.DecoderState(flags, null);
+        }
+    }
+
+    public void write(int b) throws IOException {
+        // To avoid invoking the encoder/decoder routines for single
+        // bytes, we buffer up calls to write(int) in an internal
+        // byte array to transform them into writes of decently-sized
+        // arrays.
+
+        if (buffer == null) {
+            buffer = new byte[1024];
+        }
+        if (bpos >= buffer.length) {
+            // internal buffer full; write it out.
+            internalWrite(buffer, 0, bpos, false);
+            bpos = 0;
+        }
+        buffer[bpos++] = (byte) b;
+    }
+
+    /**
+     * Flush any buffered data from calls to write(int).  Needed
+     * before doing a write(byte[], int, int) or a close().
+     */
+    private void flushBuffer() throws IOException {
+        if (bpos > 0) {
+            internalWrite(buffer, 0, bpos, false);
+            bpos = 0;
+        }
+    }
+
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (len <= 0) return;
+        flushBuffer();
+        internalWrite(b, off, len, false);
+    }
+
+    public void close() throws IOException {
+        flushBuffer();
+        internalWrite(EMPTY, 0, 0, true);
+        out.close();
+    }
+
+    /**
+     * Write the given bytes to the encoder/decoder.
+     *
+     * @param finish true if this is the last batch of input, to cause
+     *        encoder/decoder state to be finalized.
+     */
+    private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
+        if (encode) {
+            // len*8/5+10 is an overestimate of the most bytes the
+            // encoder can produce for len bytes of input.
+            estate.output = embiggen(estate.output, len*8/5+10);
+            Base64.encodeInternal(b, off, len, estate, finish);
+            out.write(estate.output, 0, estate.op);
+        } else {
+            // len*3/4+10 is an overestimate of the most bytes the
+            // decoder can produce for len bytes of input.
+            dstate.output = embiggen(dstate.output, len*3/4+10);
+            if (!Base64.decodeInternal(b, off, len, dstate, finish)) {
+                throw new IOException("bad base-64");
+            }
+            out.write(dstate.output, 0, dstate.op);
+        }
+    }
+
+    /**
+     * If b.length is at least len, return b.  Otherwise return a new
+     * byte array of length len.
+     */
+    private byte[] embiggen(byte[] b, int len) {
+        if (b == null || b.length < len) {
+            return new byte[len];
+        } else {
+            return b;
+        }
+    }
+}
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/java/com/android/common/Search.java b/common/java/com/android/common/Search.java
new file mode 100644
index 0000000..55fa6f5
--- /dev/null
+++ b/common/java/com/android/common/Search.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common;
+
+/**
+ * Utilities for search implementations.
+ *
+ * @see android.app.SearchManager
+ */
+public class Search {
+
+    /**
+     * Key for the source identifier set by the application that launched a search intent.
+     * The identifier is search-source specific string. It can be used
+     * by the search provider to keep statistics of where searches are started from.
+     *
+     * The source identifier is stored in the {@link android.app.SearchManager#APP_DATA}
+     * Bundle in {@link android.content.Intent#ACTION_SEARCH} and
+     * {@link android.content.Intent#ACTION_WEB_SEARCH} intents.
+     */
+    public final static String SOURCE = "source";
+
+    private Search() { }   // don't instantiate
+}
diff --git a/common/tests/src/com/android/common/Base64Test.java b/common/tests/src/com/android/common/Base64Test.java
index 5c9712a..e6b491f 100644
--- a/common/tests/src/com/android/common/Base64Test.java
+++ b/common/tests/src/com/android/common/Base64Test.java
@@ -18,6 +18,9 @@
 
 import junit.framework.TestCase;
 
+import java.io.ByteArrayOutputStream;
+import java.util.Random;
+
 public class Base64Test extends TestCase {
     private static final String TAG = "B64Test";
 
@@ -55,6 +58,22 @@
         }
     }
 
+    /** Assert that actual equals the first len bytes of expected. */
+    private void assertEquals(byte[] expected, int len, byte[] actual, int alen) {
+        assertEquals(len, alen);
+        for (int i = 0; i < len; ++i) {
+            assertEquals(expected[i], actual[i]);
+        }
+    }
+
+    /** Assert that actual equals the first len bytes of expected. */
+    private void assertEquals(byte[] expected, byte[] actual) {
+        assertEquals(expected.length, actual.length);
+        for (int i = 0; i < expected.length; ++i) {
+            assertEquals(expected[i], actual[i]);
+        }
+    }
+
     public void testDecodeExtraChars() throws Exception {
         // padding 0
         assertEquals("hello, world", decodeString("aGVsbG8sIHdvcmxk"));
@@ -205,4 +224,187 @@
         assertEquals(out_60.replaceAll("\n", ""), encodeToString(in_60, Base64.NO_WRAP));
         assertEquals(out_61.replaceAll("\n", ""), encodeToString(in_61, Base64.NO_WRAP));
     }
+
+    /**
+     * Tests that Base64.encodeInternal does correct handling of the
+     * tail for each call.
+     */
+    public void testEncodeInternal() throws Exception {
+        byte[] input = { (byte) 0x61, (byte) 0x62, (byte) 0x63 };
+        byte[] output = new byte[100];
+
+        Base64.EncoderState state = new Base64.EncoderState(Base64.NO_PADDING | Base64.NO_WRAP,
+                                                            output);
+
+        Base64.encodeInternal(input, 0, 3, state, false);
+        assertEquals("YWJj".getBytes(), 4, state.output, state.op);
+        assertEquals(0, state.tailLen);
+
+        Base64.encodeInternal(input, 0, 3, state, false);
+        assertEquals("YWJj".getBytes(), 4, state.output, state.op);
+        assertEquals(0, state.tailLen);
+
+        Base64.encodeInternal(input, 0, 1, state, false);
+        assertEquals(0, state.op);
+        assertEquals(1, state.tailLen);
+
+        Base64.encodeInternal(input, 0, 1, state, false);
+        assertEquals(0, state.op);
+        assertEquals(2, state.tailLen);
+
+        Base64.encodeInternal(input, 0, 1, state, false);
+        assertEquals("YWFh".getBytes(), 4, state.output, state.op);
+        assertEquals(0, state.tailLen);
+
+        Base64.encodeInternal(input, 0, 2, state, false);
+        assertEquals(0, state.op);
+        assertEquals(2, state.tailLen);
+
+        Base64.encodeInternal(input, 0, 2, state, false);
+        assertEquals("YWJh".getBytes(), 4, state.output, state.op);
+        assertEquals(1, state.tailLen);
+
+        Base64.encodeInternal(input, 0, 2, state, false);
+        assertEquals("YmFi".getBytes(), 4, state.output, state.op);
+        assertEquals(0, state.tailLen);
+
+        Base64.encodeInternal(input, 0, 1, state, true);
+        assertEquals("YQ".getBytes(), 2, state.output, state.op);
+    }
+
+    /**
+     * Tests that Base64OutputStream produces exactly the same results
+     * as calling Base64.encode/.decode on an in-memory array.
+     */
+    public void testOutputStream() throws Exception {
+        int[] flagses = { Base64.DEFAULT,
+                          Base64.NO_PADDING,
+                          Base64.NO_WRAP,
+                          Base64.NO_PADDING | Base64.NO_WRAP,
+                          Base64.CRLF,
+                          Base64.WEB_SAFE };
+        int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 };
+        Random rng = new Random(32176L);
+
+        // input needs to be at least 1024 bytes to test filling up
+        // the write(int) buffer.
+        byte[] input = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
+                        "Quisque congue eleifend odio, eu ornare nulla facilisis eget. " +
+                        "Integer eget elit diam, sit amet laoreet nibh. Quisque enim " +
+                        "urna, pharetra vitae consequat eget, adipiscing eu ante. " +
+                        "Aliquam venenatis arcu nec nibh imperdiet tempor. In id dui " +
+                        "eget lorem aliquam rutrum vel vitae eros. In placerat ornare " +
+                        "pretium. Curabitur non fringilla mi. Fusce ultricies, turpis " +
+                        "eu ultrices suscipit, ligula nisi consectetur eros, dapibus " +
+                        "aliquet dui sapien a turpis. Donec ultricies varius ligula, " +
+                        "ut hendrerit arcu malesuada at. Praesent sed elit pretium " +
+                        "eros luctus gravida. In ac dolor lorem. Cras condimentum " +
+                        "convallis elementum. Phasellus vel felis in nulla ultrices " +
+                        "venenatis. Nam non tortor non orci convallis convallis. " +
+                        "Nam tristique lacinia hendrerit. Pellentesque habitant morbi " +
+                        "tristique senectus et netus et malesuada fames ac turpis " +
+                        "egestas. Vivamus cursus, nibh eu imperdiet porta, magna " +
+                        "ipsum mollis mauris, sit amet fringilla mi nisl eu mi. " +
+                        "Phasellus posuere, leo at ultricies vehicula, massa risus " +
+                        "volutpat sapien, eu tincidunt diam ipsum eget nulla. Cras " +
+                        "molestie dapibus commodo. Ut vel tellus at massa gravida " +
+                        "semper non sed orci.").getBytes();
+
+        for (int f = 0; f < flagses.length; ++f) {
+            int flags = flagses[f];
+
+            byte[] expected = Base64.encode(input, flags);
+
+            ByteArrayOutputStream baos;
+            Base64OutputStream b64os;
+            byte[] actual;
+            int p;
+
+            // ----- test encoding ("input" -> "expected") -----
+
+            // one large write(byte[]) of the whole input
+            baos = new ByteArrayOutputStream();
+            b64os = new Base64OutputStream(baos, flags);
+            b64os.write(input);
+            b64os.close();
+            actual = baos.toByteArray();
+            assertEquals(expected, actual);
+
+            // many calls to write(int)
+            baos = new ByteArrayOutputStream();
+            b64os = new Base64OutputStream(baos, flags);
+            for (int i = 0; i < input.length; ++i) {
+                b64os.write(input[i]);
+            }
+            b64os.close();
+            actual = baos.toByteArray();
+            assertEquals(expected, actual);
+
+            // intermixed sequences of write(int) with
+            // write(byte[],int,int) of various lengths.
+            baos = new ByteArrayOutputStream();
+            b64os = new Base64OutputStream(baos, flags);
+            p = 0;
+            while (p < input.length) {
+                int l = writeLengths[rng.nextInt(writeLengths.length)];
+                l = Math.min(l, input.length-p);
+                if (l >= 0) {
+                    b64os.write(input, p, l);
+                    p += l;
+                } else {
+                    l = Math.min(-l, input.length-p);
+                    for (int i = 0; i < l; ++i) {
+                        b64os.write(input[p+i]);
+                    }
+                    p += l;
+                }
+            }
+            b64os.close();
+            actual = baos.toByteArray();
+            assertEquals(expected, actual);
+
+            // ----- test decoding ("expected" -> "input") -----
+
+            // one large write(byte[]) of the whole input
+            baos = new ByteArrayOutputStream();
+            b64os = new Base64OutputStream(baos, flags, false);
+            b64os.write(expected);
+            b64os.close();
+            actual = baos.toByteArray();
+            assertEquals(input, actual);
+
+            // many calls to write(int)
+            baos = new ByteArrayOutputStream();
+            b64os = new Base64OutputStream(baos, flags, false);
+            for (int i = 0; i < expected.length; ++i) {
+                b64os.write(expected[i]);
+            }
+            b64os.close();
+            actual = baos.toByteArray();
+            assertEquals(input, actual);
+
+            // intermixed sequences of write(int) with
+            // write(byte[],int,int) of various lengths.
+            baos = new ByteArrayOutputStream();
+            b64os = new Base64OutputStream(baos, flags, false);
+            p = 0;
+            while (p < expected.length) {
+                int l = writeLengths[rng.nextInt(writeLengths.length)];
+                l = Math.min(l, expected.length-p);
+                if (l >= 0) {
+                    b64os.write(expected, p, l);
+                    p += l;
+                } else {
+                    l = Math.min(-l, expected.length-p);
+                    for (int i = 0; i < l; ++i) {
+                        b64os.write(expected[p+i]);
+                    }
+                    p += l;
+                }
+            }
+            b64os.close();
+            actual = baos.toByteArray();
+            assertEquals(input, actual);
+        }
+    }
 }
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/common/tools/make-iana-tld-pattern.py b/common/tools/make-iana-tld-pattern.py
new file mode 100755
index 0000000..ece4dcf
--- /dev/null
+++ b/common/tools/make-iana-tld-pattern.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+
+from urllib2 import urlopen
+
+TLD_PREFIX = r"""
+    /**
+     *  Regular expression pattern to match all IANA top-level domains.
+     *  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 frameworks/base/common/tools/make-iana-tld-pattern.py
+     */
+    public static final Pattern TOP_LEVEL_DOMAIN = Pattern.compile(
+"""
+TLD_SUFFIX = '");'
+
+URL_PREFIX = r"""
+    /**
+     *  Regular expression pattern to match RFC 1738 URLs
+     *  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 frameworkds/base/common/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
+"""
+
+URL_SUFFIX = r"""
+        + "|(?:(?: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
+"""
+
+class Bucket:
+    def __init__(self, baseLetter):
+        self.base=baseLetter
+        self.words=[]
+        self.letters=[]
+
+    def dump(self, isWebUrl=False, isFirst=False, isLast=False):
+        if (len(self.words) == 0) and (len(self.letters) == 0):
+            return ''
+
+        self.words.sort()
+        self.letters.sort()
+
+        output = '        ';
+
+        if isFirst:
+            if isWebUrl:
+                output += '+ "'
+            else:
+                output += '"('
+        else:
+            output += '+ "|'
+
+        if len(self.words) != 0:
+            output += '('
+
+            if isWebUrl:
+                output += '?:'
+
+        firstWord = 1
+        for word in self.words:
+            if firstWord == 0:
+                output += '|'
+            firstWord = 0
+            for letter in word:
+                if letter == '-':
+                    output += '\\\\'  # escape the '-' character.
+                output += letter
+
+        if len(self.words) > 0 and len(self.letters) > 0:
+            output += '|'
+
+        if len(self.letters) == 1:
+            output += '%c%c' % (self.base, self.letters[0])
+        elif len(self.letters) > 0:
+            output += '%c[' % self.base
+
+            for letter in self.letters:
+                output += letter
+
+            output += ']'
+
+        if len(self.words) != 0:
+            output += ')'
+
+        if not isLast:
+            output += '"'
+            output += '\n'
+
+        return output;
+
+    def add(self, line):
+        length = len(line)
+
+        if line.startswith('#') or (length == 0):
+            return;
+
+        if length == 2:
+            self.letters.append(line[1:2])
+        else:
+            self.words.append(line)
+
+def getBucket(buckets, line):
+    letter = line[0]
+    bucket = buckets.get(letter)
+
+    if bucket is None:
+        bucket = Bucket(letter)
+        buckets[letter] = bucket
+
+    return bucket
+
+def makePattern(prefix, suffix, buckets, isWebUrl=False):
+    output = prefix
+
+    output += getBucket(buckets, 'a').dump(isFirst=True, isWebUrl=isWebUrl)
+
+    for letter in range(ord('b'), ord('z')):
+        output += getBucket(buckets, chr(letter)).dump(isWebUrl=isWebUrl)
+
+    output += getBucket(buckets, 'z').dump(isLast=True, isWebUrl=isWebUrl)
+
+    if isWebUrl:
+        output += '))"'
+    else:
+        output += ')'
+
+    output += suffix
+
+    print output
+
+if __name__ == "__main__":
+    f = urlopen('http://data.iana.org/TLD/tlds-alpha-by-domain.txt')
+    domains = f.readlines()
+    f.close()
+
+    buckets = {}
+
+    for domain in domains:
+        domain = domain.lower()
+
+        if len(domain) > 0:
+            getBucket(buckets, domain[0]).add(domain.strip())
+
+    makePattern(TLD_PREFIX, TLD_SUFFIX, buckets, isWebUrl=False)
+    makePattern(URL_PREFIX, URL_SUFFIX, buckets, isWebUrl=True)
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..0a18fe5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -52,6 +52,7 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.PackageParser.Package;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
@@ -84,7 +85,8 @@
 import android.os.StatFs;
 import android.os.Vibrator;
 import android.os.FileUtils.FileStatus;
-import android.storage.StorageManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.ClipboardManager;
 import android.util.AndroidRuntimeException;
@@ -197,9 +199,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 +440,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 +491,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 +2221,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 +2244,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 {
@@ -2593,14 +2648,13 @@
         // SD-to-internal app size threshold: currently set to 1 MB
         private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
 
-        @Override
-        public int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
+        public int recommendAppInstallLocation(Package pkg) {
             // Initial implementation:
             // Package size = code size + cache size + data size
             // If code size > 1 MB, install on SD card.
             // Else install on internal NAND flash, unless space on NAND is less than 10%
 
-            if ((packageURI == null) || (appInfo == null)) {
+            if (pkg == null) {
                 return INSTALL_PARSE_FAILED_NOT_APK;
             }
 
@@ -2616,44 +2670,71 @@
 
             double pctNandFree = (double)availInternalFlashSize / (double)totalInternalFlashSize;
 
-            final String archiveFilePath = packageURI.getPath();
+            final String archiveFilePath = pkg.mScanPath;
             File apkFile = new File(archiveFilePath);
             long pkgLen = apkFile.length();
 
+            boolean auto = true;
+            // To make final copy
+            long reqInstallSize = pkgLen;
+            // For dex files
+            long reqInternalSize = 1 * pkgLen;
+            boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
+            boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize);
+            boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk &&
+                    (reqInternalSize < availInternalFlashSize);
+            boolean fitsOnInt = intThresholdOk && intAvailOk;
+
             // Consider application flags preferences as well...
-            boolean installOnlyOnSD = ((appInfo.flags & PackageManager.INSTALL_ON_SDCARD) != 0);
-
-            // These are not very precise measures, but I guess it is hard to estimate sizes
-            // before installing the package.
-            // As a shortcut, I am assuming that the package fits on NAND flash if the available
-            // space is three times that of the APK size. For SD, we only worry about the APK size.
-            // Since packages are downloaded into SD, this might not even be necessary.
-            boolean fitsOnSD = (pkgLen < availSDSize) && ((2 * pkgLen) < availInternalFlashSize);
-            boolean fitsOnInternalFlash = ((pkgLen * 3) < availInternalFlashSize);
-
-            // Does not fit, recommend no installation.
-            if (!fitsOnSD && !fitsOnInternalFlash) {
-                return INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            }
-
-            if (pkgLen < (INSTALL_ON_SD_THRESHOLD) && fitsOnInternalFlash && !(installOnlyOnSD)) {
-                // recommend internal NAND likely
-                if (pctNandFree < LOW_NAND_FLASH_TRESHOLD) {
-                    // Low space on NAND (<10%) - install on SD
-                    return INSTALL_ON_SDCARD;
+            boolean installOnlyOnSd = (pkg.installLocation ==
+                    PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+            boolean installOnlyInternal = (pkg.installLocation ==
+                    PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            if (installOnlyInternal) {
+                // If set explicitly in manifest,
+                // let that override everything else
+                auto = false;
+            } else if (installOnlyOnSd){
+                // Check if this can be accommodated on the sdcard
+                if (fitsOnSd) {
+                    auto = false;
                 }
-                return INSTALL_ON_INTERNAL_FLASH;
             } else {
-                if (fitsOnSD) {
-                    // Recommend SD card
-                    return INSTALL_ON_SDCARD;
-                } else if (fitsOnInternalFlash && (pctNandFree >= LOW_NAND_FLASH_TRESHOLD) &&
-                        !(installOnlyOnSD)) {
-                    return INSTALL_ON_INTERNAL_FLASH;
-                } else {
-                    return INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                // Check if user option is enabled
+                boolean setInstallLoc = Settings.System.getInt(mContext.getContentResolver(),
+                        Settings.System.SET_INSTALL_LOCATION, 0) != 0;
+                if (setInstallLoc) {
+                    // Pick user preference
+                    int installPreference = Settings.System.getInt(mContext.getContentResolver(),
+                            Settings.System.DEFAULT_INSTALL_LOCATION,
+                            PackageInfo.INSTALL_LOCATION_AUTO);
+                    if (installPreference == 1) {
+                        installOnlyInternal = true;
+                        auto = false;
+                    } else if (installPreference == 2) {
+                        installOnlyOnSd = true;
+                        auto = false;
+                    }
                 }
             }
+            if (!auto) {
+                if (installOnlyOnSd) {
+                    return fitsOnSd ? INSTALL_ON_SDCARD : INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                } else if (installOnlyInternal){
+                    // Check on internal flash
+                    return fitsOnInt ? INSTALL_ON_INTERNAL_FLASH : INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                }
+            }
+            // Try to install internally
+            if (fitsOnInt) {
+                return INSTALL_ON_INTERNAL_FLASH;
+            }
+            // Try the sdcard now.
+            if (fitsOnSd) {
+                return INSTALL_ON_SDCARD;
+            }
+            // Return error code
+            return INSTALL_FAILED_INSUFFICIENT_STORAGE;
         }
 
         private final ContextImpl mContext;
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/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 3046a2c..5a9a675 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1279,16 +1279,6 @@
     public final static String APP_DATA = "app_data";
 
     /**
-     * Intent app_data bundle key: Use this key with the bundle from
-     * {@link android.content.Intent#getBundleExtra
-     * content.Intent.getBundleExtra(APP_DATA)} to obtain the source identifier
-     * set by the activity that launched the search.
-     *
-     * @hide
-     */
-    public final static String SOURCE = "source";
-
-    /**
      * Intent extra data key: Use {@link android.content.Intent#getBundleExtra
      * content.Intent.getBundleExtra(SEARCH_MODE)} to get the search mode used
      * to launch the intent.
@@ -1638,8 +1628,6 @@
     /**
      * Intent action for starting the global search settings activity.
      * The global search provider should handle this intent.
-     * 
-     * @hide Pending API council approval.
      */
     public final static String INTENT_ACTION_SEARCH_SETTINGS 
             = "android.search.action.SEARCH_SETTINGS";
diff --git a/core/java/android/app/SearchableInfo.java b/core/java/android/app/SearchableInfo.java
index 9897742..4496354 100644
--- a/core/java/android/app/SearchableInfo.java
+++ b/core/java/android/app/SearchableInfo.java
@@ -73,7 +73,7 @@
     private final boolean mIncludeInGlobalSearch;
     private final boolean mQueryAfterZeroResults;
     private final boolean mAutoUrlDetect;
-    private final String mSettingsDescription;
+    private final int mSettingsDescriptionId;
     private final String mSuggestAuthority;
     private final String mSuggestPath;
     private final String mSuggestSelection;
@@ -155,11 +155,11 @@
     }
     
     /**
-     * Gets the description to use for this source in system search settings, or null if
-     * none has been specified.
+     * Gets the resource ID of the description string to use for this source in system search
+     * settings, or {@code 0} if none has been specified.
      */
-    public String getSettingsDescription() {
-        return mSettingsDescription;
+    public int getSettingsDescriptionId() {
+        return mSettingsDescriptionId;
     }
 
     /**
@@ -311,8 +311,8 @@
         mAutoUrlDetect = a.getBoolean(
                 com.android.internal.R.styleable.Searchable_autoUrlDetect, false);
 
-        mSettingsDescription = a.getString(
-                com.android.internal.R.styleable.Searchable_searchSettingsDescription);
+        mSettingsDescriptionId = a.getResourceId(
+                com.android.internal.R.styleable.Searchable_searchSettingsDescription, 0);
         mSuggestAuthority = a.getString(
                 com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
         mSuggestPath = a.getString(
@@ -495,7 +495,7 @@
                         + ",suggestAuthority=" + searchable.getSuggestAuthority()
                         + ",target=" + searchable.getSearchActivity().getClassName()
                         + ",global=" + searchable.shouldIncludeInGlobalSearch()
-                        + ",settingsDescription=" + searchable.getSettingsDescription()
+                        + ",settingsDescription=" + searchable.getSettingsDescriptionId()
                         + ",threshold=" + searchable.getSuggestThreshold());
             } else {
                 Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data");
@@ -746,7 +746,7 @@
         mQueryAfterZeroResults = in.readInt() != 0;
         mAutoUrlDetect = in.readInt() != 0;
         
-        mSettingsDescription = in.readString();
+        mSettingsDescriptionId = in.readInt();
         mSuggestAuthority = in.readString();
         mSuggestPath = in.readString();
         mSuggestSelection = in.readString();
@@ -784,7 +784,7 @@
         dest.writeInt(mQueryAfterZeroResults ? 1 : 0);
         dest.writeInt(mAutoUrlDetect ? 1 : 0);
         
-        dest.writeString(mSettingsDescription);
+        dest.writeInt(mSettingsDescriptionId);
         dest.writeString(mSuggestAuthority);
         dest.writeString(mSuggestPath);
         dest.writeString(mSuggestSelection);
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..bdbe341 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -16,6 +16,8 @@
 
 package android.content;
 
+import android.accounts.Account;
+import android.app.ActivityThread;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
@@ -30,8 +32,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.TextUtils;
-import android.accounts.Account;
 import android.util.Config;
+import android.util.EventLog;
 import android.util.Log;
 
 import java.io.File;
@@ -41,8 +43,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
+import java.util.Random;
 import java.util.ArrayList;
-import java.util.Collection;
 
 
 /**
@@ -62,7 +64,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";
@@ -140,6 +166,11 @@
     /** @hide */
     public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
 
+    // Always log queries which take 100ms+; shorter queries are
+    // sampled accordingly.
+    private static final int SLOW_THRESHOLD_MILLIS = 100;
+    private final Random mRandom = new Random();  // guarded by itself
+
     public ContentResolver(Context context) {
         mContext = context;
     }
@@ -212,12 +243,15 @@
             return null;
         }
         try {
+            long startTime = System.currentTimeMillis();
             Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);
-            if(qCursor == null) {
+            if (qCursor == null) {
                 releaseProvider(provider);
                 return null;
             }
-            //Wrap the cursor object into CursorWrapperInner object
+            long durationMillis = System.currentTimeMillis() - startTime;
+            maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
+            // Wrap the cursor object into CursorWrapperInner object
             return new CursorWrapperInner(qCursor, provider);
         } catch (RemoteException e) {
             releaseProvider(provider);
@@ -549,7 +583,11 @@
             throw new IllegalArgumentException("Unknown URL " + url);
         }
         try {
-            return provider.insert(url, values);
+            long startTime = System.currentTimeMillis();
+            Uri createdRow = provider.insert(url, values);
+            long durationMillis = System.currentTimeMillis() - startTime;
+            maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
+            return createdRow;
         } catch (RemoteException e) {
             return null;
         } finally {
@@ -604,7 +642,11 @@
             throw new IllegalArgumentException("Unknown URL " + url);
         }
         try {
-            return provider.bulkInsert(url, values);
+            long startTime = System.currentTimeMillis();
+            int rowsCreated = provider.bulkInsert(url, values);
+            long durationMillis = System.currentTimeMillis() - startTime;
+            maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
+            return rowsCreated;
         } catch (RemoteException e) {
             return 0;
         } finally {
@@ -629,7 +671,11 @@
             throw new IllegalArgumentException("Unknown URL " + url);
         }
         try {
-            return provider.delete(url, where, selectionArgs);
+            long startTime = System.currentTimeMillis();
+            int rowsDeleted = provider.delete(url, where, selectionArgs);
+            long durationMillis = System.currentTimeMillis() - startTime;
+            maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
+            return rowsDeleted;
         } catch (RemoteException e) {
             return -1;
         } finally {
@@ -657,7 +703,11 @@
             throw new IllegalArgumentException("Unknown URI " + uri);
         }
         try {
-            return provider.update(uri, values, where, selectionArgs);
+            long startTime = System.currentTimeMillis();
+            int rowsUpdated = provider.update(uri, values, where, selectionArgs);
+            long durationMillis = System.currentTimeMillis() - startTime;
+            maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
+            return rowsUpdated;
         } catch (RemoteException e) {
             return -1;
         } finally {
@@ -976,15 +1026,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 +1076,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 +1097,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) {
@@ -1159,6 +1244,78 @@
         }
     }
 
+    /**
+     * Returns sampling percentage for a given duration.
+     *
+     * Always returns at least 1%.
+     */
+    private int samplePercentForDuration(long durationMillis) {
+        if (durationMillis >= SLOW_THRESHOLD_MILLIS) {
+            return 100;
+        }
+        return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1;
+    }
+
+    private void maybeLogQueryToEventLog(long durationMillis,
+                                         Uri uri, String[] projection,
+                                         String selection, String sortOrder) {
+        int samplePercent = samplePercentForDuration(durationMillis);
+        if (samplePercent < 100) {
+            synchronized (mRandom) {
+                if (mRandom.nextInt(100) >= samplePercent) {
+                    return;
+                }
+            }
+        }
+
+        StringBuilder projectionBuffer = new StringBuilder(100);
+        if (projection != null) {
+            for (int i = 0; i < projection.length; ++i) {
+                // Note: not using a comma delimiter here, as the
+                // multiple arguments to EventLog.writeEvent later
+                // stringify with a comma delimiter, which would make
+                // parsing uglier later.
+                if (i != 0) projectionBuffer.append('/');
+                projectionBuffer.append(projection[i]);
+            }
+        }
+
+        // ActivityThread.currentPackageName() only returns non-null if the
+        // current thread is an application main thread.  This parameter tells
+        // us whether an event loop is blocked, and if so, which app it is.
+        String blockingPackage = ActivityThread.currentPackageName();
+
+        EventLog.writeEvent(
+            EventLogTags.CONTENT_QUERY_OPERATION,
+            uri.toString(),
+            projectionBuffer.toString(),
+            selection != null ? selection : "",
+            sortOrder != null ? sortOrder : "",
+            durationMillis,
+            blockingPackage != null ? blockingPackage : "",
+            samplePercent);
+    }
+
+    private void maybeLogUpdateToEventLog(
+        long durationMillis, Uri uri, String operation, String selection) {
+        int samplePercent = samplePercentForDuration(durationMillis);
+        if (samplePercent < 100) {
+            synchronized (mRandom) {
+                if (mRandom.nextInt(100) >= samplePercent) {
+                    return;
+                }
+            }
+        }
+        String blockingPackage = ActivityThread.currentPackageName();
+        EventLog.writeEvent(
+            EventLogTags.CONTENT_UPDATE_OPERATION,
+            uri.toString(),
+            operation,
+            selection != null ? selection : "",
+            durationMillis,
+            blockingPackage != null ? blockingPackage : "",
+            samplePercent);
+    }
 
     private final class CursorWrapperInner extends CursorWrapper {
         private IContentProvider mContentProvider;
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/EventLogTags.logtags b/core/java/android/content/EventLogTags.logtags
new file mode 100644
index 0000000..a815b95
--- /dev/null
+++ b/core/java/android/content/EventLogTags.logtags
@@ -0,0 +1,6 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package android.content;
+
+52002 content_query_operation (uri|3),(projection|3),(selection|3),(sortorder|3),(time|1|3),(blocking_package|3),(sample_percent|1|6)
+52003 content_update_operation (uri|3),(operation|3),(selection|3),(time|1|3),(blocking_package|3),(sample_percent|1|6)
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..9ff2e25 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);
@@ -1037,7 +1041,7 @@
                     continue;
                 }
 
-                SyncStorageEngine.AuthorityInfo settings = mSyncStorageEngine.getAuthority(
+                SyncStorageEngine.AuthorityInfo settings = mSyncStorageEngine.getOrCreateAuthority(
                         account, syncAdapterType.type.authority);
                 SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings);
                 pw.print("    "); pw.print(settings.authority);
@@ -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/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 07a1f46..fcb910d 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -632,9 +632,11 @@
         }
     }
 
-    public AuthorityInfo getAuthority(Account account, String authority) {
+    public AuthorityInfo getOrCreateAuthority(Account account, String authority) {
         synchronized (mAuthorities) {
-            return getAuthorityLocked(account, authority, null);
+            return getOrCreateAuthorityLocked(account, authority,
+                    -1 /* assign a new identifier if creating a new authority */,
+                    true /* write to storage if this results in a change */);
         }
     }
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 808c839..123d9b7 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -178,12 +178,20 @@
     public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 1<<13;
     
     /**
+     * Value for {@link #flags}: set to true if this application would like to
+     * request the VM to operate under the safe mode. Comes from
+     * {@link android.R.styleable#AndroidManifestApplication_safeMode
+     * android:safeMode} of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_VM_SAFE_MODE = 1<<14;
+
+    /**
      * Value for {@link #flags}: this is false if the application has set
      * its android:allowBackup to false, true otherwise.
      * 
      * {@hide}
      */
-    public static final int FLAG_ALLOW_BACKUP = 1<<14;
+    public static final int FLAG_ALLOW_BACKUP = 1<<15;
 
     /**
      * Value for {@link #flags}: this is false if the application has set
@@ -194,7 +202,7 @@
      *
      * {@hide}
      */
-    public static final int FLAG_KILL_AFTER_RESTORE = 1<<15;
+    public static final int FLAG_KILL_AFTER_RESTORE = 1<<16;
 
     /**
      * Value for {@link #flags}: this is true if the application has set
@@ -205,7 +213,7 @@
      *
      * {@hide}
      */
-    public static final int FLAG_RESTORE_NEEDS_APPLICATION = 1<<16;
+    public static final int FLAG_RESTORE_NEEDS_APPLICATION = 1<<17;
 
     /**
      * Value for {@link #flags}: this is true if the application has set
@@ -215,7 +223,7 @@
      *
      * {@hide}
      */
-    public static final int FLAG_NEVER_ENCRYPT = 1<<17;
+    public static final int FLAG_NEVER_ENCRYPT = 1<<18;
 
     /**
      * Value for {@link #flags}: Set to true if the application has been
@@ -223,7 +231,7 @@
      *
      * {@hide}
      */
-    public static final int FLAG_FORWARD_LOCK = 1<<18;
+    public static final int FLAG_FORWARD_LOCK = 1<<19;
 
     /**
      * Value for {@link #flags}: Set to true if the application is
@@ -231,7 +239,7 @@
      *
      * {@hide}
      */
-    public static final int FLAG_ON_SDCARD = 1<<19;
+    public static final int FLAG_ON_SDCARD = 1<<20;
 
     /**
      * Value for {@link #flags}: Set to true if the application is
@@ -239,7 +247,7 @@
      *
      * {@hide}
      */
-    public static final int FLAG_NATIVE_DEBUGGABLE = 1<<20;
+    public static final int FLAG_NATIVE_DEBUGGABLE = 1<<21;
 
     /**
      * Flags associated with the application.  Any combination of
@@ -250,7 +258,7 @@
      * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
      * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
      * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS},
-     * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}
+     * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE}
      */
     public int flags = 0;
     
@@ -270,6 +278,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/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index a8ce889..c003355 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -131,6 +131,34 @@
      * The features that this application has said it requires.
      */
     public FeatureInfo[] reqFeatures;
+
+    /**
+     * Constant corresponding to <code>auto</code> in
+     * the {@link android.R.attr#installLocation} attribute.
+     * @hide
+     */
+    public static final int INSTALL_LOCATION_AUTO = 0;
+    /**
+     * Constant corresponding to <code>internalOnly</code> in
+     * the {@link android.R.attr#installLocation} attribute.
+     * @hide
+     */
+    public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1;
+    /**
+     * Constant corresponding to <code>preferExternal</code> in
+     * the {@link android.R.attr#installLocation} attribute.
+     * @hide
+     */
+    public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2;
+    /**
+     * The launch mode style requested by the activity.  From the
+     * {@link android.R.attr#installLocation} attribute, one of
+     * {@link #INSTALL_LOCATION_AUTO},
+     * {@link #INSTALL_LOCATION_INTERNAL_ONLY},
+     * {@link #INSTALL_LOCATION_PREFER_EXTERNAL}
+     * @hide
+     */
+    public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
     
     public PackageInfo() {
     }
@@ -168,6 +196,7 @@
         dest.writeTypedArray(signatures, parcelableFlags);
         dest.writeTypedArray(configPreferences, parcelableFlags);
         dest.writeTypedArray(reqFeatures, parcelableFlags);
+        dest.writeInt(installLocation);
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -202,5 +231,6 @@
         signatures = source.createTypedArray(Signature.CREATOR);
         configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
         reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
+        installLocation = source.readInt();
     }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 745628a..a61eab9 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,11 +622,19 @@
     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.
+     * If applications explicitly set installLocation in their manifest, that
+     * preference takes precedence. If not a recommended location is returned
+     * based on current available storage on internal flash or sdcard.
+     * @param pkgInfo PackageParser.Package of the package that is to be installed.
      * Call utility method to obtain.
-     * @param packageURI URI identifying the package's APK file.
      * @return {@link INSTALL_ON_INTERNAL_FLASH} if it is best to install package on internal
      * storage, {@link INSTALL_ON_SDCARD} if it is best to install package on SD card,
      * and {@link INSTALL_FAILED_INSUFFICIENT_STORAGE} if insufficient space to safely install
@@ -626,7 +643,7 @@
      * This recommendation does take into account the package's own flags.
      * @hide
      */
-    public abstract int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI);
+    public abstract int recommendAppInstallLocation(PackageParser.Package pkg);
 
     /**
      * Retrieve overall information about an application package that is
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bac55cc..d97b3dd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -177,6 +177,7 @@
         pi.sharedUserId = p.mSharedUserId;
         pi.sharedUserLabel = p.mSharedUserLabel;
         pi.applicationInfo = p.applicationInfo;
+        pi.installLocation = p.installLocation;
         if ((flags&PackageManager.GET_GIDS) != 0) {
             pi.gids = gids;
         }
@@ -709,6 +710,9 @@
                     com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
         }
         sa.recycle();
+        pkg.installLocation = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_installLocation,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
 
         // Resource boolean are -1, so 1 means we don't know the value.
         int supportsSmallScreens = 1;
@@ -1425,6 +1429,12 @@
         }
 
         if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_safeMode,
+                false)) {
+            ai.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE;
+        }
+
+        if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
                 true)) {
             ai.flags |= ApplicationInfo.FLAG_HAS_CODE;
@@ -2138,7 +2148,7 @@
                     havePerm = true;
                 }
                 if (writePermission != null) {
-                    writePermission = readPermission.intern();
+                    writePermission = writePermission.intern();
                     havePerm = true;
                 }
 
@@ -2610,6 +2620,8 @@
          */
         public ArrayList<FeatureInfo> reqFeatures = null;
 
+        public int installLocation;
+
         public Package(String _name) {
             packageName = _name;
             applicationInfo.packageName = _name;
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/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 47c2cac..c0bff66 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -723,6 +723,7 @@
         private static final String KEY_FOCAL_LENGTH = "focal-length";
         private static final String KEY_HORIZONTAL_VIEW_ANGLE = "horizontal-view-angle";
         private static final String KEY_VERTICAL_VIEW_ANGLE = "vertical-view-angle";
+        private static final String KEY_EXPOSURE_COMPENSATION = "exposure-compensation";
         // Parameter key suffix for supported values.
         private static final String SUPPORTED_VALUES_SUFFIX = "-values";
 
@@ -1051,7 +1052,8 @@
         }
 
         /**
-         * Sets the rate at which preview frames are received.
+         * Sets the rate at which preview frames are received. This is the
+         * target frame rate. The actual frame rate depends on the driver.
          *
          * @param fps the frame rate (frames per second)
          */
@@ -1060,8 +1062,9 @@
         }
 
         /**
-         * Returns the setting for the rate at which preview frames
-         * are received.
+         * Returns the setting for the rate at which preview frames are
+         * received. This is the target frame rate. The actual frame rate
+         * depends on the driver.
          *
          * @return the frame rate setting (frames per second)
          */
@@ -1540,6 +1543,41 @@
         }
 
         /**
+         * Gets the current exposure compensation setting.
+         *
+         * @return the current exposure compensation value multiplied by 100.
+         *         null if exposure compensation is not supported. Ex: -100
+         *         means -1 EV. 130 means +1.3 EV.
+         * @hide
+         */
+        public int getExposureCompensation() {
+            return getInt(KEY_EXPOSURE_COMPENSATION);
+        }
+
+        /**
+         * Sets the exposure compensation.
+         *
+         * @param value exposure compensation multiplied by 100. Ex: -100 means
+         *        -1 EV. 130 means +1.3 EV.
+         * @hide
+         */
+        public void setExposureCompensation(int value) {
+            set(KEY_EXPOSURE_COMPENSATION, value);
+        }
+
+        /**
+         * Gets the supported exposure compensation.
+         *
+         * @return a List of Integer constants. null if exposure compensation is
+         *         not supported. The list is sorted from small to large. Ex:
+         *         -100, -66, -33, 0, 33, 66, 100.
+         * @hide
+         */
+        public List<Integer> getSupportedExposureCompensation() {
+            return splitInt(get(KEY_EXPOSURE_COMPENSATION + SUPPORTED_VALUES_SUFFIX));
+        }
+
+        /**
          * Gets current zoom value. This also works when smooth zoom is in
          * progress.
          *
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/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index e40f1b8..ed76b15 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -40,224 +40,174 @@
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 
+import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
 import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
 import org.apache.harmony.xnet.provider.jsse.SSLContextImpl;
 import org.apache.harmony.xnet.provider.jsse.SSLParameters;
 
 /**
- * SSLSocketFactory that provides optional (on debug devices, only) skipping of ssl certificfate
- * chain validation and custom read timeouts used just when connecting to the server/negotiating
- * an ssl session.
- *
- * You can skip the ssl certificate checking at runtime by setting socket.relaxsslcheck=yes on
- * devices that do not have have ro.secure set.
+ * SSLSocketFactory implementation with several extra features:
+ * <ul>
+ * <li>Timeout specification for SSL handshake operations
+ * <li>Optional SSL session caching with {@link SSLSessionCache}
+ * <li>On development devices, "setprop socket.relaxsslcheck yes" bypasses all
+ * SSL certificate checks, for testing with development servers
+ * </ul>
+ * Note that the handshake timeout does not apply to actual connection.
+ * If you want a connection timeout as well, use {@link #createSocket()} and
+ * {@link Socket#connect(SocketAddress, int)}.
  */
 public class SSLCertificateSocketFactory extends SSLSocketFactory {
+    private static final String TAG = "SSLCertificateSocketFactory";
 
-    private static final String LOG_TAG = "SSLCertificateSocketFactory";
-
-    private static final TrustManager[] TRUST_MANAGER = new TrustManager[] {
+    private static final TrustManager[] INSECURE_TRUST_MANAGER = new TrustManager[] {
         new X509TrustManager() {
-            public X509Certificate[] getAcceptedIssuers() {
-                return null;
-            }
-
-            public void checkClientTrusted(X509Certificate[] certs,
-                    String authType) { }
-
-            public void checkServerTrusted(X509Certificate[] certs,
-                    String authType) { }
+            public X509Certificate[] getAcceptedIssuers() { return null; }
+            public void checkClientTrusted(X509Certificate[] certs, String authType) { }
+            public void checkServerTrusted(X509Certificate[] certs, String authType) { }
         }
     };
 
-    private final SSLSocketFactory mFactory;
+    private SSLSocketFactory mInsecureFactory = null;
+    private SSLSocketFactory mSecureFactory = null;
 
-    private final int mSocketReadTimeoutForSslHandshake;
+    private final int mHandshakeTimeoutMillis;
+    private final SSLClientSessionCache mSessionCache;
 
-    /**
-     * Do not use this constructor (will be deprecated).  Use {@link #getDefault(int)} instead.
-     */
-    public SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake)
-            throws NoSuchAlgorithmException, KeyManagementException {
-        this(socketReadTimeoutForSslHandshake, null /* cache */);
+    /** @deprecated Use {@link #getDefault(int)} instead. */
+    public SSLCertificateSocketFactory(int handshakeTimeoutMillis) {
+        this(handshakeTimeoutMillis, null /* cache */);
     }
 
-    private SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake,
-            SSLClientSessionCache cache) throws NoSuchAlgorithmException, KeyManagementException {
-        SSLContextImpl sslContext = new SSLContextImpl();
-        sslContext.engineInit(null /* kms */,
-            TRUST_MANAGER, new java.security.SecureRandom(),
-            cache /* client cache */, null /* server cache */);
-        this.mFactory = sslContext.engineGetSocketFactory();
-        this.mSocketReadTimeoutForSslHandshake = socketReadTimeoutForSslHandshake;
+    private SSLCertificateSocketFactory(int handshakeTimeoutMillis, SSLSessionCache cache) {
+        mHandshakeTimeoutMillis = handshakeTimeoutMillis;
+        mSessionCache = cache == null ? null : cache.mSessionCache;
     }
 
     /**
      * Returns a new instance of a socket factory using the specified socket read
      * timeout while connecting with the server/negotiating an ssl session.
      *
-     * @param socketReadTimeoutForSslHandshake the socket read timeout used for performing
-     *        ssl handshake. The socket read timeout is set back to 0 after the handshake.
-     * @return a new SocketFactory, or null on error
+     * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
+     *         for none.  The socket timeout is reset to 0 after the handshake.
+     * @return a new SocketFactory with the specified parameters
      */
-    public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake) {
-        return getDefault(socketReadTimeoutForSslHandshake, null /* cache */);
+    public static SocketFactory getDefault(int handshakeTimeoutMillis) {
+        return getDefault(handshakeTimeoutMillis, null /* cache */);
     }
 
     /**
-     * Returns a new instance of a socket factory using the specified socket read
-     * timeout while connecting with the server/negotiating an ssl session.
+     * Returns a new instance of a socket factory using the specified socket
+     * read timeout while connecting with the server/negotiating an ssl session
      * Persists ssl sessions using the provided {@link SSLClientSessionCache}.
      *
-     * @param socketReadTimeoutForSslHandshake the socket read timeout used for performing
-     *        ssl handshake. The socket read timeout is set back to 0 after the handshake.
-     * @param cache The {@link SSLClientSessionCache} to use, if any.
-     * @return a new SocketFactory, or null on error
+     * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
+     *         for none.  The socket timeout is reset to 0 after the handshake.
+     * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
+     * @return a new SocketFactory with the specified parameters
+     */
+    public static SocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) {
+        return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache);
+    }
+
+    /**
+     * Returns a socket factory (also named SSLSocketFactory, but in a different
+     * namespace) for use with the Apache HTTP stack.
      *
-     * @hide
-    */
-    public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake,
-            SSLClientSessionCache cache) {
+     * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
+     *         for none.  The socket timeout is reset to 0 after the handshake.
+     * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
+     * @return a new SocketFactory with the specified parameters
+     */
+    public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(
+            int handshakeTimeoutMillis,
+            SSLSessionCache cache) {
+        return new org.apache.http.conn.ssl.SSLSocketFactory(
+                new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache));
+    }
+
+    private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) {
         try {
-            return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake, cache);
-        } catch (NoSuchAlgorithmException e) {
-            Log.e(LOG_TAG, 
-                    "SSLCertifcateSocketFactory.getDefault" +
-                    " NoSuchAlgorithmException " , e);
-            return null;
+            SSLContextImpl sslContext = new SSLContextImpl();
+            sslContext.engineInit(null, trustManagers, null, mSessionCache, null);
+            return sslContext.engineGetSocketFactory();
         } catch (KeyManagementException e) {
-            Log.e(LOG_TAG, 
-                    "SSLCertifcateSocketFactory.getDefault" +
-                    " KeyManagementException " , e);
-            return null; 
+            Log.wtf(TAG, e);
+            return (SSLSocketFactory) SSLSocketFactory.getDefault();  // Fallback
         }
     }
 
-    private boolean hasValidCertificateChain(Certificate[] certs) 
-            throws IOException {
-        boolean trusted = (certs != null && (certs.length > 0));
-
-        if (trusted) {
-            try {
-                // the authtype we pass in doesn't actually matter
-                SSLParameters.getDefaultTrustManager()
-                        .checkServerTrusted((X509Certificate[]) certs, "RSA");
-            } catch (GeneralSecurityException e) { 
-                String exceptionMessage = e != null ? e.getMessage() : "none";
-                if (Config.LOGD) {
-                    Log.d(LOG_TAG,"hasValidCertificateChain(): sec. exception: "
-                         + exceptionMessage);
-                }
-                trusted = false;
-            }
-        }
-
-        return trusted;
-    }
-
-    private void validateSocket(SSLSocket sslSock, String destHost) 
-            throws IOException
-    {
-        if (Config.LOGV) {
-            Log.v(LOG_TAG,"validateSocket() to host "+destHost);
-        }
-
-        String relaxSslCheck = SystemProperties.get("socket.relaxsslcheck");
-        String secure = SystemProperties.get("ro.secure");
-
+    private synchronized SSLSocketFactory getDelegate() {
         // only allow relaxing the ssl check on non-secure builds where the relaxation is
         // specifically requested.
-        if ("0".equals(secure) && "yes".equals(relaxSslCheck)) {
-            if (Config.LOGD) {
-                Log.d(LOG_TAG,"sys prop socket.relaxsslcheck is set," +
-                        " ignoring invalid certs");
+        if ("0".equals(SystemProperties.get("ro.secure")) &&
+            "yes".equals(SystemProperties.get("socket.relaxsslcheck"))) {
+            if (mInsecureFactory == null) {
+                Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***");
+                mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER);
             }
-            return;
-        }
-
-        Certificate[] certs = null;
-        sslSock.setUseClientMode(true);
-        sslSock.startHandshake();
-        certs = sslSock.getSession().getPeerCertificates();
-
-        // check that the root certificate in the chain belongs to
-        // a CA we trust
-        if (certs == null) {
-            Log.e(LOG_TAG, 
-                    "[SSLCertificateSocketFactory] no trusted root CA");
-            throw new IOException("no trusted root CA");
-        }
-
-        if (Config.LOGV) {
-            Log.v(LOG_TAG,"validateSocket # certs = " +certs.length);
-        }
-
-        if (!hasValidCertificateChain(certs)) {
-            if (Config.LOGD) {
-                Log.d(LOG_TAG,"validateSocket(): certificate untrusted!");
+            return mInsecureFactory;
+        } else {
+            if (mSecureFactory == null) {
+                mSecureFactory = makeSocketFactory(null);
             }
-            throw new IOException("Certificate untrusted");
-        }
-
-        X509Certificate lastChainCert = (X509Certificate) certs[0];
-
-        if (!DomainNameValidator.match(lastChainCert, destHost)) {
-            if (Config.LOGD) {
-                Log.d(LOG_TAG,"validateSocket(): domain name check failed");
-            }
-            throw new IOException("Domain Name check failed");
+            return mSecureFactory;
         }
     }
 
-    public Socket createSocket(Socket socket, String s, int i, boolean flag)
-            throws IOException
-    {
-        throw new IOException("Cannot validate certification without a hostname");       
+    @Override
+    public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
+        OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
+        s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        return s;
     }
 
-    public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr2, int j)
-            throws IOException
-    {
-        throw new IOException("Cannot validate certification without a hostname");       
+    @Override
+    public Socket createSocket() throws IOException {
+        OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
+        s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        return s;
     }
 
-    public Socket createSocket(InetAddress inaddr, int i) throws IOException {
-        throw new IOException("Cannot validate certification without a hostname");       
+    @Override
+    public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort)
+            throws IOException {
+        OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
+                addr, port, localAddr, localPort);
+        s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        return s;
     }
 
-    public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException {
-        SSLSocket sslSock = (SSLSocket) mFactory.createSocket(s, i, inaddr, j);
-
-        if (mSocketReadTimeoutForSslHandshake >= 0) {
-            sslSock.setSoTimeout(mSocketReadTimeoutForSslHandshake);
-        }
-
-        validateSocket(sslSock,s);
-        sslSock.setSoTimeout(0);
-        
-        return sslSock;
+    @Override
+    public Socket createSocket(InetAddress addr, int port) throws IOException {
+        OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
+        s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        return s;
     }
 
-    public Socket createSocket(String s, int i) throws IOException {
-        SSLSocket sslSock = (SSLSocket) mFactory.createSocket(s, i);
-
-        if (mSocketReadTimeoutForSslHandshake >= 0) {
-            sslSock.setSoTimeout(mSocketReadTimeoutForSslHandshake);
-        }
-        
-        validateSocket(sslSock,s);
-        sslSock.setSoTimeout(0);
-
-        return sslSock;
+    @Override
+    public Socket createSocket(String host, int port, InetAddress localAddr, int localPort)
+            throws IOException {
+        OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
+                host, port, localAddr, localPort);
+        s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        return s;
     }
 
+    @Override
+    public Socket createSocket(String host, int port) throws IOException {
+        OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
+        s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        return s;
+    }
+
+    @Override
     public String[] getDefaultCipherSuites() {
-        return mFactory.getSupportedCipherSuites();
+        return getDelegate().getSupportedCipherSuites();
     }
 
+    @Override
     public String[] getSupportedCipherSuites() {
-        return mFactory.getSupportedCipherSuites();
+        return getDelegate().getSupportedCipherSuites();
     }
 }
-
-
diff --git a/core/java/android/net/SSLSessionCache.java b/core/java/android/net/SSLSessionCache.java
new file mode 100644
index 0000000..4cbeb94
--- /dev/null
+++ b/core/java/android/net/SSLSessionCache.java
@@ -0,0 +1,68 @@
+/*
+ * 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.net;
+
+import org.apache.harmony.xnet.provider.jsse.FileClientSessionCache;
+import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * File-based cache of established SSL sessions.  When re-establishing a
+ * connection to the same server, using an SSL session cache can save some time,
+ * power, and bandwidth by skipping directly to an encrypted stream.
+ * This is a persistent cache which can span executions of the application.
+ *
+ * @see SSLCertificateSocketFactory
+ */
+public final class SSLSessionCache {
+    private static final String TAG = "SSLSessionCache";
+    /* package */ final SSLClientSessionCache mSessionCache;
+
+    /**
+     * Create a session cache using the specified directory.
+     * Individual session entries will be files within the directory.
+     * Multiple instances for the same directory share data internally.
+     *
+     * @param dir to store session files in (created if necessary)
+     * @throws IOException if the cache can't be opened
+     */
+    public SSLSessionCache(File dir) throws IOException {
+        mSessionCache = FileClientSessionCache.usingDirectory(dir);
+    }
+
+    /**
+     * Create a session cache at the default location for this app.
+     * Multiple instances share data internally.
+     *
+     * @param context for the application
+     */
+    public SSLSessionCache(Context context) {
+        File dir = context.getDir("sslcache", Context.MODE_PRIVATE);
+        SSLClientSessionCache cache = null;
+        try {
+            cache = FileClientSessionCache.usingDirectory(dir);
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to create SSL session cache in " + dir, e);
+        }
+        mSessionCache = cache;
+    }
+}
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/Process.java b/core/java/android/os/Process.java
index 699ddb2..4887783 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -504,6 +504,9 @@
             argsForZygote.add("--runtime-init");
             argsForZygote.add("--setuid=" + uid);
             argsForZygote.add("--setgid=" + gid);
+            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
+                argsForZygote.add("--enable-safemode");
+            }
             if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
                 argsForZygote.add("--enable-debugger");
             }
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..3d1ef25 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.
@@ -120,6 +109,11 @@
     int unmountSecureContainer(String id);
 
     /*
+     * Returns true if the specified container is mounted
+     */
+    boolean isSecureContainerMounted(String id);
+
+    /*
      * Rename an unmounted secure container.
      * Returns an int consistent with MountServiceResultCode
      */
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/provider/Settings.java b/core/java/android/provider/Settings.java
index bacaf43..076903b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1473,6 +1473,21 @@
         public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse";
 
         /**
+         * Let user pick default install location.
+         * @hide
+         */
+        public static final String SET_INSTALL_LOCATION = "set_install_location";
+
+        /**
+         * Default install location value.
+         * 0 = auto, let system decide
+         * 1 = internal
+         * 2 = sdcard
+         * @hide
+         */
+        public static final String DEFAULT_INSTALL_LOCATION = "default_install_location";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          * @hide
@@ -2799,22 +2814,6 @@
                 "sms_outgoing_check_max_count";
 
         /**
-         * Enable use of ssl session caching.
-         * 'db' - save each session in a (per process) database
-         * 'file' - save each session in a (per process) file
-         * not set or any other value - normal java in-memory caching
-         * @hide
-         */
-        public static final String SSL_SESSION_CACHE = "ssl_session_cache";
-
-        /**
-         * How many bytes long a message has to be, in order to be gzipped.
-         * @hide
-         */
-        public static final String SYNC_MIN_GZIP_BYTES =
-                "sync_min_gzip_bytes";
-
-        /**
          * The number of promoted sources in GlobalSearch.
          * @hide
          */
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/speech/RecognitionListener.java b/core/java/android/speech/RecognitionListener.java
index 5434887..2f5bcc3 100644
--- a/core/java/android/speech/RecognitionListener.java
+++ b/core/java/android/speech/RecognitionListener.java
@@ -79,7 +79,8 @@
      * time between {@link #onBeginningOfSpeech()} and {@link #onResults(Bundle)} when partial
      * results are ready. This method may be called zero, one or multiple times for each call to
      * {@link RecognitionManager#startListening(Intent)}, depending on the speech recognition
-     * service implementation.
+     * service implementation.  To request partial results, use
+     * {@link RecognizerIntent#EXTRA_PARTIAL_RESULTS}
      * 
      * @param partialResults the returned results. To retrieve the results in
      *        ArrayList&lt;String&gt; format use {@link Bundle#getStringArrayList(String)} with
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 334b049..241a8b0 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -77,6 +77,7 @@
      *   <li>{@link #EXTRA_PROMPT}
      *   <li>{@link #EXTRA_LANGUAGE}
      *   <li>{@link #EXTRA_MAX_RESULTS}
+     *   <li>{@link #EXTRA_PARTIAL_RESULTS}
      * </ul>
      * 
      * <p> Result extras (returned in the result, not to be specified in the request):
@@ -166,6 +167,13 @@
     public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
 
     /**
+     * Optional boolean to indicate whether partial results should be returned by the recognizer
+     * as the user speaks (default is false).  The server may ignore a request for partial
+     * results in some or all cases.
+     */
+    public static final String EXTRA_PARTIAL_RESULTS = "android.speech.extra.PARTIAL_RESULTS";
+
+    /**
      * When the intent is {@link #ACTION_RECOGNIZE_SPEECH}, the speech input activity will
      * return results to you via the activity results mechanism.  Alternatively, if you use this
      * extra to supply a PendingIntent, the results will be added to its bundle and the 
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/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 9581080..9a8ee02 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -61,6 +61,7 @@
 
     float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
     float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
+    int mLastTouch;
 
     private VelocityTracker mNext;
 
@@ -105,8 +106,11 @@
      * Reset the velocity tracker back to its initial state.
      */
     public void clear() {
-        for (int i = 0; i < MotionEvent.BASE_AVAIL_POINTERS; i++) {
-            mPastTime[i][0] = 0;
+        final long[][] pastTime = mPastTime;
+        for (int p = 0; p < MotionEvent.BASE_AVAIL_POINTERS; p++) {
+            for (int i = 0; i < NUM_PAST; i++) {
+                pastTime[p][i] = 0;
+            }
         }
     }
     
@@ -133,42 +137,11 @@
     }
 
     private void addPoint(int pos, float x, float y, long time) {
-        int drop = -1;
-        int i;
-        if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time);
-        final long[] pastTime = mPastTime[pos];
-        for (i=0; i<NUM_PAST; i++) {
-            if (pastTime[i] == 0) {
-                break;
-            } else if (pastTime[i] < time-LONGEST_PAST_TIME) {
-                if (localLOGV) Log.v(TAG, "Dropping past too old at "
-                        + i + " time=" + pastTime[i]);
-                drop = i;
-            }
-        }
-        if (localLOGV) Log.v(TAG, "Add index: " + i);
-        if (i == NUM_PAST && drop < 0) {
-            drop = 0;
-        }
-        if (drop == i) drop--;
-        final float[] pastX = mPastX[pos];
-        final float[] pastY = mPastY[pos];
-        if (drop >= 0) {
-            if (localLOGV) Log.v(TAG, "Dropping up to #" + drop);
-            final int start = drop+1;
-            final int count = NUM_PAST-drop-1;
-            System.arraycopy(pastX, start, pastX, 0, count);
-            System.arraycopy(pastY, start, pastY, 0, count);
-            System.arraycopy(pastTime, start, pastTime, 0, count);
-            i -= (drop+1);
-        }
-        pastX[i] = x;
-        pastY[i] = y;
-        pastTime[i] = time;
-        i++;
-        if (i < NUM_PAST) {
-            pastTime[i] = 0;
-        }
+        final int lastTouch = (mLastTouch + 1) % NUM_PAST;
+        mPastX[pos][lastTouch] = x;
+        mPastY[pos][lastTouch] = y;
+        mPastTime[pos][lastTouch] = time;
+        mLastTouch = lastTouch;
     }
 
     /**
@@ -199,36 +172,41 @@
             final float[] pastX = mPastX[pos];
             final float[] pastY = mPastY[pos];
             final long[] pastTime = mPastTime[pos];
-
+            final int lastTouch = mLastTouch;
+        
+            // find oldest acceptable time
+            int oldestTouch = lastTouch;
+            if (pastTime[lastTouch] > 0) { // cleared ?
+                oldestTouch = (lastTouch + 1) % NUM_PAST;
+                final float acceptableTime = pastTime[lastTouch] - LONGEST_PAST_TIME;
+                while (pastTime[oldestTouch] < acceptableTime) {
+                    oldestTouch = (oldestTouch + 1) % NUM_PAST;
+                }
+            }
+        
             // Kind-of stupid.
-            final float oldestX = pastX[0];
-            final float oldestY = pastY[0];
-            final long oldestTime = pastTime[0];
+            final float oldestX = pastX[oldestTouch];
+            final float oldestY = pastY[oldestTouch];
+            final long oldestTime = pastTime[oldestTouch];
             float accumX = 0;
             float accumY = 0;
-            int N=0;
-            while (N < NUM_PAST) {
-                if (pastTime[N] == 0) {
-                    break;
-                }
-                N++;
-            }
+            float N = (lastTouch - oldestTouch + NUM_PAST) % NUM_PAST + 1;
             // Skip the last received event, since it is probably pretty noisy.
             if (N > 3) N--;
 
             for (int i=1; i < N; i++) {
-                final int dur = (int)(pastTime[i] - oldestTime);
+                final int j = (oldestTouch + i) % NUM_PAST;
+                final int dur = (int)(pastTime[j] - oldestTime);
                 if (dur == 0) continue;
-                float dist = pastX[i] - oldestX;
+                float dist = pastX[j] - oldestX;
                 float vel = (dist/dur) * units;   // pixels/frame.
-                if (accumX == 0) accumX = vel;
-                else accumX = (accumX + vel) * .5f;
-
-                dist = pastY[i] - oldestY;
+                accumX = (accumX == 0) ? vel : (accumX + vel) * .5f;
+            
+                dist = pastY[j] - oldestY;
                 vel = (dist/dur) * units;   // pixels/frame.
-                if (accumY == 0) accumY = vel;
-                else accumY = (accumY + vel) * .5f;
+                accumY = (accumY == 0) ? vel : (accumY + vel) * .5f;
             }
+            
             mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
                     : Math.min(accumX, maxVelocity);
             mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5c465e..fb19dcf 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.detach();
+        }
         mCurrentAnimation = null;
     }
 
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 000e4ce..349b7e5 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -256,6 +256,37 @@
     }
 
     /**
+     * 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;
+    }
+
+    /**
+     * @hide
+     */
+    public void detach() {
+        if (mStarted && !mEnded) {
+            mEnded = true;
+            if (mListener != null) mListener.onAnimationEnd(this);
+        }
+    }
+
+    /**
      * Whether or not the animation has been initialized.
      *
      * @return Has this animation been initialized.
@@ -745,10 +776,10 @@
         if (expired) {
             if (mRepeatCount == mRepeated) {
                 if (!mEnded) {
+                    mEnded = true;
                     if (mListener != null) {
                         mListener.onAnimationEnd(this);
                     }
-                    mEnded = true;
                 }
             } else {
                 if (mRepeatCount > 0) {
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/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/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 631e7d8..da0c5a2 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -295,7 +295,10 @@
         /** from --peer-wait */
         boolean peerWait;
 
-        /** from --enable-debugger, --enable-checkjni, --enable-assert */
+        /**
+         * From --enable-debugger, --enable-checkjni, --enable-assert, and
+         * --enable-safemode
+         */
         int debugFlags;
 
         /** from --classpath */
@@ -363,6 +366,8 @@
                             arg.substring(arg.indexOf('=') + 1));
                 } else if (arg.equals("--enable-debugger")) {
                     debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
+                } else if (arg.equals("--enable-safemode")) {
+                    debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
                 } else if (arg.equals("--enable-checkjni")) {
                     debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
                 } else if (arg.equals("--enable-assert")) {
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/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 713e7258..a57c71b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1249,11 +1249,7 @@
                 android:theme="@style/Theme.Dialog.Alert"
                 android:excludeFromRecents="true">
         </activity>
-        <activity android:name="com.android.internal.app.UsbStorageActivity"
-                android:excludeFromRecents="true">
-        </activity>
-        <activity android:name="com.android.internal.app.UsbStorageStopActivity"
-                android:theme="@style/Theme.Dialog.Alert"
+        <activity android:name="com.android.server.status.UsbStorageActivity"
                 android:excludeFromRecents="true">
         </activity>
         <activity android:name="com.android.internal.app.ExternalMediaFormatActivity"
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/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 54781e3..70bc000 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -215,6 +215,10 @@
          running on a device that is running in user mode. -->
     <attr name="debuggable" format="boolean" />
     
+    <!-- Flag indicating whether the application requests the VM to operate in
+         the safe mode.  -->
+    <attr name="safeMode" format="boolean" />
+
     <!-- Flag indicating whether the given application component is available
          to other applications.  If false, it can only be accessed by
          applications with its same user id (which usually means only by
@@ -600,6 +604,20 @@
          Application class to avoid interference with application logic. -->
     <attr name="restoreNeedsApplication" format="boolean" />
 
+    <!-- The default install location defined by an application. -->
+    <attr name="installLocation">
+        <!-- Let the system decide ideal install location -->
+        <enum name="auto" value="0" />
+        <!-- Explicitly request to be installed on internal phone storate
+             only. -->
+        <enum name="internalOnly" value="1" />
+        <!-- Prefer to be installed on sdcard. There is no guarantee that
+             the system will honour this request. The application might end
+             up being installed on internal storage if external media
+             is unavailable or too full. -->
+        <enum name="preferExternal" value="2" />
+    </attr>
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -624,6 +642,7 @@
         <attr name="versionName" />
         <attr name="sharedUserId" />
         <attr name="sharedUserLabel" />
+        <attr name="installLocation" />
     </declare-styleable>
     
     <!-- The <code>application</code> tag describes application-level components
@@ -670,6 +689,7 @@
              override the component specific values). -->
         <attr name="enabled" />
         <attr name="debuggable" />
+        <attr name="safeMode" />
         <!-- Name of activity to be launched for managing the application's space on the device. -->
         <attr name="manageSpaceActivity" />
         <attr name="allowClearUserData" />
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/public.xml b/core/res/res/values/public.xml
index bd4a3eb..7706f30 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1224,9 +1224,11 @@
   <public type="attr" name="expandableListViewWhiteStyle" id="0x010102b6" />
 
 <!-- ===============================================================
-     Resources proposed for Flan.
+     Resources proposed for Froyo.
      =============================================================== -->
   <eat-comment />
   <public type="attr" name="neverEncrypt" id="0x010102b7" />
+  <public type="attr" name="installLocation" id="0x010102b8" />
+  <public type="attr" name="safeMode" id="0x010102b9" />
     
 </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/docs/html/resources/articles/images/live_wallpapers_small.png b/docs/html/resources/articles/images/live_wallpapers_small.png
new file mode 100644
index 0000000..3b49b24
--- /dev/null
+++ b/docs/html/resources/articles/images/live_wallpapers_small.png
Binary files differ
diff --git a/docs/html/resources/articles/index.jd b/docs/html/resources/articles/index.jd
index 4fda6d7..d2f0996 100644
--- a/docs/html/resources/articles/index.jd
+++ b/docs/html/resources/articles/index.jd
@@ -77,6 +77,11 @@
 </dl>
 
 <dl>
+  <dt><a href="{@docRoot}resources/articles/live-wallpapers.html">Live Wallpapers</a></dt>
+  <dd>Live wallpapers are richer, animated, interactive backgrounds that users can display in their home screens. Learn how to create a live wallpaper and bundle it in an application that users can install on their devices.</dd>
+</dl>
+
+<dl>
   <dt><a href="{@docRoot}resources/articles/on-screen-inputs.html">Onscreen Input Methods</a></dt>
   <dd>The Input Method Framework (IMF) allows users to take advantage of on-screen input methods, such as software keyboards. This article provides an overview of Input Method Editors (IMEs) and how applications interact with them.</dd>
 </dl>
diff --git a/docs/html/resources/articles/live-wallpapers.jd b/docs/html/resources/articles/live-wallpapers.jd
new file mode 100644
index 0000000..8dda879
--- /dev/null
+++ b/docs/html/resources/articles/live-wallpapers.jd
@@ -0,0 +1,84 @@
+page.title=Live Wallpapers
+@jd:body
+
+<p>Starting with Android 2.1 (API Level 7), users can now enjoy <em>live
+wallpapers</em> &mdash; richer, animated, interactive backgrounds &mdash; on
+their home screens. A live wallpaper is very similar to a normal Android
+application and has access to all the facilities of the platform: SGL (2D
+drawing), OpenGL (3D drawing), GPS, accelerometers, network access, etc. The
+live wallpapers included on Nexus One demonstrate the use of some of these APIs
+to create fun and interesting user experiences. For instance, the Grass
+wallpaper uses the phone's location to compute sunrise and sunset times in order
+to display the appropriate sky.</p>
+
+<img src="images/live_wallpapers_small.png" style="align:center" />
+
+<p>Creating your own live wallpaper is easy, especially if you have had
+previous experience with <a
+href="../../../reference/android/view/SurfaceView.html"><code>SurfaceView</code></a> or <a
+href="../../../reference/android/graphics/Canvas.html"><code>Canvas</code></a>. 
+To learn how to create a live wallpaper, you should check out the <a 
+href="../samples/CubeLiveWallpaper/index.html">CubeLiveWallpaper sample code</a>.</p>
+
+<p>In terms of implementation, a live wallpaper is very similar to a regular
+Android <a href="../../../reference/android/app/Service.html">service</a>. The
+only difference is the addition of a new method, <a
+href="../../../reference/android/service/wallpaper/WallpaperService.
+html#onCreateEngine()"><code>onCreateEngine()</code></a>, whose goal is to create a <a
+href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html">
+<code>WallpaperService.Engine</code></a>. The engine is responsible for
+handling the lifecycle and drawing of a wallpaper. The system provides a surface
+on which you can draw, just like you would with a <code>SurfaceView</code></a>.
+Drawing a wallpaper can be very expensive so you should optimize your code
+as much as possible to avoid using too much CPU, not only for battery life
+but also to avoid slowing down the rest of the system. That is also why the
+most important part of the lifecycle of a wallpaper is <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onVisibilityChanged%28boolean%29">when it becomes invisible</a>. 
+When invisible, such as when the user launches an application that covers
+the home screen, a wallpaper must stop all activity.</p>
+
+<p>The engine can also implement several methods to interact with the user
+or the home application. For instance, if you want your wallpaper to scroll
+along when the user swipes from one home screen to another, you can use <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onOffsetsChanged%28float,%20float,%20float,%20float,%20int,%20int%29"><code>onOffsetsChanged()</code></a>.
+To react to touch events, simply implement <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onTouchEvent%28android.view.MotionEvent%29"><code>onTouchEvent(MotionEvent)</code></a>.
+Finally, applications can send arbitrary commands to the live wallpaper.
+Currently, only the standard home application sends commands to the <a
+href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onCommand%28java.lang.String,%20int,%20int,%20int,%20android.os.Bundle,%20boolean%29"><code>onCommand()</code></a>
+method of the live wallpaper:</p>
+
+<ul>
+<li><code>android.wallpaper.tap</code>: When the user taps an empty space
+on the workspace. This command is interpreted by the Nexus and Water live
+wallpapers to make the wallpaper react to user interaction. For instance,
+if you tap an empty space on the Water live wallpaper, new ripples appear
+under your finger.</li>
+<li><code>android.home.drop</code>: When the user drops an icon or a widget
+on the workspace. This command is also interpreted by the Nexus and Water
+live wallpapers.</li>
+</ul>
+
+<p>If you are developing a live wallpaper, remember that the feature is
+supported only on Android 2.1 (API level 7) and higher versions of the platform.
+To ensure that your application can only be installed on devices that support
+live wallpapers, remember to add the following to the application's manifest
+before publishing to Android Market:</p>
+
+<ul>
+<li><code>&lt;uses-sdk android:minSdkVersion="7" /&gt;</code>, which indicates
+to Android Market and the platform that your application requires Android 2.1 or
+higher. For more information, see the <a href="../../../guide/appendix/api-levels.html">API
+Levels</a> and the documentation for the
+<a href="../../../guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a>
+element.</li>
+<li><code>&lt;uses-feature android:name="android.software.live_wallpaper" /&gt;</code>,
+which tells Android Market that your application includes a live wallpaper
+Android Market uses this feature as a filter, when presenting users lists of
+available applications. When you declaring this feature, Android Market
+displays your application only to users whose devices support live wallpapers,
+while hiding it from other devices on which it would not be able to run. For
+more information, see the documentation for the
+<a href="../../../guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature&gt;</code></a>
+element.</li>
+</ul>
+
+<p>Many great live wallpapers are already available on Android Market and
+we can't wait to see more!</p>
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index e337e38..0972029 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -83,6 +83,9 @@
         <li><a href="<?cs var:toroot ?>resources/articles/live-folders.html">
                 <span class="en">Live Folders</span>
                 </a></li>
+        <li><a href="<?cs var:toroot ?>resources/articles/live-wallpapers.html">
+                <span class="en">Live Wallpapers</span>
+                </a> <span class="new">new!</span></li>
         <li><a href="<?cs var:toroot ?>resources/articles/on-screen-inputs.html">
                 <span class="en">Onscreen Input Methods</span>
                 </a></li>
@@ -178,28 +181,34 @@
               </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/BluetoothChat/index.html">
                 <span class="en">Bluetooth Chat</span>
-              </a> <span class="new">new!</span></li>
+              </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/BusinessCard/index.html">
                 <span class="en">Business Card</span>
-              </a> <span class="new">new!</span></li>
+              </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/ContactManager/index.html">
                 <span class="en">Contact Manager</span>
-              </a> <span class="new">new!</span></li>
+              </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/Home/index.html">
                 <span class="en">Home</span>
               </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/JetBoy/index.html">
                 <span class="en">JetBoy</span>
               </a></li>
+          <li><a href="<?cs var:toroot ?>resources/samples/CubeLiveWallpaper/index.html">
+                <span class="en">Live Wallpaper</span>
+                </a> <span class="new">new!</span></li>
           <li><a href="<?cs var:toroot ?>resources/samples/LunarLander/index.html">
                 <span class="en">Lunar Lander</span>
               </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/MultiResolution/index.html">
                 <span class="en">Multiple Resolutions</span>
-              </a> <span class="new">new!</span></li>
+              </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/NotePad/index.html">
                 <span class="en">Note Pad</span>
               </a></li>
+          <li><a href="<?cs var:toroot ?>resources/samples/SampleSyncAdapter/index.html">
+                <span class="en">Sample Sync Adapter</span>
+              </a> <span class="new">new!</span></li>
           <li><a href="<?cs var:toroot ?>resources/samples/SearchableDictionary/index.html">
                 <span class="en">Searchable Dictionary</span>
               </a></li>
@@ -211,10 +220,10 @@
               </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/Wiktionary/index.html">
                 <span class="en">Wiktionary</span>
-              </a> <span class="new">new!</span></li>
+              </a></li>
           <li><a href="<?cs var:toroot ?>resources/samples/WiktionarySimple/index.html">
                 <span class="en">Wiktionary (Simplified)</span>
-              </a> <span class="new">new!</span></li>
+              </a></li>
         </ul>
       </li>
     </ul>
diff --git a/docs/html/resources/samples/images/CubeLiveWallpaper1.png b/docs/html/resources/samples/images/CubeLiveWallpaper1.png
new file mode 100644
index 0000000..55bc1e9
--- /dev/null
+++ b/docs/html/resources/samples/images/CubeLiveWallpaper1.png
Binary files differ
diff --git a/docs/html/resources/samples/images/CubeLiveWallpaper3.png b/docs/html/resources/samples/images/CubeLiveWallpaper3.png
new file mode 100644
index 0000000..2747a88
--- /dev/null
+++ b/docs/html/resources/samples/images/CubeLiveWallpaper3.png
Binary files differ
diff --git a/docs/html/resources/samples/index.jd b/docs/html/resources/samples/index.jd
index 0beb781..5ebf41c 100644
--- a/docs/html/resources/samples/index.jd
+++ b/docs/html/resources/samples/index.jd
@@ -27,10 +27,10 @@
 platforms) and allow you to view the source files in your browser. </p>
 
 <div class="special">
-  <p>Some of the samples in this listing are not yet available in the
-  SDK. While we work to update the SDK, you can
-  <a href="{@docRoot}shareables/latest_samples.zip">download the latest samples</a> as a ZIP
-  archive.</p>
+  <p>Some of the samples in this listing may not yet be available in the
+  SDK. To ensure that you have the latest versions of the samples, you can
+  <a href="{@docRoot}shareables/latest_samples.zip">download the samples pack</a>
+  as a .zip archive.</p>
 </div>
 
 <dl>
@@ -55,11 +55,15 @@
 
  <dt><a href="Home/index.html">Home</a></dt>
   <dd>A home screen replacement application.</dd>
-  
+
  <dt><a href="JetBoy/index.html">JetBoy</a></dt>
   <dd>A game that demonstrates the SONiVOX JET interactive music technology,
   with {@link android.media.JetPlayer}.</dd>
-    
+
+ <dt><a href="CubeLiveWallpaper/index.html">Live Wallpaper</a></dt>
+  <dd>An application that demonstrates how to create a live wallpaper and 
+  bundle it in an application that users can install on their devices.</dd>
+
  <dt><a href="LunarLander/index.html">Lunar Lander</a></dt>
   <dd>A classic Lunar Lander game.</dd>
 
@@ -70,14 +74,21 @@
  <dt><a href="NotePad/index.html">Note Pad</a></dt>
   <dd>An application for saving notes. Similar (but not identical) to the 
     <a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad tutorial</a>.</dd>
-  
+
+ <dt><a href="SampleSyncAdapter/index.html">SampleSyncAdapter</a></dt>
+  <dd>Demonstrates how an application can communicate with a
+cloud-based service and synchronize its data with data stored locally in a
+content provider. The sample uses two related parts of the Android framework
+&mdash; the account manager and the synchronization manager (through a sync
+adapter).</dd>
+
  <dt><a href="SearchableDictionary/index.html">Searchable Dictionary</a></dt>
   <dd>A sample application that demonstrates Android's search framework, 
   including how to provide search suggestions for Quick Search Box.</dd>
-  
+
  <dt><a href="Snake/index.html">Snake</a></dt>
   <dd>An implementation of the classic game "Snake."</dd>
-  
+
  <dt><a href="SoftKeyboard/index.html">Soft Keyboard</a></dt>
   <dd>An example of writing an input method for a software keyboard.</dd>
 
diff --git a/docs/html/shareables/latest_samples.zip b/docs/html/shareables/latest_samples.zip
index 42fad99..34102c5 100644
--- a/docs/html/shareables/latest_samples.zip
+++ b/docs/html/shareables/latest_samples.zip
Binary files differ
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
index 09b4bf4..5a4531b 100644
--- a/graphics/java/android/graphics/YuvImage.java
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -21,13 +21,11 @@
 /**
  * YuvImage contains YUV data and provides a method that compresses a region of
  * the YUV data to a Jpeg. The YUV data should be provided as a single byte
- * array irrespective of the number of image planes in it. The stride of each
- * image plane should be provided as well.
+ * array irrespective of the number of image planes in it.
+ * Currently only PixelFormat.YCbCr_420_SP and PixelFormat.YCbCr_422_I are supported.
  *
- * To compress a rectangle region in the YUV data, users have to specify a
- * region by width, height and offsets, where each image plane has a
- * corresponding offset. All offsets are measured as a displacement in bytes
- * from yuv[0], where yuv[0] is the beginning of the yuv data.
+ * To compress a rectangle region in the YUV data, users have to specify the
+ * region by left, top, width and height.
  */
 public class YuvImage {
 
@@ -55,21 +53,56 @@
     private int[] mStrides;
 
     /**
+     * The width of the image.
+     */
+    private int mWidth;
+
+    /**
+     * The height of the the image.
+     */
+    private int mHeight;
+
+    /**
      * Construct an YuvImage.
      *
-     * @param yuv The YUV data. In the case of more than one image plane, all the planes must be
-     *            concatenated into a single byte array.
-     * @param format The YUV data format as defined in {@link PixelFormat}.
-     * @param strides Row bytes of each image plane.
+     * @param yuv     The YUV data. In the case of more than one image plane, all the planes must be
+     *                concatenated into a single byte array.
+     * @param format  The YUV data format as defined in {@link PixelFormat}.
+     * @param width   The width of the YuvImage.
+     * @param height  The height of the YuvImage.
+     * @param strides (Optional) Row bytes of each image plane. If yuv contains padding, the stride
+     *                of each image must be provided. If strides is null, the method assumes no
+     *                padding and derives the row bytes by format and width itself.
+     * @throws IllegalArgumentException if format is not support; width or height <= 0; or yuv is
+     *                null.
      */
-    public YuvImage(byte[] yuv, int format, int[] strides) {
-        if ((yuv == null) || (strides == null)) {
+    public YuvImage(byte[] yuv, int format, int width, int height, int[] strides) {
+        if (format != PixelFormat.YCbCr_420_SP &&
+                format != PixelFormat.YCbCr_422_I) {
             throw new IllegalArgumentException(
-                    "yuv or strides cannot be null");
+                    "only support PixelFormat.YCbCr_420_SP " +
+                    "and PixelFormat.YCbCr_422_I for now");
         }
+
+        if (width <= 0  || height <= 0) {
+            throw new IllegalArgumentException(
+                    "width and height must large than 0");
+        }
+
+        if (yuv == null) {
+            throw new IllegalArgumentException("yuv cannot be null");
+        }
+
+        if (strides == null) {
+            mStrides = calculateStrides(width, format);
+        } else {
+            mStrides = strides;
+        }
+
         mData = yuv;
         mFormat = format;
-        mStrides = strides;
+        mWidth = width;
+        mHeight = height;
     }
 
     /**
@@ -77,22 +110,21 @@
      * Only PixelFormat.YCbCr_420_SP and PixelFormat.YCbCr_422_I
      * are supported for now.
      *
-     * @param width The width of the rectangle region.
-     * @param height The height of the rectangle region.
-     * @param offsets The offsets of the rectangle region in each image plane.
-     *                The offsets are measured as a displacement in bytes from
-     *                yuv[0], where yuv[0] is the beginning of the yuv data.
-     * @param quality  Hint to the compressor, 0-100. 0 meaning compress for
-     *                 small size, 100 meaning compress for max quality.
-     * @param stream   The outputstream to write the compressed data.
-     *
-     * @return true if successfully compressed to the specified stream.
-     *
+     * @param rectangle The rectangle region to be compressed. The medthod checks if rectangle is
+     *                  inside the image. Also, the method modifies rectangle if the chroma pixels
+     *                  in it are not matched with the luma pixels in it.
+     * @param quality   Hint to the compressor, 0-100. 0 meaning compress for
+     *                  small size, 100 meaning compress for max quality.
+     * @param stream    OutputStream to write the compressed data.
+     * @return          True if the compression is successful.
+     * @throws IllegalArgumentException if rectangle is invalid; quality is not within [0,
+     *                  100]; or stream is null.
      */
-    public boolean compressToJpeg(int width, int height, int[] offsets, int quality,
-            OutputStream stream) {
-        if (!validate(mFormat, width, height, offsets)) {
-            return false;
+    public boolean compressToJpeg(Rect rectangle, int quality, OutputStream stream) {
+        Rect wholeImage = new Rect(0, 0, mWidth, mHeight);
+        if (!wholeImage.contains(rectangle)) {
+            throw new IllegalArgumentException(
+                    "rectangle is not inside the image");
         }
 
         if (quality < 0 || quality > 100) {
@@ -100,14 +132,19 @@
         }
 
         if (stream == null) {
-            throw new NullPointerException();
+            throw new IllegalArgumentException("stream cannot be null");
         }
 
-        return nativeCompressToJpeg(mData, mFormat, width, height, offsets,
-                mStrides, quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
+        adjustRectangle(rectangle);
+        int[] offsets = calculateOffsets(rectangle.left, rectangle.top);
+
+        return nativeCompressToJpeg(mData, mFormat, rectangle.width(),
+                rectangle.height(), offsets, mStrides, quality, stream,
+                new byte[WORKING_COMPRESS_STORAGE]);
     }
 
-    /**
+
+   /**
      * @return the YUV data.
      */
     public byte[] getYuvData() {
@@ -128,37 +165,71 @@
         return mStrides;
     }
 
-    protected boolean validate(int format, int width, int height, int[] offsets) {
-        if (format != PixelFormat.YCbCr_420_SP &&
-                format != PixelFormat.YCbCr_422_I) {
-            throw new IllegalArgumentException(
-                    "only support PixelFormat.YCbCr_420_SP " +
-                    "and PixelFormat.YCbCr_422_I for now");
+    /**
+     * @return the width of the image.
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * @return the height of the image.
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    int[] calculateOffsets(int left, int top) {
+        int[] offsets = null;
+        if (mFormat == PixelFormat.YCbCr_420_SP) {
+            offsets = new int[] {top * mStrides[0] + left,
+                  mHeight * mStrides[0] + top / 2 * mStrides[1]
+                  + left / 2 * 2 };
+            return offsets;
         }
 
-        if (offsets.length != mStrides.length) {
-            throw new IllegalArgumentException(
-                    "the number of image planes are mismatched");
+        if (mFormat == PixelFormat.YCbCr_422_I) {
+            offsets = new int[] {top * mStrides[0] + left / 2 * 4};
+            return offsets;
         }
 
-        if (width <= 0  || height <= 0) {
-            throw new IllegalArgumentException(
-                    "width and height must large than 0");
-        }
+        return offsets;
+    }
 
-        int requiredSize;
+    private int[] calculateStrides(int width, int format) {
+        int[] strides = null;
         if (format == PixelFormat.YCbCr_420_SP) {
-            requiredSize = height * mStrides[0] +(height >> 1) * mStrides[1];
-        } else {
-            requiredSize = height * mStrides[0];
+            strides = new int[] {width, width};
+            return strides;
         }
 
-        if (requiredSize > mData.length) {
-            throw new IllegalArgumentException(
-                    "width or/and height is larger than the yuv data");
+        if (format == PixelFormat.YCbCr_422_I) {
+            strides = new int[] {width * 2};
+            return strides;
         }
 
-        return true;
+        return strides;
+    }
+
+   private void adjustRectangle(Rect rect) {
+       int width = rect.width();
+       int height = rect.height();
+       if (mFormat == PixelFormat.YCbCr_420_SP) {
+           // Make sure left, top, width and height are all even.
+           width &= ~1;
+           height &= ~1;
+           rect.left &= ~1;
+           rect.top &= ~1;
+           rect.right = rect.left + width;
+           rect.bottom = rect.top + height;
+        }
+
+        if (mFormat == PixelFormat.YCbCr_422_I) {
+            // Make sure left and width are both even.
+            width &= ~1;
+            rect.left &= ~1;
+            rect.right = rect.left + width;
+        }
     }
 
     //////////// native methods
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index a5a1bb8..be06e33 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -62,7 +62,8 @@
     // AudioSink: abstraction layer for audio output
     class AudioSink : public RefBase {
     public:
-        typedef void (*AudioCallback)(
+        // Callback returns the number of bytes actually written to the buffer.
+        typedef size_t (*AudioCallback)(
                 AudioSink *audioSink, void *buffer, size_t size, void *cookie);
 
         virtual             ~AudioSink() {}
@@ -77,8 +78,7 @@
         virtual status_t    getPosition(uint32_t *position) = 0;
 
         // If no callback is specified, use the "write" API below to submit
-        // audio data. Otherwise return a full buffer of audio data on each
-        // callback.
+        // audio data.
         virtual status_t    open(
                 uint32_t sampleRate, int channelCount,
                 int format=AudioSystem::PCM_16_BIT,
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/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 843e051..8e5f05f 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -90,11 +90,11 @@
     static void AudioCallback(int event, void *user, void *info);
     void AudioCallback(int event, void *info);
 
-    static void AudioSinkCallback(
+    static size_t AudioSinkCallback(
             MediaPlayerBase::AudioSink *audioSink,
             void *data, size_t size, void *me);
 
-    void fillBuffer(void *data, size_t size);
+    size_t fillBuffer(void *data, size_t size);
 
     int64_t getRealTimeUsLocked() const;
 
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/include/ui/CameraParameters.h b/include/ui/CameraParameters.h
index 2c29bfb..e328f33 100644
--- a/include/ui/CameraParameters.h
+++ b/include/ui/CameraParameters.h
@@ -187,6 +187,13 @@
     // Vertical angle of view in degrees.
     // Example value: "42.5". Read only.
     static const char KEY_VERTICAL_VIEW_ANGLE[];
+    // Exposure compensation. The value is multiplied by 100. -100 means -1 EV.
+    // 130 means +1.3 EV.
+    // Example value: "0" or "133". Read/write.
+    static const char KEY_EXPOSURE_COMPENSATION[];
+    // Supported exposure compensation.
+    // Example value: "-100,-66,-33,0,33,66,100". Read only.
+    static const char KEY_SUPPORTED_EXPOSURE_COMPENSATION[];
 
 
         // Values for white balance settings.
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/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index 09a36f1..f374fbc 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -281,7 +281,7 @@
 // send command to camera driver
 status_t Camera::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
 {
-    LOGD("sendCommand");
+    LOGV("sendCommand");
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
     return c->sendCommand(cmd, arg1, arg2);
diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp
index c4958a0..493b9c1 100644
--- a/libs/ui/CameraParameters.cpp
+++ b/libs/ui/CameraParameters.cpp
@@ -59,6 +59,8 @@
 const char CameraParameters::KEY_FOCAL_LENGTH[] = "focal-length";
 const char CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE[] = "horizontal-view-angle";
 const char CameraParameters::KEY_VERTICAL_VIEW_ANGLE[] = "vertical-view-angle";
+const char CameraParameters::KEY_EXPOSURE_COMPENSATION[] = "exposure-compensation";
+const char CameraParameters::KEY_SUPPORTED_EXPOSURE_COMPENSATION[] = "exposure-compensation-values";
 
 // Values for white balance settings.
 const char CameraParameters::WHITE_BALANCE_AUTO[] = "auto";
diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp
index e1b3ec7..4154b05a 100644
--- a/libs/ui/ICamera.cpp
+++ b/libs/ui/ICamera.cpp
@@ -344,7 +344,7 @@
             return NO_ERROR;
          } break;
         case SEND_COMMAND: {
-            LOGD("SEND_COMMAND");
+            LOGV("SEND_COMMAND");
             CHECK_INTERFACE(ICamera, data, reply);
             int command = data.readInt32();
             int arg1 = data.readInt32();
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1fac07c..8c24ee1 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -22,7 +22,6 @@
 import android.location.IGeocodeProvider;
 import android.location.IGpsStatusListener;
 import android.location.ILocationListener;
-import android.location.ILocationProvider;
 import android.location.Location;
 import android.os.Bundle;
 
@@ -81,10 +80,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/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 9fe6ab4..5529b11 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -21,7 +21,7 @@
 import android.os.Bundle;
 
 /**
- * Binder interface for location providers.
+ * Binder interface for services that implement location providers.
  *
  * {@hide}
  */
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/LocationProviderInterface.java b/location/java/android/location/LocationProviderInterface.java
new file mode 100644
index 0000000..98beffe
--- /dev/null
+++ b/location/java/android/location/LocationProviderInterface.java
@@ -0,0 +1,51 @@
+/*
+ * 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.location.Location;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * {@hide}
+ */
+public interface LocationProviderInterface {
+    String getName();
+    boolean requiresNetwork();
+    boolean requiresSatellite();
+    boolean requiresCell();
+    boolean hasMonetaryCost();
+    boolean supportsAltitude();
+    boolean supportsSpeed();
+    boolean supportsBearing();
+    int getPowerRequirement();
+    int getAccuracy();
+    boolean isEnabled();
+    void enable();
+    void disable();
+    int getStatus(Bundle extras);
+    long getStatusUpdateTime();
+    void enableLocationTracking(boolean enable);
+    void setMinTime(long minTime);
+    void updateNetworkState(int state, NetworkInfo info);
+    void updateLocation(Location location);
+    boolean sendExtraCommand(String command, Bundle extras);
+    void addListener(int uid);
+    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/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 8b5f702..dce3b27 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -26,11 +26,11 @@
 import android.location.IGpsStatusListener;
 import android.location.IGpsStatusProvider;
 import android.location.ILocationManager;
-import android.location.ILocationProvider;
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
+import android.location.LocationProviderInterface;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.SntpClient;
@@ -65,7 +65,7 @@
  *
  * {@hide}
  */
-public class GpsLocationProvider extends ILocationProvider.Stub {
+public class GpsLocationProvider implements LocationProviderInterface {
 
     private static final String TAG = "GpsLocationProvider";
 
@@ -374,6 +374,13 @@
     }
 
     /**
+     * Returns the name of this provider.
+     */
+    public String getName() {
+        return LocationManager.GPS_PROVIDER;
+    }
+
+    /**
      * Returns true if the provider requires access to a
      * data network (e.g., the Internet), false otherwise.
      */
@@ -576,6 +583,10 @@
         }
     }
 
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
     public int getStatus(Bundle extras) {
         if (extras != null) {
             extras.putInt("satellites", mSvCount);
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index 2e0be89..abb90b7 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -16,155 +16,221 @@
 
 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.location.LocationProviderInterface;
 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 location providers implemented as services.
  *
  * {@hide}
  */
-public class LocationProviderProxy implements IBinder.DeathRecipient {
+public class LocationProviderProxy implements LocationProviderInterface {
 
     private static final String TAG = "LocationProviderProxy";
 
+    private final Context mContext;
     private final String mName;
-    private final ILocationProvider mProvider;
+    private ILocationProvider mProvider;
+    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 location providers implemented in a separate service
+    public LocationProviderProxy(Context context, String name, String serviceName,
+            Handler handler) {
+        mContext = context;
         mName = name;
-        mProvider = provider;
-        try {
-            provider.asBinder().linkToDeath(this, 0);
-        } catch (RemoteException e) {
-            Log.e(TAG, "linkToDeath failed", e);
-            mDead = true;
+        mHandler = handler;
+        mContext.bindService(new Intent(serviceName), 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 +239,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 +272,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 +292,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/location/java/com/android/internal/location/MockProvider.java b/location/java/com/android/internal/location/MockProvider.java
index 7d9e86c..2f6fdee 100644
--- a/location/java/com/android/internal/location/MockProvider.java
+++ b/location/java/com/android/internal/location/MockProvider.java
@@ -17,9 +17,9 @@
 package com.android.internal.location;
 
 import android.location.ILocationManager;
-import android.location.ILocationProvider;
 import android.location.Location;
 import android.location.LocationProvider;
+import android.location.LocationProviderInterface;
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -33,7 +33,7 @@
  *
  * {@hide}
  */
-public class MockProvider extends ILocationProvider.Stub {
+public class MockProvider implements LocationProviderInterface {
     private final String mName;
     private final ILocationManager mLocationManager;
     private final boolean mRequiresNetwork;
@@ -73,6 +73,10 @@
         mLocation = new Location(name);
     }
 
+    public String getName() {
+        return mName;
+    }
+
     public void disable() {
         mEnabled = false;
     }
@@ -81,6 +85,10 @@
         mEnabled = true;
     }
 
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
     public int getStatus(Bundle extras) {
         if (mHasStatus) {
             extras.clear();
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 8c1b0ea..34252ab 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -16,10 +16,14 @@
 
 package android.media;
 
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.IContentProvider;
-import android.content.ContentUris;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.graphics.BitmapFactory;
@@ -42,11 +46,12 @@
 import android.util.Log;
 import android.util.Xml;
 
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -586,6 +591,9 @@
                     }
                     if (genreCode >= 0 && genreCode < ID3_GENRES.length) {
                         value = ID3_GENRES[genreCode];
+                    } else if (genreCode == 255) {
+                        // 255 is defined to be unknown
+                        value = null;
                     }
                 }
                 mGenre = value;
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/libmedia/MediaScannerClient.cpp b/media/libmedia/MediaScannerClient.cpp
index bd3596e..bb3717f 100644
--- a/media/libmedia/MediaScannerClient.cpp
+++ b/media/libmedia/MediaScannerClient.cpp
@@ -64,29 +64,27 @@
 
 bool MediaScannerClient::addStringTag(const char* name, const char* value)
 {
-    if (mLocaleEncoding != kEncodingNone) {
-        // don't bother caching strings that are all ASCII.
-        // call handleStringTag directly instead.
-        // check to see if value (which should be utf8) has any non-ASCII characters
-        bool nonAscii = false;
-        const char* chp = value;
-        char ch;
-        while ((ch = *chp++)) {
-            if (ch & 0x80) {
-                nonAscii = true;
-                break;
-            }
+    // don't bother caching strings that are all ASCII.
+    // call handleStringTag directly instead.
+    // check to see if value (which should be utf8) has any non-ASCII characters
+    bool nonAscii = false;
+    const char* chp = value;
+    char ch;
+    while ((ch = *chp++)) {
+        if (ch & 0x80) {
+            nonAscii = true;
+            break;
         }
-
-        if (nonAscii) {
-            // save the strings for later so they can be used for native encoding detection
-            mNames->push_back(name);
-            mValues->push_back(value);
-            return true;
-        }
-        // else fall through
     }
 
+    if (nonAscii) {
+        // save the strings for later so they can be used for native encoding detection
+        mNames->push_back(name);
+        mValues->push_back(value);
+        return true;
+    }
+    // else fall through
+
     // autodetection is not necessary, so no need to cache the values
     // pass directly to the client instead
     return handleStringTag(name, value);
@@ -198,23 +196,29 @@
 
 void MediaScannerClient::endFile()
 {
-    if (mLocaleEncoding != kEncodingNone) {
-        int size = mNames->size();
-        uint32_t encoding = kEncodingAll;
+    int size = mNames->size();
+    uint32_t encoding = kEncodingAll;
 
-        // compute a bit mask containing all possible encodings
-        for (int i = 0; i < mNames->size(); i++)
-            encoding &= possibleEncodings(mValues->getEntry(i));
+    // compute a bit mask containing all possible encodings
+    for (int i = 0; i < mNames->size(); i++)
+        encoding &= possibleEncodings(mValues->getEntry(i));
 
-        // if the locale encoding matches, then assume we have a native encoding.
-        if (encoding & mLocaleEncoding)
-            convertValues(mLocaleEncoding);
+    // If one of the possible encodings matches the locale encoding, use that.
+    // Otherwise, if there is only one possible encoding, use that.
+    if (encoding & mLocaleEncoding)
+        convertValues(mLocaleEncoding);
+    else if ((encoding & (encoding - 1)) == 0)
+        convertValues(encoding);
+    else {
+        // TODO: try harder to disambiguate the encoding, perhaps by looking at
+        // other files by same artist, or even the user's entire collection.
+        // For now, fall through and insert the strings as they are.
+    }
 
-        // finally, push all name/value pairs to the client
-        for (int i = 0; i < mNames->size(); i++) {
-            if (!handleStringTag(mNames->getEntry(i), mValues->getEntry(i)))
-                break;
-        }
+    // finally, push all name/value pairs to the client
+    for (int i = 0; i < mNames->size(); i++) {
+        if (!handleStringTag(mNames->getEntry(i), mValues->getEntry(i)))
+            break;
     }
     // else addStringTag() has done all the work so we have nothing to do
 
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/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 8e61011..55b06f4 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1597,9 +1597,12 @@
     AudioOutput *me = (AudioOutput *)cookie;
     AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
 
-    (*me->mCallback)(
+    size_t actualSize = (*me->mCallback)(
             me, buffer->raw, buffer->size, me->mCallbackCookie);
-    me->snoopWrite(buffer->raw, buffer->size);
+
+    if (actualSize > 0) {
+        me->snoopWrite(buffer->raw, actualSize);
+    }
 }
 
 #undef LOG_TAG
@@ -1629,14 +1632,75 @@
     return NO_ERROR;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+struct CallbackThread : public Thread {
+    CallbackThread(const wp<MediaPlayerBase::AudioSink> &sink,
+                   MediaPlayerBase::AudioSink::AudioCallback cb,
+                   void *cookie);
+
+protected:
+    virtual ~CallbackThread();
+
+    virtual bool threadLoop();
+
+private:
+    wp<MediaPlayerBase::AudioSink> mSink;
+    MediaPlayerBase::AudioSink::AudioCallback mCallback;
+    void *mCookie;
+    void *mBuffer;
+    size_t mBufferSize;
+
+    CallbackThread(const CallbackThread &);
+    CallbackThread &operator=(const CallbackThread &);
+};
+
+CallbackThread::CallbackThread(
+        const wp<MediaPlayerBase::AudioSink> &sink,
+        MediaPlayerBase::AudioSink::AudioCallback cb,
+        void *cookie)
+    : mSink(sink),
+      mCallback(cb),
+      mCookie(cookie),
+      mBuffer(NULL),
+      mBufferSize(0) {
+}
+
+CallbackThread::~CallbackThread() {
+    if (mBuffer) {
+        free(mBuffer);
+        mBuffer = NULL;
+    }
+}
+
+bool CallbackThread::threadLoop() {
+    sp<MediaPlayerBase::AudioSink> sink = mSink.promote();
+    if (sink == NULL) {
+        return false;
+    }
+
+    if (mBuffer == NULL) {
+        mBufferSize = sink->bufferSize();
+        mBuffer = malloc(mBufferSize);
+    }
+
+    size_t actualSize =
+        (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie);
+
+    if (actualSize > 0) {
+        sink->write(mBuffer, actualSize);
+    }
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 status_t MediaPlayerService::AudioCache::open(
         uint32_t sampleRate, int channelCount, int format, int bufferCount,
         AudioCallback cb, void *cookie)
 {
     LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
-    if (cb != NULL) {
-        return UNKNOWN_ERROR;  // TODO: implement this.
-    }
     if (mHeap->getHeapID() < 0) {
         return NO_INIT;
     }
@@ -1645,9 +1709,25 @@
     mChannelCount = (uint16_t)channelCount;
     mFormat = (uint16_t)format;
     mMsecsPerFrame = 1.e3 / (float) sampleRate;
+
+    if (cb != NULL) {
+        mCallbackThread = new CallbackThread(this, cb, cookie);
+    }
     return NO_ERROR;
 }
 
+void MediaPlayerService::AudioCache::start() {
+    if (mCallbackThread != NULL) {
+        mCallbackThread->run("AudioCache callback");
+    }
+}
+
+void MediaPlayerService::AudioCache::stop() {
+    if (mCallbackThread != NULL) {
+        mCallbackThread->requestExitAndWait();
+    }
+}
+
 ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size)
 {
     LOGV("write(%p, %u)", buffer, size);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index ffe1ba0..5c03e47 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -139,9 +139,9 @@
                 int bufferCount = 1,
                 AudioCallback cb = NULL, void *cookie = NULL);
 
-        virtual void            start() {}
+        virtual void            start();
         virtual ssize_t         write(const void* buffer, size_t size);
-        virtual void            stop() {}
+        virtual void            stop();
         virtual void            flush() {}
         virtual void            pause() {}
         virtual void            close() {}
@@ -171,6 +171,8 @@
         uint32_t            mSize;
         int                 mError;
         bool                mCommandComplete;
+
+        sp<Thread>          mCallbackThread;
     };
 
 public:
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/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 4926920..12d7ee2 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -187,12 +187,12 @@
 }
 
 // static
-void AudioPlayer::AudioSinkCallback(
+size_t AudioPlayer::AudioSinkCallback(
         MediaPlayerBase::AudioSink *audioSink,
         void *buffer, size_t size, void *cookie) {
     AudioPlayer *me = (AudioPlayer *)cookie;
 
-    me->fillBuffer(buffer, size);
+    return me->fillBuffer(buffer, size);
 }
 
 void AudioPlayer::AudioCallback(int event, void *info) {
@@ -201,17 +201,18 @@
     }
 
     AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
-    fillBuffer(buffer->raw, buffer->size);
+    size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size);
+
+    buffer->size = numBytesWritten;
 }
 
-void AudioPlayer::fillBuffer(void *data, size_t size) {
+size_t AudioPlayer::fillBuffer(void *data, size_t size) {
     if (mNumFramesPlayed == 0) {
         LOGV("AudioCallback");
     }
 
     if (mReachedEOS) {
-        memset(data, 0, size);
-        return;
+        return 0;
     }
 
     size_t size_done = 0;
@@ -244,7 +245,6 @@
 
             if (err != OK) {
                 mReachedEOS = true;
-                memset((char *)data + size_done, 0, size_remaining);
                 break;
             }
 
@@ -285,7 +285,9 @@
     }
 
     Mutex::Autolock autoLock(mLock);
-    mNumFramesPlayed += size / mFrameSize;
+    mNumFramesPlayed += size_done / mFrameSize;
+
+    return size_done;
 }
 
 int64_t AudioPlayer::getRealTimeUs() {
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..1ff38ee 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);
     }
 
@@ -1510,7 +1540,7 @@
 
     if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
         || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
-        || !memcmp(header, "ftypM4A ", 8)) {
+        || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)) {
         *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
         *confidence = 0.1;
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 0355a82..75b7b6f 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -112,7 +112,6 @@
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
@@ -139,6 +138,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 +1284,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 +1931,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 +2258,7 @@
 }
 
 status_t OMXCodec::stop() {
-    CODEC_LOGV("stop");
+    CODEC_LOGV("stop mState=%d", mState);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -2309,6 +2317,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/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java b/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java
index 6b3093f..1434d3f 100644
--- a/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java
+++ b/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java
@@ -43,10 +43,9 @@
     private TestThread mThread;
 
     private static final int[] mTestFiles = new int[] {
-        // FIXME: Restore when Stagefright bug is fixed
         R.raw.organ441,
         R.raw.sine441,
-        //R.raw.test1,
+        R.raw.test1,
         R.raw.test2,
         R.raw.test3,
         R.raw.test4,
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/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 7686aa0..ba6024f 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -56,5 +56,6 @@
     <bool name="def_mount_ums_autostart">false</bool>
     <bool name="def_mount_ums_prompt">true</bool>
     <bool name="def_mount_ums_notify_enabled">true</bool>
+    <bool name="set_install_location">true</bool>
 
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 015b487..8b75b61 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -864,6 +865,9 @@
 
         loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
                 R.bool.def_notification_pulse);
+        loadBooleanSetting(stmt, Settings.System.SET_INSTALL_LOCATION, R.bool.set_install_location);
+        loadSetting(stmt, Settings.System.DEFAULT_INSTALL_LOCATION,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
         stmt.close();
     }
 
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..e12f2e1 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -29,25 +29,27 @@
 
 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;
 import android.location.ILocationManager;
-import android.location.ILocationProvider;
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
+import android.location.LocationProviderInterface;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
@@ -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,14 +108,14 @@
     private static boolean sProvidersLoaded = false;
 
     private final Context mContext;
-    private IGeocodeProvider mGeocodeProvider;
+    private GeocoderProxy mGeocodeProvider;
     private IGpsStatusProvider mGpsStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
     private LocationWorkerHandler mLocationHandler;
 
     // Cache the real providers for use in addTestProvider() and removeTestProvider()
-     LocationProviderProxy mNetworkLocationProvider;
-     LocationProviderProxy mGpsLocationProvider;
+     LocationProviderInterface mNetworkLocationProvider;
+     LocationProviderInterface mGpsLocationProvider;
 
     // Handler messages
     private static final int MESSAGE_LOCATION_CHANGED = 1;
@@ -131,10 +134,10 @@
     /**
      * List of location providers.
      */
-    private final ArrayList<LocationProviderProxy> mProviders =
-        new ArrayList<LocationProviderProxy>();
-    private final HashMap<String, LocationProviderProxy> mProvidersByName
-        = new HashMap<String, LocationProviderProxy>();
+    private final ArrayList<LocationProviderInterface> mProviders =
+        new ArrayList<LocationProviderInterface>();
+    private final HashMap<String, LocationProviderInterface> mProvidersByName
+        = new HashMap<String, LocationProviderInterface>();
 
     /**
      * Object used internally for synchronization
@@ -408,14 +411,13 @@
         }
     }
 
-    private void addProvider(LocationProviderProxy provider) {
+    private void addProvider(LocationProviderInterface provider) {
         mProviders.add(provider);
         mProvidersByName.put(provider.getName(), provider);
     }
 
-    private void removeProvider(LocationProviderProxy provider) {
+    private void removeProvider(LocationProviderInterface provider) {
         mProviders.remove(provider);
-        provider.unlinkProvider();
         mProvidersByName.remove(provider.getName());
     }
 
@@ -443,12 +445,27 @@
         // Attempt to load "real" providers first
         if (GpsLocationProvider.isSupported()) {
             // Create a gps location provider
-            GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
-            mGpsStatusProvider = provider.getGpsStatusProvider();
-            mNetInitiatedListener = provider.getNetInitiatedListener();
-            LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
-            addProvider(proxy);
-            mGpsLocationProvider = proxy;
+            GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
+            mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
+            mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
+            addProvider(gpsProvider);
+            mGpsLocationProvider = gpsProvider;
+        }
+
+        // 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 +501,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 +524,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;
@@ -611,7 +589,7 @@
         }
         ArrayList<String> out = new ArrayList<String>(mProviders.size());
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            LocationProviderProxy p = mProviders.get(i);
+            LocationProviderInterface p = mProviders.get(i);
             out.add(p.getName());
         }
         return out;
@@ -636,7 +614,7 @@
         }
         ArrayList<String> out = new ArrayList<String>(mProviders.size());
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            LocationProviderProxy p = mProviders.get(i);
+            LocationProviderInterface p = mProviders.get(i);
             String name = p.getName();
             if (isAllowedProviderSafe(name)) {
                 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
@@ -650,7 +628,7 @@
 
     private void updateProvidersLocked() {
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            LocationProviderProxy p = mProviders.get(i);
+            LocationProviderInterface p = mProviders.get(i);
             boolean isEnabled = p.isEnabled();
             String name = p.getName();
             boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
@@ -667,7 +645,7 @@
     private void updateProviderListenersLocked(String provider, boolean enabled) {
         int listeners = 0;
 
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             return;
         }
@@ -857,8 +835,8 @@
             Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
         }
 
-        LocationProviderProxy proxy = mProvidersByName.get(provider);
-        if (proxy == null) {
+        LocationProviderInterface p = mProvidersByName.get(provider);
+        if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
 
@@ -876,14 +854,14 @@
             }
 
             if (newUid) {
-                proxy.addListener(callingUid);
+                p.addListener(callingUid);
             }
 
             boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
             if (isProviderEnabled) {
                 long minTimeForProvider = getMinTimeLocked(provider);
-                proxy.setMinTime(minTimeForProvider);
-                proxy.enableLocationTracking(true);
+                p.setMinTime(minTimeForProvider);
+                p.enableLocationTracking(true);
             } else {
                 // Notify the listener that updates are currently disabled
                 receiver.callProviderEnabledLocked(provider, false);
@@ -943,9 +921,9 @@
                 // Call dispose() on the obsolete update records.
                 for (UpdateRecord record : oldRecords.values()) {
                     if (!providerHasListener(record.mProvider, callingUid, receiver)) {
-                        LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
-                        if (proxy != null) {
-                            proxy.removeListener(callingUid);
+                        LocationProviderInterface p = mProvidersByName.get(record.mProvider);
+                        if (p != null) {
+                            p.removeListener(callingUid);
                         }
                     }
                     record.disposeLocked();
@@ -969,7 +947,7 @@
                     hasOtherListener = true;
                 }
 
-                LocationProviderProxy p = mProvidersByName.get(provider);
+                LocationProviderInterface p = mProvidersByName.get(provider);
                 if (p != null) {
                     if (hasOtherListener) {
                         p.setMinTime(getMinTimeLocked(provider));
@@ -1026,12 +1004,12 @@
         }
 
         synchronized (mLock) {
-            LocationProviderProxy proxy = mProvidersByName.get(provider);
-            if (proxy == null) {
+            LocationProviderInterface p = mProvidersByName.get(provider);
+            if (p == null) {
                 return false;
             }
     
-            return proxy.sendExtraCommand(command, extras);
+            return p.sendExtraCommand(command, extras);
         }
     }
 
@@ -1281,7 +1259,7 @@
             mProximityReceiver = new Receiver(mProximityListener);
 
             for (int i = mProviders.size() - 1; i >= 0; i--) {
-                LocationProviderProxy provider = mProviders.get(i);
+                LocationProviderInterface provider = mProviders.get(i);
                 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
             }
         }
@@ -1313,7 +1291,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
      */
@@ -1331,8 +1309,8 @@
     }
 
     private Bundle _getProviderInfoLocked(String provider) {
-        LocationProviderProxy p = mProvidersByName.get(provider);
-        if (p == null) {
+        LocationProviderInterface p = mProvidersByName.get(provider);
+        if (p == null || !p.isEnabled()) {
             return null;
         }
 
@@ -1379,7 +1357,7 @@
     private boolean _isProviderEnabledLocked(String provider) {
         checkPermissionsSafe(provider);
 
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
@@ -1402,7 +1380,7 @@
     private Location _getLastKnownLocationLocked(String provider) {
         checkPermissionsSafe(provider);
 
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
@@ -1444,7 +1422,7 @@
             return;
         }
 
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             return;
         }
@@ -1527,9 +1505,9 @@
 
                         // notify other providers of the new location
                         for (int i = mProviders.size() - 1; i >= 0; i--) {
-                            LocationProviderProxy proxy = mProviders.get(i);
-                            if (!provider.equals(proxy.getName())) {
-                                proxy.updateLocation(location);
+                            LocationProviderInterface p = mProviders.get(i);
+                            if (!provider.equals(p.getName())) {
+                                p.updateLocation(location);
                             }
                         }
 
@@ -1552,10 +1530,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)};
@@ -1617,8 +1595,8 @@
                 // Notify location providers of current network state
                 synchronized (mLock) {
                     for (int i = mProviders.size() - 1; i >= 0; i--) {
-                        LocationProviderProxy provider = mProviders.get(i);
-                        if (provider.requiresNetwork()) {
+                        LocationProviderInterface provider = mProviders.get(i);
+                        if (provider.isEnabled() && provider.requiresNetwork()) {
                             provider.updateNetworkState(mNetworkState, info);
                         }
                     }
@@ -1669,13 +1647,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 +1660,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;
     }
@@ -1728,16 +1696,16 @@
             // remove the real provider if we are replacing GPS or network provider
             if (LocationManager.GPS_PROVIDER.equals(name)
                     || LocationManager.NETWORK_PROVIDER.equals(name)) {
-                LocationProviderProxy proxy = mProvidersByName.get(name);
-                if (proxy != null) {
-                    proxy.enableLocationTracking(false);
-                    removeProvider(proxy);
+                LocationProviderInterface p = mProvidersByName.get(name);
+                if (p != null) {
+                    p.enableLocationTracking(false);
+                    removeProvider(p);
                 }
             }
             if (mProvidersByName.get(name) != null) {
                 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
             }
-            addProvider(new LocationProviderProxy(name, provider));
+            addProvider(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..456244a 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -23,29 +23,34 @@
 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;
 import android.text.TextUtils;
 import android.util.Log;
 import java.util.ArrayList;
+import java.util.HashSet;
 
 import java.io.File;
 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 +114,40 @@
     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
+     * Private hash of currently mounted secure containers.
      */
-    public MountService(Context context) {
-        mContext = context;
+    private HashSet<String> mAsecMountSet = new HashSet<String>();
 
-        // 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 +161,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 +169,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 +182,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 +196,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 +214,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 +235,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 +278,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 +298,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 +354,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 +402,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 +456,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 +603,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 +613,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 +704,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 +732,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 +774,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 +816,124 @@
     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;
+        }
+
+        if (rc == StorageResultCode.OperationSucceeded) {
+            synchronized (mAsecMountSet) {
+                mAsecMountSet.add(id);
+            }
         }
         return rc;
     }
 
     public int unmountSecureContainer(String id) {
         validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+        waitForReady();
+        warnOnNotMounted();
 
-        int rc = MountServiceResultCode.OperationSucceeded;
+        synchronized (mAsecMountSet) {
+            if (!mAsecMountSet.contains(id)) {
+                return StorageResultCode.OperationFailedVolumeNotMounted;
+            }
+         }
+
+        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;
+        }
+
+        if (rc == StorageResultCode.OperationSucceeded) {
+            synchronized (mAsecMountSet) {
+                mAsecMountSet.remove(id);
+            }
         }
         return rc;
     }
 
+    public boolean isSecureContainerMounted(String id) {
+        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        waitForReady();
+        warnOnNotMounted();
+
+        synchronized (mAsecMountSet) {
+            return mAsecMountSet.contains(id);
+        }
+    }
+
     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..3657133 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -330,9 +330,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 +406,7 @@
         mSound.setUsesWakeLock(context);
         mToastQueue = new ArrayList<ToastRecord>();
         mHandler = new WorkerHandler();
+
         mStatusBarService = statusBar;
         statusBar.setNotificationCallbacks(mNotificationCallbacks);
 
@@ -440,7 +441,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..1cda2ff 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -20,6 +20,7 @@
 import com.android.internal.app.ResolverActivity;
 import com.android.common.FastXmlSerializer;
 import com.android.common.XmlUtils;
+import com.android.server.JournaledFile;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -74,7 +75,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 +149,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 +303,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 +334,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 +409,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 +2124,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 +2372,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;
@@ -2724,9 +2750,9 @@
                                     + pkgSetting.name;
                                 reportSettingsProblem(Log.WARN, msg);
                             }
+                            // And now uninstall the old package.
+                            mInstaller.remove(pkgSetting.origPackage.name, useEncryptedFSDir);
                         }
-                        // And now uninstall the old package.
-                        mInstaller.remove(pkgSetting.origPackage.name, useEncryptedFSDir);
                         mSettings.removePackageLP(pkgSetting.origPackage.name);
                     }
                 }
@@ -2810,6 +2836,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 +4034,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 +5028,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 +5280,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 +5371,7 @@
                 File dataDir = new File(pkg.applicationInfo.dataDir);
                 dataDir.delete();
             }
+            schedulePackageCleaning(packageName);
             synchronized (mPackages) {
                 if (outInfo != null) {
                     outInfo.removedUid = mSettings.removePackageLP(packageName);
@@ -5302,7 +5384,7 @@
                 mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
             }
             // Save settings now
-            mSettings.writeLP ();
+            mSettings.writeLP();
         }
     }
 
@@ -6745,6 +6827,7 @@
     private static final class Settings {
         private final File mSettingsFilename;
         private final File mBackupSettingsFilename;
+        private final File mPackageListFilename;
         private final HashMap<String, PackageSetting> mPackages =
                 new HashMap<String, PackageSetting>();
         // List of replaced system applications
@@ -6791,6 +6874,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 {
@@ -6822,6 +6909,7 @@
                     -1, -1);
             mSettingsFilename = new File(systemDir, "packages.xml");
             mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
+            mPackageListFilename = new File(systemDir, "packages.list");
         }
 
         PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage,
@@ -7354,6 +7442,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();
@@ -7369,6 +7465,61 @@
                         |FileUtils.S_IRGRP|FileUtils.S_IWGRP
                         |FileUtils.S_IROTH,
                         -1, -1);
+
+                // Write package list file now, use a JournaledFile.
+                //
+                File tempFile = new File(mPackageListFilename.toString() + ".tmp");
+                JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
+
+                str = new FileOutputStream(journal.chooseForWrite());
+                try {
+                    StringBuilder sb = new StringBuilder();
+                    for (PackageSetting pkg : mPackages.values()) {
+                        ApplicationInfo ai = pkg.pkg.applicationInfo;
+                        String  dataPath = ai.dataDir;
+                        boolean isDebug  = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+
+                        // Avoid any application that has a space in its path
+                        // or that is handled by the system.
+                        if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID)
+                            continue;
+
+                        // we store on each line the following information for now:
+                        //
+                        // pkgName    - package name
+                        // userId     - application-specific user id
+                        // debugFlag  - 0 or 1 if the package is debuggable.
+                        // dataPath   - path to package's data path
+                        //
+                        // NOTE: We prefer not to expose all ApplicationInfo flags for now.
+                        //
+                        // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
+                        // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
+                        //   system/core/run-as/run-as.c
+                        //
+                        sb.setLength(0);
+                        sb.append(ai.packageName);
+                        sb.append(" ");
+                        sb.append((int)ai.uid);
+                        sb.append(isDebug ? " 1 " : " 0 ");
+                        sb.append(dataPath);
+                        sb.append("\n");
+                        str.write(sb.toString().getBytes());
+                    }
+                    str.flush();
+                    str.close();
+                    journal.commit();
+                }
+                catch (Exception  e) {
+                    journal.rollback();
+                }
+
+                FileUtils.setPermissions(mPackageListFilename.toString(),
+                        FileUtils.S_IRUSR|FileUtils.S_IWUSR
+                        |FileUtils.S_IRGRP|FileUtils.S_IWGRP
+                        |FileUtils.S_IROTH,
+                        -1, -1);
+
                 return;
 
             } catch(XmlPullParserException e) {
@@ -7376,7 +7527,7 @@
             } catch(java.io.IOException e) {
                 Log.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e);
             }
-            // Clean up partially written file
+            // Clean up partially written files
             if (mSettingsFilename.exists()) {
                 if (!mSettingsFilename.delete()) {
                     Log.i(TAG, "Failed to clean up mangled file: " + mSettingsFilename);
@@ -7386,7 +7537,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 +7575,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 +7765,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 +7895,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 +8425,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 +8455,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 +8466,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 +8490,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 +8499,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 +8565,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 +8590,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 +8611,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 +8652,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 +8665,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 +8680,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 +8702,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 +8729,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..87ed252 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1949,6 +1949,9 @@
             if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                 debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
             }
+            if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0) {
+                debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
+            }
             if ("1".equals(SystemProperties.get("debug.checkjni"))) {
                 debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
             }
@@ -11941,7 +11944,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 +11963,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 +12149,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/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index f5aeaf0..20209e4 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -37,6 +37,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
@@ -93,6 +94,9 @@
     private IBinder mClockIcon;
     private IconData mClockData;
 
+    // storage
+    private StorageManager mStorageManager;
+
     // battery
     private IBinder mBatteryIcon;
     private IconData mBatteryData;
@@ -407,6 +411,11 @@
         mClockIcon = service.addIcon(mClockData, null);
         updateClock();
 
+        // storage
+        mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+        mStorageManager.registerListener(
+                new com.android.server.status.StorageNotification(context));
+
         // battery
         mBatteryData = IconData.makeIcon("battery",
                 null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 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/services/java/com/android/server/status/StorageNotification.java b/services/java/com/android/server/status/StorageNotification.java
new file mode 100644
index 0000000..3b79049
--- /dev/null
+++ b/services/java/com/android/server/status/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.server.status;
+
+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.server.status.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.server.status.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/UsbStorageActivity.java b/services/java/com/android/server/status/UsbStorageActivity.java
similarity index 71%
rename from core/java/com/android/internal/app/UsbStorageActivity.java
rename to services/java/com/android/server/status/UsbStorageActivity.java
index 34ae2b4..7a2a2d6 100644
--- a/core/java/com/android/internal/app/UsbStorageActivity.java
+++ b/services/java/com/android/server/status/UsbStorageActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.app;
+package com.android.server.status;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
@@ -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/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/TestLocationProvider.java b/test-runner/android/test/TestLocationProvider.java
deleted file mode 100644
index dc07585..0000000
--- a/test-runner/android/test/TestLocationProvider.java
+++ /dev/null
@@ -1,196 +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.test;
-
-
-import android.location.Criteria;
-import android.location.ILocationManager;
-import android.location.ILocationProvider;
-import android.location.Location;
-import android.location.LocationProvider;
-import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Log;
-
-/**
- * @hide - This is part of a framework that is under development and should not be used for
- * active development.
- */
-public class TestLocationProvider extends ILocationProvider.Stub {
-
-    public static final String PROVIDER_NAME = "test";
-    public static final double LAT = 0;
-    public static final double LON = 1;
-    public static final double ALTITUDE = 10000;
-    public static final float SPEED = 10;
-    public static final float BEARING = 1;
-    public static final int STATUS = LocationProvider.AVAILABLE;
-    private static final long LOCATION_INTERVAL = 1000;
-
-    private static final String TAG = "TestLocationProvider";
-
-    private final ILocationManager mLocationManager;
-    private Location mLocation;
-    private boolean mEnabled;
-    private TestLocationProviderThread mThread;
-
-    private class TestLocationProviderThread extends Thread {
-
-        private boolean mDone = false;
-
-        public TestLocationProviderThread() {
-            super("TestLocationProviderThread");
-        }
-
-        public void run() {            
-            // thread exits after disable() is called
-            synchronized (this) {
-                while (!mDone) {
-                    try {
-                        wait(LOCATION_INTERVAL);
-                    } catch (InterruptedException e) {
-                    }
-                    
-                    if (!mDone) {
-                        TestLocationProvider.this.updateLocation();
-                    }
-                }
-            }
-        }
-        
-        synchronized void setDone() {
-            mDone = true;
-            notify();
-        }
-    }
-
-    public TestLocationProvider(ILocationManager locationManager) {
-        mLocationManager = locationManager;
-        mLocation = new Location(PROVIDER_NAME);
-    }
-
-    public int getAccuracy() {
-        return Criteria.ACCURACY_COARSE;
-    }
-
-    public int getPowerRequirement() {
-        return Criteria.NO_REQUIREMENT;
-    }
-
-    public boolean hasMonetaryCost() {
-        return false;
-    }
-
-    public boolean requiresCell() {
-        return false;
-    }
-
-    public boolean requiresNetwork() {
-        return false;
-    }
-
-    public boolean requiresSatellite() {
-        return false;
-    }
-
-    public boolean supportsAltitude() {
-        return true;
-    }
-
-    public boolean supportsBearing() {
-        return true;
-    }
-
-    public boolean supportsSpeed() {
-        return true;
-    }
-
-    public synchronized void disable() {
-        mEnabled = false;
-        if (mThread != null) {
-            mThread.setDone();
-            try {
-                mThread.join();
-            } catch (InterruptedException e) {
-            }
-            mThread = null;
-        }
-    }
-
-    public synchronized void enable() {
-       mEnabled = true;
-        mThread = new TestLocationProviderThread();
-        mThread.start();
-    }
-
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    public int getStatus(Bundle extras) {
-        return STATUS;
-    }
-
-    public long getStatusUpdateTime() {
-        return 0;
-    }
-
-    public void enableLocationTracking(boolean enable) {
-    }
-
-    public void setMinTime(long minTime) {
-    }
-
-    public void updateNetworkState(int state, NetworkInfo info) {
-    }
-
-    public void updateLocation(Location location) {
-    }
-
-    public boolean sendExtraCommand(String command, Bundle extras) {
-        return false;
-    }
-
-    public void addListener(int uid) {
-    }
-
-    public void removeListener(int uid) {
-    }
-
-    private void updateLocation() {
-        long time = SystemClock.uptimeMillis();
-        long multiplier = (time/5000)%500000;
-        mLocation.setLatitude(LAT*multiplier);
-        mLocation.setLongitude(LON*multiplier);
-        mLocation.setAltitude(ALTITUDE);
-        mLocation.setSpeed(SPEED);
-        mLocation.setBearing(BEARING*multiplier);
-
-        Bundle extras = new Bundle();
-        extras.putInt("extraTest", 24);
-        mLocation.setExtras(extras);
-        mLocation.setTime(time);
-        try {
-            mLocationManager.reportLocation(mLocation);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException calling updateLocation");
-        }
-    }
-
-}
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/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index cbe0253..c8339ed 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -30,6 +30,7 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
@@ -443,7 +444,7 @@
      * @hide
      */
     @Override
-    public int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
+    public int recommendAppInstallLocation(PackageParser.Package pkg) {
         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/res/raw/install_loc_auto b/tests/AndroidTests/res/raw/install_loc_auto
new file mode 100644
index 0000000..60dda18
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_loc_auto
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_internal b/tests/AndroidTests/res/raw/install_loc_internal
new file mode 100644
index 0000000..1bc33ca
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_loc_internal
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_sdcard b/tests/AndroidTests/res/raw/install_loc_sdcard
new file mode 100644
index 0000000..6604e35
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_loc_sdcard
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_unspecified b/tests/AndroidTests/res/raw/install_loc_unspecified
new file mode 100644
index 0000000..88bbace
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_loc_unspecified
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 163ddd5..07bd489 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,8 @@
 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.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
@@ -51,15 +54,28 @@
 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;
+import android.provider.Settings;
 
 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 +162,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 +175,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 +278,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 +288,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 +315,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 +374,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 +412,526 @@
         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();
+        }
+    }
+
+    public void invokeRecommendAppInstallLocation(String outFileName,
+            int fileResId, int expected) {
+        int origSetting = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 0);
+        try {
+            // Make sure the set install location setting is diabled.
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.SET_INSTALL_LOCATION, 0);
+            File filesDir = mContext.getFilesDir();
+            File outFile = new File(filesDir, outFileName);
+            Uri packageURI = getInstallablePackage(fileResId, outFile);
+            PackageParser.Package pkg = parsePackage(packageURI);
+            assertNotNull(pkg);
+            int installLoc = getPm().recommendAppInstallLocation(pkg);
+            Log.i(TAG, "expected=" + expected +", installLoc="+installLoc);
+            // Atleast one of the specified expected flags should be set.
+            boolean onFlash = (installLoc &
+                    PackageManager.INSTALL_ON_INTERNAL_FLASH) != 0;
+            boolean onSd = (installLoc &
+                    PackageManager.INSTALL_ON_SDCARD) != 0;
+            boolean expOnFlash = (expected &
+                    PackageManager.INSTALL_ON_INTERNAL_FLASH) != 0;
+            boolean expOnSd = (expected &
+                    PackageManager.INSTALL_ON_SDCARD) != 0;
+            assertTrue(expOnFlash == onFlash || expOnSd == onSd);
+        } finally {
+            // Restore original setting
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.SET_INSTALL_LOCATION, origSetting);
+        }
+    }
+
+    /*
+     * Tests if an apk can be installed on internal flash by
+     * explicitly specifying in its manifest.
+     */
+    public void testInstallLocationInternal() {
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_internal, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests if an apk can be installed on internal flash by
+     * explicitly specifying in its manifest and filling up
+     * internal flash. Should fail to install.
+     * TODO
+     */
+    public void xxxtestInstallLocationInternalFail() {
+    }
+
+    /*
+     * Tests if an apk can be installed on sdcard by
+     * explicitly specifying in its manifest.
+     */
+    public void testInstallLocationSdcard() {
+        // TODO No guarantee this will be on sdcard.
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_sdcard, PackageManager.INSTALL_ON_SDCARD
+                | PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests if an apk can be installed on sdcard by
+     * explicitly specifying in its manifest and filling up
+     * the sdcard. Should result in install failure
+     * TODO
+     */
+    public void xxxtestInstallLocationSdcardFail() {
+    }
+
+    /*
+     * Tests if an apk can be installed by specifying
+     * auto for install location
+     */
+    public void xxxtestInstallLocationAutoInternal() {
+        // TODO clear and make room on internal flash
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_auto, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests if an apk can be installed by specifying
+     * auto for install location
+     */
+    public void testInstallLocationAutoSdcard() {
+        // TODO clear and make room on sdcard.
+        // Fill up internal
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_auto, PackageManager.INSTALL_ON_SDCARD |
+                PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests if an apk can be installed by specifying
+     * auto for install location
+     * fill up both internal and sdcard
+     * TODO
+     */
+    public void xxxtestInstallLocationAutoFail() {
+    }
+    /*
+     * Tests where an apk gets installed based
+     * on a not specifying anything in manifest.
+     */
+    public void testInstallLocationUnspecifiedInt() {
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    public void xxxtestInstallLocationUnspecifiedStorage() {
+        // TODO Fill up internal storage
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_SDCARD
+                | PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests where an apk gets installed by expcitly setting
+     * the user specified install location
+     */
+    public void testInstallLocationUserSpecifiedInternal() {
+        // Enable user setting
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 1);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, 1);
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests where an apk gets installed by expcitly setting
+     * the user specified install location
+     */
+    public void testInstallLocationUserSpecifiedSdcard() {
+        // Enable user setting
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 1);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, 2);
+        int i = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, 0);
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_SDCARD);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 0);
+    }
+    /*
+     * Tests where an apk gets installed by expcitly setting
+     * the user specified install location
+     */
+    public void testInstallLocationUserSpecifiedAuto() {
+        // Enable user setting
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 1);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, 0);
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 0);
+    }
+
+    /*
+     * 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;