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