Merge "Honor '+' in phone export in vCard."
diff --git a/Android.mk b/Android.mk
index 369cdfd..682f286 100644
--- a/Android.mk
+++ b/Android.mk
@@ -112,7 +112,7 @@
 	core/java/android/os/ICheckinService.aidl \
 	core/java/android/os/IMessenger.aidl \
 	core/java/android/os/IMountService.aidl \
-	core/java/android/os/IMountServiceObserver.aidl \
+	core/java/android/os/IMountServiceListener.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/INetStatService.aidl \
 	core/java/android/os/IParentalControlCallback.aidl \
@@ -317,8 +317,7 @@
 	xml/src/main/java/org/w3c
 
 non_base_dirs := \
-	../../external/apache-http/src/org/apache/http \
-        ../../external/oauth/core/src/main/java/net/oauth
+	../../external/apache-http/src/org/apache/http
 
 # These are relative to frameworks/base
 dirs_to_document := \
@@ -549,7 +548,6 @@
 
 ext_dirs := \
 	../../external/apache-http/src \
-        ../../external/oauth/core/src/main/java \
 	../../external/tagsoup/src
 
 ext_src_files := $(call all-java-files-under,$(ext_dirs))
diff --git a/api/8.xml b/api/8.xml
index d5e5fc9..a07537c 100644
--- a/api/8.xml
+++ b/api/8.xml
@@ -173386,573 +173386,6 @@
 </parameter>
 </method>
 </class>
-<class name="CallbackProxy"
- extends="android.os.Handler"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility=""
->
-<constructor name="CallbackProxy"
- type="android.webkit.CallbackProxy"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="w" type="android.webkit.WebView">
-</parameter>
-</constructor>
-<method name="addMessageToConsole"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-<parameter name="lineNumber" type="int">
-</parameter>
-<parameter name="sourceID" type="java.lang.String">
-</parameter>
-</method>
-<method name="createWindow"
- return="android.webkit.WebView"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dialog" type="boolean">
-</parameter>
-<parameter name="userGesture" type="boolean">
-</parameter>
-</method>
-<method name="doUpdateVisitedHistory"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="isReload" type="boolean">
-</parameter>
-</method>
-<method name="getBackForwardList"
- return="android.webkit.WebBackForwardList"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getProgress"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getVisitedHistory"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="callback" type="android.webkit.ValueCallback&lt;java.lang.String[]&gt;">
-</parameter>
-</method>
-<method name="getWebChromeClient"
- return="android.webkit.WebChromeClient"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onCloseWindow"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="window" type="android.webkit.WebView">
-</parameter>
-</method>
-<method name="onDownloadStart"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="userAgent" type="java.lang.String">
-</parameter>
-<parameter name="contentDisposition" type="java.lang.String">
-</parameter>
-<parameter name="mimetype" type="java.lang.String">
-</parameter>
-<parameter name="contentLength" type="long">
-</parameter>
-</method>
-<method name="onExceededDatabaseQuota"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="databaseIdentifier" type="java.lang.String">
-</parameter>
-<parameter name="currentQuota" type="long">
-</parameter>
-<parameter name="estimatedSize" type="long">
-</parameter>
-<parameter name="totalUsedQuota" type="long">
-</parameter>
-<parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
-</parameter>
-</method>
-<method name="onFormResubmission"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dontResend" type="android.os.Message">
-</parameter>
-<parameter name="resend" type="android.os.Message">
-</parameter>
-</method>
-<method name="onGeolocationPermissionsHidePrompt"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onGeolocationPermissionsShowPrompt"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="origin" type="java.lang.String">
-</parameter>
-<parameter name="callback" type="android.webkit.GeolocationPermissions.Callback">
-</parameter>
-</method>
-<method name="onJsAlert"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="message" type="java.lang.String">
-</parameter>
-</method>
-<method name="onJsBeforeUnload"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="message" type="java.lang.String">
-</parameter>
-</method>
-<method name="onJsConfirm"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="message" type="java.lang.String">
-</parameter>
-</method>
-<method name="onJsPrompt"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="message" type="java.lang.String">
-</parameter>
-<parameter name="defaultValue" type="java.lang.String">
-</parameter>
-</method>
-<method name="onJsTimeout"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onLoadResource"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-</method>
-<method name="onPageFinished"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-</method>
-<method name="onPageStarted"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="favicon" type="android.graphics.Bitmap">
-</parameter>
-</method>
-<method name="onProgressChanged"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="newProgress" type="int">
-</parameter>
-</method>
-<method name="onReachedMaxAppCacheSize"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="spaceNeeded" type="long">
-</parameter>
-<parameter name="totalUsedQuota" type="long">
-</parameter>
-<parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
-</parameter>
-</method>
-<method name="onReceivedError"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="errorCode" type="int">
-</parameter>
-<parameter name="description" type="java.lang.String">
-</parameter>
-<parameter name="failingUrl" type="java.lang.String">
-</parameter>
-</method>
-<method name="onReceivedHttpAuthRequest"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="handler" type="android.webkit.HttpAuthHandler">
-</parameter>
-<parameter name="hostName" type="java.lang.String">
-</parameter>
-<parameter name="realmName" type="java.lang.String">
-</parameter>
-</method>
-<method name="onReceivedIcon"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="icon" type="android.graphics.Bitmap">
-</parameter>
-</method>
-<method name="onReceivedTitle"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="title" type="java.lang.String">
-</parameter>
-</method>
-<method name="onRequestFocus"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onSavePassword"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="schemePlusHost" type="java.lang.String">
-</parameter>
-<parameter name="username" type="java.lang.String">
-</parameter>
-<parameter name="password" type="java.lang.String">
-</parameter>
-<parameter name="resumeMsg" type="android.os.Message">
-</parameter>
-</method>
-<method name="onScaleChanged"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldScale" type="float">
-</parameter>
-<parameter name="newScale" type="float">
-</parameter>
-</method>
-<method name="onTooManyRedirects"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cancelMsg" type="android.os.Message">
-</parameter>
-<parameter name="continueMsg" type="android.os.Message">
-</parameter>
-</method>
-<method name="onUnhandledKeyEvent"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="event" type="android.view.KeyEvent">
-</parameter>
-</method>
-<method name="setDownloadListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="client" type="android.webkit.DownloadListener">
-</parameter>
-</method>
-<method name="setWebChromeClient"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="client" type="android.webkit.WebChromeClient">
-</parameter>
-</method>
-<method name="setWebViewClient"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="client" type="android.webkit.WebViewClient">
-</parameter>
-</method>
-<method name="shouldOverrideUrlLoading"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-</method>
-<method name="uiOverrideKeyEvent"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="event" type="android.view.KeyEvent">
-</parameter>
-</method>
-<method name="uiOverrideUrlLoading"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="overrideUrl" type="java.lang.String">
-</parameter>
-</method>
-</class>
 <class name="CookieManager"
  extends="java.lang.Object"
  abstract="false"
@@ -174438,16 +173871,6 @@
  visibility="protected"
 >
 </method>
-<field name="mProxy"
- type="android.webkit.CallbackProxy"
- transient="false"
- volatile="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
 <field name="mResult"
  type="boolean"
  transient="false"
diff --git a/api/current.xml b/api/current.xml
index 9bd5c01..5e34322 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -33641,6 +33641,17 @@
  visibility="public"
 >
 </field>
+<field name="STORAGE_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;storage&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TELEPHONY_SERVICE"
  type="java.lang.String"
  transient="false"
@@ -80913,6 +80924,17 @@
  visibility="public"
 >
 </field>
+<field name="TAG_GPS_DATESTAMP"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;GPSDateStamp&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TAG_GPS_LATITUDE"
  type="java.lang.String"
  transient="false"
@@ -80957,6 +80979,17 @@
  visibility="public"
 >
 </field>
+<field name="TAG_GPS_TIMESTAMP"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;GPSTimeStamp&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TAG_IMAGE_LENGTH"
  type="java.lang.String"
  transient="false"
@@ -109684,6 +109717,16 @@
  visibility="public"
 >
 </field>
+<field name="BOOTLOADER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="BRAND"
  type="java.lang.String"
  transient="false"
@@ -109744,6 +109787,16 @@
  visibility="public"
 >
 </field>
+<field name="HARDWARE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="HOST"
  type="java.lang.String"
  transient="false"
@@ -109794,6 +109847,16 @@
  visibility="public"
 >
 </field>
+<field name="RADIO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TAGS"
  type="java.lang.String"
  transient="false"
@@ -110017,6 +110080,17 @@
  visibility="public"
 >
 </field>
+<field name="FROYO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="Bundle"
  extends="java.lang.Object"
@@ -136054,6 +136128,126 @@
 </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"
@@ -163009,6 +163203,22 @@
 <parameter name="handler" type="android.os.Handler">
 </parameter>
 </constructor>
+<constructor name="GestureDetector"
+ type="android.view.GestureDetector"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="listener" type="android.view.GestureDetector.OnGestureListener">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+<parameter name="ignoreMultitouch" type="boolean">
+</parameter>
+</constructor>
 <method name="isLongpressEnabled"
  return="boolean"
  abstract="false"
@@ -168383,6 +168593,233 @@
 >
 </field>
 </class>
+<class name="ScaleGestureDetector"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ScaleGestureDetector"
+ type="android.view.ScaleGestureDetector"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="listener" type="android.view.ScaleGestureDetector.OnScaleGestureListener">
+</parameter>
+</constructor>
+<method name="getCurrentSpan"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEventTime"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFocusX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFocusY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPreviousSpan"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getScaleFactor"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTimeDelta"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isInProgress"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onTouchEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
+</class>
+<interface name="ScaleGestureDetector.OnScaleGestureListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onScale"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="detector" type="android.view.ScaleGestureDetector">
+</parameter>
+</method>
+<method name="onScaleBegin"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="detector" type="android.view.ScaleGestureDetector">
+</parameter>
+</method>
+<method name="onScaleEnd"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="detector" type="android.view.ScaleGestureDetector">
+</parameter>
+</method>
+</interface>
+<class name="ScaleGestureDetector.SimpleOnScaleGestureListener"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.ScaleGestureDetector.OnScaleGestureListener">
+</implements>
+<constructor name="ScaleGestureDetector.SimpleOnScaleGestureListener"
+ type="android.view.ScaleGestureDetector.SimpleOnScaleGestureListener"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onScale"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="detector" type="android.view.ScaleGestureDetector">
+</parameter>
+</method>
+<method name="onScaleBegin"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="detector" type="android.view.ScaleGestureDetector">
+</parameter>
+</method>
+<method name="onScaleEnd"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="detector" type="android.view.ScaleGestureDetector">
+</parameter>
+</method>
+</class>
 <class name="SoundEffectConstants"
  extends="java.lang.Object"
  abstract="false"
@@ -185890,573 +186327,6 @@
 </parameter>
 </method>
 </class>
-<class name="CallbackProxy"
- extends="android.os.Handler"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility=""
->
-<constructor name="CallbackProxy"
- type="android.webkit.CallbackProxy"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="w" type="android.webkit.WebView">
-</parameter>
-</constructor>
-<method name="addMessageToConsole"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-<parameter name="lineNumber" type="int">
-</parameter>
-<parameter name="sourceID" type="java.lang.String">
-</parameter>
-</method>
-<method name="createWindow"
- return="android.webkit.WebView"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dialog" type="boolean">
-</parameter>
-<parameter name="userGesture" type="boolean">
-</parameter>
-</method>
-<method name="doUpdateVisitedHistory"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="isReload" type="boolean">
-</parameter>
-</method>
-<method name="getBackForwardList"
- return="android.webkit.WebBackForwardList"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getProgress"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getVisitedHistory"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="callback" type="android.webkit.ValueCallback&lt;java.lang.String[]&gt;">
-</parameter>
-</method>
-<method name="getWebChromeClient"
- return="android.webkit.WebChromeClient"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onCloseWindow"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="window" type="android.webkit.WebView">
-</parameter>
-</method>
-<method name="onDownloadStart"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="userAgent" type="java.lang.String">
-</parameter>
-<parameter name="contentDisposition" type="java.lang.String">
-</parameter>
-<parameter name="mimetype" type="java.lang.String">
-</parameter>
-<parameter name="contentLength" type="long">
-</parameter>
-</method>
-<method name="onExceededDatabaseQuota"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="databaseIdentifier" type="java.lang.String">
-</parameter>
-<parameter name="currentQuota" type="long">
-</parameter>
-<parameter name="estimatedSize" type="long">
-</parameter>
-<parameter name="totalUsedQuota" type="long">
-</parameter>
-<parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
-</parameter>
-</method>
-<method name="onFormResubmission"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dontResend" type="android.os.Message">
-</parameter>
-<parameter name="resend" type="android.os.Message">
-</parameter>
-</method>
-<method name="onGeolocationPermissionsHidePrompt"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onGeolocationPermissionsShowPrompt"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="origin" type="java.lang.String">
-</parameter>
-<parameter name="callback" type="android.webkit.GeolocationPermissions.Callback">
-</parameter>
-</method>
-<method name="onJsAlert"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="message" type="java.lang.String">
-</parameter>
-</method>
-<method name="onJsBeforeUnload"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="message" type="java.lang.String">
-</parameter>
-</method>
-<method name="onJsConfirm"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="message" type="java.lang.String">
-</parameter>
-</method>
-<method name="onJsPrompt"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="message" type="java.lang.String">
-</parameter>
-<parameter name="defaultValue" type="java.lang.String">
-</parameter>
-</method>
-<method name="onJsTimeout"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onLoadResource"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-</method>
-<method name="onPageFinished"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-</method>
-<method name="onPageStarted"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-<parameter name="favicon" type="android.graphics.Bitmap">
-</parameter>
-</method>
-<method name="onProgressChanged"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="newProgress" type="int">
-</parameter>
-</method>
-<method name="onReachedMaxAppCacheSize"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="spaceNeeded" type="long">
-</parameter>
-<parameter name="totalUsedQuota" type="long">
-</parameter>
-<parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
-</parameter>
-</method>
-<method name="onReceivedError"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="errorCode" type="int">
-</parameter>
-<parameter name="description" type="java.lang.String">
-</parameter>
-<parameter name="failingUrl" type="java.lang.String">
-</parameter>
-</method>
-<method name="onReceivedHttpAuthRequest"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="handler" type="android.webkit.HttpAuthHandler">
-</parameter>
-<parameter name="hostName" type="java.lang.String">
-</parameter>
-<parameter name="realmName" type="java.lang.String">
-</parameter>
-</method>
-<method name="onReceivedIcon"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="icon" type="android.graphics.Bitmap">
-</parameter>
-</method>
-<method name="onReceivedTitle"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="title" type="java.lang.String">
-</parameter>
-</method>
-<method name="onRequestFocus"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onSavePassword"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="schemePlusHost" type="java.lang.String">
-</parameter>
-<parameter name="username" type="java.lang.String">
-</parameter>
-<parameter name="password" type="java.lang.String">
-</parameter>
-<parameter name="resumeMsg" type="android.os.Message">
-</parameter>
-</method>
-<method name="onScaleChanged"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldScale" type="float">
-</parameter>
-<parameter name="newScale" type="float">
-</parameter>
-</method>
-<method name="onTooManyRedirects"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cancelMsg" type="android.os.Message">
-</parameter>
-<parameter name="continueMsg" type="android.os.Message">
-</parameter>
-</method>
-<method name="onUnhandledKeyEvent"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="event" type="android.view.KeyEvent">
-</parameter>
-</method>
-<method name="setDownloadListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="client" type="android.webkit.DownloadListener">
-</parameter>
-</method>
-<method name="setWebChromeClient"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="client" type="android.webkit.WebChromeClient">
-</parameter>
-</method>
-<method name="setWebViewClient"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="client" type="android.webkit.WebViewClient">
-</parameter>
-</method>
-<method name="shouldOverrideUrlLoading"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="java.lang.String">
-</parameter>
-</method>
-<method name="uiOverrideKeyEvent"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="event" type="android.view.KeyEvent">
-</parameter>
-</method>
-<method name="uiOverrideUrlLoading"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="overrideUrl" type="java.lang.String">
-</parameter>
-</method>
-</class>
 <class name="CookieManager"
  extends="java.lang.Object"
  abstract="false"
@@ -186942,16 +186812,6 @@
  visibility="protected"
 >
 </method>
-<field name="mProxy"
- type="android.webkit.CallbackProxy"
- transient="false"
- volatile="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
 <field name="mResult"
  type="boolean"
  transient="false"
diff --git a/core/java/android/webkit/HttpDateTime.java b/common/java/com/android/common/HttpDateTime.java
similarity index 99%
rename from core/java/android/webkit/HttpDateTime.java
rename to common/java/com/android/common/HttpDateTime.java
index 042953c..f4052cc 100644
--- a/core/java/android/webkit/HttpDateTime.java
+++ b/common/java/com/android/common/HttpDateTime.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.webkit;
+package com.android.common;
 
 import android.text.format.Time;
 
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 6c65bd8..cf6e0e7 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -84,6 +84,7 @@
 import android.os.StatFs;
 import android.os.Vibrator;
 import android.os.FileUtils.FileStatus;
+import android.storage.StorageManager;
 import android.telephony.TelephonyManager;
 import android.text.ClipboardManager;
 import android.util.AndroidRuntimeException;
@@ -180,6 +181,7 @@
     private Context mReceiverRestrictedContext = null;
     private SearchManager mSearchManager = null;
     private SensorManager mSensorManager = null;
+    private StorageManager mStorageManager = null;
     private Vibrator mVibrator = null;
     private LayoutInflater mLayoutInflater = null;
     private StatusBarManager mStatusBarManager = null;
@@ -879,6 +881,8 @@
             return getSearchManager();
         } else if (SENSOR_SERVICE.equals(name)) {
             return getSensorManager();
+        } else if (STORAGE_SERVICE.equals(name)) {
+            return getStorageManager();
         } else if (VIBRATOR_SERVICE.equals(name)) {
             return getVibrator();
         } else if (STATUS_BAR_SERVICE.equals(name)) {
@@ -1041,6 +1045,20 @@
         return mSensorManager;
     }
 
+    private StorageManager getStorageManager() {
+        synchronized (mSync) {
+            if (mStorageManager == null) {
+                try {
+                    mStorageManager = new StorageManager(mMainThread.getHandler().getLooper());
+                } catch (RemoteException rex) {
+                    Log.e(TAG, "Failed to create StorageManager", rex);
+                    mStorageManager = null;
+                }
+            }
+        }
+        return mStorageManager;
+    }
+
     private Vibrator getVibrator() {
         synchronized (mSync) {
             if (mVibrator == null) {
@@ -2146,6 +2164,11 @@
                     filter.addDataScheme("package");
                     mContext.registerReceiverInternal(sPackageRemovedReceiver,
                             filter, null, null, null);
+                    // Register for events related to sdcard installation.
+                    IntentFilter sdFilter = new IntentFilter();
+                    sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+                    mContext.registerReceiverInternal(sPackageRemovedReceiver,
+                            sdFilter, null, null, null);
                 }
             }
         }
@@ -2163,32 +2186,56 @@
         private static final class PackageRemovedReceiver extends BroadcastReceiver {
             @Override
             public void onReceive(Context context, Intent intent) {
-                Uri data = intent.getData();
-                String ssp;
-                if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
-                    boolean needCleanup = false;
-                    synchronized (sSync) {
-                        Iterator<ResourceName> it = sIconCache.keySet().iterator();
-                        while (it.hasNext()) {
-                            ResourceName nm = it.next();
-                            if (nm.packageName.equals(ssp)) {
-                                //Log.i(TAG, "Removing cached drawable for " + nm);
-                                it.remove();
-                                needCleanup = true;
-                            }
-                        }
-                        it = sStringCache.keySet().iterator();
-                        while (it.hasNext()) {
-                            ResourceName nm = it.next();
-                            if (nm.packageName.equals(ssp)) {
-                                //Log.i(TAG, "Removing cached string for " + nm);
-                                it.remove();
-                                needCleanup = true;
-                            }
+                String pkgList[] = null;
+                String action = intent.getAction();
+                boolean immediateGc = false;
+                if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    immediateGc = true;
+                } else {
+                    Uri data = intent.getData();
+                    if (data != null) {
+                        String ssp = data.getSchemeSpecificPart();
+                        if (ssp != null) {
+                            pkgList = new String[] { ssp };
                         }
                     }
-                    if (needCleanup || ActivityThread.currentActivityThread().hasPackageInfo(ssp)) {
-                        ActivityThread.currentActivityThread().scheduleGcIdler();
+                }
+                if (pkgList != null && (pkgList.length > 0)) {
+                    boolean needCleanup = false;
+                    boolean hasPkgInfo = false;
+                    for (String ssp : pkgList) {
+                        synchronized (sSync) {
+                            Iterator<ResourceName> it = sIconCache.keySet().iterator();
+                            while (it.hasNext()) {
+                                ResourceName nm = it.next();
+                                if (nm.packageName.equals(ssp)) {
+                                    //Log.i(TAG, "Removing cached drawable for " + nm);
+                                    it.remove();
+                                    needCleanup = true;
+                                }
+                            }
+                            it = sStringCache.keySet().iterator();
+                            while (it.hasNext()) {
+                                ResourceName nm = it.next();
+                                if (nm.packageName.equals(ssp)) {
+                                    //Log.i(TAG, "Removing cached string for " + nm);
+                                    it.remove();
+                                    needCleanup = true;
+                                }
+                            }
+                        }
+                        if (!hasPkgInfo) {
+                            hasPkgInfo = ActivityThread.currentActivityThread().hasPackageInfo(ssp);
+                        }
+                    }
+                    if (needCleanup || hasPkgInfo) {
+                        if (immediateGc) {
+                            // Schedule an immediate gc.
+                            Runtime.getRuntime().gc();
+                        } else {
+                            ActivityThread.currentActivityThread().scheduleGcIdler();
+                        }
                     }
                 }
             }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 321ba5c..5aefe4c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1109,6 +1109,8 @@
      * @see android.app.SearchManager
      * @see #SENSOR_SERVICE
      * @see android.hardware.SensorManager
+     * @see #STORAGE_SERVICE
+     * @see android.storage.StorageManager
      * @see #VIBRATOR_SERVICE
      * @see android.os.Vibrator
      * @see #CONNECTIVITY_SERVICE
@@ -1240,6 +1242,16 @@
     public static final String SENSOR_SERVICE = "sensor";
     
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.storage.StorageManager} for accesssing system storage
+     * functions.
+     *
+     * @see #getSystemService
+     * @see android.storage.StorageManager
+     */
+    public static final String STORAGE_SERVICE = "storage";
+
+    /**
      * Use with {@link #getSystemService} to retrieve a
      * com.android.server.WallpaperService for accessing wallpapers.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bbd359b..e957e20 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1355,6 +1355,60 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
+
+    /**
+     * Broadcast Action: Resources for a set of packages (which were
+     * previously unavailable) are currently
+     * available since the media on which they exist is available.
+     * The extra data {@link #EXTRA_CHANGED_PACKAGE_LIST} contains a
+     * list of packages whose availability changed.
+     * The extra data {@link #EXTRA_CHANGED_UID_LIST} contains a
+     * list of uids of packages whose availability changed.
+     * Note that the
+     * packages in this list do <em>not</em> receive this broadcast.
+     * The specified set of packages are now available on the system.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages
+     * whose resources(were previously unavailable) are currently available.
+     * {@link #EXTRA_CHANGED_UID_LIST} is the set of uids of the
+     * packages whose resources(were previously unavailable)
+     * are  currently available.
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_RESOURCES_AVAILABLE =
+        "android.intent.action.MEDIA_RESOURCES_AVAILABILE";
+
+    /**
+     * Broadcast Action: Resources for a set of packages are currently
+     * unavailable since the media on which they exist is unavailable.
+     * The extra data {@link #EXTRA_CHANGED_PACKAGE_LIST} contains a
+     * list of packages whose availability changed.
+     * The extra data {@link #EXTRA_CHANGED_UID_LIST} contains a
+     * list of uids of packages whose availability changed.
+     * The specified set of packages can no longer be
+     * launched and are practically unavailable on the system.
+     * <p>Inclues the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages
+     * whose resources are no longer available.
+     * {@link #EXTRA_CHANGED_UID_LIST} is the set of packages
+     * whose resources are no longer available.
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_RESOURCES_UNAVAILABLE =
+        "android.intent.action.MEDIA_RESOURCES_UNAVAILABILE";
+
     /**
      * Broadcast Action:  The current system wallpaper has changed.  See
      * {@link android.app.WallpaperManager} for retrieving the new wallpaper.
@@ -2136,13 +2190,34 @@
             "android.intent.extra.changed_component_name";
 
     /**
-     * This field is part of {@link android.content.Intent#ACTION_PACKAGE_CHANGED}
+     * This field is part of {@link android.content.Intent#ACTION_PACKAGE_CHANGED},
      * and contains a string array of all of the components that have changed.
      */
     public static final String EXTRA_CHANGED_COMPONENT_NAME_LIST =
             "android.intent.extra.changed_component_name_list";
 
     /**
+     * This field is part of
+     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE},
+     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE}
+     * and contains a string array of all of the components that have changed.
+     * @hide
+     */
+    public static final String EXTRA_CHANGED_PACKAGE_LIST =
+            "android.intent.extra.changed_package_list";
+
+    /**
+     * This field is part of
+     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE},
+     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE}
+     * and contains an integer array of uids of all of the components
+     * that have changed.
+     * @hide
+     */
+    public static final String EXTRA_CHANGED_UID_LIST =
+            "android.intent.extra.changed_uid_list";
+
+    /**
      * @hide
      * Magic extra system code can use when binding, to give a label for
      * who it is that has bound to a service.  This is an integer giving
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1e45f17..745628a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -615,13 +615,13 @@
     /**
      * Determines best place to install an application: either SD or internal FLASH.
      * Tweak the algorithm for best results.
-     * @param appInfo ApplicationInfo object og the package to install.
+     * @param appInfo ApplicationInfo object of the package to install.
      * Call utility method to obtain.
      * @param packageURI URI identifying the package's APK file.
-     * @return <code>INSTALL_ON_INTERNAL_FLASH</code> if it is best to install package on internal
-     * storage, <code>INSTALL_ON_SDCARD</code> if it is best to install package on SD card,
-     * and <code>INSTALL_FAILED_INSUFFICIENT_STORAGE</code> if insufficient space to safely install
-     * the application. <code>INSTALL_PARSE_FAILED_NOT_APK</code> Is returned if any input
+     * @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
+     * the application. {@link INSTALL_PARSE_FAILED_NOT_APK} Is returned if any input
      * parameter is <code>null</code>.
      * This recommendation does take into account the package's own flags.
      * @hide
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index b819fa0..7362394 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -115,6 +115,11 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         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);
+        mContext.registerReceiver(receiver, sdFilter);
     }
 
     public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index e3c25ec..e4b0191 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -290,6 +290,10 @@
     @Override
     protected void onAllReferencesReleased() {
         if (isOpen()) {
+            if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+                Log.d(TAG, "captured_sql|" + mPath + "|DETACH DATABASE " +
+                        getDatabaseName(mPath) + ";");
+            }
             if (SQLiteDebug.DEBUG_SQL_CACHE) {
                 mTimeClosed = getTime();
             }
@@ -1648,6 +1652,9 @@
      */
     public void execSQL(String sql) throws SQLException {
         long timeStart = Debug.threadCpuTimeNanos();
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            Log.v(TAG, SQLiteDebug.captureSql(this.getPath(), sql, null));
+        }
         lock();
         try {
             native_execSQL(sql);
@@ -1673,7 +1680,9 @@
         if (bindArgs == null) {
             throw new IllegalArgumentException("Empty bindArgs");
         }
-
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            Log.v(TAG, SQLiteDebug.captureSql(this.getPath(), sql, bindArgs));
+        }
         long timeStart = Debug.threadCpuTimeNanos();
         lock();
         SQLiteStatement statement = null;
@@ -1732,6 +1741,10 @@
         mLeakedException = new IllegalStateException(path +
             " SQLiteDatabase created and never closed");
         mFactory = factory;
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            Log.d(TAG, "captured_sql|" + mPath + "|ATTACH DATABASE '" + mPath +
+                    "' as " + getDatabaseName(mPath) + ";");
+        }
         dbopen(mPath, mFlags);
         if (SQLiteDebug.DEBUG_SQL_CACHE) {
             mTimeOpened = getTime();
@@ -1741,6 +1754,10 @@
             setLocale(Locale.getDefault());
         } catch (RuntimeException e) {
             Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e);
+            if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+                Log.d(TAG, "captured_sql|" + mPath + "|DETACH DATABASE " +
+                        getDatabaseName(mPath) + ";");
+            }
             dbclose();
             if (SQLiteDebug.DEBUG_SQL_CACHE) {
                 mTimeClosed = getTime();
@@ -1753,6 +1770,20 @@
         return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis());
     }
 
+    private String getDatabaseName(String path) {
+        if (path == null || path.trim().length() == 0) {
+            return "db not specified?";
+        }
+
+        if (path.equalsIgnoreCase(":memory:")) {
+            return "memorydb";
+        }
+        String[] tokens = path.split("/");
+        String[] lastNodeTokens = tokens[tokens.length - 1].split("\\.", 2);
+        return (lastNodeTokens.length == 1) ? lastNodeTokens[0]
+                : lastNodeTokens[0] + lastNodeTokens[1];
+    }
+
     /**
      * return whether the DB is opened as read only.
      * @return true if DB is opened as read only
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index d4d3059..b12034a 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -16,12 +16,11 @@
 
 package android.database.sqlite;
 
-import android.util.Config;
 import android.util.Log;
 
 /**
  * Provides debugging info about all SQLite databases running in the current process.
- * 
+ *
  * {@hide}
  */
 public final class SQLiteDebug {
@@ -38,6 +37,13 @@
             Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE);
 
     /**
+     * Controls the capturing and printing of complete sql statement including the bind args and
+     * the database name.
+     */
+    public static final boolean DEBUG_CAPTURE_SQL =
+            Log.isLoggable("SQLiteCaptureSql", Log.VERBOSE);
+
+    /**
      * Controls the stack trace reporting of active cursors being
      * finalized.
      */
@@ -45,20 +51,20 @@
             Log.isLoggable("SQLiteCursorClosing", Log.VERBOSE);
 
     /**
-     * Controls the tracking of time spent holding the database lock. 
+     * Controls the tracking of time spent holding the database lock.
      */
     public static final boolean DEBUG_LOCK_TIME_TRACKING =
             Log.isLoggable("SQLiteLockTime", Log.VERBOSE);
 
     /**
-     * Controls the printing of stack traces when tracking the time spent holding the database lock. 
+     * Controls the printing of stack traces when tracking the time spent holding the database lock.
      */
     public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE =
             Log.isLoggable("SQLiteLockStackTrace", Log.VERBOSE);
 
     /**
      * Contains statistics about the active pagers in the current process.
-     * 
+     *
      * @see #getPagerStats(PagerStats)
      */
     public static class PagerStats {
@@ -82,13 +88,13 @@
      * @return The size of the SQLite heap in bytes.
      */
     public static native long getHeapSize();
-    
+
     /**
      * Returns the amount of allocated memory in the SQLite heap.
      * @return The allocated size in bytes.
      */
     public static native long getHeapAllocatedSize();
-    
+
     /**
      * Returns the amount of free memory in the SQLite heap.
      * @return The freed size in bytes.
@@ -115,4 +121,62 @@
     static synchronized void notifyActiveCursorFinalized() {
         sNumActiveCursorsFinalized++;
     }
+
+    /**
+     * returns a  message containing the given database name (path) and the string built by
+     * replacing "?" characters in the given sql string with the corresponding
+     * positional values from the given param bindArgs.
+     *
+     * @param path the database name
+     * @param sql sql string with possibly "?" for bindargs
+     * @param bindArgs args for "?"s in the above string
+     * @return the String to be logged
+     */
+    /* package */ static String captureSql(String path, String sql, Object[] bindArgs) {
+        // how many bindargs in sql
+        sql = sql.trim();
+        String args[] = sql.split("\\?");
+        // how many "?"s in the given sql string?
+        int varArgsInSql = (sql.endsWith("?")) ? args.length : args.length - 1;
+
+        // how many bind args do we have in the given input param bindArgs
+        int bindArgsLen = (bindArgs == null) ? 0 : bindArgs.length;
+        if (varArgsInSql < bindArgsLen) {
+            return "too many bindArgs provided. " +
+                    "# of bindArgs = " + bindArgsLen + ", # of varargs = " + varArgsInSql +
+                    "; sql = " + sql;
+        }
+
+        // if there are no bindArgs, we are done. log the sql as is.
+        if (bindArgsLen == 0 && varArgsInSql == 0) {
+            return logSql(path, sql);
+        }
+
+        StringBuilder buf = new StringBuilder();
+
+        // take the supplied bindArgs and plug them into sql
+        for (int i = 0; i < bindArgsLen; i++) {
+            buf.append(args[i]);
+            buf.append(bindArgs[i]);
+        }
+
+        // does given sql have more varArgs than the supplied bindArgs
+        // if so, assign nulls to the extra varArgs in sql
+        for (int i = bindArgsLen; i < varArgsInSql; i ++) {
+            buf.append(args[i]);
+            buf.append("null");
+        }
+
+        // if there are any characters left in the given sql string AFTER the last "?"
+        // log them also. for example, if the given sql = "select * from test where a=? and b=1
+        // then the following code appends " and b=1" string to buf.
+        if (varArgsInSql < args.length) {
+            buf.append(args[varArgsInSql]);
+        }
+        return logSql(path, buf.toString());
+    }
+
+    private static String logSql(String path, String sql) {
+        return "captured_sql|" + path + "|" + sql + ";";
+    }
 }
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 00b0a86..1159c1d 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,6 +16,12 @@
 
 package android.database.sqlite;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
 import android.util.Log;
 
 /**
@@ -47,6 +53,11 @@
      */
     protected int nStatement = 0;
 
+    /**
+     * stores all bindargs for debugging purposes
+     */
+    private Map<Integer, String> mBindArgs = null;
+
     /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
         if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
             Log.d(TAG, "processing sql: " + sql);
@@ -127,6 +138,9 @@
      * @param index The 1-based index to the parameter to bind null to
      */
     public void bindNull(int index) {
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            addToBindArgs(index, "null");
+        }
         acquireReference();
         try {
             native_bind_null(index);
@@ -143,6 +157,9 @@
      * @param value The value to bind
      */
     public void bindLong(int index, long value) {
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            addToBindArgs(index, value + "");
+        }
         acquireReference();
         try {
             native_bind_long(index, value);
@@ -159,6 +176,9 @@
      * @param value The value to bind
      */
     public void bindDouble(int index, double value) {
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            addToBindArgs(index, value + "");
+        }
         acquireReference();
         try {
             native_bind_double(index, value);
@@ -175,6 +195,9 @@
      * @param value The value to bind
      */
     public void bindString(int index, String value) {
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            addToBindArgs(index, "'" + value + "'");
+        }
         if (value == null) {
             throw new IllegalArgumentException("the bind value at index " + index + " is null");
         }
@@ -194,6 +217,9 @@
      * @param value The value to bind
      */
     public void bindBlob(int index, byte[] value) {
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            addToBindArgs(index, "blob");
+        }
         if (value == null) {
             throw new IllegalArgumentException("the bind value at index " + index + " is null");
         }
@@ -209,6 +235,9 @@
      * Clears all existing bindings. Unset bindings are treated as NULL.
      */
     public void clearBindings() {
+        if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+            mBindArgs = null;
+        }
         acquireReference();
         try {
             native_clear_bindings();
@@ -230,6 +259,39 @@
     }
 
     /**
+     * this method is called under the debug flag {@link SQLiteDebug.DEBUG_CAPTURE_SQL} only.
+     * it collects the bindargs as they are called by the callers the bind... methods in this
+     * class.
+     */
+    private void addToBindArgs(int index, String obj) {
+        if (mBindArgs == null) {
+            mBindArgs = new HashMap<Integer, String>();
+        }
+        mBindArgs.put(index, obj);
+    }
+
+    /**
+     * constructs all the bindargs in sequence and returns a String Array of the values.
+     * it uses the HashMap built up by the above method.
+     *
+     * @return the string array of bindArgs with the args arranged in sequence
+     */
+    /* package */ String[] getBindArgs() {
+        if (mBindArgs == null) {
+            return null;
+        }
+        Set<Integer> indexSet = mBindArgs.keySet();
+        ArrayList<Integer> indexList = new ArrayList<Integer>(indexSet);
+        Collections.sort(indexList);
+        int len = indexList.size();
+        String[] bindObjs = new String[len];
+        for (int i = 0; i < len; i++) {
+            bindObjs[i] = mBindArgs.get(indexList.get(i));
+        }
+        return bindObjs;
+    }
+
+    /**
      * Compiles SQL into a SQLite program.
      *
      * <P>The database lock must be held when calling this method.
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index cc714ee..0cee3c5 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -17,7 +17,6 @@
 package android.database.sqlite;
 
 import android.os.Debug;
-import android.os.SystemClock;
 import android.util.Log;
 
 /**
@@ -56,6 +55,9 @@
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
                 Log.v(TAG, "execute() for [" + mSql + "]");
             }
+            if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+                Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs()));
+            }
             native_execute();
             mDatabase.logTimeStat(mSql, timeStart);
         } finally {
@@ -83,6 +85,9 @@
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
                 Log.v(TAG, "executeInsert() for [" + mSql + "]");
             }
+            if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+                Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs()));
+            }
             native_execute();
             mDatabase.logTimeStat(mSql, timeStart);
             return mDatabase.lastInsertRow();
@@ -109,6 +114,9 @@
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
                 Log.v(TAG, "simpleQueryForLong() for [" + mSql + "]");
             }
+            if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+                Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs()));
+            }
             long retValue = native_1x1_long();
             mDatabase.logTimeStat(mSql, timeStart);
             return retValue;
@@ -135,6 +143,9 @@
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
                 Log.v(TAG, "simpleQueryForString() for [" + mSql + "]");
             }
+            if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
+                Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs()));
+            }
             String retValue = native_1x1_string();
             mDatabase.logTimeStat(mSql, timeStart);
             return retValue;
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index da6af9d..e1327dd 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -29,6 +29,7 @@
 import java.security.cert.X509Certificate;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
+import java.util.Date;
 
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSession;
@@ -51,20 +52,24 @@
             = new CertificateChainValidator();
 
     /**
-     * @return The singleton instance of the certificator chain validator
+     * @return The singleton instance of the certificates chain validator
      */
     public static CertificateChainValidator getInstance() {
         return sInstance;
     }
 
     /**
-     * Creates a new certificate chain validator. This is a pivate constructor.
+     * Creates a new certificate chain validator. This is a private constructor.
      * If you need a Certificate chain validator, call getInstance().
      */
     private CertificateChainValidator() {}
 
     /**
      * Performs the handshake and server certificates validation
+     * Notice a new chain will be rebuilt by tracing the issuer and subject
+     * before calling checkServerTrusted().
+     * And if the last traced certificate is self issued and it is expired, it
+     * will be dropped.
      * @param sslSocket The secure connection socket
      * @param domain The website domain
      * @return An SSL error object if there is an error and null otherwise
@@ -127,7 +132,58 @@
             }
         }
 
-        // first, we validate the chain using the standard validation
+        // Clean up the certificates chain and build a new one.
+        // Theoretically, we shouldn't have to do this, but various web servers
+        // in practice are mis-configured to have out-of-order certificates or
+        // expired self-issued root certificate.
+        int chainLength = serverCertificates.length;
+        if (serverCertificates.length > 1) {
+          // 1. we clean the received certificates chain.
+          // We start from the end-entity certificate, tracing down by matching
+          // the "issuer" field and "subject" field until we can't continue.
+          // This helps when the certificates are out of order or
+          // some certificates are not related to the site.
+          int currIndex;
+          for (currIndex = 0; currIndex < serverCertificates.length; ++currIndex) {
+            boolean foundNext = false;
+            for (int nextIndex = currIndex + 1;
+                 nextIndex < serverCertificates.length;
+                 ++nextIndex) {
+              if (serverCertificates[currIndex].getIssuerDN().equals(
+                  serverCertificates[nextIndex].getSubjectDN())) {
+                foundNext = true;
+                // Exchange certificates so that 0 through currIndex + 1 are in proper order
+                if (nextIndex != currIndex + 1) {
+                  X509Certificate tempCertificate = serverCertificates[nextIndex];
+                  serverCertificates[nextIndex] = serverCertificates[currIndex + 1];
+                  serverCertificates[currIndex + 1] = tempCertificate;
+                }
+                break;
+              }
+            }
+            if (!foundNext) break;
+          }
+
+          // 2. we exam if the last traced certificate is self issued and it is expired.
+          // If so, we drop it and pass the rest to checkServerTrusted(), hoping we might
+          // have a similar but unexpired trusted root.
+          chainLength = currIndex + 1;
+          X509Certificate lastCertificate = serverCertificates[chainLength - 1];
+          Date now = new Date();
+          if (lastCertificate.getSubjectDN().equals(lastCertificate.getIssuerDN())
+              && now.after(lastCertificate.getNotAfter())) {
+            --chainLength;
+          }
+        }
+
+        // 3. Now we copy the newly built chain into an appropriately sized array.
+        X509Certificate[] newServerCertificates = null;
+        newServerCertificates = new X509Certificate[chainLength];
+        for (int i = 0; i < chainLength; ++i) {
+          newServerCertificates[i] = serverCertificates[i];
+        }
+
+        // first, we validate the new chain using the standard validation
         // solution; if we do not find any errors, we are done; if we
         // fail the standard validation, we re-validate again below,
         // this time trying to retrieve any individual errors we can
@@ -135,167 +191,21 @@
         //
         try {
             SSLParameters.getDefaultTrustManager().checkServerTrusted(
-                serverCertificates, "RSA");
+                newServerCertificates, "RSA");
 
             // no errors!!!
             return null;
         } catch (CertificateException e) {
+            sslSocket.getSession().invalidate();
+
             if (HttpLog.LOGV) {
                 HttpLog.v(
                     "failed to pre-validate the certificate chain, error: " +
                     e.getMessage());
             }
-        }
-
-        sslSocket.getSession().invalidate();
-
-        SslError error = null;
-
-        // we check the root certificate separately from the rest of the
-        // chain; this is because we need to know what certificate in
-        // the chain resulted in an error if any
-        currCertificate =
-            serverCertificates[serverCertificates.length - 1];
-        if (currCertificate == null) {
-            closeSocketThrowException(
-                sslSocket, "root certificate is null");
-        }
-
-        // check if the last certificate in the chain (root) is trusted
-        X509Certificate[] rootCertificateChain = { currCertificate };
-        try {
-            SSLParameters.getDefaultTrustManager().checkServerTrusted(
-                rootCertificateChain, "RSA");
-        } catch (CertificateExpiredException e) {
-            String errorMessage = e.getMessage();
-            if (errorMessage == null) {
-                errorMessage = "root certificate has expired";
-            }
-
-            if (HttpLog.LOGV) {
-                HttpLog.v(errorMessage);
-            }
-
-            error = new SslError(
-                SslError.SSL_EXPIRED, currCertificate);
-        } catch (CertificateNotYetValidException e) {
-            String errorMessage = e.getMessage();
-            if (errorMessage == null) {
-                errorMessage = "root certificate not valid yet";
-            }
-
-            if (HttpLog.LOGV) {
-                HttpLog.v(errorMessage);
-            }
-
-            error = new SslError(
-                SslError.SSL_NOTYETVALID, currCertificate);
-        } catch (CertificateException e) {
-            String errorMessage = e.getMessage();
-            if (errorMessage == null) {
-                errorMessage = "root certificate not trusted";
-            }
-
-            if (HttpLog.LOGV) {
-                HttpLog.v(errorMessage);
-            }
-
             return new SslError(
                 SslError.SSL_UNTRUSTED, currCertificate);
         }
-
-        // Then go through the certificate chain checking that each
-        // certificate trusts the next and that each certificate is
-        // within its valid date range. Walk the chain in the order
-        // from the CA to the end-user
-        X509Certificate prevCertificate =
-            serverCertificates[serverCertificates.length - 1];
-
-        for (int i = serverCertificates.length - 2; i >= 0; --i) {
-            currCertificate = serverCertificates[i];
-
-            // if a certificate is null, we cannot verify the chain
-            if (currCertificate == null) {
-                closeSocketThrowException(
-                    sslSocket, "null certificate in the chain");
-            }
-
-            // verify if trusted by chain
-            if (!prevCertificate.getSubjectDN().equals(
-                    currCertificate.getIssuerDN())) {
-                String errorMessage = "not trusted by chain";
-
-                if (HttpLog.LOGV) {
-                    HttpLog.v(errorMessage);
-                }
-
-                return new SslError(
-                    SslError.SSL_UNTRUSTED, currCertificate);
-            }
-
-            try {
-                currCertificate.verify(prevCertificate.getPublicKey());
-            } catch (GeneralSecurityException e) {
-                String errorMessage = e.getMessage();
-                if (errorMessage == null) {
-                    errorMessage = "not trusted by chain";
-                }
-
-                if (HttpLog.LOGV) {
-                    HttpLog.v(errorMessage);
-                }
-
-                return new SslError(
-                    SslError.SSL_UNTRUSTED, currCertificate);
-            }
-
-            // verify if the dates are valid
-            try {
-              currCertificate.checkValidity();
-            } catch (CertificateExpiredException e) {
-                String errorMessage = e.getMessage();
-                if (errorMessage == null) {
-                    errorMessage = "certificate expired";
-                }
-
-                if (HttpLog.LOGV) {
-                    HttpLog.v(errorMessage);
-                }
-
-                if (error == null ||
-                    error.getPrimaryError() < SslError.SSL_EXPIRED) {
-                    error = new SslError(
-                        SslError.SSL_EXPIRED, currCertificate);
-                }
-            } catch (CertificateNotYetValidException e) {
-                String errorMessage = e.getMessage();
-                if (errorMessage == null) {
-                    errorMessage = "certificate not valid yet";
-                }
-
-                if (HttpLog.LOGV) {
-                    HttpLog.v(errorMessage);
-                }
-
-                if (error == null ||
-                    error.getPrimaryError() < SslError.SSL_NOTYETVALID) {
-                    error = new SslError(
-                        SslError.SSL_NOTYETVALID, currCertificate);
-                }
-            }
-
-            prevCertificate = currCertificate;
-        }
-
-        // if we do not have an error to report back to the user, throw
-        // an exception (a generic error will be reported instead)
-        if (error == null) {
-            closeSocketThrowException(
-                sslSocket,
-                "failed to pre-validate the certificate chain due to a non-standard error");
-        }
-
-        return error;
     }
 
     private void closeSocketThrowException(
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index fcd8f38..3e9fd42 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -53,14 +53,14 @@
     /** The end-user-visible name for the end product. */
     public static final String MODEL = getString("ro.product.model");
 
-    /** @pending The system bootloader version number. */
+    /** The system bootloader version number. */
     public static final String BOOTLOADER = getString("ro.bootloader");
 
-    /** @pending The radio firmware version number. */
+    /** The radio firmware version number. */
     public static final String RADIO = getString("gsm.version.baseband");
 
-    /** @pending The device serial number. */
-    public static final String SERIAL = getString("ro.serialno");
+    /** The name of the hardware (from the kernel command line or /proc). */
+    public static final String HARDWARE = getString("ro.hardware");
 
     /** Various version strings. */
     public static class VERSION {
@@ -178,6 +178,8 @@
          * January 2010: Android 2.1
          */
         public static final int ECLAIR_MR1 = 7;
+        
+        public static final int FROYO = 8;
     }
     
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index 2124e85..a5828f6 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -17,6 +17,8 @@
 
 package android.os;
 
+import android.os.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
@@ -25,44 +27,60 @@
 interface IMountService
 {
     /**
-     * Is mass storage support enabled?
+     * Registers an IMountServiceListener for receiving async
+     * notifications.
      */
-    boolean getMassStorageEnabled();
+    void registerListener(IMountServiceListener listener);
 
     /**
-     * Enable or disable mass storage support.
+     * Unregisters an IMountServiceListener
      */
-    void setMassStorageEnabled(boolean enabled);
+    void unregisterListener(IMountServiceListener listener);
 
     /**
-     * Is mass storage connected?
+     * Gets an Array of supported share methods
      */
-    boolean getMassStorageConnected();
-    
+    String[] getShareMethodList();
+
+    /**
+     * Returns true if the share method is available
+     */
+    boolean getShareMethodAvailable(String method);
+
+    /**
+     * Shares a volume via the specified method
+     * Returns an int consistent with MountServiceResultCode
+     */
+    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);
+
     /**
      * Mount external storage at given mount point.
+     * Returns an int consistent with MountServiceResultCode
      */
-    void mountVolume(String mountPoint);
+    int mountVolume(String mountPoint);
 
     /**
      * Safely unmount external storage at given mount point.
+     * Returns an int consistent with MountServiceResultCode
      */
-    void unmountVolume(String mountPoint);
+    int unmountVolume(String mountPoint);
 
     /**
      * Format external storage given a mount point.
+     * Returns an int consistent with MountServiceResultCode
      */
-    void formatVolume(String mountPoint);
-
-    /**
-     * Returns true if media notification sounds are enabled.
-     */
-    boolean getPlayNotificationSounds();
-
-    /**
-     * Sets whether or not media notification sounds are played.
-     */
-    void setPlayNotificationSounds(boolean value);
+    int formatVolume(String mountPoint);
 
     /**
      * Gets the state of an volume via it's mountpoint.
@@ -71,37 +89,41 @@
 
     /*
      * Creates a secure container with the specified parameters.
-     * On success, the filesystem container-path is returned.
+     * Returns an int consistent with MountServiceResultCode
      */
-    String createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid);
+    int createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid);
 
     /*
      * Finalize a container which has just been created and populated.
      * After finalization, the container is immutable.
+     * Returns an int consistent with MountServiceResultCode
      */
-    void finalizeSecureContainer(String id);
+    int finalizeSecureContainer(String id);
 
     /*
      * Destroy a secure container, and free up all resources associated with it.
      * NOTE: Ensure all references are released prior to deleting.
+     * Returns an int consistent with MountServiceResultCode
      */
-    void destroySecureContainer(String id);
+     int destroySecureContainer(String id);
 
     /*
      * Mount a secure container with the specified key and owner UID.
-     * On success, the filesystem container-path is returned.
+     * Returns an int consistent with MountServiceResultCode
      */
-    String mountSecureContainer(String id, String key, int ownerUid);
+    int mountSecureContainer(String id, String key, int ownerUid);
 
     /*
      * Unount a secure container.
+     * Returns an int consistent with MountServiceResultCode
      */
-    void unmountSecureContainer(String id);
+    int unmountSecureContainer(String id);
 
     /*
      * Rename an unmounted secure container.
+     * Returns an int consistent with MountServiceResultCode
      */
-    void renameSecureContainer(String oldId, String newId);
+    int renameSecureContainer(String oldId, String newId);
 
     /*
      * Returns the filesystem path of a mounted secure container.
diff --git a/core/java/android/os/IMountServiceObserver.aidl b/core/java/android/os/IMountServiceListener.aidl
similarity index 83%
rename from core/java/android/os/IMountServiceObserver.aidl
rename to core/java/android/os/IMountServiceListener.aidl
index f753649..3df64b2 100644
--- a/core/java/android/os/IMountServiceObserver.aidl
+++ b/core/java/android/os/IMountServiceListener.aidl
@@ -21,14 +21,14 @@
  *
  * @hide
  */
-interface IMountServiceObserver {
+interface IMountServiceListener {
     /**
      * 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);
+    void onShareAvailabilityChanged(String method, boolean available);
 
     /**
      * Media has been inserted
@@ -38,7 +38,7 @@
      * @param major The backing device major number.
      * @param minor The backing device minor number.
      */
-    void mediaInserted(String label, String path, int major, int minor);
+    void onMediaInserted(String label, String path, int major, int minor);
 
     /**
      * Media has been removed
@@ -49,7 +49,7 @@
      * @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);
+    void onMediaRemoved(String label, String path, int major, int minor, boolean clean);
 
     /**
      *  Volume state has changed.
@@ -61,6 +61,6 @@
      *
      * Note: State is one of the values returned by Environment.getExternalStorageState()
      */
-    void volumeStateChange(String label, String path, String oldState, String newState);
+    void onVolumeStateChanged(String label, String path, String oldState, String newState);
 
 }
diff --git a/core/java/android/os/MountServiceObserver.java b/core/java/android/os/MountServiceListener.java
similarity index 97%
rename from core/java/android/os/MountServiceObserver.java
rename to core/java/android/os/MountServiceListener.java
index 3020562..a68f464 100644
--- a/core/java/android/os/MountServiceObserver.java
+++ b/core/java/android/os/MountServiceListener.java
@@ -21,7 +21,7 @@
  * methods will all be called on your application's main thread.
  * @hide
  */
-public abstract class MountServiceObserver {
+public abstract class MountServiceListener {
     /**
      * A sharing method has changed availability state.
      *
diff --git a/core/java/android/os/MountServiceResultCode.java b/core/java/android/os/MountServiceResultCode.java
new file mode 100644
index 0000000..e71dbf4
--- /dev/null
+++ b/core/java/android/os/MountServiceResultCode.java
@@ -0,0 +1,34 @@
+/*
+ * 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/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 9953b56..324fbaa 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -73,6 +73,11 @@
         packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         packageFilter.addDataScheme("package");
         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);
+        mContext.registerReceiver(mPackageChangedReceiver, sdFilter);
     }
 
     private synchronized Searchables getSearchables() {
@@ -90,7 +95,9 @@
 
             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
                     Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
-                    Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                    Intent.ACTION_PACKAGE_CHANGED.equals(action) ||
+                    Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) ||
+                    Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
                 if (DBG) Log.d(TAG, "Got " + action);
                 // Update list of searchable activities
                 getSearchables().buildSearchableList();
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 49991bd..334b049 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -30,8 +30,14 @@
 
     /**
      * Starts an activity that will prompt the user for speech and sends it through a
-     * speech recognizer.  The results will be returned via activity results, or forwarded
-     * via a PendingIntent if one is provided.
+     * speech recognizer.  The results will be returned via activity results (in
+     * {@link Activity#onActivityResult}, if you start the intent using
+     * {@link Activity#startActivityForResult(Intent, int)}), or forwarded via a PendingIntent
+     * if one is provided.
+     * 
+     * <p>Starting this intent with just {@link Activity#startActivity(Intent)} is not supported.
+     * You must either use {@link Activity#startActivityForResult(Intent, int)}, or provide a
+     * PendingIntent, to receive recognition results.
      * 
      * <p>Required extras:
      * <ul>
@@ -47,7 +53,7 @@
      *   <li>{@link #EXTRA_RESULTS_PENDINGINTENT_BUNDLE}
      * </ul>
      * 
-     * <p> Result extras:
+     * <p> Result extras (returned in the result, not to be specified in the request):
      * <ul>
      *   <li>{@link #EXTRA_RESULTS}
      * </ul>
@@ -73,7 +79,7 @@
      *   <li>{@link #EXTRA_MAX_RESULTS}
      * </ul>
      * 
-     * <p> Result extras:
+     * <p> Result extras (returned in the result, not to be specified in the request):
      * <ul>
      *   <li>{@link #EXTRA_RESULTS}
      * </ul>
@@ -167,6 +173,7 @@
      */
     public static final String EXTRA_RESULTS_PENDINGINTENT = 
             "android.speech.extra.RESULTS_PENDINGINTENT";
+    
     /**
      * If you use {@link #EXTRA_RESULTS_PENDINGINTENT} to supply a forwarding intent, you can
      * also use this extra to supply additional extras for the final intent.  The search results
@@ -187,8 +194,10 @@
     public static final int RESULT_AUDIO_ERROR = Activity.RESULT_FIRST_USER + 4;
 
     /**
-     * An ArrayList<String> of the potential results when performing
-     * {@link #ACTION_RECOGNIZE_SPEECH}. Only present when {@link Activity#RESULT_OK} is returned.
+     * An ArrayList&lt;String&gt; of the recognition results when performing
+     * {@link #ACTION_RECOGNIZE_SPEECH}. Returned in the results; not to be specified in the
+     * recognition request. Only present when {@link Activity#RESULT_OK} is returned in
+     * an activity result. In a PendingIntent, the lack of this extra indicates failure.
      */
     public static final String EXTRA_RESULTS = "android.speech.extra.RESULTS";
     
diff --git a/core/java/android/speech/RecognizerResultsIntent.java b/core/java/android/speech/RecognizerResultsIntent.java
index 882f213..228258e 100644
--- a/core/java/android/speech/RecognizerResultsIntent.java
+++ b/core/java/android/speech/RecognizerResultsIntent.java
@@ -95,11 +95,11 @@
      * {@link #EXTRA_VOICE_SEARCH_RESULT_URLS}, if available, or else should execute a search of
      * its own choosing, based on the recognition result string.
      * 
-     * Currently this html content should be expected in the form of an "inline:" uri for the
-     * Browser. In the future this may change to a "content://" uri or some other identifier.
-     * Anyone who reads this extra should confirm that a result is in fact an "inline:" uri and
-     * back off to the urls or strings gracefully if it is not, thus maintaining future backwards
-     * compatibility if this changes.
+     * Currently this html content should be expected in the form of a uri with scheme
+     * {@link #URI_SCHEME_INLINE} for the Browser. In the future this may change to a "content://"
+     * uri or some other identifier. Anyone who reads this extra should confirm that a result is
+     * in fact an "inline:" uri and back off to the urls or strings gracefully if it is not, thus
+     * maintaining future backwards compatibility if this changes.
      * 
      * @hide not to be exposed immediately as the implementation details may change
      */
@@ -119,4 +119,11 @@
      */
     public static final String EXTRA_VOICE_SEARCH_RESULT_HTML_BASE_URLS =
             "android.speech.extras.VOICE_SEARCH_RESULT_HTML_BASE_URLS";
+    
+    /**
+     * The scheme used currently for html content in {@link #EXTRA_VOICE_SEARCH_RESULT_HTML}.
+     * 
+     * @hide not to be exposed immediately as the implementation details may change
+     */
+    public static final String URI_SCHEME_INLINE = "inline";
 }
diff --git a/core/java/android/storage/StorageEventListener.java b/core/java/android/storage/StorageEventListener.java
new file mode 100644
index 0000000..cd71090
--- /dev/null
+++ b/core/java/android/storage/StorageEventListener.java
@@ -0,0 +1,57 @@
+/*
+ * 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
new file mode 100644
index 0000000..dd8bd63
--- /dev/null
+++ b/core/java/android/storage/StorageManager.java
@@ -0,0 +1,300 @@
+/*
+ * 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/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 1e558be..3c79200 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -16,9 +16,10 @@
 
 package android.view;
 
+import android.content.Context;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
-import android.content.Context;
 
 /**
  * Detects various gestures and events using the supplied {@link MotionEvent}s.
@@ -231,6 +232,13 @@
     private float mLastMotionX;
 
     private boolean mIsLongpressEnabled;
+    
+    /**
+     * True if we are at a target API level of >= Froyo or the developer can
+     * explicitly set it. If true, input events with > 1 pointer will be ignored
+     * so we can work side by side with multitouch gesture detectors.
+     */
+    private boolean mIgnoreMultitouch;
 
     /**
      * Determines speed during touch scrolling
@@ -336,6 +344,26 @@
      * @throws NullPointerException if {@code listener} is null.
      */
     public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
+        this(context, listener, handler, context != null &&
+                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO);
+    }
+    
+    /**
+     * Creates a GestureDetector with the supplied listener.
+     * You may only use this constructor from a UI thread (this is the usual situation).
+     * @see android.os.Handler#Handler()
+     *
+     * @param context the application's context
+     * @param listener the listener invoked for all the callbacks, this must
+     * not be null.
+     * @param handler the handler to use
+     * @param ignoreMultitouch whether events involving more than one pointer should
+     * be ignored.
+     *
+     * @throws NullPointerException if {@code listener} is null.
+     */
+    public GestureDetector(Context context, OnGestureListener listener, Handler handler,
+            boolean ignoreMultitouch) {
         if (handler != null) {
             mHandler = new GestureHandler(handler);
         } else {
@@ -345,14 +373,15 @@
         if (listener instanceof OnDoubleTapListener) {
             setOnDoubleTapListener((OnDoubleTapListener) listener);
         }
-        init(context);
+        init(context, ignoreMultitouch);
     }
 
-    private void init(Context context) {
+    private void init(Context context, boolean ignoreMultitouch) {
         if (mListener == null) {
             throw new NullPointerException("OnGestureListener must not be null");
         }
         mIsLongpressEnabled = true;
+        mIgnoreMultitouch = ignoreMultitouch;
 
         // Fallback to support pre-donuts releases
         int touchSlop, doubleTapSlop;
@@ -425,7 +454,26 @@
 
         boolean handled = false;
 
-        switch (action) {
+        switch (action & MotionEvent.ACTION_MASK) {
+        case MotionEvent.ACTION_POINTER_DOWN:
+            if (mIgnoreMultitouch) {
+                // Multitouch event - abort.
+                cancel();
+            }
+            break;
+
+        case MotionEvent.ACTION_POINTER_UP:
+            // Ending a multitouch gesture and going back to 1 finger
+            if (mIgnoreMultitouch && ev.getPointerCount() == 2) {
+                int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
+                        >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
+                mLastMotionX = ev.getX(id);
+                mLastMotionY = ev.getY(id);
+                mVelocityTracker.recycle();
+                mVelocityTracker = VelocityTracker.obtain();
+            }
+            break;
+
         case MotionEvent.ACTION_DOWN:
             if (mDoubleTapListener != null) {
                 boolean hadTapMessage = mHandler.hasMessages(TAP);
@@ -462,7 +510,7 @@
             break;
 
         case MotionEvent.ACTION_MOVE:
-            if (mInLongPress) {
+            if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {
                 break;
             }
             final float scrollX = mLastMotionX - x;
@@ -525,21 +573,24 @@
             mHandler.removeMessages(LONG_PRESS);
             break;
         case MotionEvent.ACTION_CANCEL:
-            mHandler.removeMessages(SHOW_PRESS);
-            mHandler.removeMessages(LONG_PRESS);
-            mHandler.removeMessages(TAP);
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-            mIsDoubleTapping = false;
-            mStillDown = false;
-            if (mInLongPress) {
-                mInLongPress = false;
-                break;
-            }
+            cancel();
         }
         return handled;
     }
 
+    private void cancel() {
+        mHandler.removeMessages(SHOW_PRESS);
+        mHandler.removeMessages(LONG_PRESS);
+        mHandler.removeMessages(TAP);
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+        mIsDoubleTapping = false;
+        mStillDown = false;
+        if (mInLongPress) {
+            mInLongPress = false;
+        }
+    }
+
     private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
             MotionEvent secondDown) {
         if (!mAlwaysInBiggerTapRegion) {
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index f991df7..8140d61 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.util.DisplayMetrics;
-import android.util.Log;
 
 /**
  * Detects transformation gestures involving more than one pointer ("multitouch")
@@ -34,7 +33,6 @@
  *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
  *          callback will be executed when the events occur.
  * </ul>
- * @hide Pending API approval
  */
 public class ScaleGestureDetector {
     /**
@@ -45,9 +43,9 @@
      * 
      * An application will receive events in the following order:
      * <ul>
-     *  <li>One {@link OnScaleGestureListener#onScaleBegin()}
-     *  <li>Zero or more {@link OnScaleGestureListener#onScale()}
-     *  <li>One {@link OnScaleGestureListener#onTransformEnd()}
+     *  <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)}
+     *  <li>Zero or more {@link OnScaleGestureListener#onScale(ScaleGestureDetector)}
+     *  <li>One {@link OnScaleGestureListener#onScaleEnd(ScaleGestureDetector)}
      * </ul>
      */
     public interface OnScaleGestureListener {
@@ -82,8 +80,7 @@
 
         /**
          * Responds to the end of a scale gesture. Reported by existing
-         * pointers going up. If the end of a gesture would result in a fling,
-         * {@link onTransformFling()} is called instead.
+         * pointers going up.
          * 
          * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
          * and {@link ScaleGestureDetector#getFocusY()} will return the location
@@ -103,7 +100,7 @@
      * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
      * {@code true}. 
      */
-    public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
+    public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
 
         public boolean onScale(ScaleGestureDetector detector) {
             return true;
@@ -373,7 +370,7 @@
      * the two pointers forming the gesture.
      * If a gesture is ending, the focal point is the location of the
      * remaining pointer on the screen.
-     * If {@link isInProgress()} would return false, the result of this
+     * If {@link #isInProgress()} would return false, the result of this
      * function is undefined.
      * 
      * @return X coordinate of the focal point in pixels.
@@ -388,7 +385,7 @@
      * the two pointers forming the gesture.
      * If a gesture is ending, the focal point is the location of the
      * remaining pointer on the screen.
-     * If {@link isInProgress()} would return false, the result of this
+     * If {@link #isInProgress()} would return false, the result of this
      * function is undefined.
      * 
      * @return Y coordinate of the focal point in pixels.
@@ -430,7 +427,7 @@
     /**
      * Return the scaling factor from the previous scale event to the current
      * event. This value is defined as
-     * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
+     * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
      * 
      * @return The current scaling factor.
      */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 31c9b08..f5c465e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4323,6 +4323,13 @@
      */
     public void cancelLongPress() {
         removeLongPressCallback();
+
+        /*
+         * The prepressed state handled by the tap callback is a display
+         * construct, but the tap callback will post a long press callback
+         * less its own timeout. Remove it here.
+         */
+        removeTapCallback();
     }
 
     /**
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 22dca3a..87cab3c 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -30,6 +30,8 @@
 import java.util.ArrayList;
 import java.util.Map;
 
+import com.android.common.HttpDateTime;
+
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA1Digest;
 
@@ -52,6 +54,7 @@
     private static final String NO_STORE = "no-store";
     private static final String NO_CACHE = "no-cache";
     private static final String MAX_AGE = "max-age";
+    private static final String MANIFEST_MIME = "text/cache-manifest";
 
     private static long CACHE_THRESHOLD = 6 * 1024 * 1024;
     private static long CACHE_TRIM_AMOUNT = 2 * 1024 * 1024;
@@ -658,6 +661,15 @@
         // if the contentLength is already larger than CACHE_MAX_SIZE, skip it
         if (headers.getContentLength() > CACHE_MAX_SIZE) return null;
 
+        // The HTML 5 spec, section 6.9.4, step 7.3 of the application cache
+        // process states that HTTP caching rules are ignored for the
+        // purposes of the application cache download process.
+        // At this point we can't tell that if a file is part of this process,
+        // except for the manifest, which has its own mimeType.
+        // TODO: work out a way to distinguish all responses that are part of
+        // the application download process and skip them.
+        if (MANIFEST_MIME.equals(mimeType)) return null;
+
         // TODO: if authenticated or secure, return null
         CacheResult ret = new CacheResult();
         ret.httpStatusCode = statusCode;
diff --git a/core/java/android/webkit/ContentLoader.java b/core/java/android/webkit/ContentLoader.java
index 19aa087..5eb54b0 100644
--- a/core/java/android/webkit/ContentLoader.java
+++ b/core/java/android/webkit/ContentLoader.java
@@ -31,7 +31,6 @@
 class ContentLoader extends StreamLoader {
 
     private String mUrl;
-    private Context mContext;
     private String mContentType;
 
     /**
@@ -40,11 +39,9 @@
      * @param rawUrl "content:" url pointing to content to be loaded. This url
      *               is the same url passed in to the WebView.
      * @param loadListener LoadListener to pass the content to
-     * @param context Context to use to access the asset.
      */
-    ContentLoader(String rawUrl, LoadListener loadListener, Context context) {
+    ContentLoader(String rawUrl, LoadListener loadListener) {
         super(loadListener);
-        mContext = context;
 
         /* strip off mimetype */
         int mimeIndex = rawUrl.lastIndexOf('?');
@@ -81,7 +78,7 @@
 
         try {
             mDataStream = mContext.getContentResolver().openInputStream(uri);
-            mHandler.status(1, 1, 0, "OK");
+            mHandler.status(1, 1, 200, "OK");
         } catch (java.io.FileNotFoundException ex) {
             mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
             return false;
@@ -112,11 +109,9 @@
      *
      * @param url "content:" url pointing to content to be loaded
      * @param loadListener LoadListener to pass the content to
-     * @param context Context to use to access the asset.
      */
-    public static void requestUrl(String url, LoadListener loadListener,
-            Context context) {
-        ContentLoader loader = new ContentLoader(url, loadListener, context);
+    public static void requestUrl(String url, LoadListener loadListener) {
+        ContentLoader loader = new ContentLoader(url, loadListener);
         loader.load();
     }
 
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index fca591f..94bedde 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -20,6 +20,8 @@
 import android.net.WebAddress;
 import android.util.Log;
 
+import com.android.common.HttpDateTime;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java
index 6c5d10d..2a68a5d8 100644
--- a/core/java/android/webkit/DataLoader.java
+++ b/core/java/android/webkit/DataLoader.java
@@ -16,6 +16,10 @@
 
 package android.webkit;
 
+import android.net.http.EventHandler;
+
+import com.android.internal.R;
+
 import java.io.ByteArrayInputStream;
 
 import org.apache.harmony.luni.util.Base64;
@@ -49,14 +53,22 @@
         } else {
             data = url.getBytes();
         }
-        mDataStream = new ByteArrayInputStream(data);
-        mContentLength = data.length;
+        if (data != null) {
+            mDataStream = new ByteArrayInputStream(data);
+            mContentLength = data.length;
+        }
     }
 
     @Override
     protected boolean setupStreamAndSendStatus() {
-        mHandler.status(1, 1, 0, "OK");
-        return true;
+        if (mDataStream != null) {
+            mHandler.status(1, 1, 200, "OK");
+            return true;
+        } else {
+            mHandler.error(EventHandler.ERROR,
+                    mContext.getString(R.string.httpError));
+            return false;
+        }
     }
 
     @Override
diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java
index 974ccbf..e856cde 100644
--- a/core/java/android/webkit/FileLoader.java
+++ b/core/java/android/webkit/FileLoader.java
@@ -38,7 +38,6 @@
 class FileLoader extends StreamLoader {
 
     private String mPath;  // Full path to the file to load
-    private Context mContext;  // Application context, used for asset/res loads
     private int mType;  // Indicates the type of the load
     private boolean mAllowFileAccess; // Allow/block file system access
 
@@ -57,16 +56,14 @@
      *
      * @param url Full file url pointing to content to be loaded
      * @param loadListener LoadListener to pass the content to
-     * @param context Context to use to access the asset.
      * @param asset true if url points to an asset.
      * @param allowFileAccess true if this WebView is allowed to access files
      *                        on the file system.
      */
-    FileLoader(String url, LoadListener loadListener, Context context,
-            int type, boolean allowFileAccess) {
+    FileLoader(String url, LoadListener loadListener, int type,
+            boolean allowFileAccess) {
         super(loadListener);
         mType = type;
-        mContext = context;
         mAllowFileAccess = allowFileAccess;
 
         // clean the Url
@@ -174,7 +171,7 @@
                 mDataStream = new FileInputStream(mPath);
                 mContentLength = (new File(mPath)).length();
             }
-            mHandler.status(1, 1, 0, "OK");
+            mHandler.status(1, 1, 200, "OK");
 
         } catch (java.io.FileNotFoundException ex) {
             mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
@@ -198,14 +195,13 @@
      *
      * @param url Full file url pointing to content to be loaded
      * @param loadListener LoadListener to pass the content to
-     * @param context Context to use to access the asset.
      * @param asset true if url points to an asset.
      * @param allowFileAccess true if this FileLoader can load files from the
      *                        file system.
      */
     public static void requestUrl(String url, LoadListener loadListener,
-            Context context, int type, boolean allowFileAccess) {
-        FileLoader loader = new FileLoader(url, loadListener, context, type,
+            int type, boolean allowFileAccess) {
+        FileLoader loader = new FileLoader(url, loadListener, type,
                 allowFileAccess);
         loader.load();
     }
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 51f60c3..58eca38 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -141,22 +141,21 @@
             return true;
         }
         if (URLUtil.isAssetUrl(url)) {
-            FileLoader.requestUrl(url, loadListener, loadListener.getContext(),
-                    FileLoader.TYPE_ASSET, true);
+            FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_ASSET,
+                    true);
             return true;
         } else if (URLUtil.isResourceUrl(url)) {
-            FileLoader.requestUrl(url, loadListener, loadListener.getContext(),
-                    FileLoader.TYPE_RES, true);
+            FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_RES,
+                    true);
             return true;
         } else if (URLUtil.isFileUrl(url)) {
-            FileLoader.requestUrl(url, loadListener, loadListener.getContext(),
-                    FileLoader.TYPE_FILE, settings.getAllowFileAccess());
+            FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_FILE,
+                    settings.getAllowFileAccess());
             return true;
         } else if (URLUtil.isContentUrl(url)) {
             // Send the raw url to the ContentLoader because it will do a
             // permission check and the url has to match..
-            ContentLoader.requestUrl(loadListener.url(), loadListener,
-                                     loadListener.getContext());
+            ContentLoader.requestUrl(loadListener.url(), loadListener);
             return true;
         } else if (URLUtil.isDataUrl(url)) {
             DataLoader.requestUrl(url, loadListener);
diff --git a/core/java/android/webkit/JsResult.java b/core/java/android/webkit/JsResult.java
index 0c86e0a..e61ab21 100644
--- a/core/java/android/webkit/JsResult.java
+++ b/core/java/android/webkit/JsResult.java
@@ -26,7 +26,10 @@
     private boolean mTriedToNotifyBeforeReady;
     // This is a basic result of a confirm or prompt dialog.
     protected boolean mResult;
-    // This is the caller of the prompt and is the object that is waiting.
+    /**
+     *  This is the caller of the prompt and is the object that is waiting.
+     *  @hide
+     */
     protected final CallbackProxy mProxy;
     // This is the default value of the result.
     private final boolean mDefaultValue;
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index 623ff29..ce26268 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.content.Context;
 import android.net.http.EventHandler;
 import android.net.http.Headers;
 import android.os.Handler;
@@ -52,7 +53,8 @@
     private static final int MSG_DATA = 102;  // Send data to loader
     private static final int MSG_END = 103;  // Send endData to loader
 
-    protected LoadListener mHandler; // loader class
+    protected final Context mContext;
+    protected final LoadListener mHandler; // loader class
     protected InputStream mDataStream; // stream to read data from
     protected long mContentLength; // content length of data
     private byte [] mData; // buffer to pass data to loader with.
@@ -66,6 +68,7 @@
      */
     StreamLoader(LoadListener loadlistener) {
         mHandler = loadlistener;
+        mContext = loadlistener.getContext();
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a6b74c4..6627973 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -342,6 +342,7 @@
      * choice. Maybe make this in the buildspec later.
      */
     private static final int TOUCH_SENT_INTERVAL = 50;
+    private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
 
     /**
      * Helper class to get velocity for fling
@@ -464,7 +465,7 @@
     private int mHeldMotionless;
 
     // whether support multi-touch
-    private static boolean mSupportMultiTouch;
+    private boolean mSupportMultiTouch;
     // use the framework's ScaleGestureDetector to handle multi-touch
     private ScaleGestureDetector mScaleDetector;
     // minimum scale change during multi-touch zoom
@@ -4343,11 +4344,12 @@
 
         // pass the touch events from UI thread to WebCore thread
         if (mForwardTouchEvents && (action != MotionEvent.ACTION_MOVE
-                || eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
+                || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
             WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
             ted.mAction = action;
             ted.mX = viewToContentX((int) x + mScrollX);
             ted.mY = viewToContentY((int) y + mScrollY);
+            ted.mEventTime = eventTime;
             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
             mLastSentTouchTime = eventTime;
         }
@@ -4611,6 +4613,7 @@
                             ted.mAction = WebViewCore.ACTION_DOUBLETAP;
                             ted.mX = viewToContentX((int) x + mScrollX);
                             ted.mY = viewToContentY((int) y + mScrollY);
+                            ted.mEventTime = eventTime;
                             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         } else if (mFullScreenHolder == null) {
                             doDoubleTap();
@@ -5650,6 +5653,7 @@
                         ted.mAction = WebViewCore.ACTION_LONGPRESS;
                         ted.mX = viewToContentX((int) mLastTouchX + mScrollX);
                         ted.mY = viewToContentY((int) mLastTouchY + mScrollY);
+                        ted.mEventTime = SystemClock.uptimeMillis();
                         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     } else if (mPreventDrag == PREVENT_DRAG_NO) {
                         mTouchMode = TOUCH_DONE_MODE;
@@ -6634,6 +6638,16 @@
     }
 
     /**
+     * Set the time to wait between passing touches to WebCore. See also the
+     * TOUCH_SENT_INTERVAL member for further discussion.
+     *
+     * @hide This is only used by the DRT test application.
+     */
+    public void setTouchInterval(int interval) {
+        mCurrentTouchInterval = interval;
+    }
+
+    /**
      *  Update our cache with updatedText.
      *  @param updatedText  The new text to put in our cache.
      */
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 387942f..6e45e39 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -512,7 +512,7 @@
     private native void nativeTouchUp(int touchGeneration,
             int framePtr, int nodePtr, int x, int y);
 
-    private native int nativeHandleTouchEvent(int action, int x, int y);
+    private native int nativeHandleTouchEvent(int action, int x, int y, long time);
 
     private native void nativeUpdateFrameCache();
 
@@ -731,6 +731,7 @@
         int mAction;
         int mX;
         int mY;
+        long mEventTime;
     }
 
     static class GeolocationPermissionsData {
@@ -1189,7 +1190,7 @@
                                     mWebView.mPrivateHandler,
                                     WebView.PREVENT_TOUCH_ID, ted.mAction,
                                     nativeHandleTouchEvent(ted.mAction, ted.mX,
-                                            ted.mY)).sendToTarget();
+                                    ted.mY, ted.mEventTime)).sendToTarget();
                             break;
                         }
 
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 67c0def..6b669d8 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -428,12 +428,21 @@
                 if (mListAdapter == null && mList != null) {
                     getSectionsFromIndexer();
                 }
+                if (mList != null) {
+                    mList.requestDisallowInterceptTouchEvent(true);
+                }
 
                 cancelFling();
                 return true;
             }
-        } else if (action == MotionEvent.ACTION_UP) {
+        } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
             if (mState == STATE_DRAGGING) {
+                if (mList != null) {
+                    // ViewGroup does the right thing already, but there might
+                    // be other classes that don't properly reset on touch-up,
+                    // so do this explicitly just in case.
+                    mList.requestDisallowInterceptTouchEvent(false);
+                }
                 setState(STATE_VISIBLE);
                 final Handler handler = mHandler;
                 handler.removeCallbacks(mScrollFade);
diff --git a/core/java/com/android/internal/app/UsbStorageActivity.java b/core/java/com/android/internal/app/UsbStorageActivity.java
index 37ea352..34ae2b4 100644
--- a/core/java/com/android/internal/app/UsbStorageActivity.java
+++ b/core/java/com/android/internal/app/UsbStorageActivity.java
@@ -24,7 +24,9 @@
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Environment;
 import android.os.IMountService;
+import android.os.MountServiceResultCode;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -116,7 +118,8 @@
             IMountService mountService = IMountService.Stub.asInterface(ServiceManager
                     .getService("mount"));
             if (mountService != null) {
-                umsOn = mountService.getMassStorageEnabled();
+                umsOn = mountService.getVolumeShared(
+                        Environment.getExternalStorageDirectory().getPath(), "ums");
             }
         } catch (android.os.RemoteException exc) {
             // pass
@@ -140,10 +143,13 @@
         }
 
         try {
-            mountService.setMassStorageEnabled(true);
+            if (mountService.shareVolume(
+                    Environment.getExternalStorageDirectory().getPath(), "ums") !=
+                            MountServiceResultCode.OperationSucceeded) {
+                showSharingError();
+            }
         } catch (RemoteException e) {
             showSharingError();
-            return;
         }
     }
 
@@ -156,7 +162,8 @@
         }
 
         try {
-            mountService.setMassStorageEnabled(false);
+            mountService.unshareVolume(
+                    Environment.getExternalStorageDirectory().getPath(), "ums");
         } catch (RemoteException e) {
             showStoppingError();
             return;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e964a8f..dfcc8f7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2705,6 +2705,7 @@
         mWifiOnTimer = new StopwatchTimer(-3, null, mUnpluggables);
         mWifiRunningTimer = new StopwatchTimer(-4, null, mUnpluggables);
         mBluetoothOnTimer = new StopwatchTimer(-5, null, mUnpluggables);
+        mAudioOnTimer = new StopwatchTimer(-6, null, mUnpluggables);
         mOnBattery = mOnBatteryInternal = false;
         mTrackBatteryPastUptime = 0;
         mTrackBatteryPastRealtime = 0;
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index fa1ee0d..1d22de3 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -656,6 +656,15 @@
         }
     }
 
+    /* enable poisoning of memory of freed objects */
+    property_get("dalvik.vm.gc.overwritefree", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:overwritefree";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.overwritefree should be 'true' or 'false'");
+    }
+
     /* enable debugging; set suspend=y to pause during VM init */
 #ifdef HAVE_ANDROID_OS
     /* use android ADB transport */
diff --git a/core/res/res/layout/contact_header.xml b/core/res/res/layout/contact_header.xml
index 128adc5..e5b6c35 100644
--- a/core/res/res/layout/contact_header.xml
+++ b/core/res/res/layout/contact_header.xml
@@ -88,6 +88,7 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:visibility="gone"
+        android:contentDescription="@string/description_star"
         style="?android:attr/starStyle" />
 
 </LinearLayout>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8f97c13..30d0da7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2194,4 +2194,10 @@
     <string name="reset">Reset</string>
     <!-- Label for <input type="submit"> button in html -->
     <string name="submit">Submit</string>
+
+    <!-- String describing the Star/Favorite checkbox
+
+         Used by AccessibilityService to announce the purpose of the view.
+    -->
+    <string name="description_star">favorite</string>
 </resources>
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java
new file mode 100644
index 0000000..ea807bd
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.database.sqlite;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for the SQLiteDebug
+ */
+public class SQLiteDebugTest extends TestCase {
+    private static final String TEST_DB = "test.db";
+
+    public void testCaptureSql() {
+        String rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=? and b=1",
+                new Object[] {"blah"});
+        String expectedVal = "select * from t1 where a='blah' and b=1";
+        assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal));
+
+        rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=?",
+                new Object[] {"blah"});
+        expectedVal = "select * from t1 where a='blah'";
+        assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal));
+
+        rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=1",
+                new Object[] {"blah"});
+        assertTrue(rslt.startsWith("too many bindArgs provided."));
+
+        rslt = SQLiteDebug.captureSql(TEST_DB, "update t1 set a=? where b=?",
+                new Object[] {"blah", "foo"});
+        expectedVal = "update t1 set a='blah' where b='foo'";
+        assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal));
+    }
+}
diff --git a/docs/html/resources/samples/images/SampleSyncAdapter1.png b/docs/html/resources/samples/images/SampleSyncAdapter1.png
new file mode 100644
index 0000000..242fd36
--- /dev/null
+++ b/docs/html/resources/samples/images/SampleSyncAdapter1.png
Binary files differ
diff --git a/docs/html/resources/samples/images/SampleSyncAdapter2.png b/docs/html/resources/samples/images/SampleSyncAdapter2.png
new file mode 100644
index 0000000..055beaf
--- /dev/null
+++ b/docs/html/resources/samples/images/SampleSyncAdapter2.png
Binary files differ
diff --git a/docs/html/resources/samples/images/SampleSyncAdapter3.png b/docs/html/resources/samples/images/SampleSyncAdapter3.png
new file mode 100644
index 0000000..8ec468e
--- /dev/null
+++ b/docs/html/resources/samples/images/SampleSyncAdapter3.png
Binary files differ
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
new file mode 100644
index 0000000..b26e47d
--- /dev/null
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -0,0 +1,129 @@
+/*
+ * 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.renderscript;
+
+
+/**
+ * @hide
+ *
+ **/
+public class FieldPacker {
+    public FieldPacker(int len) {
+        mPos = 0;
+        mData = new byte[len];
+    }
+
+    public void align(int v) {
+        while ((mPos & (v - 1)) != 0) {
+            mData[mPos++] = 0;
+        }
+    }
+
+    void reset() {
+        mPos = 0;
+    }
+
+    void addI8(byte v) {
+        mData[mPos++] = v;
+    }
+
+    void addI16(short v) {
+        align(2);
+        mData[mPos++] = (byte)(v & 0xff);
+        mData[mPos++] = (byte)(v >> 8);
+    }
+
+    void addI32(int v) {
+        align(4);
+        mData[mPos++] = (byte)(v & 0xff);
+        mData[mPos++] = (byte)((v >> 8) & 0xff);
+        mData[mPos++] = (byte)((v >> 16) & 0xff);
+        mData[mPos++] = (byte)((v >> 24) & 0xff);
+    }
+
+    void addI64(long v) {
+        align(8);
+        mData[mPos++] = (byte)(v & 0xff);
+        mData[mPos++] = (byte)((v >> 8) & 0xff);
+        mData[mPos++] = (byte)((v >> 16) & 0xff);
+        mData[mPos++] = (byte)((v >> 24) & 0xff);
+        mData[mPos++] = (byte)((v >> 32) & 0xff);
+        mData[mPos++] = (byte)((v >> 40) & 0xff);
+        mData[mPos++] = (byte)((v >> 48) & 0xff);
+        mData[mPos++] = (byte)((v >> 56) & 0xff);
+    }
+
+    void addU8(short v) {
+        if ((v < 0) || (v > 0xff)) {
+            throw new IllegalArgumentException("Saving value out of range for type");
+        }
+        mData[mPos++] = (byte)v;
+    }
+
+    void addU16(int v) {
+        if ((v < 0) || (v > 0xffff)) {
+            throw new IllegalArgumentException("Saving value out of range for type");
+        }
+        align(2);
+        mData[mPos++] = (byte)(v & 0xff);
+        mData[mPos++] = (byte)(v >> 8);
+    }
+
+    void addU32(long v) {
+        if ((v < 0) || (v > 0xffffffff)) {
+            throw new IllegalArgumentException("Saving value out of range for type");
+        }
+        align(4);
+        mData[mPos++] = (byte)(v & 0xff);
+        mData[mPos++] = (byte)((v >> 8) & 0xff);
+        mData[mPos++] = (byte)((v >> 16) & 0xff);
+        mData[mPos++] = (byte)((v >> 24) & 0xff);
+    }
+
+    void addU64(long v) {
+        if (v < 0) {
+            throw new IllegalArgumentException("Saving value out of range for type");
+        }
+        align(8);
+        mData[mPos++] = (byte)(v & 0xff);
+        mData[mPos++] = (byte)((v >> 8) & 0xff);
+        mData[mPos++] = (byte)((v >> 16) & 0xff);
+        mData[mPos++] = (byte)((v >> 24) & 0xff);
+        mData[mPos++] = (byte)((v >> 32) & 0xff);
+        mData[mPos++] = (byte)((v >> 40) & 0xff);
+        mData[mPos++] = (byte)((v >> 48) & 0xff);
+        mData[mPos++] = (byte)((v >> 56) & 0xff);
+    }
+
+    void addF32(float v) {
+        addI32(Float.floatToRawIntBits(v));
+    }
+
+    void addF64(float v) {
+        addI64(Double.doubleToRawLongBits(v));
+    }
+
+    final byte[] getData() {
+        return mData;
+    }
+
+    private final byte mData[];
+    private int mPos;
+
+}
+
+
diff --git a/graphics/java/android/renderscript/Matrix2f.java b/graphics/java/android/renderscript/Matrix2f.java
new file mode 100644
index 0000000..4b5e61b
--- /dev/null
+++ b/graphics/java/android/renderscript/Matrix2f.java
@@ -0,0 +1,58 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Matrix2f {
+
+    public Matrix2f() {
+        mMat = new float[4];
+        loadIdentity();
+    }
+
+    public float get(int i, int j) {
+        return mMat[i*2 + j];
+    }
+
+    public void set(int i, int j, float v) {
+        mMat[i*2 + j] = v;
+    }
+
+    public void loadIdentity() {
+        mMat[0] = 1;
+        mMat[1] = 0;
+
+        mMat[2] = 0;
+        mMat[3] = 1;
+    }
+
+    public void load(Matrix2f src) {
+        System.arraycopy(mMat, 0, src, 0, 4);
+    }
+
+    final float[] mMat;
+}
+
+
+
diff --git a/graphics/java/android/renderscript/Matrix3f.java b/graphics/java/android/renderscript/Matrix3f.java
new file mode 100644
index 0000000..19d7b43
--- /dev/null
+++ b/graphics/java/android/renderscript/Matrix3f.java
@@ -0,0 +1,63 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Matrix3f {
+
+    public Matrix3f() {
+        mMat = new float[9];
+        loadIdentity();
+    }
+
+    public float get(int i, int j) {
+        return mMat[i*3 + j];
+    }
+
+    public void set(int i, int j, float v) {
+        mMat[i*3 + j] = v;
+    }
+
+    public void loadIdentity() {
+        mMat[0] = 1;
+        mMat[1] = 0;
+        mMat[2] = 0;
+
+        mMat[3] = 0;
+        mMat[4] = 1;
+        mMat[5] = 0;
+
+        mMat[6] = 0;
+        mMat[7] = 0;
+        mMat[8] = 1;
+    }
+
+    public void load(Matrix3f src) {
+        System.arraycopy(mMat, 0, src, 0, 9);
+    }
+
+    final float[] mMat;
+}
+
+
diff --git a/graphics/java/android/renderscript/Matrix.java b/graphics/java/android/renderscript/Matrix4f.java
similarity index 90%
rename from graphics/java/android/renderscript/Matrix.java
rename to graphics/java/android/renderscript/Matrix4f.java
index a266d6b..ebd5bde 100644
--- a/graphics/java/android/renderscript/Matrix.java
+++ b/graphics/java/android/renderscript/Matrix4f.java
@@ -24,9 +24,9 @@
  * @hide
  *
  **/
-public class Matrix {
+public class Matrix4f {
 
-    public Matrix() {
+    public Matrix4f() {
         mMat = new float[16];
         loadIdentity();
     }
@@ -49,7 +49,7 @@
         mMat[5] = 1;
         mMat[6] = 0;
         mMat[7] = 0;
-    
+
         mMat[8] = 0;
         mMat[9] = 0;
         mMat[10] = 1;
@@ -61,8 +61,8 @@
         mMat[15] = 1;
     }
 
-    public void load(Matrix src) {
-        mMat = src.mMat;
+    public void load(Matrix4f src) {
+        System.arraycopy(mMat, 0, src, 0, 16);
     }
 
     public void loadRotate(float rot, float x, float y, float z) {
@@ -77,7 +77,7 @@
         rot *= (float)(java.lang.Math.PI / 180.0f);
         c = (float)java.lang.Math.cos(rot);
         s = (float)java.lang.Math.sin(rot);
-    
+
         float len = (float)java.lang.Math.sqrt(x*x + y*y + z*z);
         if (!(len != 1)) {
             float recipLen = 1.f / len;
@@ -91,7 +91,7 @@
         float zx = z * x;
         float xs = x * s;
         float ys = y * s;
-        float zs = z * s;		
+        float zs = z * s;
         mMat[ 0] = x*x*nc +  c;
         mMat[ 4] =  xy*nc - zs;
         mMat[ 8] =  zx*nc + ys;
@@ -109,7 +109,7 @@
         mMat[5] = y;
         mMat[10] = z;
     }
-    
+
     public void loadTranslate(float x, float y, float z) {
         loadIdentity();
         mMat[12] = x;
@@ -117,7 +117,7 @@
         mMat[14] = z;
     }
 
-    public void loadMultiply(Matrix lhs, Matrix rhs) {
+    public void loadMultiply(Matrix4f lhs, Matrix4f rhs) {
         for (int i=0 ; i<4 ; i++) {
             float ri0 = 0;
             float ri1 = 0;
@@ -159,31 +159,28 @@
         mMat[15]= 0;
     }
 
-    public void multiply(Matrix rhs) {
-        Matrix tmp = new Matrix();
+    public void multiply(Matrix4f rhs) {
+        Matrix4f tmp = new Matrix4f();
         tmp.loadMultiply(this, rhs);
         load(tmp);
     }
     public void rotate(float rot, float x, float y, float z) {
-        Matrix tmp = new Matrix();
+        Matrix4f tmp = new Matrix4f();
         tmp.loadRotate(rot, x, y, z);
         multiply(tmp);
     }
     public void scale(float x, float y, float z) {
-        Matrix tmp = new Matrix();
+        Matrix4f tmp = new Matrix4f();
         tmp.loadScale(x, y, z);
         multiply(tmp);
     }
     public void translate(float x, float y, float z) {
-        Matrix tmp = new Matrix();
+        Matrix4f tmp = new Matrix4f();
         tmp.loadTranslate(x, y, z);
         multiply(tmp);
     }
 
-
-
-    float[] mMat;
-
+    final float[] mMat;
 }
 
 
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 84f6f2d..1b155d7 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -96,16 +96,16 @@
         static final int PROJECTION_OFFSET = 16;
         static final int TEXTURE_OFFSET = 32;
 
-        Matrix mModel;
-        Matrix mProjection;
-        Matrix mTexture;
+        Matrix4f mModel;
+        Matrix4f mProjection;
+        Matrix4f mTexture;
 
         public Allocation mAlloc;
 
         public MatrixAllocation(RenderScript rs) {
-            mModel = new Matrix();
-            mProjection = new Matrix();
-            mTexture = new Matrix();
+            mModel = new Matrix4f();
+            mProjection = new Matrix4f();
+            mTexture = new Matrix4f();
 
             mAlloc = Allocation.createSized(rs, Element.createUser(rs, Element.DataType.FLOAT_32), 48);
             mAlloc.subData1D(MODELVIEW_OFFSET, 16, mModel.mMat);
@@ -118,17 +118,17 @@
             mAlloc = null;
         }
 
-        public void loadModelview(Matrix m) {
+        public void loadModelview(Matrix4f m) {
             mModel = m;
             mAlloc.subData1D(MODELVIEW_OFFSET, 16, m.mMat);
         }
 
-        public void loadProjection(Matrix m) {
+        public void loadProjection(Matrix4f m) {
             mProjection = m;
             mAlloc.subData1D(PROJECTION_OFFSET, 16, m.mMat);
         }
 
-        public void loadTexture(Matrix m) {
+        public void loadTexture(Matrix4f m) {
             mTexture = m;
             mAlloc.subData1D(TEXTURE_OFFSET, 16, m.mMat);
         }
@@ -152,8 +152,8 @@
 
         public void setupProjectionNormalized(int w, int h) {
             // range -1,1 in the narrow axis at z = 0.
-            Matrix m1 = new Matrix();
-            Matrix m2 = new Matrix();
+            Matrix4f m1 = new Matrix4f();
+            Matrix4f m2 = new Matrix4f();
 
             if(w > h) {
                 float aspect = ((float)w) / h;
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Vector2f.java
new file mode 100644
index 0000000..567d57fa
--- /dev/null
+++ b/graphics/java/android/renderscript/Vector2f.java
@@ -0,0 +1,37 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Vector2f {
+    public Vector2f() {
+    }
+
+    public float x;
+    public float y;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Vector3f.java b/graphics/java/android/renderscript/Vector3f.java
new file mode 100644
index 0000000..f2842f3
--- /dev/null
+++ b/graphics/java/android/renderscript/Vector3f.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Vector3f {
+    public Vector3f() {
+    }
+
+    public float x;
+    public float y;
+    public float z;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Vector4f.java b/graphics/java/android/renderscript/Vector4f.java
new file mode 100644
index 0000000..fabd959
--- /dev/null
+++ b/graphics/java/android/renderscript/Vector4f.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Vector4f {
+    public Vector4f() {
+    }
+
+    public float x;
+    public float y;
+    public float z;
+    public float w;
+}
+
+
+
diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h
index 30b7ad9..42d50e5 100644
--- a/include/media/stagefright/CachingDataSource.h
+++ b/include/media/stagefright/CachingDataSource.h
@@ -33,6 +33,8 @@
 
     virtual ssize_t readAt(off_t offset, void *data, size_t size);
 
+    virtual status_t getSize(off_t *size);
+
     virtual uint32_t flags();
 
 protected:
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
index 895cda3..98ebc48 100644
--- a/include/media/stagefright/HTTPDataSource.h
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -39,6 +39,8 @@
 
     virtual ssize_t readAt(off_t offset, void *data, size_t size);
 
+    virtual status_t getSize(off_t *size);
+
     virtual uint32_t flags() {
         return kWantsPrefetching;
     }
@@ -61,10 +63,16 @@
     void *mBuffer;
     size_t mBufferLength;
     off_t mBufferOffset;
-    bool mFirstRequest;
+
+    bool mContentLengthValid;
+    unsigned long long mContentLength;
 
     status_t mInitCheck;
 
+    void init(
+            const char *_host, int port, const char *_path,
+            const KeyedVector<String8, String8> *headers);
+
     ssize_t sendRangeRequest(size_t offset);
     void initHeaders(const KeyedVector<String8, String8> *overrides);
 
diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h
index 26cde38..c7db9a6 100644
--- a/include/private/opengles/gl_context.h
+++ b/include/private/opengles/gl_context.h
@@ -36,7 +36,12 @@
 
 namespace android {
 
-const unsigned int OGLES_NUM_COMPRESSED_TEXTURE_FORMATS = 10;
+
+const unsigned int OGLES_NUM_COMPRESSED_TEXTURE_FORMATS = 10
+#ifdef GL_OES_compressed_ETC1_RGB8_texture
+        + 1
+#endif
+        ;
 
 class EGLTextureObject;
 class EGLSurfaceManager;
@@ -289,7 +294,6 @@
     vec4_t      normalizedObjPosition;
     vec4_t      spotDir;
     vec4_t      normalizedSpotDir;
-    vec4_t      objViewer;
     GLfixed     spotExp;
     GLfixed     spotCutoff;
     GLfixed     spotCutoffCosine;
@@ -322,9 +326,10 @@
     material_t          front;
     light_model_t       lightModel;
     color_material_t    colorMaterial;
+    vec4_t              implicitSceneEmissionAndAmbient;
+    vec4_t              objViewer;
     uint32_t            enabledLights;
     GLboolean           enable;
-    vec4_t              implicitSceneEmissionAndAmbient;
     GLenum              shadeModel;
     typedef void (*light_fct_t)(ogles_context_t*, vertex_t*);
     void (*lightVertex)(ogles_context_t* c, vertex_t* v);
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 2b4dab9..8b5f702 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -885,6 +885,7 @@
             switch (status) {
                 case GPS_STATUS_SESSION_BEGIN:
                     mNavigating = true;
+                    mEngineOn = true;
                     break;
                 case GPS_STATUS_SESSION_END:
                     mNavigating = false;
@@ -894,6 +895,7 @@
                     break;
                 case GPS_STATUS_ENGINE_OFF:
                     mEngineOn = false;
+                    mNavigating = false;
                     break;
             }
 
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index ba7e9bb..e9d3372 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -41,6 +41,8 @@
     public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
     public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
     public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+    public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
     public static final String TAG_WHITE_BALANCE = "WhiteBalance";
 
     // Constants used for the Orientation Exif tag.
@@ -62,8 +64,7 @@
     static {
         System.loadLibrary("exif");
         sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
-        //TODO: uncomment this when our EXIF datetime is encoded as UTC
-        //sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+        sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
     }
 
     private String mFilename;
@@ -248,7 +249,7 @@
     }
 
     /**
-     * Returns number of milliseconds since Jan. 1, 1970, midnight GMT.
+     * Returns number of milliseconds since Jan. 1, 1970, midnight.
      * Returns -1 if the date time information if not available.
      * @hide
      */
@@ -258,9 +259,32 @@
 
         ParsePosition pos = new ParsePosition(0);
         try {
-            Date date = sFormatter.parse(dateTimeString, pos);
-            if (date == null) return -1;
-            return date.getTime();
+            Date datetime = sFormatter.parse(dateTimeString, pos);
+            if (datetime == null) return -1;
+            return datetime.getTime();
+        } catch (IllegalArgumentException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
+     * Returns -1 if the date time information if not available.
+     * @hide
+     */
+    public long getGpsDateTime() {
+        String date = mAttributes.get(TAG_GPS_DATESTAMP);
+        String time = mAttributes.get(TAG_GPS_TIMESTAMP);
+        if (date == null || time == null) return -1;
+
+        String dateTimeString = date + ' ' + time;
+        if (dateTimeString == null) return -1;
+
+        ParsePosition pos = new ParsePosition(0);
+        try {
+            Date datetime = sFormatter.parse(dateTimeString, pos);
+            if (datetime == null) return -1;
+            return datetime.getTime();
         } catch (IllegalArgumentException ex) {
             return -1;
         }
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index fcb02f4..8c1b0ea 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -744,7 +744,7 @@
                         values.put(Images.Media.LONGITUDE, latlng[1]);
                     }
 
-                    long time = exif.getDateTime();
+                    long time = exif.getGpsDateTime();
                     if (time != -1) {
                         values.put(Images.Media.DATE_TAKEN, time);
                     }
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index efe7ebb..01578c1 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -249,7 +249,8 @@
                         kKeyTime, &mPositionTimeMediaUs));
 
             mPositionTimeRealUs =
-                ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
+                -mLatencyUs
+                + ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
                     / mSampleRate;
 
             LOGV("buffer->size() = %d, "
@@ -297,6 +298,10 @@
 int64_t AudioPlayer::getMediaTimeUs() {
     Mutex::Autolock autoLock(mLock);
 
+    if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) {
+        return 0;
+    }
+
     return mPositionTimeMediaUs + (getRealTimeUsLocked() - mPositionTimeRealUs);
 }
 
@@ -307,7 +312,7 @@
     *realtime_us = mPositionTimeRealUs;
     *mediatime_us = mPositionTimeMediaUs;
 
-    return mPositionTimeRealUs != -1 || mPositionTimeMediaUs != -1;
+    return mPositionTimeRealUs != -1 && mPositionTimeMediaUs != -1;
 }
 
 status_t AudioPlayer::seekTo(int64_t time_us) {
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
index 8d04ead..1ca463e 100644
--- a/media/libstagefright/CachingDataSource.cpp
+++ b/media/libstagefright/CachingDataSource.cpp
@@ -65,6 +65,10 @@
     return mSource->initCheck();
 }
 
+status_t CachingDataSource::getSize(off_t *size) {
+    return mSource->getSize(size);
+}
+
 uint32_t CachingDataSource::flags() {
     return mSource->flags();
 }
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
index e1ddfef..4b630b9 100644
--- a/media/libstagefright/HTTPDataSource.cpp
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -34,7 +34,7 @@
         HTTPStream *http, const String8 &headers,
         string *host, string *path, int *port) {
     String8 request;
-    request.append("HEAD ");
+    request.append("GET ");
     request.append(path->c_str());
     request.append(" HTTP/1.1\r\n");
     request.append(headers);
@@ -94,19 +94,9 @@
 }
 
 HTTPDataSource::HTTPDataSource(
-        const char *uri, const KeyedVector<String8, String8> *headers)
-    : mHttp(new HTTPStream),
-      mHost(NULL),
-      mPort(0),
-      mPath(NULL),
-      mBuffer(malloc(kBufferSize)),
-      mBufferLength(0),
-      mBufferOffset(0),
-      mFirstRequest(true) {
+        const char *uri, const KeyedVector<String8, String8> *headers) {
     CHECK(!strncasecmp("http://", uri, 7));
 
-    initHeaders(headers);
-
     string host;
     string path;
     int port;
@@ -133,33 +123,27 @@
         host = string(host, 0, colon - host.c_str());
     }
 
-    do {
-        LOGI("Connecting to host '%s', port %d, path '%s'",
-             host.c_str(), port, path.c_str());
-
-        mInitCheck = mHttp->connect(host.c_str(), port);
-
-        if (mInitCheck != OK) {
-            return;
-        }
-    } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port));
-
-    mHost = strdup(host.c_str());
-    mPort = port;
-    mPath = strdup(path.c_str());
+    init(host.c_str(), port, path.c_str(), headers);
 }
 
 HTTPDataSource::HTTPDataSource(
         const char *_host, int port, const char *_path,
-        const KeyedVector<String8, String8> *headers)
-    : mHttp(new HTTPStream),
-      mHost(NULL),
-      mPort(0),
-      mPath(NULL),
-      mBuffer(malloc(kBufferSize)),
-      mBufferLength(0),
-      mBufferOffset(0),
-      mFirstRequest(true) {
+        const KeyedVector<String8, String8> *headers) {
+    init(_host, port, _path, headers);
+}
+
+void HTTPDataSource::init(
+        const char *_host, int port, const char *_path,
+        const KeyedVector<String8, String8> *headers) {
+    mHttp = new HTTPStream;
+    mHost = NULL;
+    mPort = 0;
+    mPath = NULL,
+    mBuffer = malloc(kBufferSize);
+    mBufferLength = 0;
+    mBufferOffset = 0;
+    mContentLengthValid = false;
+
     initHeaders(headers);
 
     string host = _host;
@@ -168,13 +152,22 @@
     LOGI("Connecting to host '%s', port %d, path '%s'",
          host.c_str(), port, path.c_str());
 
+    int numRedirectsRemaining = 5;
     do {
         mInitCheck = mHttp->connect(host.c_str(), port);
 
         if (mInitCheck != OK) {
             return;
         }
-    } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port));
+    } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port)
+             && numRedirectsRemaining-- > 0);
+
+    string value;
+    if (mHttp->find_header_value("Content-Length", &value)) {
+        char *end;
+        mContentLength = strtoull(value.c_str(), &end, 10);
+        mContentLengthValid = true;
+    }
 
     mHost = strdup(host.c_str());
     mPort = port;
@@ -185,6 +178,22 @@
     return mInitCheck;
 }
 
+status_t HTTPDataSource::getSize(off_t *size) {
+    *size = 0;
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (!mContentLengthValid) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    *size = mContentLength;
+
+    return OK;
+}
+
 HTTPDataSource::~HTTPDataSource() {
     mHttp->disconnect();
 
@@ -272,14 +281,11 @@
     }
 
     ssize_t contentLength = 0;
-    if (mFirstRequest || offset != mBufferOffset + mBufferLength) {
-        if (!mFirstRequest) {
-            LOGV("new range offset=%ld (old=%ld)",
-                 offset, mBufferOffset + mBufferLength);
+    if (offset != (off_t)(mBufferOffset + mBufferLength)) {
+        LOGV("new range offset=%ld (old=%ld)",
+             offset, mBufferOffset + mBufferLength);
 
-            mHttp->disconnect();
-        }
-        mFirstRequest = false;
+        mHttp->disconnect();
 
         contentLength = sendRangeRequest(offset);
 
@@ -298,6 +304,12 @@
 
     ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
 
+    if (num_bytes_received < 0) {
+        mBufferLength = 0;
+
+        return num_bytes_received;
+    }
+
     mBufferLength = (size_t)num_bytes_received;
 
     size_t copy = mBufferLength;
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 02f9439..3711acac 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -52,7 +52,7 @@
 
     CHECK_EQ(mSocket, -1);
     mSocket = socket(AF_INET, SOCK_STREAM, 0);
-    
+
     if (mSocket < 0) {
         return UNKNOWN_ERROR;
     }
@@ -132,6 +132,14 @@
     return send(data, strlen(data));
 }
 
+// A certain application spawns a local webserver that sends invalid responses,
+// specifically it terminates header line with only a newline instead of the
+// CRLF (carriage-return followed by newline) required by the HTTP specs.
+// The workaround accepts both behaviours but could potentially break
+// legitimate responses that use a single newline to "fold" headers, which is
+// why it's not yet on by default.
+#define WORKAROUND_FOR_MISSING_CR       0
+
 status_t HTTPStream::receive_line(char *line, size_t size) {
     if (mState != CONNECTED) {
         return ERROR_NOT_CONNECTED;
@@ -157,16 +165,27 @@
             return ERROR_CONNECTION_LOST;
         }
 
-        if (saw_CR && c == '\n') {
+#if WORKAROUND_FOR_MISSING_CR
+        if (c == '\n') {
+            // We have a complete line.
+
+            line[saw_CR ? length - 1 : length] = '\0';
+            return OK;
+        }
+#else
+        if (saw_CR &&  c == '\n') {
             // We have a complete line.
 
             line[length - 1] = '\0';
             return OK;
         }
+#endif
 
         saw_CR = (c == '\r');
 
-        CHECK(length + 1 < size);
+        if (length + 1 >= size) {
+            return ERROR_MALFORMED;
+        }
         line[length++] = c;
     }
 }
@@ -175,7 +194,7 @@
     *http_status = -1;
     mHeaders.clear();
 
-    char line[1024];
+    char line[2048];
     status_t err = receive_line(line, sizeof(line));
     if (err != OK) {
         return err;
@@ -257,11 +276,11 @@
             }
 
             disconnect();
-            return ERROR_IO;
+            return total == 0 ? ERROR_IO : total;
         } else if (n == 0) {
             disconnect();
 
-            return ERROR_CONNECTION_LOST;
+            return total == 0 ? ERROR_CONNECTION_LOST : total;
         }
 
         total += (size_t)n;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index aee4d15..2cf0ddf 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -38,7 +38,7 @@
     void stop();
     bool reachedEOS();
 
-    int64_t getDuration() const;
+    int64_t getDurationUs() const;
     void writeTrackHeader(int32_t trackID);
 
 private:
@@ -46,6 +46,7 @@
     sp<MetaData> mMeta;
     sp<MediaSource> mSource;
     volatile bool mDone;
+    int64_t mMaxTimeStampUs;
 
     pthread_t mThread;
 
@@ -140,7 +141,7 @@
          it != mTracks.end(); ++it) {
         (*it)->stop();
 
-        int64_t duration = (*it)->getDuration();
+        int64_t duration = (*it)->getDurationUs();
         if (duration > max_duration) {
             max_duration = duration;
         }
@@ -162,7 +163,7 @@
         writeInt32(now);           // creation time
         writeInt32(now);           // modification time
         writeInt32(1000);          // timescale
-        writeInt32(max_duration);
+        writeInt32(max_duration / 1000);
         writeInt32(0x10000);       // rate
         writeInt16(0x100);         // volume
         writeInt16(0);             // reserved
@@ -316,6 +317,7 @@
       mMeta(source->getFormat()),
       mSource(source),
       mDone(false),
+      mMaxTimeStampUs(0),
       mCodecSpecificData(NULL),
       mCodecSpecificDataSize(0),
       mReachedEOS(false) {
@@ -343,6 +345,7 @@
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
 
     mDone = false;
+    mMaxTimeStampUs = 0;
     mReachedEOS = false;
 
     pthread_create(&mThread, &attr, ThreadWrapper, this);
@@ -483,6 +486,10 @@
         int64_t timestampUs;
         CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
 
+        if (timestampUs > mMaxTimeStampUs) {
+            mMaxTimeStampUs = timestampUs;
+        }
+
         // Our timestamp is in ms.
         info.timestamp = (timestampUs + 500) / 1000;
 
@@ -495,8 +502,8 @@
     mReachedEOS = true;
 }
 
-int64_t MPEG4Writer::Track::getDuration() const {
-    return 10000;  // XXX
+int64_t MPEG4Writer::Track::getDurationUs() const {
+    return mMaxTimeStampUs;
 }
 
 void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
@@ -516,7 +523,7 @@
         mOwner->writeInt32(now);           // modification time
         mOwner->writeInt32(trackID);
         mOwner->writeInt32(0);             // reserved
-        mOwner->writeInt32(getDuration());
+        mOwner->writeInt32(getDurationUs() / 1000);
         mOwner->writeInt32(0);             // reserved
         mOwner->writeInt32(0);             // reserved
         mOwner->writeInt16(0);             // layer
@@ -555,7 +562,7 @@
           mOwner->writeInt32(now);           // creation time
           mOwner->writeInt32(now);           // modification time
           mOwner->writeInt32(1000);          // timescale
-          mOwner->writeInt32(getDuration());
+          mOwner->writeInt32(getDurationUs() / 1000);
           mOwner->writeInt16(0);             // language code XXX
           mOwner->writeInt16(0);             // predefined
         mOwner->endBox();
diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp
index a61cccb..6d3e87c 100644
--- a/media/sdutils/sdutil.cpp
+++ b/media/sdutils/sdutil.cpp
@@ -38,65 +38,27 @@
     }
 }
 
-static bool isMounted(const char* mountPoint) {
-    char s[2000];
-    FILE *f = fopen("/proc/mounts", "r");
-    bool mounted = false;
-
-    while (fgets(s, sizeof(s), f))
-    {
-        char *c, *path = NULL;
-
-        for (c = s; *c; c++) 
-        {
-            if (*c == ' ') 
-            {
-                *c = 0;
-                path = c + 1;
-                break;
-            }
-        }
-
-        for (c = path; *c; c++) 
-        {
-            if (*c == ' ') 
-            {
-                *c = '\0';
-                break;
-            }
-        }
-
-        if (strcmp(mountPoint, path) == 0) {
-            mounted = true;
-            break;
-        }
-    }
-
-    fclose(f);
-    return mounted;
-}
-
-static void millisecondSleep(int milliseconds) {
-	struct timespec reqt, remt;	
-	reqt.tv_sec = milliseconds / 1000;
-	reqt.tv_nsec = 1000000 * (milliseconds % 1000);
-	nanosleep(&reqt, &remt) ;
-
-}
-
 static int mount(const char* path) {
     String16 string(path);
-    gMountService->mountVolume(string);
-    
-    for (int i = 0; i < 60; i++) {
-        if (isMounted(path)) {
-            return 0;
-        }
-        millisecondSleep(500);
-    }
-        
-    fprintf(stderr, "failed to mount %s\n", path);   
-    return -1;
+    return gMountService->mountVolume(string);
+}
+
+static int share(const char *path, const char *method) {
+    String16 sPath(path);
+    String16 sMethod(method);
+    return gMountService->shareVolume(sPath, sMethod);
+}
+
+static int unshare(const char *path, const char *method) {
+    String16 sPath(path);
+    String16 sMethod(method);
+    return gMountService->unshareVolume(sPath, sMethod);
+}
+
+static bool shared(const char *path, const char *method) {
+    String16 sPath(path);
+    String16 sMethod(method);
+    return gMountService->getVolumeShared(sPath, sMethod);
 }
 
 static int asec_create(const char *id, int sizeMb, const char *fstype,
@@ -105,39 +67,35 @@
     String16 sFstype(fstype);
     String16 sKey(key);
 
-    String16 r = gMountService->createSecureContainer(sId, sizeMb, sFstype,
-                                                      sKey, ownerUid);
-    return 0;
+    return gMountService->createSecureContainer(
+            sId, sizeMb, sFstype, sKey, ownerUid);
 }
 
 static int asec_finalize(const char *id) {
     String16 sId(id);
-    gMountService->finalizeSecureContainer(sId);
-    return 0;
+    return gMountService->finalizeSecureContainer(sId);
 }
 
 static int asec_destroy(const char *id) {
     String16 sId(id);
-    gMountService->destroySecureContainer(sId);
-    return 0;
+    return gMountService->destroySecureContainer(sId);
 }
 
 static int asec_mount(const char *id, const char *key, int ownerUid) {
     String16 sId(id);
     String16 sKey(key);
-    gMountService->mountSecureContainer(sId, sKey, ownerUid);
-    return 0;
+    return gMountService->mountSecureContainer(sId, sKey, ownerUid);
 }
 
-static void asec_unmount(const char *id) {
+static int asec_unmount(const char *id) {
     String16 sId(id);
-    gMountService->unmountSecureContainer(sId);
+    return gMountService->unmountSecureContainer(sId);
 }
 
-static void asec_rename(const char *oldId, const char *newId) {
+static int asec_rename(const char *oldId, const char *newId) {
     String16 sOldId(oldId);
     String16 sNewId(newId);
-    gMountService->renameSecureContainer(sOldId, sNewId);
+    return gMountService->renameSecureContainer(sOldId, sNewId);
 }
 
 static int asec_path(const char *id) {
@@ -148,98 +106,85 @@
 
 static int unmount(const char* path) {
     String16 string(path);
-    gMountService->unmountVolume(string);
-
-    for (int i = 0; i < 20; i++) {
-        if (!isMounted(path)) {
-            return 0;
-        }
-        millisecondSleep(500);
-    }
-        
-    fprintf(stderr, "failed to unmount %s\n", path);   
-    return -1;
+    return gMountService->unmountVolume(string);
 }
 
 static int format(const char* path) {
     String16 string(path);
-
-    if (isMounted(path))
-        return -EBUSY;
-    gMountService->formatVolume(string);
-
-    return 0;
-}
-
-static int umsEnable(bool enable) {
-    gMountService->setMassStorageEnabled(enable);
-    return 0;
+    return gMountService->formatVolume(string);
 }
 
 };
 
+static void usage(void);
+
 int main(int argc, char **argv)
 {
-    const char* command = (argc > 1 ? argv[1] : "");
-    const char* argument = (argc > 2 ? argv[2] : "");
+    if (argc < 2)
+        usage();
+
+    android::init();
+    int rc = 0;
     
-    if (strcmp(command, "mount") == 0) {
-        android::init();
-        return android::mount(argument);
-    } else if (strcmp(command, "format") == 0) {
-        android::init();
-        return android::format(argument);
-    } else if (strcmp(command, "unmount") == 0) {
-        android::init();
-        return android::unmount(argument);
-    } else if (strcmp(command, "ums") == 0) {
-        if (strcmp(argument, "enable") == 0) {
-            android::init();
-            return android::umsEnable(true);
-        } else if (strcmp(argument, "disable") == 0) {
-            android::init();
-            return android::umsEnable(false);
-        }
-    } else if (!strcmp(command, "asec")) {
-        const char* id = (argc > 3 ? argv[3] : NULL);
+    if (strcmp(argv[1], "mount") == 0) {
+        rc = android::mount(argv[2]);
+    } else if (strcmp(argv[1], "format") == 0) {
+        rc = android::format(argv[2]);
+    } else if (strcmp(argv[1], "unmount") == 0) {
+        rc = android::unmount(argv[2]);
+    } else if (strcmp(argv[1], "share") == 0) {
+        if (argc != 3)
+            usage();
+        rc = android::share(argv[2], argv[3]);
+    } else if (strcmp(argv[1], "unshare") == 0) {
+        if (argc != 3)
+            usage();
+        rc = android::unshare(argv[2], argv[3]);
+    } else if (strcmp(argv[1], "shared") == 0) {
+        if (argc != 3)
+            usage();
+        fprintf(stdout, "%s\n", (android::shared(argv[2], argv[3]) ? "true" : "false"));
+    } else if (!strcmp(argv[1], "asec")) {
+        if (argc < 3)
+            usage();
 
-        if (!id)
-            goto usage;
-
-        android::init();
-        if (!strcmp(argument, "create")) {
+        if (!strcmp(argv[2], "create")) {
 
             if (argc != 8)
-                goto usage;
-            return android::asec_create(id, atoi(argv[4]), argv[5], argv[6],
-                                        atoi(argv[7]));
-        } else if (!strcmp(argument, "finalize")) {
-            return android::asec_finalize(id);
-        } else if (!strcmp(argument, "destroy")) {
-            return android::asec_destroy(id);
-        } else if (!strcmp(argument, "mount")) {
-            if (argc == 6)
-                return android::asec_mount(id, argv[4], atoi(argv[5]));
-        } else if (!strcmp(argument, "rename")) {
-            if (argc == 5) {
-                android::asec_rename(id, argv[4]);
-                return 0;
-            }
-        } else if (!strcmp(argument, "unmount")) {
-            android::asec_unmount(id);
-            return 0;
-        } else if (!strcmp(argument, "path")) {
-            return android::asec_path(id);
+                usage();
+            rc = android::asec_create(argv[3], atoi(argv[4]), argv[5], argv[6], atoi(argv[7]));
+        } else if (!strcmp(argv[3], "finalize")) {
+            rc = android::asec_finalize(argv[3]);
+        } else if (!strcmp(argv[3], "destroy")) {
+            return android::asec_destroy(argv[3]);
+        } else if (!strcmp(argv[3], "mount")) {
+            if (argc != 6)
+                usage();
+            rc = android::asec_mount(argv[3], argv[4], atoi(argv[5]));
+        } else if (!strcmp(argv[3], "rename")) {
+            if (argc != 5)
+                usage();
+            rc = android::asec_rename(argv[3], argv[4]);
+        } else if (!strcmp(argv[3], "unmount")) {
+            rc = android::asec_unmount(argv[3]);
+        } else if (!strcmp(argv[3], "path")) {
+            rc = android::asec_path(argv[3]);
         }
     }
-    
-usage:
+
+    fprintf(stdout, "Operation completed with code %d\n", rc);
+    return rc;
+}
+
+static void usage()
+{
     fprintf(stderr, "usage:\n"
                     "    sdutil mount <mount path>          - mounts the SD card at the given mount point\n"
                     "    sdutil unmount <mount path>        - unmounts the SD card at the given mount point\n"
                     "    sdutil format <mount path>         - formats the SD card at the given mount point\n"
-                    "    sdutil ums enable                  - enables USB mass storage\n"
-                    "    sdutil ums disable                 - disables USB mass storage\n"
+                    "    sdutil share <path> <method>       - shares a volume\n"
+                    "    sdutil unshare <path> <method>     - unshares a volume\n"
+                    "    sdutil shared <path> <method>      - Queries volume share state\n"
                     "    sdutil asec create <id> <sizeMb> <fstype> <key> <ownerUid>\n"
                     "    sdutil asec finalize <id>\n"
                     "    sdutil asec destroy <id>\n"
@@ -248,5 +193,5 @@
                     "    sdutil asec rename <oldId, newId>\n"
                     "    sdutil asec path <id>\n"
                     );
-    return -1;
+    exit(1);
 }
diff --git a/mms-common/java/com/android/common/CharacterSets.java b/mms-common/java/com/android/mmscommon/CharacterSets.java
similarity index 100%
rename from mms-common/java/com/android/common/CharacterSets.java
rename to mms-common/java/com/android/mmscommon/CharacterSets.java
diff --git a/mms-common/java/com/android/common/ContentType.java b/mms-common/java/com/android/mmscommon/ContentType.java
similarity index 100%
rename from mms-common/java/com/android/common/ContentType.java
rename to mms-common/java/com/android/mmscommon/ContentType.java
diff --git a/mms-common/java/com/android/common/EncodedStringValue.java b/mms-common/java/com/android/mmscommon/EncodedStringValue.java
similarity index 100%
rename from mms-common/java/com/android/common/EncodedStringValue.java
rename to mms-common/java/com/android/mmscommon/EncodedStringValue.java
diff --git a/mms-common/java/com/android/common/InvalidHeaderValueException.java b/mms-common/java/com/android/mmscommon/InvalidHeaderValueException.java
similarity index 100%
rename from mms-common/java/com/android/common/InvalidHeaderValueException.java
rename to mms-common/java/com/android/mmscommon/InvalidHeaderValueException.java
diff --git a/mms-common/java/com/android/common/MmsException.java b/mms-common/java/com/android/mmscommon/MmsException.java
similarity index 100%
rename from mms-common/java/com/android/common/MmsException.java
rename to mms-common/java/com/android/mmscommon/MmsException.java
diff --git a/mms-common/java/com/android/common/PduHeaders.java b/mms-common/java/com/android/mmscommon/PduHeaders.java
similarity index 100%
rename from mms-common/java/com/android/common/PduHeaders.java
rename to mms-common/java/com/android/mmscommon/PduHeaders.java
diff --git a/mms-common/java/com/android/common/mms/ContentType.java b/mms-common/java/com/android/mmscommon/mms/ContentType.java
similarity index 99%
rename from mms-common/java/com/android/common/mms/ContentType.java
rename to mms-common/java/com/android/mmscommon/mms/ContentType.java
index 0fdb46c..f21eba8 100644
--- a/mms-common/java/com/android/common/mms/ContentType.java
+++ b/mms-common/java/com/android/mmscommon/mms/ContentType.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.android.mms.mms;
+package com.android.mmscommon.mms;
 
 import java.util.ArrayList;
 
diff --git a/mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/AcknowledgeInd.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/AcknowledgeInd.java
diff --git a/mms-common/java/com/android/common/mms/pdu/Base64.java b/mms-common/java/com/android/mmscommon/mms/pdu/Base64.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/Base64.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/Base64.java
diff --git a/mms-common/java/com/android/common/mms/pdu/DeliveryInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/DeliveryInd.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/DeliveryInd.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/DeliveryInd.java
diff --git a/mms-common/java/com/android/common/mms/pdu/GenericPdu.java b/mms-common/java/com/android/mmscommon/mms/pdu/GenericPdu.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/GenericPdu.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/GenericPdu.java
diff --git a/mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java b/mms-common/java/com/android/mmscommon/mms/pdu/MultimediaMessagePdu.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/MultimediaMessagePdu.java
diff --git a/mms-common/java/com/android/common/mms/pdu/NotificationInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/NotificationInd.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/NotificationInd.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/NotificationInd.java
diff --git a/mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/NotifyRespInd.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/NotifyRespInd.java
diff --git a/mms-common/java/com/android/common/mms/pdu/PduBody.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduBody.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/PduBody.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/PduBody.java
diff --git a/mms-common/java/com/android/common/mms/pdu/PduComposer.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduComposer.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/PduComposer.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/PduComposer.java
diff --git a/mms-common/java/com/android/common/mms/pdu/PduContentTypes.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduContentTypes.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/PduContentTypes.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/PduContentTypes.java
diff --git a/mms-common/java/com/android/common/mms/pdu/PduParser.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/PduParser.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java
diff --git a/mms-common/java/com/android/common/mms/pdu/PduPart.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPart.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/PduPart.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/PduPart.java
diff --git a/mms-common/java/com/android/common/mms/pdu/PduPersister.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/PduPersister.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java
diff --git a/mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java b/mms-common/java/com/android/mmscommon/mms/pdu/QuotedPrintable.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/QuotedPrintable.java
diff --git a/mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/ReadOrigInd.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/ReadOrigInd.java
diff --git a/mms-common/java/com/android/common/mms/pdu/ReadRecInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/ReadRecInd.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/ReadRecInd.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/ReadRecInd.java
diff --git a/mms-common/java/com/android/common/mms/pdu/RetrieveConf.java b/mms-common/java/com/android/mmscommon/mms/pdu/RetrieveConf.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/RetrieveConf.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/RetrieveConf.java
diff --git a/mms-common/java/com/android/common/mms/pdu/SendConf.java b/mms-common/java/com/android/mmscommon/mms/pdu/SendConf.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/SendConf.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/SendConf.java
diff --git a/mms-common/java/com/android/common/mms/pdu/SendReq.java b/mms-common/java/com/android/mmscommon/mms/pdu/SendReq.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/pdu/SendReq.java
rename to mms-common/java/com/android/mmscommon/mms/pdu/SendReq.java
diff --git a/mms-common/java/com/android/common/mms/util/AbstractCache.java b/mms-common/java/com/android/mmscommon/mms/util/AbstractCache.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/util/AbstractCache.java
rename to mms-common/java/com/android/mmscommon/mms/util/AbstractCache.java
diff --git a/mms-common/java/com/android/common/mms/util/PduCache.java b/mms-common/java/com/android/mmscommon/mms/util/PduCache.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/util/PduCache.java
rename to mms-common/java/com/android/mmscommon/mms/util/PduCache.java
diff --git a/mms-common/java/com/android/common/mms/util/PduCacheEntry.java b/mms-common/java/com/android/mmscommon/mms/util/PduCacheEntry.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/util/PduCacheEntry.java
rename to mms-common/java/com/android/mmscommon/mms/util/PduCacheEntry.java
diff --git a/mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java
similarity index 100%
rename from mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java
rename to mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk
index c2e9f31..6cb146c 100644
--- a/opengl/libagl/Android.mk
+++ b/opengl/libagl/Android.mk
@@ -29,7 +29,7 @@
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 LOCAL_CFLAGS += -fvisibility=hidden
 
-LOCAL_SHARED_LIBRARIES := libcutils libhardware libutils libpixelflinger
+LOCAL_SHARED_LIBRARIES := libcutils libhardware libutils libpixelflinger libETC1
 LOCAL_LDLIBS := -lpthread -ldl
 
 ifeq ($(TARGET_ARCH),arm)
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index f211bca..ca715db 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -217,18 +217,26 @@
 {
     uint32_t en = c->lighting.enabledLights;
     // Vector from object to viewer, in eye coordinates
-    const vec4_t eyeViewer = { 0, 0, 0x1000, 0 };
     while (en) {
         const int i = 31 - gglClz(en);
         en &= ~(1<<i);
         light_t& l = c->lighting.lights[i];
+#if OBJECT_SPACE_LIGHTING
         c->transforms.mvui.point4(&c->transforms.mvui,
                 &l.objPosition, &l.position);
+#else
+        l.objPosition = l.position;
+#endif
         vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
-        c->transforms.mvui.point4(&c->transforms.mvui,
-                &l.objViewer, &eyeViewer);
-        vnorm3(l.objViewer.v, l.objViewer.v);
     }
+    const vec4_t eyeViewer = { 0, 0, 0x10000, 0 };
+#if OBJECT_SPACE_LIGHTING
+    c->transforms.mvui.point3(&c->transforms.mvui,
+            &c->lighting.objViewer, &eyeViewer);
+    vnorm3(c->lighting.objViewer.v, c->lighting.objViewer.v);
+#else
+    c->lighting.objViewer = eyeViewer;
+#endif
 }
 
 static inline void validate_light(ogles_context_t* c)
@@ -337,6 +345,7 @@
 {
     // emission and ambient for the whole scene
     vec4_t r = c->lighting.implicitSceneEmissionAndAmbient;
+    const vec4_t objViewer = c->lighting.objViewer;
 
     uint32_t en = c->lighting.enabledLights;
     if (ggl_likely(en)) {
@@ -347,7 +356,11 @@
         c->arrays.normal.fetch(c, n.v,
             c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));
 
-        // TODO: right now we handle GL_RESCALE_NORMALS as if ti were
+#if !OBJECT_SPACE_LIGHTING
+        c->transforms.mvui.point3(&c->transforms.mvui, &n, &n);
+#endif
+
+        // TODO: right now we handle GL_RESCALE_NORMALS as if it were
         // GL_NORMALIZE. We could optimize this by  scaling mvui 
         // appropriately instead.
         if (c->transforms.rescaleNormals)
@@ -380,13 +393,13 @@
             s = dot3(n.v, d.v);
             s = (s<0) ? (twoSide?(-s):0) : s;
             vsa3(t.v, l.implicitDiffuse.v, s, l.implicitAmbient.v);
-            
+
             // specular
             if (ggl_unlikely(s && l.implicitSpecular.v[3])) {
                 vec4_t h;
-                h.x = d.x + l.objViewer.x;
-                h.y = d.y + l.objViewer.y;
-                h.z = d.z + l.objViewer.z;
+                h.x = d.x + objViewer.x;
+                h.y = d.y + objViewer.y;
+                h.z = d.z + objViewer.z;
                 vnorm3(h.v, h.v);
                 s = dot3(n.v, h.v);
                 s = (s<0) ? (twoSide?(-s):0) : s;
@@ -515,15 +528,18 @@
     case GL_POSITION: {
         ogles_validate_transform(c, transform_state_t::MODELVIEW);
         transform_t& mv = c->transforms.modelview.transform;
-        memcpy(light.position.v, params, sizeof(light.position.v));
-        mv.point4(&mv, &light.position, &light.position);
+        mv.point4(&mv, &light.position, reinterpret_cast<vec4_t const*>(params));
         invalidate_lighting(c);
         return;
     }
     case GL_SPOT_DIRECTION: {
+#if OBJECT_SPACE_LIGHTING
         ogles_validate_transform(c, transform_state_t::MVUI);
         transform_t& mvui = c->transforms.mvui;
-        mvui.point3(&mvui, &light.spotDir, (vec4_t*)params);
+        mvui.point3(&mvui, &light.spotDir, reinterpret_cast<vec4_t const*>(params));
+#else
+        light.spotDir = *reinterpret_cast<vec4_t const*>(params);
+#endif
         vnorm3(light.normalizedSpotDir.v, light.spotDir.v);
         invalidate_lighting(c);
         return;
@@ -748,8 +764,8 @@
     case GL_SPECULAR:   what = c->lighting.front.specular.v; break;
     case GL_EMISSION:   what = c->lighting.front.emission.v; break;
     case GL_AMBIENT_AND_DIFFUSE:
-        what  = c->lighting.front.ambient.v; break;
-        other = c->lighting.front.diffuse.v; break;
+        what  = c->lighting.front.ambient.v;
+        other = c->lighting.front.diffuse.v;
         break;
     case GL_SHININESS:
         c->lighting.front.shininess = gglFloatToFixed(params[0]);
@@ -788,8 +804,8 @@
     case GL_SPECULAR:   what = c->lighting.front.specular.v; break;
     case GL_EMISSION:   what = c->lighting.front.emission.v; break;
     case GL_AMBIENT_AND_DIFFUSE:
-        what = c->lighting.front.ambient.v; break;
-        other= c->lighting.front.diffuse.v; break;
+        what  = c->lighting.front.ambient.v;
+        other = c->lighting.front.diffuse.v;
         break;
     case GL_SHININESS:
         c->lighting.front.shininess = gglFloatToFixed(params[0]);
diff --git a/opengl/libagl/light.h b/opengl/libagl/light.h
index 6dae25f..39e3309 100644
--- a/opengl/libagl/light.h
+++ b/opengl/libagl/light.h
@@ -22,6 +22,13 @@
 #include <stddef.h>
 #include <sys/types.h>
 
+
+// Set to 1 for object-space lighting evaluation.
+// There are still some bugs with object-space lighting,
+// especially visible in the San Angeles demo.
+#define OBJECT_SPACE_LIGHTING   0
+
+
 namespace android {
 
 namespace gl {
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index 3c50977..9520f04 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -55,6 +55,7 @@
 static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point3__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
 
 // ----------------------------------------------------------------------------
@@ -209,7 +210,7 @@
 {
     flags = 0;
     ops = OP_ALL;
-    point3 = point4__mvui;
+    point3 = point3__mvui;
     point4 = point4__mvui;
 }
 
@@ -600,17 +601,31 @@
     GLfloat r[16];
     const GLfloat* const mv = modelview.top().elements();
     
-    // TODO: we need a faster invert, especially for when the modelview
-    // is a rigid-body matrix
+    /*
+    When evaluating the lighting equation in eye-space, normals
+    are transformed by the upper 3x3 modelview inverse-transpose.
+    http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
+
+    (note that inverse-transpose is distributive).
+    Also note that:
+        l(obj) = inv(modelview).l(eye) for local light
+        l(obj) =  tr(modelview).l(eye) for infinite light
+    */
+
     invert(r, mv);
 
     GLfixed* const x = mvui.matrix.m;
-    for (int i=0 ; i<4 ; i++) {
-        x[I(i,0)] = gglFloatToFixed(r[I(i,0)]);
-        x[I(i,1)] = gglFloatToFixed(r[I(i,1)]);
-        x[I(i,2)] = gglFloatToFixed(r[I(i,2)]);
-        x[I(i,4)] = gglFloatToFixed(r[I(i,3)]);
-    }
+
+#if OBJECT_SPACE_LIGHTING
+    for (int i=0 ; i<4 ; i++)
+        for (int j=0 ; j<4 ; j++)
+            x[I(i,j)] = gglFloatToFixed(r[I(i,j)]);
+#else
+    for (int i=0 ; i<4 ; i++)
+        for (int j=0 ; j<4 ; j++)
+            x[I(i,j)] = gglFloatToFixed(r[I(j,i)]);
+#endif
+
     mvui.picker();
 }
 
@@ -739,8 +754,22 @@
     lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
 }
 
+void point3__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    // this is used for transforming light positions back to object space.
+    // w is used as a switch for directional lights, so we need
+    // to preserve it.
+    const GLfixed* const m = mx->matrix.m;
+    const GLfixed rx = rhs->x;
+    const GLfixed ry = rhs->y;
+    const GLfixed rz = rhs->z;
+    lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]);
+    lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
+    lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
+    lhs->w = 0;
+}
+
 void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
-    // this used for transforming light positions back to object space.
+    // this is used for transforming light positions back to object space.
     // w is used as a switch for directional lights, so we need
     // to preserve it.
     const GLfixed* const m = mx->matrix.m;
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
index 0f1f27d..1224a96 100644
--- a/opengl/libagl/state.cpp
+++ b/opengl/libagl/state.cpp
@@ -51,6 +51,9 @@
     //        "GL_OES_point_size_array "              // TODO
     //        "GL_OES_point_sprite "                  // TODO
     "GL_OES_EGL_image "                     // OK
+#ifdef GL_OES_compressed_ETC1_RGB8_texture
+    "GL_OES_compressed_ETC1_RGB8_texture "  // OK
+#endif
     "GL_ARB_texture_compression "           // OK
     "GL_ARB_texture_non_power_of_two "      // OK
     "GL_ANDROID_user_clip_plane "           // OK
@@ -386,6 +389,7 @@
 
 void glGetIntegerv(GLenum pname, GLint *params)
 {
+    int i;
     ogles_context_t* c = ogles_context_t::get();
     switch (pname) {
     case GL_ALIASED_POINT_SIZE_RANGE:
@@ -431,6 +435,10 @@
         params[ 7] = GL_PALETTE8_R5_G6_B5_OES;
         params[ 8] = GL_PALETTE8_RGBA4_OES;
         params[ 9] = GL_PALETTE8_RGB5_A1_OES;
+        i = 10;
+#ifdef GL_OES_compressed_ETC1_RGB8_texture
+        params[i++] = GL_ETC1_RGB8_OES;
+#endif
         break;
     case GL_DEPTH_BITS:
         params[0] = c->rasterizer.state.buffers.depth.format ? 0 : 16;
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index fae89b9..a1a776f 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -24,6 +24,7 @@
 #include "TextureObjectManager.h"
 
 #include <private/ui/android_natives_priv.h>
+#include <ETC1/etc1.h>
 
 #ifdef LIBAGL_USE_GRALLOC_COPYBITS
 #include "copybit.h"
@@ -1081,11 +1082,6 @@
         ogles_error(c, GL_INVALID_ENUM);
         return;
     }
-    if ((internalformat < GL_PALETTE4_RGB8_OES ||
-         internalformat > GL_PALETTE8_RGB5_A1_OES)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
     if (width<0 || height<0 || border!=0) {
         ogles_error(c, GL_INVALID_VALUE);
         return;
@@ -1121,6 +1117,12 @@
         format      = GL_RGBA;
         type        = GL_UNSIGNED_SHORT_5_5_5_1;
         break;
+#ifdef GL_OES_compressed_ETC1_RGB8_texture
+    case GL_ETC1_RGB8_OES:
+        format      = GL_RGB;
+        type        = GL_UNSIGNED_BYTE;
+        break;
+#endif
     default:
         ogles_error(c, GL_INVALID_ENUM);
         return;
@@ -1133,6 +1135,30 @@
 
     int32_t size;
     GGLSurface* surface;
+
+#ifdef GL_OES_compressed_ETC1_RGB8_texture
+    if (internalformat == GL_ETC1_RGB8_OES) {
+        GLsizei compressedSize = etc1_get_encoded_data_size(width, height);
+        if (compressedSize > imageSize) {
+            ogles_error(c, GL_INVALID_VALUE);
+            return;
+        }
+        int error = createTextureSurface(c, &surface, &size,
+                level, format, type, width, height);
+        if (error) {
+            ogles_error(c, error);
+            return;
+        }
+        if (etc1_decode_image(
+                (const etc1_byte*)data,
+                (etc1_byte*)surface->data,
+                width, height, 3, surface->stride*3) != 0) {
+            ogles_error(c, GL_INVALID_OPERATION);
+        }
+        return;
+    }
+#endif
+
     // all mipmap levels are specified at once.
     const int numLevels = level<0 ? -level : 1;
 
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index d36207b..d23b7d0 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -7,6 +7,7 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.IMountService;
+import android.os.MountServiceResultCode;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
@@ -96,11 +97,13 @@
         int errCode = CREATE_FAILED;
         // Create new container
         if ((newCachePath = createSdDir(packageURI, newCacheId, key)) != null) {
+            if (localLOGV) Log.i(TAG, "Created container for " + newCacheId
+                    + " at path : " + newCachePath);
             File resFile = new File(newCachePath, resFileName);
             errCode = COPY_FAILED;
-            if (localLOGV) Log.i(TAG, "Trying to copy " + codePath + " to " + resFile);
             // Copy file from codePath
             if (FileUtils.copyFile(new File(codePath), resFile)) {
+                if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile);
                 errCode = FINALIZE_FAILED;
                 if (finalizeSdDir(newCacheId)) {
                     errCode = PASS;
@@ -115,18 +118,23 @@
                 break;
             case COPY_FAILED:
                 errMsg = "COPY_FAILED";
+                if (localLOGV) Log.i(TAG, "Destroying " + newCacheId +
+                        " at path " + newCachePath + " after " + errMsg);
                 destroySdDir(newCacheId);
                 break;
             case FINALIZE_FAILED:
                 errMsg = "FINALIZE_FAILED";
+                if (localLOGV) Log.i(TAG, "Destroying " + newCacheId +
+                        " at path " + newCachePath + " after " + errMsg);
                 destroySdDir(newCacheId);
                 break;
             default:
                 errMsg = "PASS";
+            if (localLOGV) Log.i(TAG, "Unmounting " + newCacheId +
+                    " at path " + newCachePath + " after " + errMsg);
                 unMountSdDir(newCacheId);
                 break;
         }
-        Log.i(TAG, "Status: " + errMsg);
         if (errCode != PASS) {
             return null;
         }
@@ -143,47 +151,39 @@
         if ((len - (mbLen * 1024 * 1024)) > 0) {
             mbLen++;
         }
-        if (localLOGV) Log.i(TAG, "mbLen="+mbLen);
+        if (localLOGV) Log.i(TAG, "mbLen=" + mbLen);
         String cachePath = null;
         int ownerUid = Process.myUid();
         try {
-            cachePath = mountService.createSecureContainer(containerId,
-                mbLen,
-                "vfat", sdEncKey, ownerUid);
+            int rc = mountService.createSecureContainer(
+                    containerId, mbLen, "vfat", sdEncKey, ownerUid);
+
+            if (rc != MountServiceResultCode.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) {
+                    Log.e(TAG, String.format("Container creation-cleanup failed (%d)", rc));
+                    return null;
+                }
+
+                // XXX: Does this ever actually succeed?
+                rc = mountService.createSecureContainer(
+                        containerId, mbLen, "vfat", sdEncKey, ownerUid);
+                if (rc != MountServiceResultCode.OperationSucceeded) {
+                    Log.e(TAG, String.format("Container creation retry failed (%d)", rc));
+                }
+            }
+
+            cachePath = mountService.getSecureContainerPath(containerId);
             if (localLOGV) Log.i(TAG, "Trying to create secure container for  "
                     + containerId + ", cachePath =" + cachePath);
             return cachePath;
-        } catch(IllegalStateException e) {
-            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
         } catch(RemoteException e) {
-            Log.e(TAG, "MounteService not running?");
+            Log.e(TAG, "MountService not running?");
             return null;
         }
-        // TODO just fail here and let the user delete later on.
-        try {
-            mountService.destroySecureContainer(containerId);
-            if (localLOGV) Log.i(TAG, "Destroying cache for " + containerId
-                    + ", cachePath =" + cachePath);
-        } catch(IllegalStateException e) {
-            Log.e(TAG, "Failed to destroy existing cache: " + e);
-            return null;
-        } catch(RemoteException e) {
-            Log.e(TAG, "MounteService not running?");
-            return null;
-        }
-       try {
-            cachePath = mountService.createSecureContainer(containerId,
-                mbLen,
-                "vfat", sdEncKey, ownerUid);
-            if (localLOGV) Log.i(TAG, "Trying to install again " + containerId
-                   + ", cachePath =" + cachePath);
-            return cachePath;
-        } catch(IllegalStateException e) {
-            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
-        } catch(RemoteException e) {
-            Log.e(TAG, "MounteService not running?");
-        }
-        return null;
     }
 
     private boolean destroySdDir(String containerId) {
@@ -194,7 +194,7 @@
         } catch (IllegalStateException e) {
             Log.i(TAG, "Failed to destroy container : " + containerId);
         } catch(RemoteException e) {
-            Log.e(TAG, "MounteService not running?");
+            Log.e(TAG, "MountService not running?");
         }
         return false;
     }
@@ -206,7 +206,7 @@
         } catch (IllegalStateException e) {
             Log.i(TAG, "Failed to finalize container for pkg : " + containerId);
         } catch(RemoteException e) {
-            Log.e(TAG, "MounteService not running?");
+            Log.e(TAG, "MountService not running?");
         }
         return false;
     }
@@ -218,19 +218,21 @@
         } catch (IllegalStateException e) {
             Log.e(TAG, "Failed to unmount id:  " + containerId + " with exception " + e);
         } catch(RemoteException e) {
-            Log.e(TAG, "MounteService not running?");
+            Log.e(TAG, "MountService not running?");
         }
         return false;
     }
 
     private String mountSdDir(String containerId, String key) {
         try {
-            return getMountService().mountSecureContainer(containerId, key, Process.myUid());
-        } catch (IllegalStateException e) {
-            Log.e(TAG, "Failed to mount id: " +
-                    containerId + " with exception " + e);
+            int rc = getMountService().mountSecureContainer(containerId, key, Process.myUid());
+            if (rc == MountServiceResultCode.OperationSucceeded) {
+                return getMountService().getSecureContainerPath(containerId);
+            } else {
+                Log.e(TAG, String.format("Failed to mount id %s with rc %d ", containerId, rc));
+            }
         } catch(RemoteException e) {
-            Log.e(TAG, "MounteService not running?");
+            Log.e(TAG, "MountService not running?");
         }
         return null;
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 265d138..4080a6a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -17,9 +17,10 @@
 package com.android.providers.settings;
 
 import java.io.FileNotFoundException;
-import java.util.Random;
-import java.security.SecureRandom;
+import java.io.UnsupportedEncodingException;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
 
 import android.backup.BackupManager;
 import android.content.ContentProvider;
@@ -197,6 +198,14 @@
             final String value = c.moveToNext() ? c.getString(0) : null;
             if (value == null) {
                 final SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+                String serial = SystemProperties.get("ro.serialno");
+                if (serial != null) {
+                    try {
+                        random.setSeed(serial.getBytes("UTF-8"));
+                    } catch (UnsupportedEncodingException ignore) {
+                        // stick with default seed
+                    }
+                }
                 final String newAndroidIdValue = Long.toHexString(random.nextLong());
                 Log.d(TAG, "Generated and saved new ANDROID_ID");
                 final ContentValues values = new ContentValues();
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index f67a7ae..b56b13b 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -174,6 +174,11 @@
         packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         packageFilter.addDataScheme("package");
         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);
+        mContext.registerReceiver(broadcastReceiver, sdFilter);
 
         // boot completed
         IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 18a5615..f330651 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -772,16 +772,33 @@
             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
             filter.addDataScheme("package");
             mContext.registerReceiver(this, filter);
+             // Register for events related to sdcard installation.
+            IntentFilter sdFilter = new IntentFilter();
+            sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+            mContext.registerReceiver(this, sdFilter);
         }
         
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (mLock) {
-                Uri data = intent.getData();
-                if (data != null) {
-                    String pkg = data.getSchemeSpecificPart();
-                    removeLocked(pkg);
-                    mBroadcastStats.remove(pkg);
+                String action = intent.getAction();
+                String pkgList[] = null;
+                if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                } else {
+                    Uri data = intent.getData();
+                    if (data != null) {
+                        String pkg = data.getSchemeSpecificPart();
+                        if (pkg != null) {
+                            pkgList = new String[]{pkg};
+                        }
+                    }
+                }
+                if (pkgList != null && (pkgList.length > 0)) {
+                    for (String pkg : pkgList) {
+                        removeLocked(pkg);
+                        mBroadcastStats.remove(pkg);
+                    }
                 }
             }
         }
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index ec7c60b..684f117 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -145,6 +145,11 @@
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
         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);
+        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
     }
 
     @Override
@@ -1070,36 +1075,55 @@
                     }
                 }
             } else {
-                Uri uri = intent.getData();
-                if (uri == null) {
+                boolean added = false;
+                String pkgList[] = null;
+                if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    added = true;
+                } if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    added = false;
+                } else  {
+                    Uri uri = intent.getData();
+                    if (uri == null) {
+                        return;
+                    }
+                    String pkgName = uri.getSchemeSpecificPart();
+                    if (pkgName == null) {
+                        return;
+                    }
+                    pkgList = new String[] { pkgName };
+                    added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+                }
+                if (pkgList == null || pkgList.length == 0) {
                     return;
                 }
-                String pkgName = uri.getSchemeSpecificPart();
-                if (pkgName == null) {
-                    return;
-                }
-                
-                if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                if (added) {
                     synchronized (mAppWidgetIds) {
                         Bundle extras = intent.getExtras();
                         if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
-                            // The package was just upgraded
-                            updateProvidersForPackageLocked(pkgName);
+                            for (String pkgName : pkgList) {
+                                // The package was just upgraded
+                                updateProvidersForPackageLocked(pkgName);
+                            }
                         } else {
                             // The package was just added
-                            addProvidersForPackageLocked(pkgName);
+                            for (String pkgName : pkgList) {
+                                addProvidersForPackageLocked(pkgName);
+                            }
                         }
                         saveStateLocked();
                     }
-                }
-                else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                } else {
                     Bundle extras = intent.getExtras();
                     if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
                         // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
                     } else {
                         synchronized (mAppWidgetIds) {
-                            removeProvidersForPackageLocked(pkgName);
-                            saveStateLocked();
+                            for (String pkgName : pkgList) {
+                                removeProvidersForPackageLocked(pkgName);
+                                saveStateLocked();
+                            }
                         }
                     }
                 }
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 62dcb08..0562c55 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -157,7 +157,6 @@
     final Object mAgentConnectLock = new Object();
     IBackupAgent mConnectedAgent;
     volatile boolean mConnecting;
-    volatile boolean mBackupOrRestoreInProgress = false;
     volatile long mLastBackupPass;
     volatile long mNextBackupPass;
 
@@ -217,7 +216,6 @@
     // Persistently track the need to do a full init
     static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
     HashSet<String> mPendingInits = new HashSet<String>();  // transport names
-    volatile boolean mInitInProgress = false;
 
     // ----- Asynchronous backup/restore handler thread -----
 
@@ -237,9 +235,6 @@
                 IBackupTransport transport = getTransport(mCurrentTransport);
                 if (transport == null) {
                     Log.v(TAG, "Backup requested but no transport available");
-                    synchronized (mQueueLock) {
-                        mBackupOrRestoreInProgress = false;
-                    }
                     mWakelock.release();
                     break;
                 }
@@ -267,9 +262,6 @@
                         (new PerformBackupTask(transport, queue, oldJournal)).run();
                     } else {
                         Log.v(TAG, "Backup requested but nothing pending");
-                        synchronized (mQueueLock) {
-                            mBackupOrRestoreInProgress = false;
-                        }
                         mWakelock.release();
                     }
                 }
@@ -440,13 +432,10 @@
                             // can't really do more than bail here
                         }
                     } else {
-                        // Don't run backups now if we're disabled, not yet
-                        // fully set up, in the middle of a backup already,
-                        // or racing with an initialize pass.
-                        if (mEnabled && mProvisioned
-                                && !mBackupOrRestoreInProgress && !mInitInProgress) {
+                        // Don't run backups now if we're disabled or not yet
+                        // fully set up.
+                        if (mEnabled && mProvisioned) {
                             if (DEBUG) Log.v(TAG, "Running a backup pass");
-                            mBackupOrRestoreInProgress = true;
 
                             // Acquire the wakelock and pass it to the backup thread.  it will
                             // be released once backup concludes.
@@ -455,8 +444,7 @@
                             Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
                             mBackupHandler.sendMessage(msg);
                         } else {
-                            Log.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned
-                                    + " b=" + mBackupOrRestoreInProgress + " i=" + mInitInProgress);
+                            Log.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
                         }
                     }
                 }
@@ -469,7 +457,6 @@
             if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
                 synchronized (mQueueLock) {
                     if (DEBUG) Log.v(TAG, "Running a device init");
-                    mInitInProgress = true;
 
                     // Acquire the wakelock and pass it to the init thread.  it will
                     // be released once init concludes.
@@ -542,6 +529,11 @@
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
         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);
+        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
     }
 
     private void parseLeftoverJournals() {
@@ -678,35 +670,53 @@
         public void onReceive(Context context, Intent intent) {
             if (DEBUG) Log.d(TAG, "Received broadcast " + intent);
 
-            Uri uri = intent.getData();
-            if (uri == null) {
-                return;
-            }
-            String pkgName = uri.getSchemeSpecificPart();
-            if (pkgName == null) {
-                return;
-            }
-
             String action = intent.getAction();
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+            boolean replacing = false;
+            boolean added = false;
+            Bundle extras = intent.getExtras();
+            String pkgList[] = null;
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
+                    Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                Uri uri = intent.getData();
+                if (uri == null) {
+                    return;
+                }
+                String pkgName = uri.getSchemeSpecificPart();
+                if (pkgName != null) {
+                    pkgList = new String[] { pkgName };
+                }
+                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+            } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) {
+                added = true;
+                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            } else if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                added = false;
+                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            }
+            if (pkgList == null || pkgList.length == 0) {
+                return;
+            }
+            if (added) {
                 synchronized (mBackupParticipants) {
-                    Bundle extras = intent.getExtras();
-                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
-                        // The package was just upgraded
-                        updatePackageParticipantsLocked(pkgName);
-                    } else {
-                        // The package was just added
-                        addPackageParticipantsLocked(pkgName);
+                    for (String pkgName : pkgList) {
+                        if (replacing) {
+                            // The package was just upgraded
+                            updatePackageParticipantsLocked(pkgName);
+                        } else {
+                            // The package was just added
+                            addPackageParticipantsLocked(pkgName);
+                        }
                     }
                 }
-            }
-            else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                Bundle extras = intent.getExtras();
-                if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+            } else {
+                if (replacing) {
                     // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
                 } else {
                     synchronized (mBackupParticipants) {
-                        removePackageParticipantsLocked(pkgName);
+                        for (String pkgName : pkgList) {
+                            removePackageParticipantsLocked(pkgName);
+                        }
                     }
                 }
             }
@@ -1133,7 +1143,7 @@
                 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
                     // The backend reports that our dataset has been wiped.  We need to
                     // reset all of our bookkeeping and instead run a new backup pass for
-                    // everything.  This must come after mBackupOrRestoreInProgress is cleared.
+                    // everything.
                     EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
                     resetBackupState(mStateDir);
                 }
@@ -1166,14 +1176,8 @@
                     Log.e(TAG, "Unable to remove backup journal file " + mJournal);
                 }
 
-                // Only once we're entirely finished do we indicate our completion
-                // and release the wakelock
-                synchronized (mQueueLock) {
-                    mBackupOrRestoreInProgress = false;
-                }
-
+                // Only once we're entirely finished do we release the wakelock
                 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
-                    // This must come after mBackupOrRestoreInProgress is cleared.
                     backupNow();
                 }
 
@@ -1635,9 +1639,6 @@
                 }
 
                 // done; we can finally release the wakelock
-                synchronized (mQueueLock) {
-                    mBackupOrRestoreInProgress = false;
-                }
                 mWakelock.release();
             }
         }
@@ -1762,9 +1763,6 @@
                 }
 
                 // Last but not least, release the cpu
-                synchronized (mQueueLock) {
-                    mBackupOrRestoreInProgress = false;
-                }
                 mWakelock.release();
             }
         }
@@ -1826,10 +1824,7 @@
             } catch (Exception e) {
                 Log.e(TAG, "Unexpected error performing init", e);
             } finally {
-                // Done; indicate that we're finished and release the wakelock
-                synchronized (mQueueLock) {
-                    mInitInProgress = false;
-                }
+                // Done; release the wakelock
                 mWakelock.release();
             }
         }
@@ -2220,16 +2215,9 @@
             }
 
             synchronized (mQueueLock) {
-                if (mBackupOrRestoreInProgress) {
-                    Log.e(TAG, "Backup pass in progress, restore aborted");
-                    return -1;
-                }
-
                 for (int i = 0; i < mRestoreSets.length; i++) {
                     if (token == mRestoreSets[i].token) {
                         long oldId = Binder.clearCallingIdentity();
-                        // Suppress backups until the restore operation is finished
-                        mBackupOrRestoreInProgress = true;
                         mWakelock.acquire();
                         Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                         msg.obj = new RestoreParams(mRestoreTransport, observer, token);
@@ -2276,9 +2264,7 @@
         synchronized (mQueueLock) {
             pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
                     + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
-                    + (!mBackupOrRestoreInProgress ? "not " : "") + "in progress / "
-                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init / "
-                    + (!mInitInProgress ? "not " : "") + "initializing");
+                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
             pw.println("Last backup pass: " + mLastBackupPass
                     + " (now = " + System.currentTimeMillis() + ')');
             pw.println("  next scheduled: " + mNextBackupPass);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 405dc2e..dc942a2 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -335,6 +335,22 @@
     class PackageReceiver extends android.content.BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            String pkgList[] = null;
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
+                    Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                Uri uri = intent.getData();
+                String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+                if (pkg != null) {
+                    pkgList = new String[] { pkg };
+                }
+            } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) ||
+                    Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            }
+            if (pkgList == null || pkgList.length == 0) {
+                return;
+            }
             synchronized (mMethodMap) {
                 buildInputMethodListLocked(mMethodList, mMethodMap);
 
@@ -352,38 +368,45 @@
 
                 boolean changed = false;
 
-                Uri uri = intent.getData();
-                String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
-                if (curIm != null && curIm.getPackageName().equals(pkg)) {
-                    ServiceInfo si = null;
-                    try {
-                        si = mContext.getPackageManager().getServiceInfo(
-                                curIm.getComponent(), 0);
-                    } catch (PackageManager.NameNotFoundException ex) {
-                    }
-                    if (si == null) {
-                        // Uh oh, current input method is no longer around!
-                        // Pick another one...
-                        Log.i(TAG, "Current input method removed: " + curInputMethodId);
-                        if (!chooseNewDefaultIME()) {
-                            changed = true;
-                            curIm = null;
-                            curInputMethodId = "";
-                            Log.i(TAG, "Unsetting current input method");
-                            Settings.Secure.putString(mContext.getContentResolver(),
-                                    Settings.Secure.DEFAULT_INPUT_METHOD,
-                                    curInputMethodId);
+                if (curIm != null) {
+                    boolean foundPkg = false;
+                    for (String pkg : pkgList) {
+                        if (curIm.getPackageName().equals(pkg)) {
+                            foundPkg = true;
+                            break;
                         }
                     }
+                    if (foundPkg) {
+                        ServiceInfo si = null;
+                        try {
+                            si = mContext.getPackageManager().getServiceInfo(
+                                    curIm.getComponent(), 0);
+                        } catch (PackageManager.NameNotFoundException ex) {
+                        }
+                        if (si == null) {
+                            // Uh oh, current input method is no longer around!
+                            // Pick another one...
+                            Log.i(TAG, "Current input method removed: " + curInputMethodId);
+                            if (!chooseNewDefaultIME()) {
+                                changed = true;
+                                curIm = null;
+                                curInputMethodId = "";
+                                Log.i(TAG, "Unsetting current input method");
+                                Settings.Secure.putString(mContext.getContentResolver(),
+                                        Settings.Secure.DEFAULT_INPUT_METHOD,
+                                        curInputMethodId);
+                            }
+                        }
 
-                } else if (curIm == null) {
-                    // We currently don't have a default input method... is
-                    // one now available?
-                    changed = chooseNewDefaultIME();
-                }
+                    } else if (curIm == null) {
+                        // We currently don't have a default input method... is
+                        // one now available?
+                        changed = chooseNewDefaultIME();
+                    }
 
-                if (changed) {
-                    updateFromSettingsLocked();
+                    if (changed) {
+                        updateFromSettingsLocked();
+                    }
                 }
             }
         }
@@ -415,13 +438,19 @@
             }
         });
 
+        PackageReceiver mBroadcastReceiver = new PackageReceiver();
         IntentFilter packageFilt = new IntentFilter();
         packageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
         packageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
         packageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
         packageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         packageFilt.addDataScheme("package");
-        mContext.registerReceiver(new PackageReceiver(), packageFilt);
+        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);
+        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
 
         IntentFilter screenOnOffFilt = new IntentFilter();
         screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 1c82c94..3a42b37 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -484,6 +484,8 @@
         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);
+        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
 
         // listen for settings changes
         ContentResolver resolver = mContext.getContentResolver();
@@ -1549,43 +1551,54 @@
             String action = intent.getAction();
 
             if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
-                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
+                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
+                    || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
                 synchronized (mLock) {
-                    int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                    if (uid >= 0) {
-                        ArrayList<Receiver> removedRecs = null;
-                        for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
-                            for (int j=i.size()-1; j>=0; j--) {
-                                UpdateRecord ur = i.get(j);
-                                if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
-                                    if (removedRecs == null) {
-                                        removedRecs = new ArrayList<Receiver>();
-                                    }
-                                    if (!removedRecs.contains(ur.mReceiver)) {
-                                        removedRecs.add(ur.mReceiver);
+                    int uidList[] = null;
+                    if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                        uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
+                    } else {
+                        uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)};
+                    }
+                    if (uidList == null || uidList.length == 0) {
+                        return;
+                    }
+                    for (int uid : uidList) {
+                        if (uid >= 0) {
+                            ArrayList<Receiver> removedRecs = null;
+                            for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
+                                for (int j=i.size()-1; j>=0; j--) {
+                                    UpdateRecord ur = i.get(j);
+                                    if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
+                                        if (removedRecs == null) {
+                                            removedRecs = new ArrayList<Receiver>();
+                                        }
+                                        if (!removedRecs.contains(ur.mReceiver)) {
+                                            removedRecs.add(ur.mReceiver);
+                                        }
                                     }
                                 }
                             }
-                        }
-                        ArrayList<ProximityAlert> removedAlerts = null;
-                        for (ProximityAlert i : mProximityAlerts.values()) {
-                            if (i.mUid == uid) {
-                                if (removedAlerts == null) {
-                                    removedAlerts = new ArrayList<ProximityAlert>();
-                                }
-                                if (!removedAlerts.contains(i)) {
-                                    removedAlerts.add(i);
+                            ArrayList<ProximityAlert> removedAlerts = null;
+                            for (ProximityAlert i : mProximityAlerts.values()) {
+                                if (i.mUid == uid) {
+                                    if (removedAlerts == null) {
+                                        removedAlerts = new ArrayList<ProximityAlert>();
+                                    }
+                                    if (!removedAlerts.contains(i)) {
+                                        removedAlerts.add(i);
+                                    }
                                 }
                             }
-                        }
-                        if (removedRecs != null) {
-                            for (int i=removedRecs.size()-1; i>=0; i--) {
-                                removeUpdatesLocked(removedRecs.get(i));
+                            if (removedRecs != null) {
+                                for (int i=removedRecs.size()-1; i>=0; i--) {
+                                    removeUpdatesLocked(removedRecs.get(i));
+                                }
                             }
-                        }
-                        if (removedAlerts != null) {
-                            for (int i=removedAlerts.size()-1; i>=0; i--) {
-                                removeProximityAlertLocked(removedAlerts.get(i).mIntent);
+                            if (removedAlerts != null) {
+                                for (int i=removedAlerts.size()-1; i>=0; i--) {
+                                    removeProximityAlertLocked(removedAlerts.get(i).mIntent);
+                                }
                             }
                         }
                     }
@@ -1599,7 +1612,7 @@
                     mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
                 }
                 NetworkInfo info =
-                        (NetworkInfo)intent.getExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+                    (NetworkInfo)intent.getExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
 
                 // Notify location providers of current network state
                 synchronized (mLock) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 05cea46..6382646 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,9 +16,6 @@
 
 package com.android.server;
 
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +24,10 @@
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.IMountService;
+import android.os.IMountServiceListener;
+import android.os.MountServiceResultCode;
+import android.os.RemoteException;
+import android.os.IBinder;
 import android.os.Environment;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -36,13 +37,8 @@
 import android.util.Log;
 import java.util.ArrayList;
 
-import android.provider.Settings;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-
 import java.io.File;
 import java.io.FileReader;
-import java.lang.IllegalStateException;
 
 /**
  * MountService implements an to the mount service daemon
@@ -53,6 +49,9 @@
     
     private static final String TAG = "MountService";
 
+    /*
+     * Internal vold volume state constants
+     */
     class VolumeState {
         public static final int Init       = -1;
         public static final int NoMedia    = 0;
@@ -66,73 +65,50 @@
         public static final int SharedMnt  = 8;
     }
 
+    /*
+     * Internal vold response code constants
+     */
     class VoldResponseCode {
+        /*
+         * 100 series - Requestion action was initiated; expect another reply
+         *              before proceeding with a new command.
+         */
         public static final int VolumeListResult               = 110;
         public static final int AsecListResult                 = 111;
 
-        public static final int ShareAvailabilityResult        = 210;
+        /*
+         * 200 series - Requestion action has been successfully completed.
+         */
+        public static final int ShareStatusResult              = 210;
         public static final int AsecPathResult                 = 211;
+        public static final int ShareEnabledResult             = 212;
 
+        /*
+         * 400 series - Command was accepted, but the requested action
+         *              did not take place.
+         */
+        public static final int OpFailedNoMedia                = 401;
+        public static final int OpFailedMediaBlank             = 402;
+        public static final int OpFailedMediaCorrupt           = 403;
+        public static final int OpFailedVolNotMounted          = 404;
+        public static final int OpFailedVolBusy                = 405;
+
+        /*
+         * 600 series - Unsolicited broadcasts.
+         */
         public static final int VolumeStateChange              = 605;
-        public static final int VolumeMountFailedBlank         = 610;
-        public static final int VolumeMountFailedDamaged       = 611;
-        public static final int VolumeMountFailedNoMedia       = 612;
         public static final int ShareAvailabilityChange        = 620;
         public static final int VolumeDiskInserted             = 630;
         public static final int VolumeDiskRemoved              = 631;
         public static final int VolumeBadRemoval               = 632;
     }
 
-
-    /**
-     * Binder context for this service
-     */
-    private Context mContext;
-    
-    /**
-     * connectorr object for communicating with vold
-     */
-    private NativeDaemonConnector mConnector;
-
-    /**
-     * 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 mShowSafeUnmountNotificationWhenUnmounted;
-
-    private boolean mPlaySounds;
-
-    private boolean mMounted;
-
-    private SettingsWatcher mSettingsWatcher;
-    private boolean mAutoStartUms;
-    private boolean mPromptUms;
-    private boolean mUmsActiveNotify;
-
-    private boolean mUmsConnected = false;
-    private boolean mUmsEnabled = false;
-    private boolean mUmsEnabling = false;
-
-    private String  mLegacyState = Environment.MEDIA_REMOVED;
-
-    private PackageManagerService mPms;
+    private Context                               mContext;
+    private NativeDaemonConnector                 mConnector;
+    private String                                mLegacyState = Environment.MEDIA_REMOVED;
+    private PackageManagerService                 mPms;
+    private boolean                               mUmsEnabling;
+    private ArrayList<MountServiceBinderListener> mListeners;
 
     /**
      * Constructs a new MountService instance
@@ -142,7 +118,9 @@
     public MountService(Context context) {
         mContext = context;
 
+        // XXX: This will go away soon in favor of IMountServiceObserver
         mPms = (PackageManagerService) ServiceManager.getService("package");
+
         // Register a BOOT_COMPLETED handler so that we can start
         // our NativeDaemonConnector. We defer the startup so that we don't
         // start processing events before we ought-to
@@ -150,78 +128,9 @@
                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
 
         mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
-        mShowSafeUnmountNotificationWhenUnmounted = false;
-
-        mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
-
-        ContentResolver cr = mContext.getContentResolver();
-        mAutoStartUms = (Settings.Secure.getInt(
-                cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
-        mPromptUms = (Settings.Secure.getInt(
-                cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
-        mUmsActiveNotify = (Settings.Secure.getInt(
-                cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
-
-        mSettingsWatcher = new SettingsWatcher(new Handler());
+        mListeners = new ArrayList<MountServiceBinderListener>();
     }
   
-    private class SettingsWatcher extends ContentObserver {
-        public SettingsWatcher(Handler handler) {
-            super(handler);
-            ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(Settings.System.getUriFor(
-                    Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND), false, this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.MOUNT_UMS_AUTOSTART), false, this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.MOUNT_UMS_PROMPT), false, this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED), false, this);
-        }
-
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-            ContentResolver cr = mContext.getContentResolver();
-
-            boolean newPlayNotificationSounds = (Settings.Secure.getInt(
-                    cr, Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND, 1) == 1);
-
-            boolean newUmsAutostart = (Settings.Secure.getInt(
-                    cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
-
-            if (newUmsAutostart != mAutoStartUms) {
-                mAutoStartUms = newUmsAutostart;
-            }
-
-            boolean newUmsPrompt = (Settings.Secure.getInt(
-                    cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
-
-            if (newUmsPrompt != mPromptUms) {
-                mPromptUms = newUmsAutostart;
-            }
-
-            boolean newUmsNotifyEnabled = (Settings.Secure.getInt(
-                    cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
-
-            if (mUmsEnabled) {
-                if (newUmsNotifyEnabled) {
-                    Intent intent = new Intent();
-                    intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
-                    PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-                    setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
-                            com.android.internal.R.string.usb_storage_stop_notification_message,
-                            com.android.internal.R.drawable.stat_sys_warning,
-                            false, true, pi);
-                } else {
-                    setUsbStorageNotification(0, 0, 0, false, false, null);
-                }
-            }
-            if (newUmsNotifyEnabled != mUmsActiveNotify) {
-                mUmsActiveNotify = newUmsNotifyEnabled;
-            }
-        }
-    }
-
     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -232,8 +141,7 @@
                  * event to trigger MediaScanner
                  */
                 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
-                    notifyMediaMounted(
-                            Environment.getExternalStorageDirectory().getPath(), false);
+                    updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED);
                     return;
                 }
 
@@ -244,15 +152,394 @@
         }
     };
 
-    public void shutdown() {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.SHUTDOWN)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires SHUTDOWN permission");
+    private final class MountServiceBinderListener implements IBinder.DeathRecipient {
+        final IMountServiceListener mListener;
+
+        MountServiceBinderListener(IMountServiceListener listener) {
+            mListener = listener;
+ 
         }
 
-        Log.d(TAG, "Shutting down");
-        String state = Environment.getExternalStorageState();
+        public void binderDied() {
+            Log.d(TAG, "An IMountServiceListener has died!");
+            synchronized(mListeners) {
+                mListeners.remove(this);
+                mListener.asBinder().unlinkToDeath(this, 0);
+            }
+        }
+    }
+
+    int doShareUnshareVolume(String path, String method, boolean enable) {
+        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+
+        // TODO: Add support for multiple share methods
+        if (!method.equals("ums")) {
+            throw new IllegalArgumentException(String.format("Method %s not supported", method));
+        }
+
+        /*
+         * If the volume is mounted and we're enabling then unmount it
+         */
+        String vs = getVolumeState(path);
+        if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
+            mUmsEnabling = enable; // Supress unmounted events
+            unmountVolume(path);
+            mUmsEnabling = false; // Unsupress unmounted events
+        }
+
+        try {
+            mConnector.doCommand(String.format(
+                    "volume %sshare %s %s", (enable ? "" : "un"), path, method));
+        } catch (NativeDaemonConnectorException e) {
+            Log.e(TAG, "Failed to share/unshare", e);
+            return MountServiceResultCode.OperationFailedInternalError;
+        }
+
+        /*
+         * If we disabled UMS then mount the volume
+         */
+        if (!enable) {
+            if (mountVolume(path) != MountServiceResultCode.OperationSucceeded) {
+                Log.e(TAG, String.format(
+                        "Failed to remount %s after disabling share method %s", path, method));
+                /*
+                 * Even though the mount failed, the unshare didn't so don't indicate an error.
+                 * The mountVolume() call will have set the storage state and sent the necessary
+                 * broadcasts.
+                 */
+            }
+        }
+
+        return MountServiceResultCode.OperationSucceeded;
+    }
+
+    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 + "}");
+
+        String oldState = mLegacyState;
+        mLegacyState = state;
+
+        synchronized (mListeners) {
+            for (int i = mListeners.size() -1; i >= 0; i--) {
+                MountServiceBinderListener bl = mListeners.get(i);
+                try {
+                    bl.mListener.onVolumeStateChanged("", path, oldState, state);
+                } catch (RemoteException rex) {
+                    Log.e(TAG, "Listener dead");
+                    mListeners.remove(i);
+                } catch (Exception ex) {
+                    Log.e(TAG, "Listener failed", ex);
+                }
+            }
+        }
+    }
+
+    /**
+     *
+     * Callback from NativeDaemonConnector
+     */
+    public void onDaemonConnected() {
+        /*
+         * Since we'll be calling back into the NativeDaemonConnector,
+         * we need to do our work in a new thread.
+         */
+        new Thread() {
+            public void run() {
+                /**
+                 * Determine media state and UMS detection status
+                 */
+                String path = Environment.getExternalStorageDirectory().getPath();
+                String state = Environment.MEDIA_REMOVED;
+
+                try {
+                    String[] vols = mConnector.doListCommand(
+                        "volume list", VoldResponseCode.VolumeListResult);
+                    for (String volstr : vols) {
+                        String[] tok = volstr.split(" ");
+                        // FMT: <label> <mountpoint> <state>
+                        if (!tok[1].equals(path)) {
+                            Log.w(TAG, String.format(
+                                    "Skipping unknown volume '%s'",tok[1]));
+                            continue;
+                        }
+                        int st = Integer.parseInt(tok[2]);
+                        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));
+                            }
+                        } else if (st == VolumeState.Mounted) {
+                            state = Environment.MEDIA_MOUNTED;
+                            Log.i(TAG, "Media already mounted on daemon connection");
+                        } else if (st == VolumeState.Shared) {
+                            state = Environment.MEDIA_SHARED;
+                            Log.i(TAG, "Media shared on daemon connection");
+                        } else {
+                            throw new Exception(String.format("Unexpected state %d", st));
+                        }
+                    }
+                    if (state != null) {
+                        updatePublicVolumeState(path, state);
+                    }
+                } catch (Exception e) {
+                    Log.e(TAG, "Error processing initial volume state", e);
+                    updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
+                }
+
+                try {
+                    boolean avail = getShareMethodAvailable("ums");
+                    notifyShareAvailabilityChange("ums", avail);
+                } catch (Exception ex) {
+                    Log.w(TAG, "Failed to get share availability");
+                }
+            }
+        }.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.
+             * Format: "NNN Volume <label> <path> state changed
+             * from <old_#> (<old_str>) to <new_#> (<new_str>)"
+             */
+            notifyVolumeStateChange(
+                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
+                            Integer.parseInt(cooked[10]));
+        } else if (code == VoldResponseCode.ShareAvailabilityChange) {
+            // FMT: NNN Share method <method> now <available|unavailable>
+            boolean avail = false;
+            if (cooked[5].equals("available")) {
+                avail = true;
+            }
+            notifyShareAvailabilityChange(cooked[3], avail);
+        } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
+                   (code == VoldResponseCode.VolumeDiskRemoved) ||
+                   (code == VoldResponseCode.VolumeBadRemoval)) {
+            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
+            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
+            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
+            final String label = cooked[2];
+            final String path = cooked[3];
+            int major = -1;
+            int minor = -1;
+
+            try {
+                String devComp = cooked[6].substring(1, cooked[6].length() -1);
+                String[] devTok = devComp.split(":");
+                major = Integer.parseInt(devTok[0]);
+                minor = Integer.parseInt(devTok[1]);
+            } catch (Exception ex) {
+                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) {
+                                Log.w(TAG, String.format("Insertion mount failed (%d)", rc));
+                            }
+                        } catch (Exception ex) {
+                            Log.w(TAG, "Failed to mount media on insertion", ex);
+                        }
+                    }
+                }.start();
+            } else if (code == VoldResponseCode.VolumeDiskRemoved) {
+                /*
+                 * This event gets trumped if we're already in BAD_REMOVAL state
+                 */
+                if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
+                    return true;
+                }
+                /* Send the media unmounted event first */
+                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+                mContext.sendBroadcast(in);
+
+                updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
+                in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
+            } else if (code == VoldResponseCode.VolumeBadRemoval) {
+                /* Send the media unmounted event first */
+                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+                mContext.sendBroadcast(in);
+
+                updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
+                in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
+            } else {
+                Log.e(TAG, String.format("Unknown code {%d}", code));
+            }
+        } else {
+            return false;
+        }
+
+        if (in != null) {
+            mContext.sendBroadcast(in);
+	}
+       return true;
+    }
+
+    void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
+        String vs = getVolumeState(path);
+
+        Intent in = null;
+
+        if (newState == VolumeState.Init) {
+        } else if (newState == VolumeState.NoMedia) {
+            // NoMedia is handled via Disk Remove events
+        } else if (newState == VolumeState.Idle) {
+            /*
+             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
+             * if we're in the process of enabling UMS
+             */
+            if (!vs.equals(
+                    Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
+                            Environment.MEDIA_NOFS) && !vs.equals(
+                                    Environment.MEDIA_UNMOUNTABLE) && !mUmsEnabling) {
+                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+            }
+        } else if (newState == VolumeState.Pending) {
+        } else if (newState == VolumeState.Checking) {
+            updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
+            in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
+        } else if (newState == VolumeState.Mounted) {
+            updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
+            // Update media status on PackageManagerService to mount packages on sdcard
+            mPms.updateExternalMediaStatus(true);
+            in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
+            in.putExtra("read-only", false);
+        } else if (newState == VolumeState.Unmounting) {
+            mPms.updateExternalMediaStatus(false);
+            in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
+        } else if (newState == VolumeState.Formatting) {
+        } else if (newState == VolumeState.Shared) {
+            /* Send the media unmounted event first */
+            updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+            in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+            mContext.sendBroadcast(in);
+
+            updatePublicVolumeState(path, Environment.MEDIA_SHARED);
+            in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
+        } else if (newState == VolumeState.SharedMnt) {
+            Log.e(TAG, "Live shared mounts not supported yet!");
+            return;
+        } else {
+            Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
+        }
+
+        if (in != null) {
+            mContext.sendBroadcast(in);
+        }
+    }
+
+    void notifyShareAvailabilityChange(String method, final boolean avail) {
+        if (!method.equals("ums")) {
+           Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
+           return;
+        }
+
+        synchronized (mListeners) {
+            for (int i = mListeners.size() -1; i >= 0; i--) {
+                MountServiceBinderListener bl = mListeners.get(i);
+                try {
+                    bl.mListener.onShareAvailabilityChanged(method, avail);
+                } catch (RemoteException rex) {
+                    Log.e(TAG, "Listener dead");
+                    mListeners.remove(i);
+                } catch (Exception ex) {
+                    Log.e(TAG, "Listener failed", ex);
+                }
+            }
+        }
+
+        Intent intent;
+        if (avail) {
+            intent = new Intent(Intent.ACTION_UMS_CONNECTED);
+        } else {
+            intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
+        }
+        mContext.sendBroadcast(intent);
+    }
+
+    void validatePermission(String perm) {
+        if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(String.format("Requires %s permission", perm));
+        }
+    }
+
+    /**
+     * Exposed API calls below here
+     */
+
+    public void registerListener(IMountServiceListener listener) {
+        synchronized (mListeners) {
+            MountServiceBinderListener bl = new MountServiceBinderListener(listener);
+            try {
+                listener.asBinder().linkToDeath(bl, 0);
+                mListeners.add(bl);
+            } catch (RemoteException rex) {
+                Log.e(TAG, "Failed to link to listener death");
+            }
+        }
+    }
+
+    public void unregisterListener(IMountServiceListener listener) {
+        synchronized (mListeners) {
+            for(MountServiceBinderListener bl : mListeners) {
+                if (bl.mListener == listener) {
+                    mListeners.remove(mListeners.indexOf(bl));
+                    return;
+                }
+            }
+        }
+    }
+
+    public void shutdown() {
+        validatePermission(android.Manifest.permission.SHUTDOWN);
+
+        Log.i(TAG, "Shutting down");
+
+        String path = Environment.getExternalStorageDirectory().getPath();
+        String state = getVolumeState(path);
 
         if (state.equals(Environment.MEDIA_SHARED)) {
             /*
@@ -262,10 +549,8 @@
              * the UMS host could have dirty FAT cache entries
              * yet to flush.
              */
-            try {
-               setMassStorageEnabled(false);
-            } catch (Exception e) {
-                Log.e(TAG, "ums disable failed", e);
+            if (unshareVolume(path, "ums") != MountServiceResultCode.OperationSucceeded) {
+                Log.e(TAG, "UMS disable on shutdown failed");
             }
         } else if (state.equals(Environment.MEDIA_CHECKING)) {
             /*
@@ -292,86 +577,81 @@
             /*
              * If the media is mounted, then gracefully unmount it.
              */
+            if (unmountVolume(path) != MountServiceResultCode.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 getShareMethodAvailable(String method) {
+        ArrayList<String> rsp = mConnector.doCommand("share status " + method);
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code;
             try {
-                String m = Environment.getExternalStorageDirectory().toString();
-                unmountVolume(m);
-
-                int retries = 12;
-                while (!state.equals(Environment.MEDIA_UNMOUNTED) && (retries-- >=0)) {
-                    try {
-                        Thread.sleep(1000);
-                    } catch (InterruptedException iex) {
-                        Log.e(TAG, "Interrupted while waiting for media", iex);
-                        break;
-                    }
-                    state = Environment.getExternalStorageState();
-                }
-                if (retries == 0) {
-                    Log.e(TAG, "Timed out waiting for media to unmount");
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "external storage unmount failed", e);
+                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;
     }
 
-    /**
-     * @return true if USB mass storage support is enabled.
-     */
-    public boolean getMassStorageEnabled() {
-        return mUmsEnabled;
+    public int shareVolume(String path, String method) {
+        return doShareUnshareVolume(path, method, true);
     }
 
-    /**
-     * Enables or disables USB mass storage support.
-     * 
-     * @param enable  true to enable USB mass storage support
-     */
-    public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
-        }
-        try {
-            String vp = Environment.getExternalStorageDirectory().getPath();
-            String vs = getVolumeState(vp);
+    public int unshareVolume(String path, String method) {
+        return doShareUnshareVolume(path, method, false);
+    }
 
-            mUmsEnabling = enable;
-            if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
-                unmountVolume(vp);
-                mUmsEnabling = false;
-                updateUsbMassStorageNotification(true, 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;
             }
-
-            setShareMethodEnabled(vp, "ums", enable);
-            mUmsEnabled = enable;
-            mUmsEnabling = false;
-            if (!enable) {
-                mountVolume(vp);
-                if (mPromptUms) {
-                    updateUsbMassStorageNotification(false, false);
-                } else {
-                    updateUsbMassStorageNotification(true, 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;
             }
-        } catch (IllegalStateException rex) {
-            Log.e(TAG, "Failed to set ums enable {" + enable + "}");
-            return;
         }
+        Log.e(TAG, "Got an empty response");
+        return false;
     }
-
-    /**
-     * @return true if USB mass storage is connected.
-     */
-    public boolean getMassStorageConnected() {
-        return mUmsConnected;
-    }
-
+    
     /**
      * @return state of the volume at the specified mount point
      */
-    public String getVolumeState(String mountPoint) throws IllegalStateException {
+    public String getVolumeState(String mountPoint) {
         /*
          * XXX: Until we have multiple volume discovery, just hardwire
          * this to /sdcard
@@ -388,755 +668,186 @@
     /**
      * Attempt to mount external media
      */
-    public void mountVolume(String mountPath) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
+    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);
+            }
         }
-        mConnector.doCommand(String.format("mount %s", mountPath));
+
+        return rc;
     }
 
     /**
      * Attempt to unmount external media to prepare for eject
      */
-    public void unmountVolume(String mountPath) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
-        }
-
-        // Set a flag so that when we get the unmounted event, we know
-        // to display the notification
-        mShowSafeUnmountNotificationWhenUnmounted = true;
-
-        mConnector.doCommand(String.format("unmount %s", mountPath));
-    }
-
-    /**
-     * Attempt to format external media
-     */
-    public void formatVolume(String formatPath) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
-        }
-
-        mConnector.doCommand(String.format("format %s", formatPath));
-    }
-
-    boolean getShareAvailable(String method) throws IllegalStateException  {
-        ArrayList<String> rsp = mConnector.doCommand("share_available " + method);
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code = Integer.parseInt(tok[0]);
-            if (code == VoldResponseCode.ShareAvailabilityResult) {
-                if (tok[2].equals("available"))
-                    return true;
-                return false;
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
-    }
-
-    /**
-     * Enables or disables USB mass storage support.
-     * 
-     * @param enable  true to enable USB mass storage support
-     */
-    void setShareMethodEnabled(String mountPoint, String method,
-                               boolean enable) throws IllegalStateException {
-        mConnector.doCommand(String.format(
-                "%sshare %s %s", (enable ? "" : "un"), mountPoint, method));
-    }
-
-
-    /**
-     * Returns true if we're playing media notification sounds.
-     */
-    public boolean getPlayNotificationSounds() {
-        return mPlaySounds;
-    }
-
-    /**
-     * Set whether or not we're playing media notification sounds.
-     */
-    public void setPlayNotificationSounds(boolean enabled) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SETTINGS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires WRITE_SETTINGS permission");
-        }
-        mPlaySounds = enabled;
-        SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
-    }
-
-    void updatePublicVolumeState(String mountPoint, String state) {
-        if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
-            Log.w(TAG, "Multiple volumes not currently supported");
-            return;
-        }
-        Log.i(TAG, "State for {" + mountPoint + "} = {" + state + "}");
-        mLegacyState = state;
-    }
-
-    /**
-     * Update the state of the USB mass storage notification
-     */
-    void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
+    public int unmountVolume(String path) {
+        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
         try {
-
-            if (getMassStorageConnected() && !suppressIfConnected) {
-                Intent intent = new Intent();
-                intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-                setUsbStorageNotification(
-                        com.android.internal.R.string.usb_storage_notification_title,
-                        com.android.internal.R.string.usb_storage_notification_message,
-                        com.android.internal.R.drawable.stat_sys_data_usb,
-                        sound, true, pi);
+            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 {
-                setUsbStorageNotification(0, 0, 0, false, false, null);
+                return MountServiceResultCode.OperationFailedInternalError;
             }
-        } catch (IllegalStateException e) {
-            // Nothing to do
-        }
-    }
-
-    void handlePossibleExplicitUnmountBroadcast(String path) {
-        if (mMounted) {
-            mMounted = false;
-            // Update media status on PackageManagerService to unmount packages on sdcard
-            mPms.updateExternalMediaStatus(false);
-            Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
-                    Uri.parse("file://" + path));
-            mContext.sendBroadcast(intent);
         }
     }
 
     /**
+     * Synchronously formats a volume
      *
-     * Callback from NativeDaemonConnector
+     * @param path The volume path to format
+     * @return Error code from MountServiceResultCode
      */
-    public void onDaemonConnected() {
-        /*
-         * Since we'll be calling back into the NativeDaemonConnector,
-         * we need to do our work in a new thread.
-         */
-        new Thread() {
-            public void run() {
-                /**
-                 * Determine media state and UMS detection status
-                 */
-                String path = Environment.getExternalStorageDirectory().getPath();
-                String state = Environment.MEDIA_REMOVED;
+    public int formatVolume(String path) {
+        validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
 
-                try {
-                    String[] vols = mConnector.doListCommand(
-                        "list_volumes", VoldResponseCode.VolumeListResult);
-                    for (String volstr : vols) {
-                        String[] tok = volstr.split(" ");
-                        // FMT: <label> <mountpoint> <state>
-                        if (!tok[1].equals(path)) {
-                            Log.w(TAG, String.format(
-                                    "Skipping unknown volume '%s'",tok[1]));
-                            continue;
-                        }
-                        int st = Integer.parseInt(tok[2]);
-                        if (st == VolumeState.NoMedia) {
-                            state = Environment.MEDIA_REMOVED;
-                        } else if (st == VolumeState.Idle) {
-                            state = null;
-                            try {
-                                mountVolume(path);
-                            } catch (Exception ex) {
-                                Log.e(TAG, "Connection-mount failed", ex);
-                            }
-                        } else if (st == VolumeState.Mounted) {
-                            state = Environment.MEDIA_MOUNTED;
-                            Log.i(TAG, "Media already mounted on daemon connection");
-                        } else if (st == VolumeState.Shared) {
-                            state = Environment.MEDIA_SHARED;
-                            Log.i(TAG, "Media shared on daemon connection");
-                        } else {
-                            throw new Exception(String.format("Unexpected state %d", st));
-                        }
-                    }
-                    if (state != null) {
-                        updatePublicVolumeState(path, state);
-                    }
-                } catch (Exception e) {
-                    Log.e(TAG, "Error processing initial volume state", e);
-                    updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
-                }
-
-                try {
-                    boolean avail = getShareAvailable("ums");
-                    notifyShareAvailabilityChange("ums", avail);
-                } catch (Exception ex) {
-                    Log.w(TAG, "Failed to get share availability");
-                }
-            }
-        }.start();
-    }
-
-    /**
-     *
-     * Callback from NativeDaemonConnector
-     */
-    public boolean onEvent(int code, String raw, String[] cooked) {
-        // Log.d(TAG, "event {" + raw + "}");
-        if (code == VoldResponseCode.VolumeStateChange) {
-            // FMT: NNN Volume <label> <mountpoint> state changed
-            // from <old_#> (<old_str>) to <new_#> (<new_str>)
-            notifyVolumeStateChange(
-                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
-                            Integer.parseInt(cooked[10]));
-        } else if (code == VoldResponseCode.VolumeMountFailedBlank) {
-            // FMT: NNN Volume <label> <mountpoint> mount failed - no supported file-systems
-            notifyMediaNoFs(cooked[3]);
-            // FMT: NNN Volume <label> <mountpoint> mount failed - no media
-        } else if (code == VoldResponseCode.VolumeMountFailedNoMedia) {
-            notifyMediaRemoved(cooked[3]);
-        } else if (code == VoldResponseCode.VolumeMountFailedDamaged) {
-            // FMT: NNN Volume <label> <mountpoint> mount failed - filesystem check failed
-            notifyMediaUnmountable(cooked[3]);
-        } else if (code == VoldResponseCode.ShareAvailabilityChange) {
-            // FMT: NNN Share method <method> now <available|unavailable>
-            boolean avail = false;
-            if (cooked[5].equals("available")) {
-                avail = true;
-            }
-            notifyShareAvailabilityChange(cooked[3], avail);
-        } else if (code == VoldResponseCode.VolumeDiskInserted) {
-            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
-            notifyMediaInserted(cooked[3]);
-        } else if (code == VoldResponseCode.VolumeDiskRemoved) {
-            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
-            notifyMediaRemoved(cooked[3]);
-        } else if (code == VoldResponseCode.VolumeBadRemoval) {
-            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
-            notifyMediaBadRemoval(cooked[3]);
-        } else {
-            return false;
-        }
-       return true;
-    }
-
-    void notifyVolumeStateChange(String label, String mountPoint, int oldState,
-                                 int newState) throws IllegalStateException {
-        String vs = getVolumeState(mountPoint);
-
-        if (newState == VolumeState.Init) {
-        } else if (newState == VolumeState.NoMedia) {
-            // NoMedia is handled via Disk Remove events
-        } else if (newState == VolumeState.Idle) {
-            /*
-             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
-             * if we're in the process of enabling UMS
-             */
-            if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
-                !vs.equals(Environment.MEDIA_NOFS) &&
-                !vs.equals(Environment.MEDIA_UNMOUNTABLE) &&
-                !mUmsEnabling) {
-                notifyMediaUnmounted(mountPoint);
-            }
-        } else if (newState == VolumeState.Pending) {
-        } else if (newState == VolumeState.Checking) {
-            notifyMediaChecking(mountPoint);
-        } else if (newState == VolumeState.Mounted) {
-            notifyMediaMounted(mountPoint, false);
-        } else if (newState == VolumeState.Unmounting) {
-            notifyMediaUnmounting(mountPoint);
-        } else if (newState == VolumeState.Formatting) {
-        } else if (newState == VolumeState.Shared) {
-            notifyMediaShared(mountPoint, false);
-        } else if (newState == VolumeState.SharedMnt) {
-            notifyMediaShared(mountPoint, true);
-        } else {
-            Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
-        }
-    }
-
-
-    /**
-     * Broadcasts the USB mass storage connected event to all clients.
-     */
-    void notifyUmsConnected() {
-        mUmsConnected = true;
-
-        String storageState = Environment.getExternalStorageState();
-        if (!storageState.equals(Environment.MEDIA_REMOVED) &&
-            !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
-            !storageState.equals(Environment.MEDIA_CHECKING)) {
-
-            if (mAutoStartUms) {
-                try {
-                    setMassStorageEnabled(true);
-                } catch (IllegalStateException e) {
-                }
-            } else if (mPromptUms) {
-                updateUsbMassStorageNotification(false, true);
-            }
-        }
-
-        Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
-        mContext.sendBroadcast(intent);
-    }
-
-    void notifyShareAvailabilityChange(String method, final boolean avail) {
-        if (!method.equals("ums")) {
-           Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
-           return;
-        }
-
-        /*
-         * Notification needs to run in a different thread as
-         * it may need to call back into vold
-         */
-        new Thread() {
-            public void run() {
-                try {
-                    if (avail) {
-                        notifyUmsConnected();
-                    } else {
-                        notifyUmsDisconnected();
-                    }
-                } catch (Exception ex) {
-                    Log.w(TAG, "Failed to mount media on insertion");
-                }
-            }
-        }.start();
-    }
-
-    /**
-     * Broadcasts the USB mass storage disconnected event to all clients.
-     */
-    void notifyUmsDisconnected() {
-        mUmsConnected = false;
-        if (mUmsEnabled) {
-            try {
-                Log.w(TAG, "UMS disconnected while enabled!");
-                setMassStorageEnabled(false);
-            } catch (Exception ex) {
-                Log.e(TAG, "Error disabling UMS on unsafe UMS disconnect", ex);
-            }
-        }
-        updateUsbMassStorageNotification(false, false);
-        Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
-        mContext.sendBroadcast(intent);
-    }
-
-    void notifyMediaInserted(final String path) throws IllegalStateException {
-        new Thread() {
-            public void run() {
-                try {
-                    mountVolume(path);
-                } catch (Exception ex) {
-                    Log.w(TAG, "Failed to mount media on insertion", ex);
-                }
-            }
-        }.start();
-    }
-
-    /**
-     * Broadcasts the media removed event to all clients.
-     */
-    void notifyMediaRemoved(String path) throws IllegalStateException {
-
-        // Suppress this on bad removal
-        if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
-            return;
-        }
-
-        updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
-
-        updateUsbMassStorageNotification(true, false);
-
-        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);
-        handlePossibleExplicitUnmountBroadcast(path);
-
-        Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media unmounted event to all clients.
-     */
-    void notifyMediaUnmounted(String path) {
-
-        updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
-
-        // Update media status on PackageManagerService to unmount packages on sdcard
-        mPms.updateExternalMediaStatus(false);
-        if (mShowSafeUnmountNotificationWhenUnmounted) {
-            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);
-            mShowSafeUnmountNotificationWhenUnmounted = false;
-        } else {
-            setMediaStorageNotification(0, 0, 0, false, false, null);
-        }
-        updateUsbMassStorageNotification(false, false);
-
-        Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media checking event to all clients.
-     */
-    void notifyMediaChecking(String path) {
-        updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
-
-        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(true, false);
-        Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media nofs event to all clients.
-     */
-    void notifyMediaNoFs(String path) {
-        updatePublicVolumeState(path, Environment.MEDIA_NOFS);
-        
-        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(false, false);
-        intent = new Intent(Intent.ACTION_MEDIA_NOFS, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media mounted event to all clients.
-     */
-    void notifyMediaMounted(String path, boolean readOnly) {
-        updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
-
-        // Update media status on PackageManagerService to mount packages on sdcard
-        mPms.updateExternalMediaStatus(true);
-        setMediaStorageNotification(0, 0, 0, false, false, null);
-        updateUsbMassStorageNotification(false, false);
-        Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
-                Uri.parse("file://" + path));
-        intent.putExtra("read-only", readOnly);
-        mMounted = true;
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media shared event to all clients.
-     */
-    void notifyMediaShared(String path, boolean mounted) {
-        if (mounted) {
-            Log.e(TAG, "Live shared mounts not supported yet!");
-            return;
-        }
-
-        updatePublicVolumeState(path, Environment.MEDIA_SHARED);
-
-        if (mUmsActiveNotify) {
-            Intent intent = new Intent();
-            intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
-            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-            setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
-                    com.android.internal.R.string.usb_storage_stop_notification_message,
-                    com.android.internal.R.drawable.stat_sys_warning,
-                    false, true, pi);
-        }
-        handlePossibleExplicitUnmountBroadcast(path);
-        Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED,
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media bad removal event to all clients.
-     */
-    void notifyMediaBadRemoval(String path) {
-        updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
-
-        updateUsbMassStorageNotification(true, false);
-        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);
-
-        handlePossibleExplicitUnmountBroadcast(path);
-        Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media unmountable event to all clients.
-     */
-    void notifyMediaUnmountable(String path) {
-        updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
-
-        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(false, false);
-
-        handlePossibleExplicitUnmountBroadcast(path);
-
-        intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-    
-    /**
-     * Broadcasts the media eject event to all clients.
-     */
-    void notifyMediaUnmounting(String path) {
-        Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-    
-    /**
-     * 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 && mPlaySounds) {
-                mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+        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 {
-                mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+                return MountServiceResultCode.OperationFailedInternalError;
             }
-                
-            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;
-            }
-
-            if (mPlaySounds) {
-                mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
-            } else {
-                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);
+    public String[] getSecureContainerList() {
+        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        try {
+            return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
+        } catch (NativeDaemonConnectorException e) {
+            return new String[0];
         }
     }
 
-    public String[] getSecureContainerList() throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_ACCESS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_ACCESS permission");
+    public int createSecureContainer(String id, int sizeMb, String fstype,
+                                    String key, int ownerUid) {
+        validatePermission(android.Manifest.permission.ASEC_CREATE);
+
+        int rc = MountServiceResultCode.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;
         }
-        return mConnector.doListCommand("list_asec", VoldResponseCode.AsecListResult);
+        return rc;
     }
 
-    public String createSecureContainer(String id, int sizeMb, String fstype,
-                                    String key, int ownerUid) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_CREATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_CREATE permission");
+    public int finalizeSecureContainer(String id) {
+        validatePermission(android.Manifest.permission.ASEC_CREATE);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        try {
+            mConnector.doCommand(String.format("asec finalize %s", id));
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        String cmd = String.format("create_asec %s %d %s %s %d",
-                                   id, sizeMb, fstype, key, ownerUid);
-        mConnector.doCommand(cmd);
-        return getSecureContainerPath(id);
+        return rc;
     }
 
-    public void finalizeSecureContainer(String id) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_CREATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_CREATE permission");
-        }
-        mConnector.doCommand(String.format("finalize_asec %s", id));
-    }
+    public int destroySecureContainer(String id) {
+        validatePermission(android.Manifest.permission.ASEC_DESTROY);
 
-    public void destroySecureContainer(String id) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_DESTROY)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_DESTROY permission");
+        int rc = MountServiceResultCode.OperationSucceeded;
+        try {
+            mConnector.doCommand(String.format("asec destroy %s", id));
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        mConnector.doCommand(String.format("destroy_asec %s", id));
+        return rc;
     }
    
-    public String mountSecureContainer(String id, String key,
-                                       int ownerUid) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_MOUNT_UNMOUNT)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission");
+    public int mountSecureContainer(String id, String key, int ownerUid) {
+        validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
+        try {
+            mConnector.doCommand(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        String cmd = String.format("mount_asec %s %s %d",
-                                   id, key, ownerUid);
-        mConnector.doCommand(cmd);
-        return getSecureContainerPath(id);
+        return rc;
     }
 
-    public void unmountSecureContainer(String id) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_MOUNT_UNMOUNT)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission");
+    public int unmountSecureContainer(String id) {
+        validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        String cmd = String.format("asec unmount %s", id);
+        try {
+            mConnector.doCommand(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        String cmd = String.format("unmount_asec %s", id);
-        mConnector.doCommand(cmd);
+        return rc;
     }
 
-    public void renameSecureContainer(String oldId, String newId) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_RENAME)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_RENAME permission");
+    public int renameSecureContainer(String oldId, String newId) {
+        validatePermission(android.Manifest.permission.ASEC_RENAME);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        String cmd = String.format("asec rename %s %s", oldId, newId);
+        try {
+            mConnector.doCommand(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        String cmd = String.format("rename_asec %s %s", oldId, newId);
-        mConnector.doCommand(cmd);
+        return rc;
     }
 
-    public String getSecureContainerPath(String id) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_ACCESS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_ACCESS permission");
-        }
-        ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
+    public String getSecureContainerPath(String id) {
+        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        ArrayList<String> rsp = mConnector.doCommand("asec path " + id);
 
         for (String line : rsp) {
             String []tok = line.split(" ");
@@ -1144,10 +855,13 @@
             if (code == VoldResponseCode.AsecPathResult) {
                 return tok[1];
             } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+                Log.e(TAG, String.format("Unexpected response code %d", code));
+                return "";
             }
         }
-        throw new IllegalStateException("Got an empty response");
+
+        Log.e(TAG, "Got an empty response");
+        return "";
     }
 }
 
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 92ba5f8..016aa52 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -28,7 +28,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
-import java.lang.IllegalStateException;
 
 import java.util.List;
 import java.util.ArrayList;
@@ -82,12 +81,12 @@
                 listenToSocket();
             } catch (Exception e) {
                 Log.e(TAG, "Error in NativeDaemonConnector", e);
-                SystemClock.sleep(1000);
+                SystemClock.sleep(5000);
             }
         }
     }
 
-    private void listenToSocket() {
+    private void listenToSocket() throws IOException {
        LocalSocket socket = null;
 
         try {
@@ -143,31 +142,27 @@
             }
         } catch (IOException ex) {
             Log.e(TAG, "Communications error", ex);
-        }
-
-        synchronized (this) {
-            if (mOutputStream != null) {
-                try {
-                    mOutputStream.close();
-                } catch (IOException e) {
-                    Log.w(TAG, "Failed closing output stream", e);
+            throw ex;
+        } finally {
+            synchronized (this) {
+                if (mOutputStream != null) {
+                    try {
+                        mOutputStream.close();
+                    } catch (IOException e) {
+                        Log.w(TAG, "Failed closing output stream", e);
+                    }
+                    mOutputStream = null;
                 }
+            }
 
-                mOutputStream = null;
+            try {
+                if (socket != null) {
+                    socket.close();
+                }
+            } catch (IOException ex) {
+                Log.w(TAG, "Failed closing socket", ex);
             }
         }
-
-        try {
-            if (socket != null) {
-                socket.close();
-            }
-        } catch (IOException ex) {
-            Log.w(TAG, "Failed closing socket", ex);
-        }
-
-        Log.e(TAG, "Failed to connect to native daemon",
-                new IllegalStateException());
-        SystemClock.sleep(5000);
     }
 
     private void sendCommand(String command) {
@@ -204,7 +199,8 @@
     /**
      * Issue a command to the native daemon and return the responses
      */
-    public synchronized ArrayList<String> doCommand(String cmd) throws IllegalStateException {
+    public synchronized ArrayList<String> doCommand(String cmd)
+            throws NativeDaemonConnectorException  {
         sendCommand(cmd);
 
         ArrayList<String> response = new ArrayList<String>();
@@ -214,12 +210,12 @@
         while (!complete) {
             try {
                 String line = mResponseQueue.take();
-//                Log.d(TAG, "Removed off queue -> " + line);
+                Log.d(TAG, String.format("RSP -> {%s}", line));
                 String[] tokens = line.split(" ");
                 try {
                     code = Integer.parseInt(tokens[0]);
                 } catch (NumberFormatException nfe) {
-                    throw new IllegalStateException(
+                    throw new NativeDaemonConnectorException(
                             String.format("Invalid response from daemon (%s)", line));
                 }
 
@@ -233,7 +229,7 @@
 
         if (code >= ResponseCode.FailedRangeStart &&
                 code <= ResponseCode.FailedRangeEnd) {
-            throw new IllegalStateException(String.format(
+            throw new NativeDaemonConnectorException(code, String.format(
                                                "Command %s failed with code %d",
                                                 cmd, code));
         }
@@ -244,7 +240,7 @@
      * Issues a list command and returns the cooked list
      */
     public String[] doListCommand(String cmd, int expectedResponseCode)
-            throws IllegalStateException {
+            throws NativeDaemonConnectorException {
 
         ArrayList<String> rsp = doCommand(cmd);
         String[] rdata = new String[rsp.size()-1];
@@ -259,14 +255,15 @@
                 } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
                     return rdata;
                 } else {
-                    throw new IllegalStateException(
+                    throw new NativeDaemonConnectorException(
                             String.format("Expected list response %d, but got %d",
                                     expectedResponseCode, code));
                 }
             } catch (NumberFormatException nfe) {
-                throw new IllegalStateException(String.format("Error reading code '%s'", line));
+                throw new NativeDaemonConnectorException(
+                        String.format("Error reading code '%s'", line));
             }
         }
-        throw new IllegalStateException("Got an empty response");
+        throw new NativeDaemonConnectorException("Got an empty response");
     }
 }
diff --git a/services/java/com/android/server/NativeDaemonConnectorException.java b/services/java/com/android/server/NativeDaemonConnectorException.java
new file mode 100644
index 0000000..e60aaf8
--- /dev/null
+++ b/services/java/com/android/server/NativeDaemonConnectorException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+/**
+ * An exception that indicates there was an error with a NativeDaemonConnector operation
+ */
+public class NativeDaemonConnectorException extends RuntimeException
+{
+    private int mCode = -1;
+
+    public NativeDaemonConnectorException() {}
+
+    public NativeDaemonConnectorException(String error)
+    {
+        super(error);
+    }
+
+    public NativeDaemonConnectorException(int code, String error)
+    {
+        super(error);
+        mCode = code;
+    }
+
+    public int getCode() {
+        return mCode;
+    }
+}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index fc89ec8..02a0401 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -329,16 +329,27 @@
                 mUsbConnected = false;
                 updateAdbNotification();
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
-                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
-                Uri uri = intent.getData();
-                if (uri == null) {
-                    return;
+                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
+                    || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                String pkgList[] = null;
+                if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                } else {
+                    Uri uri = intent.getData();
+                    if (uri == null) {
+                        return;
+                    }
+                    String pkgName = uri.getSchemeSpecificPart();
+                    if (pkgName == null) {
+                        return;
+                    }
+                    pkgList = new String[]{pkgName};
                 }
-                String pkgName = uri.getSchemeSpecificPart();
-                if (pkgName == null) {
-                    return;
+                if (pkgList != null && (pkgList.length > 0)) {
+                    for (String pkgName : pkgList) {
+                        cancelAllNotificationsInt(pkgName, 0, 0);
+                    }
                 }
-                cancelAllNotificationsInt(pkgName, 0, 0);
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 mScreenOn = true;
                 updateNotificationPulse();
@@ -429,6 +440,8 @@
         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);
+        mContext.registerReceiver(mIntentReceiver, sdFilter);
 
         SettingsObserver observer = new SettingsObserver(mHandler);
         observer.observe();
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index cd4ae4cc..238403c 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -63,6 +63,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
@@ -73,6 +74,7 @@
 import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.MountServiceResultCode;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.ServiceManager;
@@ -2178,21 +2180,32 @@
             parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
         }
 
+        String codePath = null;
+        String resPath = null;
         if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) {
             if (ps != null && ps.resourcePathString != null) {
-                pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
+                resPath = ps.resourcePathString;
             } else {
                 // Should not happen at all. Just log an error.
                 Log.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
             }
         } else {
-            pkg.applicationInfo.publicSourceDir = pkg.mScanPath;
+            resPath = pkg.mScanPath;
         }
-        pkg.applicationInfo.sourceDir = pkg.mScanPath;
+        codePath = pkg.mScanPath;
+        // Set application objects path explicitly.
+        setApplicationInfoPaths(pkg, codePath, resPath);
         // Note that we invoke the following method only if we are about to unpack an application
         return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
     }
 
+    private static void setApplicationInfoPaths(PackageParser.Package pkg,
+            String destCodePath, String destResPath) {
+        pkg.mPath = pkg.mScanPath = destCodePath;
+        pkg.applicationInfo.sourceDir = destCodePath;
+        pkg.applicationInfo.publicSourceDir = destResPath;
+    }
+
     private static String fixProcessName(String defProcessName,
             String processName, int uid) {
         if (processName == null) {
@@ -4024,7 +4037,7 @@
                     if (res.removedInfo.args != null) {
                         // Remove the replaced package's older resources safely now
                         synchronized (mInstallLock) {
-                            res.removedInfo.args.cleanUpResourcesLI();
+                            res.removedInfo.args.doPostDeleteLI(true);
                         }
                     }
                 }
@@ -4050,13 +4063,14 @@
 
         abstract void createCopyFile();
         abstract int copyApk(IMediaContainerService imcs);
-        abstract void doPreInstall(int status);
+        abstract int doPreInstall(int status);
         abstract boolean doRename(int status, String pkgName, String oldCodePath);
-        abstract void doPostInstall(int status);
+        abstract int doPostInstall(int status);
         abstract String getCodePath();
         abstract String getResourcePath();
         // Need installer lock especially for dex file removal.
         abstract void cleanUpResourcesLI();
+        abstract boolean doPostDeleteLI(boolean delete);
     }
 
     class FileInstallArgs extends InstallArgs {
@@ -4113,10 +4127,11 @@
             return ret;
         }
 
-        void doPreInstall(int status) {
+        int doPreInstall(int status) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
             }
+            return status;
         }
 
         boolean doRename(int status, final String pkgName, String oldCodePath) {
@@ -4143,10 +4158,11 @@
             }
         }
 
-        void doPostInstall(int status) {
+        int doPostInstall(int status) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
             }
+            return status;
         }
 
         String getResourcePath() {
@@ -4219,6 +4235,11 @@
             }
             return true;
         }
+
+        boolean doPostDeleteLI(boolean delete) {
+            cleanUpResourcesLI();
+            return true;
+        }
     }
 
     class SdInstallArgs extends InstallArgs {
@@ -4233,7 +4254,7 @@
         }
 
         SdInstallArgs(String fullCodePath, String fullResourcePath) {
-            super(null, null, 0, null);
+            super(null, null, ApplicationInfo.FLAG_ON_SDCARD, null);
             // Extract cid from fullCodePath
             int eidx = fullCodePath.lastIndexOf("/");
             String subStr1 = fullCodePath.substring(0, eidx);
@@ -4242,6 +4263,11 @@
             cachePath = subStr1;
         }
 
+        SdInstallArgs(String cid) {
+            super(null, null,  ApplicationInfo.FLAG_ON_SDCARD, null);
+            this.cid = cid;
+        }
+
         void createCopyFile() {
             cid = getTempContainerId();
         }
@@ -4253,16 +4279,8 @@
                         getEncryptKey(), RES_FILE_NAME);
             } catch (RemoteException e) {
             }
-
-            if (cachePath != null) {
-                // Mount container once its created with system_uid
-                cachePath = mountSdDir(cid, Process.SYSTEM_UID);
-            }
-            if (cachePath == null) {
-                return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
-            } else {
-                return PackageManager.INSTALL_SUCCEEDED;
-            }
+            return (cachePath == null) ? PackageManager.INSTALL_FAILED_CONTAINER_ERROR :
+                PackageManager.INSTALL_SUCCEEDED;
         }
 
         @Override
@@ -4275,25 +4293,81 @@
             return cachePath + "/" + RES_FILE_NAME;
         }
 
-        void doPreInstall(int status) {
+        int doPreInstall(int status) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 // Destroy container
                 destroySdDir(cid);
+            } else {
+                // STOPSHIP Remove once new api is added in MountService
+                //boolean mounted = isContainerMounted(cid);
+                boolean mounted = false;
+                if (!mounted) {
+                    cachePath = mountSdDir(cid, Process.SYSTEM_UID);
+                    if (cachePath == null) {
+                        return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+                    }
+                }
             }
+            return status;
         }
 
         boolean doRename(int status, final String pkgName,
                 String oldCodePath) {
             String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME);
+            String newCachePath = null;
+            /*final int RENAME_FAILED = 1;
+            final int MOUNT_FAILED = 2;
+            final int DESTROY_FAILED = 3;
+            final int PASS = 4;
+            int errCode = RENAME_FAILED;
+            if (mounted) {
+                // Unmount the container
+                if (!unMountSdDir(cid)) {
+                    Log.i(TAG, "Failed to unmount " + cid + " before renaming");
+                    return false;
+                }
+                mounted = false;
+            }
+            if (renameSdDir(cid, newCacheId)) {
+                errCode = MOUNT_FAILED;
+                if ((newCachePath = mountSdDir(newCacheId, Process.SYSTEM_UID)) != null) {
+                    errCode = PASS;
+                }
+            }
+            String errMsg = "";
+            switch (errCode) {
+                case RENAME_FAILED:
+                    errMsg = "RENAME_FAILED";
+                    break;
+                case MOUNT_FAILED:
+                    errMsg = "MOUNT_FAILED";
+                    break;
+                case DESTROY_FAILED:
+                    errMsg = "DESTROY_FAILED";
+                    break;
+                default:
+                    errMsg = "PASS";
+                break;
+            }
+            Log.i(TAG, "Status: " + errMsg);
+            if (errCode != PASS) {
+                return false;
+            }
+            Log.i(TAG, "Succesfully renamed " + cid + " to " +newCacheId +
+                    " at path: " + cachePath + " to new path: " + newCachePath);
+            cid = newCacheId;
+            cachePath = newCachePath;
+            return true;
+            */
             // STOPSHIP TEMPORARY HACK FOR RENAME
             // Create new container at newCachePath
             String codePath = getCodePath();
-            String newCachePath = null;
             final int CREATE_FAILED = 1;
             final int COPY_FAILED = 3;
             final int FINALIZE_FAILED = 5;
             final int PASS = 7;
             int errCode = CREATE_FAILED;
+
             if ((newCachePath = createSdDir(new File(codePath), newCacheId)) != null) {
                 errCode = COPY_FAILED;
                 // Copy file from codePath
@@ -4334,13 +4408,18 @@
             return true;
         }
 
-        void doPostInstall(int status) {
+        int doPostInstall(int status) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
             } else {
-                // Unmount container
-                // Rename and remount based on package name and new uid
+                // STOP SHIP Change this once new api is added.
+                //boolean mounted = isContainerMounted(cid);
+                boolean mounted = false;
+                if (!mounted) {
+                    mountSdDir(cid, Process.SYSTEM_UID);
+                }
             }
+            return status;
         }
 
         private void cleanUp() {
@@ -4362,32 +4441,66 @@
             }
             cleanUp();
         }
+
+        boolean matchContainer(String app) {
+            if (cid.startsWith(app)) {
+                return true;
+            }
+            return false;
+        }
+
+        String getPackageName() {
+            int idx = cid.lastIndexOf("-");
+            if (idx == -1) {
+                return cid;
+            }
+            return cid.substring(0, idx);
+        }
+
+        boolean doPostDeleteLI(boolean delete) {
+            boolean ret = false;
+            boolean mounted = isContainerMounted(cid);
+            if (mounted) {
+                // Unmount first
+                ret = unMountSdDir(cid);
+            }
+            if (ret && delete) {
+                cleanUpResourcesLI();
+            }
+            return ret;
+        }
     };
 
     // Utility method used to create code paths based on package name and available index.
     private static String getNextCodePath(String oldCodePath, String prefix, String suffix) {
         String idxStr = "";
         int idx = 1;
+        // Fall back to default value of idx=1 if prefix is not
+        // part of oldCodePath
         if (oldCodePath != null) {
             String subStr = oldCodePath;
-            if (subStr.startsWith(prefix)) {
-                subStr = subStr.substring(prefix.length());
-            }
+            // Drop the suffix right away
             if (subStr.endsWith(suffix)) {
                 subStr = subStr.substring(0, subStr.length() - suffix.length());
             }
-            if (subStr != null) {
-                if (subStr.startsWith("-")) {
-                    subStr = subStr.substring(1);
-                }
-                try {
-                    idx = Integer.parseInt(subStr);
-                    if (idx <= 1) {
-                        idx++;
-                    } else {
-                        idx--;
+            // If oldCodePath already contains prefix find out the
+            // ending index to either increment or decrement.
+            int sidx = subStr.lastIndexOf(prefix);
+            if (sidx != -1) {
+                subStr = subStr.substring(sidx + prefix.length());
+                if (subStr != null) {
+                    if (subStr.startsWith("-")) {
+                        subStr = subStr.substring(1);
                     }
-                } catch(NumberFormatException e) {
+                    try {
+                        idx = Integer.parseInt(subStr);
+                        if (idx <= 1) {
+                            idx++;
+                        } else {
+                            idx--;
+                        }
+                    } catch(NumberFormatException e) {
+                    }
                 }
             }
         }
@@ -4752,6 +4865,9 @@
                 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;
                 }
             }
@@ -4760,9 +4876,8 @@
                 res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                 break main_flow;
             }
-            // TODO rename  pkg.mScanPath In scanPackageLI let it just set values based on mScanPath
-            pkg.applicationInfo.sourceDir = pkg.mScanPath= pkg.mPath = args.getCodePath();
-            pkg.applicationInfo.publicSourceDir = args.getResourcePath();
+            // Set application objects path explicitly after the rename
+            setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
             if(replacingExistingPackage) {
                 replacePackageLI(pkg, parseFlags, scanMode,
                         installerPackageName, res);
@@ -4976,9 +5091,9 @@
         }
         // Delete the resources here after sending the broadcast to let
         // other processes clean up before deleting resources.
-        synchronized (mInstallLock) {
-            if (info.args != null) {
-                info.args.cleanUpResourcesLI();
+        if (info.args != null) {
+            synchronized (mInstallLock) {
+                info.args.doPostDeleteLI(deleteCodeAndResources);
             }
         }
         return res;
@@ -6947,6 +7062,17 @@
             }
         }
 
+        private Set<String> findPackagesWithFlag(int flag) {
+            Set<String> ret = new HashSet<String>();
+            for (PackageSetting ps : mPackages.values()) {
+                // Has to match atleast all the flag bits set on flag
+                if ((ps.pkgFlags & flag) == flag) {
+                    ret.add(ps.name);
+                }
+            }
+            return ret;
+        }
+
         private void removeUserIdLP(int uid) {
             if (uid >= FIRST_APPLICATION_UID) {
                 int N = mUserIds.size();
@@ -7940,33 +8066,28 @@
             Log.e(TAG, "Failed to create encryption keys with exception: " + nsae);
             return null;
         }
-        try {
-            cachePath = mountService.createSecureContainer(pkgName,
-                mbLen,
-                "vfat", sdEncKey, Process.SYSTEM_UID);
-            if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install " + pkgName + ", cachePath =" + cachePath);
-            return cachePath;
-        } catch(IllegalStateException e) {
-            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
+
+        int rc = mountService.createSecureContainer(
+                pkgName, mbLen, "vfat", sdEncKey, Process.SYSTEM_UID);
+        if (rc != MountServiceResultCode.OperationSucceeded) {
+            Log.e(TAG, String.format("Failed to create container (%d)", rc));
+
+            rc = mountService.destroySecureContainer(pkgName);
+            if (rc != MountServiceResultCode.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) {
+                Log.e(TAG, String.format("Failed to create container (2nd try) (%d)", rc));
+                return null;
+            }
         }
-        // TODO just fail here and let the user delete later on.
-        try {
-            mountService.destroySecureContainer(pkgName);
-            if (DEBUG_SD_INSTALL) Log.i(TAG, "Destroying cache for " + pkgName + ", cachePath =" + cachePath);
-        } catch(IllegalStateException e) {
-            Log.e(TAG, "Failed to destroy existing cache: " + e);
-            return null;
-        } 
-       try {
-            cachePath = mountService.createSecureContainer(pkgName,
-                mbLen,
-                "vfat", sdEncKey, Process.SYSTEM_UID);
-            if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install again " + pkgName + ", cachePath =" + cachePath);
+
+        cachePath = mountService.getSecureContainerPath(pkgName);
+        if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install " + pkgName + ", cachePath =" + cachePath);
             return cachePath;
-        } catch(IllegalStateException e) {
-            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
-            return null;
-        }
     }
 
    private String mountSdDir(String pkgName, int ownerUid) {
@@ -7975,63 +8096,73 @@
            Log.e(TAG, "Failed to retrieve encryption keys to mount package code: " + pkgName + ".");
            return null;
        }
-       try {
-           return getMountService().mountSecureContainer(pkgName, sdEncKey, ownerUid);
-       } catch (IllegalStateException e) {
-           Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " exception : " + e);
+
+       int rc = getMountService().mountSecureContainer(pkgName, sdEncKey, ownerUid);
+
+       if (rc != MountServiceResultCode.OperationSucceeded) {
+           Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " rc : " + rc);
+           return null;
        }
-       return null;
+
+       return getMountService().getSecureContainerPath(pkgName);
    }
 
    private boolean unMountSdDir(String pkgName) {
        // STOPSHIP unmount directory
+       int rc = getMountService().unmountSecureContainer(pkgName);
+       if (rc != MountServiceResultCode.OperationSucceeded) {
+           Log.e(TAG, "Failed to unmount : " + pkgName + " with rc " + rc);
+           return false;
+       }
+       return true;
+   }
+
+   private boolean renameSdDir(String oldId, String newId) {
        try {
-           getMountService().unmountSecureContainer(pkgName);
+           getMountService().renameSecureContainer(oldId, newId);
            return true;
        } catch (IllegalStateException e) {
-           Log.e(TAG, "Failed to unmount : " + pkgName + " with exception " + e);
+           Log.i(TAG, "Failed ot rename  " + oldId + " to " + newId +
+                   " with exception : " + e);
        }
        return false;
    }
 
    private String getSdDir(String pkgName) {
-       String cachePath = null;
-       try {
-           cachePath = getMountService().getSecureContainerPath(pkgName);
-       } catch (IllegalStateException e) {
-           Log.e(TAG, "Failed to retrieve secure container path for pkg : " + pkgName + " with exception " + e);
-       }
-       return cachePath;
+       return getMountService().getSecureContainerPath(pkgName);
    }
 
-   private boolean finalizeSdDir(String pkgName) {
-       try {
-           getMountService().finalizeSecureContainer(pkgName);
-           return true;
-       } catch (IllegalStateException e) {
-           Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
-           return false;
-       }
-   }
+    private boolean finalizeSdDir(String pkgName) {
+        int rc = getMountService().finalizeSecureContainer(pkgName);
+        if (rc != MountServiceResultCode.OperationSucceeded) {
+            Log.i(TAG, "Failed to finalize container for pkg : " + pkgName);
+            return false;
+        }
+        return true;
+    }
 
-   private boolean destroySdDir(String pkgName) {
-       try {
-           // We need to destroy right away
-           getMountService().destroySecureContainer(pkgName);
-           return true;
-       } catch (IllegalStateException e) {
-           Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
-           return false;
-       }
-   }
+    private boolean destroySdDir(String pkgName) {
+        int rc = getMountService().destroySecureContainer(pkgName);
+        if (rc != MountServiceResultCode.OperationSucceeded) {
+            Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
+            return false;
+        }
+        return true;
+    }
 
-   static String[] getSecureContainerList() {
+    static String[] getSecureContainerList() {
+        String[] list = getMountService().getSecureContainerList();
+        return list.length == 0 ? null : list;
+    }
+
+   static boolean isContainerMounted(String cid) {
+       // STOPSHIP
+       // New api from MountService
        try {
-           return getMountService().getSecureContainerList();
+           return (getMountService().getSecureContainerPath(cid) != null);
        } catch (IllegalStateException e) {
-           Log.i(TAG, "Failed to getSecureContainerList");
        }
-       return null;
+       return false;
    }
 
    static String getTempContainerId() {
@@ -8078,6 +8209,8 @@
    }
 
    public void updateExternalMediaStatus(final boolean mediaStatus) {
+       final boolean DEBUG = true;
+       if (DEBUG) Log.i(TAG, "updateExterMediaStatus::");
        if (mediaStatus == mMediaMounted) {
            return;
        }
@@ -8086,54 +8219,155 @@
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
-               final String list[] = getSecureContainerList();
-               if (list == null || list.length == 0) {
-                   return;
+               updateExternalMediaStatusInner(mediaStatus);
+           }
+       });
+   }
+
+   void updateExternalMediaStatusInner(boolean mediaStatus) {
+       final String list[] = getSecureContainerList();
+       if (list == null || list.length == 0) {
+           return;
+       }
+       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) {
+               SdInstallArgs args = new SdInstallArgs(cid);
+               String removeEntry = null;
+               for (String app : appList) {
+                   if (args.matchContainer(app)) {
+                       removeEntry = app;
+                       break;
+                   }
                }
-               for (int i = 0; i < list.length; i++) {
-                   String mountPkg = list[i];
-                   // TODO compare with default package
-                   synchronized (mPackages) {
-                       PackageSetting ps = mSettings.mPackages.get(mountPkg);
-                       if (ps != null && (ps.pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) != 0) {
-                           if (mediaStatus) {
-                               String pkgPath = getSdDir(mountPkg);
-                               if (pkgPath == null) {
-                                   continue;
-                               }
-                               pkgPath = ps.codePathString;
-                               int parseFlags = PackageParser.PARSE_CHATTY |
-                               PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
-                               PackageParser pp = new PackageParser(pkgPath);
-                               pp.setSeparateProcesses(mSeparateProcesses);
-                               final PackageParser.Package pkg = pp.parsePackage(new File(pkgPath),
-                                       null, mMetrics, parseFlags);
-                               if (pkg == null) {
-                                   Log.w(TAG, "Failed to install package : " + mountPkg + " from sd card");
-                                   continue;
-                               }
-                               int scanMode = SCAN_MONITOR;
-                               // Scan the package
-                               if (scanPackageLI(pkg, parseFlags, scanMode) != null) {
-                                   // Grant permissions
-                                   grantPermissionsLP(pkg, false);
-                                   // Persist settings
-                                   mSettings.writeLP();
-                               } else {
-                                   Log.i(TAG, "Failed to install package: " + mountPkg + " from sdcard");
-                               }
-                           } else {
-                               // Delete package
-                               PackageRemovedInfo outInfo = new PackageRemovedInfo();
-                               boolean res = deletePackageLI(mountPkg, false, PackageManager.DONT_DELETE_DATA, outInfo);
-                               if (!res) {
-                                   Log.e(TAG, "Failed to delete pkg  from sdcard : " + mountPkg);
-                               }
-                           }
-                       }
+               if (removeEntry == null) {
+                   // No matching app on device. Skip entry or may be cleanup?
+                   // Ignore default package
+                   continue;
+               }
+               appList.remove(removeEntry);
+               PackageSetting ps = mSettings.mPackages.get(removeEntry);
+               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++;
                    }
                }
            }
-       });
+       }
+       int uidArr[] = uidList;
+       if ((num > 0) && (num < uidList.length)) {
+           uidArr = new int[num];
+           for (int i = 0; i < num; i++) {
+               uidArr[i] = uidList[i];
+           }
+       }
+       if (mediaStatus) {
+           loadMediaPackages(processCids, uidArr);
+       } else {
+           unloadMediaPackages(processCids, uidArr);
+       }
+   }
+
+   private void sendResourcesChangedBroadcast(boolean mediaStatus,
+           ArrayList<String> pkgList, int uidArr[]) {
+       int size = pkgList.size();
+       if (size > 0) {
+           // Send broadcasts here
+           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;
+           sendPackageBroadcast(action, null, extras);
+       }
+   }
+
+   void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+       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 (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
+               Log.i(TAG, "Failed to install package: " + codePath + " from sdcard");
+               continue;
+           }
+           // Parse package
+           int parseFlags = PackageParser.PARSE_CHATTY |
+           PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+           PackageParser pp = new PackageParser(codePath);
+           pp.setSeparateProcesses(mSeparateProcesses);
+           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");
+               continue;
+           }
+           setApplicationInfoPaths(pkg, codePath, codePath);
+           int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+           synchronized (mInstallLock) {
+               // Scan the package
+               if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
+                   synchronized (mPackages) {
+                       // Grant permissions
+                       grantPermissionsLP(pkg, false);
+                       // Persist settings
+                       mSettings.writeLP();
+                       retCode = PackageManager.INSTALL_SUCCEEDED;
+                       pkgList.add(pkg.packageName);
+                   }
+               } else {
+                   Log.i(TAG, "Failed to install package: " + pkg.packageName + " from sdcard");
+               }
+           }
+           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?
+   }
+
+   void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+       ArrayList<String> pkgList = new ArrayList<String>();
+       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
+           // Delete package internally
+           PackageRemovedInfo outInfo = new PackageRemovedInfo();
+           synchronized (mInstallLock) {
+               boolean res = deletePackageLI(pkgName, false,
+                       PackageManager.DONT_DELETE_DATA, outInfo);
+               if (res) {
+                   pkgList.add(pkgName);
+               } else {
+                   Log.e(TAG, "Failed to delete pkg  from sdcard : " + pkgName);
+               }
+           }
+       }
+       // Send broadcasts
+       sendResourcesChangedBroadcast(false, pkgList, uidArr);
+       Runtime.getRuntime().gc();
+       // Do clean up. Just unmount
+       for (SdInstallArgs args : keys) {
+           synchronized (mInstallLock) {
+               args.doPostDeleteLI(false);
+           }
+       }
    }
 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b723dcd..5e6881c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1196,7 +1196,7 @@
                     int uid = msg.arg1;
                     boolean restart = (msg.arg2 == 1);
                     String pkg = (String) msg.obj;
-                    forceStopPackageLocked(pkg, uid, restart);
+                    forceStopPackageLocked(pkg, uid, restart, false);
                 }
             } break;
             }
@@ -4998,7 +4998,7 @@
     }
 
     private void forceStopPackageLocked(final String packageName, int uid) {
-        forceStopPackageLocked(packageName, uid, false);
+        forceStopPackageLocked(packageName, uid, false, false);
         Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
                 Uri.fromParts("package", packageName, null));
         intent.putExtra(Intent.EXTRA_UID, uid);
@@ -5037,9 +5037,9 @@
             removeProcessLocked(procs.get(i), callerWillRestart);
         }
     }
-    
+
     private final void forceStopPackageLocked(String name, int uid,
-            boolean callerWillRestart) {
+            boolean callerWillRestart, boolean purgeCache) {
         int i, N;
 
         if (uid < 0) {
@@ -5091,6 +5091,12 @@
         }
         
         resumeTopActivityLocked(null);
+        if (purgeCache) {
+            AttributeCache ac = AttributeCache.instance();
+            if (ac != null) {
+                ac.removePackage(name);
+            }
+        }
     }
 
     private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart) {
@@ -8054,7 +8060,7 @@
             mDebugTransient = !persistent;
             if (packageName != null) {
                 final long origId = Binder.clearCallingIdentity();
-                forceStopPackageLocked(packageName, -1, false);
+                forceStopPackageLocked(packageName, -1, false, false);
                 Binder.restoreCallingIdentity(origId);
             }
         }
@@ -11935,6 +11941,7 @@
                 intent.getAction());
         if (intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
                 || intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
+                || Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())
                 || uidRemoved) {
             if (checkComponentPermission(
                     android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
@@ -11951,15 +11958,22 @@
                         }
                     }
                 } else {
-                    Uri data = intent.getData();
-                    String ssp;
-                    if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
-                        if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
-                            forceStopPackageLocked(ssp,
-                                    intent.getIntExtra(Intent.EXTRA_UID, -1), false);
-                            AttributeCache ac = AttributeCache.instance();
-                            if (ac != null) {
-                                ac.removePackage(ssp);
+                    // 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())) {
+                        String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                        if (list != null && (list.length > 0)) {
+                            for (String pkg : list) {
+                                forceStopPackageLocked(pkg, -1, false, true);
+                            }
+                        }
+                    } else {
+                        Uri data = intent.getData();
+                        String ssp;
+                        if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
+                            if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
+                                forceStopPackageLocked(ssp,
+                                        intent.getIntExtra(Intent.EXTRA_UID, -1), false, true);
                             }
                         }
                     }
@@ -12121,25 +12135,32 @@
             // installed.  Maybe in the future we want to have a special install
             // broadcast or such for apps, but we'd like to deliberately make
             // this decision.
-            boolean skip = false;
-            if (intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
-                skip = true;
-            } else if (intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())) {
-                skip = true;
-            } else if (intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
-                skip = true;
+            String skipPackages[] = null;
+            if (intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
+                    || intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
+                    || intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
+                Uri data = intent.getData();
+                if (data != null) {
+                    String pkgName = data.getSchemeSpecificPart();
+                    if (pkgName != null) {
+                        skipPackages = new String[] { pkgName };
+                    }
+                }
+            } else if (intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(intent.getAction())) {
+                skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
-            String skipPackage = (skip && intent.getData() != null)
-                    ? intent.getData().getSchemeSpecificPart()
-                    : null;
-            if (skipPackage != null && receivers != null) {
-                int NT = receivers.size();
-                for (int it=0; it<NT; it++) {
-                    ResolveInfo curt = (ResolveInfo)receivers.get(it);
-                    if (curt.activityInfo.packageName.equals(skipPackage)) {
-                        receivers.remove(it);
-                        it--;
-                        NT--;
+            if (skipPackages != null && (skipPackages.length > 0)) {
+                for (String skipPackage : skipPackages) {
+                    if (skipPackage != null) {
+                        int NT = receivers.size();
+                        for (int it=0; it<NT; it++) {
+                            ResolveInfo curt = (ResolveInfo)receivers.get(it);
+                            if (curt.activityInfo.packageName.equals(skipPackage)) {
+                                receivers.remove(it);
+                                it--;
+                                NT--;
+                            }
+                        }
                     }
                 }
             }
@@ -12923,7 +12944,7 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            forceStopPackageLocked(ii.targetPackage, -1, true);
+            forceStopPackageLocked(ii.targetPackage, -1, true, false);
             ProcessRecord app = addAppLocked(ai);
             app.instrumentationClass = className;
             app.instrumentationInfo = ai;
@@ -12978,7 +12999,7 @@
         app.instrumentationProfileFile = null;
         app.instrumentationArguments = null;
 
-        forceStopPackageLocked(app.processName, -1, false);
+        forceStopPackageLocked(app.processName, -1, false, false);
     }
 
     public void finishInstrumentation(IApplicationThread target,
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 2d0f254..7b722a5 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -1807,16 +1807,30 @@
             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
             filter.addDataScheme("package");
             mContext.registerReceiver(this, filter);
+            IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+            mContext.registerReceiver(this, sdFilter);
         }
         
         @Override
         public void onReceive(Context context, Intent intent) {
-            ArrayList<StatusBarNotification> list = null;
-            synchronized (StatusBarService.this) {
+            String pkgList[] = null;
+            if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) {
+                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            } else {
                 Uri data = intent.getData();
                 if (data != null) {
                     String pkg = data.getSchemeSpecificPart();
-                    list = mNotificationData.notificationsForPackage(pkg);
+                    if (pkg != null) {
+                        pkgList = new String[]{pkg};
+                    }
+                }
+            }
+            ArrayList<StatusBarNotification> list = null;
+            if (pkgList != null) {
+                synchronized (StatusBarService.this) {
+                    for (String pkg : pkgList) {
+                        list = mNotificationData.notificationsForPackage(pkg);
+                    }
                 }
             }
             
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 2caccf7..b07a10b 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -11,5 +11,7 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner services
 LOCAL_PACKAGE_NAME := FrameworksServicesTests
 
+LOCAL_CERTIFICATE := platform
+
 include $(BUILD_PACKAGE)
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 9856c6d..5ce109f 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -16,15 +16,10 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.frameworks.servicestests">
-    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.READ_CONTACTS" />
-    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
-    <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
-    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
 
+    <uses-permission android:name="android.permission.READ_LOGS" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/CoreTests/android/core/StringTest.java b/tests/CoreTests/android/core/StringTest.java
deleted file mode 100644
index 957dd40..0000000
--- a/tests/CoreTests/android/core/StringTest.java
+++ /dev/null
@@ -1,149 +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.core;
-
-import com.ibm.icu4jni.text.RuleBasedNumberFormat;
-import com.ibm.icu4jni.text.RuleBasedNumberFormat.RBNFType;
-
-import junit.framework.TestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-/**
- * Some String tests.
- */
-public class StringTest extends TestCase {
-    private String germanSpelloutRule = "%alt-ones:" +
-            "    -x: minus >>;" +
-            "    x.x: << komma >>;" +
-            "    null; eins; =%%main=;" +
-            "%%main:" +
-            "    null; ein; zwei; drei; vier; f\u00fcnf; sechs; sieben; acht; neun;\n" +
-            "    zehn; elf; zwu00f6lf; >>zehn;" +
-            "    20: [>>und]zwanzig;" +
-            "    30: [>>und]dreiu00dfig;" +
-            "    40: [>>und]vierzig;" +
-            "    50: [>>und]fu00fcnfzig;" +
-            "    60: [>>und]sechzig;" +
-            "    70: [>>und]siebzig;" +
-            "    80: [>>und]achtzig;" +
-            "    90: [>>und]neunzig;" +
-            "    100: hundert[>%alt-ones>];" +
-            "    200: <<hundert[>%alt-ones>];" +
-            "    1000: tausend[>%alt-ones>];" +
-            "    1100: tausendein[>%alt-ones>];" +
-            "    1200: tausend[>%alt-ones>];" +
-            "    2000: <<tausend[>%alt-ones>];";
-
-    @SmallTest
-    public void testString() throws Exception {
-        String test = "0123456789";
-        String test1 = new String("0123456789");    // different object
-        String test2 = new String("0123456780");    // different value
-        String offset = new String("xxx0123456789yyy");
-        String sub = offset.substring(3, 13);
-
-        assertEquals(test, test);
-        assertEquals(test, test1);
-        assertFalse(test.equals(test2));
-
-        assertEquals(0, test.compareTo(test1));
-        assertTrue(test1.compareTo(test2) > 0);
-        assertTrue(test2.compareTo(test1) < 0);
-
-        /* compare string with a nonzero offset, in left/right side */
-        assertEquals(0, test.compareTo(sub));
-        assertEquals(0, sub.compareTo(test));
-        assertEquals(test, sub);
-        assertEquals(sub, test);
-        /* same base, one is a substring */
-        assertFalse(offset.equals(sub));
-        assertFalse(sub.equals(offset));
-        /* wrong class */
-        assertFalse(test.equals(this));
-
-        /* null ptr - throw */
-        try {
-            test.compareTo(null);
-            fail("didn't get expected npe");
-        } catch (NullPointerException npe) {
-            // expected
-        }
-        /* null ptr - ok */
-        assertFalse(test.equals(null));
-
-        test = test.substring(1);
-        assertEquals("123456789", test);
-        assertFalse(test.equals(test1));
-
-        test = test.substring(1);
-        assertEquals("23456789", test);
-
-        test = test.substring(1);
-        assertEquals("3456789", test);
-
-        test = test.substring(1);
-        assertEquals("456789", test);
-
-        test = test.substring(3, 5);
-        assertEquals("78", test);
-
-        test = "this/is/a/path";
-        String[] strings = test.split("/");
-        assertEquals(4, strings.length);
-
-        assertEquals("this is a path", test.replaceAll("/", " "));
-        assertEquals("this is a path", test.replace("/", " "));
-
-        assertEquals(0, "abc".compareToIgnoreCase("ABC"));
-        assertTrue("abc".compareToIgnoreCase("DEF") < 0);
-        assertTrue("ABC".compareToIgnoreCase("def") < 0);
-        assertTrue("Now".compareTo("Mow") > 0);
-        assertTrue("Now".compareToIgnoreCase("Mow") > 0);
-
-        // RuleBasedNumberFormnat tests
-        RuleBasedNumberFormat format = new RuleBasedNumberFormat();
-        format.open(RBNFType.SPELLOUT);
-        String result = format.format(15);
-        assertEquals("Expected spellout format: 'fifteen' but was "
-                + result, "fifteen", result);
-        format.close();
-
-        format.open(RBNFType.DURATION);
-        result = format.format(15);
-        assertEquals("Expected spellout format: '15 sec.' but was "
-                + result, "15 sec.", result);
-        format.close();
-
-        format.open(RBNFType.ORDINAL);
-        result = format.format(15);
-        assertEquals("Expected spellout format: '15th' but was "
-                + result, "15th", result);
-        format.close();
-        format.open(germanSpelloutRule);
-
-        result = format.format(1323);
-        assertEquals("Expected spellout format: 'tausenddrei" +
-                "hundertdreiundzwanzig' but was " + result, "tausend" +
-                        "dreihundertdreiundzwanzig", result);
-            int res = format.parse("tausenddreihundertdreiundzwanzig")
-                    .intValue();
-            assertEquals("Expected spellout format: 'tausend" +
-                    "dreihundertdreiundzwanzig' but was " + res , 1323, res);
-        format.close();
-    }
-}
-
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index f33b01d..50451e7 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -16,6 +16,7 @@
 
 package com.android.dumprendertree;
 
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.webkit.MockGeolocation;
@@ -24,7 +25,7 @@
 import java.util.HashMap;
 
 public class CallbackProxy extends Handler implements EventSender, LayoutTestController {
-    
+
     private EventSender mEventSender;
     private LayoutTestController mLayoutTestController;
 
@@ -37,6 +38,15 @@
     private static final int EVENT_MOUSE_DOWN = 7;
     private static final int EVENT_MOUSE_MOVE = 8;
     private static final int EVENT_MOUSE_UP = 9;
+    private static final int EVENT_TOUCH_START = 10;
+    private static final int EVENT_TOUCH_MOVE = 11;
+    private static final int EVENT_TOUCH_END = 12;
+    private static final int EVENT_TOUCH_CANCEL = 13;
+    private static final int EVENT_ADD_TOUCH_POINT = 14;
+    private static final int EVENT_UPDATE_TOUCH_POINT = 15;
+    private static final int EVENT_RELEASE_TOUCH_POINT = 16;
+    private static final int EVENT_CLEAR_TOUCH_POINTS = 17;
+    private static final int EVENT_CANCEL_TOUCH_POINT = 18;
     
     private static final int LAYOUT_CLEAR_LIST = 20;
     private static final int LAYOUT_DISPLAY = 21;
@@ -107,6 +117,46 @@
             mEventSender.mouseUp();
             break;
 
+        case EVENT_TOUCH_START:
+            mEventSender.touchStart();
+            break;
+
+        case EVENT_TOUCH_MOVE:
+            mEventSender.touchMove();
+            break;
+
+        case EVENT_TOUCH_END:
+            mEventSender.touchEnd();
+            break;
+
+        case EVENT_TOUCH_CANCEL:
+            mEventSender.touchCancel();
+            break;
+
+        case EVENT_ADD_TOUCH_POINT:
+            mEventSender.addTouchPoint(msg.arg1, msg.arg2);
+            break;
+
+        case EVENT_UPDATE_TOUCH_POINT:
+            Bundle args = (Bundle) msg.obj;
+            int x = args.getInt("x");
+            int y = args.getInt("y");
+            int id = args.getInt("id");
+            mEventSender.updateTouchPoint(id, x, y);
+            break;
+
+        case EVENT_RELEASE_TOUCH_POINT:
+            mEventSender.releaseTouchPoint(msg.arg1);
+            break;
+
+        case EVENT_CLEAR_TOUCH_POINTS:
+            mEventSender.clearTouchPoints();
+            break;
+
+        case EVENT_CANCEL_TOUCH_POINT:
+            mEventSender.cancelTouchPoint(msg.arg1);
+            break;
+
         case LAYOUT_CLEAR_LIST:
             mLayoutTestController.clearBackForwardList();
             break;
@@ -252,6 +302,51 @@
     public void mouseUp() {
         obtainMessage(EVENT_MOUSE_UP).sendToTarget();
     }
+
+    public void touchStart() {
+        obtainMessage(EVENT_TOUCH_START).sendToTarget();
+    }
+
+    public void addTouchPoint(int x, int y) {
+        obtainMessage(EVENT_ADD_TOUCH_POINT, x, y).sendToTarget();
+    }
+
+    public void updateTouchPoint(int id, int x, int y) {
+        Bundle map = new Bundle();
+        map.putInt("x", x);
+        map.putInt("y", y);
+        map.putInt("id", id);
+        obtainMessage(EVENT_UPDATE_TOUCH_POINT, map).sendToTarget();
+    }
+
+    public void setTouchModifier(String modifier, boolean enabled) {
+        // TODO(benm): Android doesn't support key modifiers on touch events yet.
+    }
+
+    public void touchMove() {
+        obtainMessage(EVENT_TOUCH_MOVE).sendToTarget();
+    }
+
+    public void releaseTouchPoint(int id) {
+        obtainMessage(EVENT_RELEASE_TOUCH_POINT, id, 0).sendToTarget();
+    }
+
+    public void touchEnd() {
+        obtainMessage(EVENT_TOUCH_END).sendToTarget();
+    }
+
+    public void touchCancel() {
+        obtainMessage(EVENT_TOUCH_CANCEL).sendToTarget();
+    }
+
+
+    public void clearTouchPoints() {
+        obtainMessage(EVENT_CLEAR_TOUCH_POINTS).sendToTarget();
+    }
+
+    public void cancelTouchPoint(int id) {
+        obtainMessage(EVENT_CANCEL_TOUCH_POINT, id, 0).sendToTarget();
+    }
     
     // LayoutTestController Methods
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java
index 82fd8d8..23cc8f5 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java
@@ -26,4 +26,14 @@
         public void keyDown (String character);
         public void enableDOMUIEventLogging(int DOMNode);
         public void fireKeyboardEventsToElement(int DOMNode);
+        public void touchStart();
+        public void touchMove();
+        public void touchEnd();
+        public void touchCancel();
+        public void addTouchPoint(int x, int y);
+        public void updateTouchPoint(int id, int x, int y);
+        public void setTouchModifier(String modifier, boolean enabled);
+        public void releaseTouchPoint(int id);
+        public void clearTouchPoints();
+        public void cancelTouchPoint(int id);
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 32219fa..452368e 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -96,6 +96,8 @@
         // tests expect "LayoutTests" in their output.
         "storage/domstorage/localstorage/iframe-events.html",
         "storage/domstorage/sessionstorage/iframe-events.html",
+        // We do not support multi touch events.
+        "fast/events/touch/basic-multi-touch-events.html",
         // below tests (failed or crashes) are filtered out temporarily due to prioritizing
         "editing/selection/move-left-right.html",
     };
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 5763b85..e8a66c1 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -705,6 +705,7 @@
         mDumpDatabaseCallbacks = false;
         mCanOpenWindows = false;
         mEventSender.resetMouse();
+        mEventSender.clearTouchPoints();
         mPageFinished = false;
         mOneHundredPercentComplete = false;
         mDumpWebKitData = false;
@@ -769,6 +770,12 @@
 
         webview.setWebChromeClient(mChromeClient);
         webview.setWebViewClient(mViewClient);
+        // Setting a touch interval of -1 effectively disables the optimisation in WebView
+        // that stops repeated touch events flooding WebCore. The Event Sender only sends a
+        // single event rather than a stream of events (like what would generally happen in
+        // a real use of touch events in a WebView)  and so if the WebView drops the event,
+        // the test will fail as the test expects one callback for every touch it synthesizes.
+        webview.setTouchInterval(-1);
     }
 
     private WebView mWebView;
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
index eea6346..996eaba 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
@@ -16,17 +16,25 @@
 
 package com.android.dumprendertree;
 
-import android.webkit.WebView;
-import android.view.KeyEvent;
+import android.os.Handler;
+import android.os.SystemClock;
 import android.util.*;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.webkit.WebView;
 
+import java.lang.InterruptedException;
 import java.util.Arrays;
+import java.util.Vector;
 
 public class WebViewEventSender implements EventSender {
+
+    private static final String LOGTAG = "WebViewEventSender";
 	
-	WebViewEventSender(WebView webView) {
-		mWebView = webView;
-	}
+    WebViewEventSender(WebView webView) {
+        mWebView = webView;
+        mTouchPoints = new Vector();
+    }
 	
 	public void resetMouse() {
 		mouseX = mouseY = 0;
@@ -186,9 +194,162 @@
 		}
 		return KeyEvent.KEYCODE_UNKNOWN;
 	}
-	
+
+    public void touchStart() {
+        // We only support single touch so examine the first touch point only.
+        // If multi touch is enabled in the future, we need to re-examine this to send
+        // all the touch points with the event.
+        TouchPoint tp = mTouchPoints.get(0);
+
+        if (tp == null) {
+            return;
+        }
+
+        tp.setDownTime(SystemClock.uptimeMillis());
+        MotionEvent event = MotionEvent.obtain(tp.downTime(), tp.downTime(),
+                MotionEvent.ACTION_DOWN, tp.getX(), tp.getY(), 0);
+        mWebView.onTouchEvent(event);
+    }
+
+    public void touchMove() {
+        TouchPoint tp = mTouchPoints.get(0);
+
+        if (tp == null) {
+            return;
+        }
+
+        if (!tp.hasMoved()) {
+            return;
+        }
+
+        MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_MOVE, tp.getX(), tp.getY(), 0);
+        mWebView.onTouchEvent(event);
+
+        tp.setMoved(false);
+    }
+
+    public void touchEnd() {
+        TouchPoint tp = mTouchPoints.get(0);
+
+        if (tp == null) {
+            return;
+        }
+
+        MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_UP, tp.getX(), tp.getY(), 0);
+        mWebView.onTouchEvent(event);
+
+        if (tp.isReleased()) {
+            mTouchPoints.remove(0);
+        }
+    }
+
+    public void touchCancel() {
+        TouchPoint tp = mTouchPoints.get(0);
+        if (tp == null) {
+            return;
+        }
+
+        if (tp.cancelled()) {
+            MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
+                    MotionEvent.ACTION_CANCEL, tp.getX(), tp.getY(), 0);
+            mWebView.onTouchEvent(event);
+        }
+    }
+
+    public void cancelTouchPoint(int id) {
+        TouchPoint tp = mTouchPoints.get(0);
+        if (tp == null) {
+            return;
+        }
+
+        tp.cancel();
+    }
+
+    public void addTouchPoint(int x, int y) {
+        mTouchPoints.add(new TouchPoint(contentsToWindowX(x), contentsToWindowY(y)));
+        if (mTouchPoints.size() > 1) {
+            Log.w(LOGTAG, "Adding more than one touch point, but multi touch is not supported!");
+        }
+    }
+
+    public void updateTouchPoint(int id, int x, int y) {
+        TouchPoint tp = mTouchPoints.get(0);
+        if (tp == null) {
+            return;
+        }
+
+        tp.update(contentsToWindowX(x), contentsToWindowY(y));
+        tp.setMoved(true);
+    }
+
+    public void setTouchModifier(String modifier, boolean enabled) {
+        // TODO(benm): This needs implementing when Android supports sending key modifiers
+        // in touch events.
+    }
+
+    public void releaseTouchPoint(int id) {
+        TouchPoint tp = mTouchPoints.get(0);
+        if (tp == null) {
+            return;
+        }
+
+        tp.release();
+    }
+
+    public void clearTouchPoints() {
+        mTouchPoints.clear();
+    }
+
+    private int contentsToWindowX(int x) {
+        return (int) (x * mWebView.getScale()) - mWebView.getScrollX();
+    }
+
+    private int contentsToWindowY(int y) {
+        return (int) (y * mWebView.getScale()) - mWebView.getScrollY();
+    }
+
     private WebView mWebView = null;
     private int mouseX;
     private int mouseY;
 
+    private class TouchPoint {
+        private int mX;
+        private int mY;
+        private long mDownTime;
+        private boolean mReleased;
+        private boolean mMoved;
+        private boolean mCancelled;
+
+        public TouchPoint(int x, int y) {
+            mX = x;
+            mY = y;
+            mReleased = false;
+            mMoved = false;
+            mCancelled = false;
+        }
+
+        public void setDownTime(long downTime) { mDownTime = downTime; }
+        public long downTime() { return mDownTime; }
+        public void cancel() { mCancelled = true; }
+
+        public boolean cancelled() { return mCancelled; }
+
+        public void release() { mReleased = true; }
+        public boolean isReleased() { return mReleased; }
+
+        public void setMoved(boolean moved) { mMoved = moved; }
+        public boolean hasMoved() { return mMoved; }
+
+        public int getX() { return mX; }
+        public int getY() { return mY; }
+
+        public void update(int x, int y) {
+            mX = x;
+            mY = y;
+        }
+    };
+
+    private Vector<TouchPoint> mTouchPoints;
 }