Merge "Properly advertise the content length of the HTTP stream if available."
diff --git a/Android.mk b/Android.mk
index f225ecd8..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 \
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<java.lang.String[]>">
-</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..65aa5b6 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=""storage""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TELEPHONY_SERVICE"
type="java.lang.String"
transient="false"
@@ -117383,6 +117394,123 @@
</parameter>
</method>
</class>
+<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.os.StorageEventListener">
+</parameter>
+</method>
+<method name="unregisterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.os.StorageEventListener">
+</parameter>
+</method>
+</class>
<class name="SystemClock"
extends="java.lang.Object"
abstract="false"
@@ -185890,573 +186018,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<java.lang.String[]>">
-</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 +186503,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/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 6c65bd8..88a2b48 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -81,6 +81,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StorageManager;
import android.os.StatFs;
import android.os.Vibrator;
import android.os.FileUtils.FileStatus;
@@ -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) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 321ba5c..dae8e37 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.os.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.os.StorageManager} for accesssing system storage
+ * functions.
+ *
+ * @see #getSystemService
+ * @see android.os.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/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/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/os/StorageEventListener.java b/core/java/android/os/StorageEventListener.java
new file mode 100644
index 0000000..f852f42
--- /dev/null
+++ b/core/java/android/os/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.os;
+
+/**
+ * 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/os/StorageManager.java b/core/java/android/os/StorageManager.java
new file mode 100644
index 0000000..764abe0
--- /dev/null
+++ b/core/java/android/os/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.os;
+
+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.os.StorageEventListener StorageEventListener}.
+ *
+ * @param listener A {@link android.os.StorageEventListener StorageEventListener} object.
+ *
+ */
+ public void registerListener(StorageEventListener listener) {
+ if (listener == null) {
+ return;
+ }
+
+ synchronized (sListeners) {
+ sListeners.add(new ListenerDelegate(listener));
+ }
+ }
+
+ /**
+ * Unregisters a {@link android.os.StorageEventListener StorageEventListener}.
+ *
+ * @param listener A {@link android.os.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/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 22dca3a..213eaa5 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -52,6 +52,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 +659,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/DataLoader.java b/core/java/android/webkit/DataLoader.java
index 6c5d10d..2a68a5d 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/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/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/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/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index d36207b..b07d6e4 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;
@@ -147,43 +148,35 @@
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 +187,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 +199,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 +211,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/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/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index cd4ae4c..4801817 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -73,6 +73,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;
@@ -7940,33 +7941,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,64 +7971,53 @@
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
- try {
- getMountService().unmountSecureContainer(pkgName);
- return true;
- } catch (IllegalStateException e) {
- Log.e(TAG, "Failed to unmount : " + pkgName + " 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;
- }
-
- private boolean finalizeSdDir(String pkgName) {
- try {
- getMountService().finalizeSecureContainer(pkgName);
- return true;
- } catch (IllegalStateException e) {
- Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
+ 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 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 String getSdDir(String pkgName) {
+ return getMountService().getSecureContainerPath(pkgName);
+ }
- static String[] getSecureContainerList() {
- try {
- return getMountService().getSecureContainerList();
- } catch (IllegalStateException e) {
- Log.i(TAG, "Failed to getSecureContainerList");
- }
- return null;
- }
+ 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) {
+ 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() {
+ String[] list = getMountService().getSecureContainerList();
+ return list.length == 0 ? null : list;
+ }
static String getTempContainerId() {
String prefix = "smdl1tmp";