Merge "Make the Date Picker fields more consistent in locales with numeric months."
diff --git a/Android.mk b/Android.mk
index ab1e7ea..ec6f96b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -111,8 +111,8 @@
core/java/android/net/INetworkManagementEventObserver.aidl \
core/java/android/os/ICheckinService.aidl \
core/java/android/os/IMessenger.aidl \
- core/java/android/os/IMountService.aidl \
- core/java/android/os/IMountServiceListener.aidl \
+ core/java/android/os/storage/IMountService.aidl \
+ core/java/android/os/storage/IMountServiceListener.aidl \
core/java/android/os/INetworkManagementService.aidl \
core/java/android/os/INetStatService.aidl \
core/java/android/os/IParentalControlCallback.aidl \
diff --git a/api/current.xml b/api/current.xml
index e673f0f..a8f7109 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -32785,6 +32785,30 @@
<parameter name="mode" type="int">
</parameter>
</method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
<method name="getFileStreamPath"
return="java.io.File"
abstract="true"
@@ -34293,6 +34317,30 @@
<parameter name="mode" type="int">
</parameter>
</method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
<method name="getFileStreamPath"
return="java.io.File"
abstract="false"
@@ -83028,6 +83076,25 @@
<parameter name="mimeType" type="java.lang.String">
</parameter>
</method>
+<method name="scanFile"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="paths" type="java.lang.String[]">
+</parameter>
+<parameter name="mimeTypes" type="java.lang.String[]">
+</parameter>
+<parameter name="callback" type="android.media.MediaScannerConnection.ScanResultListener">
+</parameter>
+</method>
</class>
<interface name="MediaScannerConnection.MediaScannerConnectionClient"
abstract="true"
@@ -83036,6 +83103,8 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.media.MediaScannerConnection.ScanResultListener">
+</implements>
<method name="onMediaScannerConnected"
return="void"
abstract="true"
@@ -83063,6 +83132,29 @@
</parameter>
</method>
</interface>
+<interface name="MediaScannerConnection.ScanResultListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onScanCompleted"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+</interface>
<class name="Ringtone"
extends="java.lang.Object"
abstract="false"
@@ -112670,6 +112762,19 @@
visibility="public"
>
</method>
+<method name="getExternalStoragePublicDirectory"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
<method name="getExternalStorageState"
return="java.lang.String"
abstract="false"
@@ -112692,6 +112797,96 @@
visibility="public"
>
</method>
+<field name="DIRECTORY_ALARMS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_DCIM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_DOWNLOADS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_MOVIES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_MUSIC"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_NOTIFICATIONS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_PICTURES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_PODCASTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_RINGTONES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="MEDIA_BAD_REMOVAL"
type="java.lang.String"
transient="false"
@@ -117646,6 +117841,218 @@
</method>
</class>
</package>
+<package name="android.os.storage"
+>
+<class name="StorageEventListener"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StorageEventListener"
+ type="android.os.storage.StorageEventListener"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onStorageStateChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="oldState" type="java.lang.String">
+</parameter>
+<parameter name="newState" type="java.lang.String">
+</parameter>
+</method>
+<method name="onUsbMassStorageConnectionChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="connected" type="boolean">
+</parameter>
+</method>
+</class>
+<class name="StorageManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="disableUsbMassStorage"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="enableUsbMassStorage"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isUsbMassStorageConnected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isUsbMassStorageEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="registerListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.os.storage.StorageEventListener">
+</parameter>
+</method>
+<method name="unregisterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.os.storage.StorageEventListener">
+</parameter>
+</method>
+</class>
+<class name="StorageResultCode"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StorageResultCode"
+ type="android.os.storage.StorageResultCode"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="OperationFailedInternalError"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedMediaBlank"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedMediaCorrupt"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedNoMedia"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedVolumeNotMounted"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationSucceeded"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+</package>
<package name="android.preference"
>
<class name="CheckBoxPreference"
@@ -136301,126 +136708,6 @@
</method>
</interface>
</package>
-<package name="android.storage"
->
-<interface name="StorageEventListener"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onMediaInserted"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="label" type="java.lang.String">
-</parameter>
-<parameter name="path" type="java.lang.String">
-</parameter>
-<parameter name="major" type="int">
-</parameter>
-<parameter name="minor" type="int">
-</parameter>
-</method>
-<method name="onMediaRemoved"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="label" type="java.lang.String">
-</parameter>
-<parameter name="path" type="java.lang.String">
-</parameter>
-<parameter name="major" type="int">
-</parameter>
-<parameter name="minor" type="int">
-</parameter>
-<parameter name="clean" type="boolean">
-</parameter>
-</method>
-<method name="onShareAvailabilityChanged"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="method" type="java.lang.String">
-</parameter>
-<parameter name="available" type="boolean">
-</parameter>
-</method>
-<method name="onVolumeStateChanged"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="label" type="java.lang.String">
-</parameter>
-<parameter name="path" type="java.lang.String">
-</parameter>
-<parameter name="oldState" type="java.lang.String">
-</parameter>
-<parameter name="newState" type="java.lang.String">
-</parameter>
-</method>
-</interface>
-<class name="StorageManager"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="registerListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.storage.StorageEventListener">
-</parameter>
-</method>
-<method name="unregisterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.storage.StorageEventListener">
-</parameter>
-</method>
-</class>
-</package>
<package name="android.telephony"
>
<class name="CellLocation"
@@ -144151,6 +144438,30 @@
<parameter name="mode" type="int">
</parameter>
</method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
<method name="getFileStreamPath"
return="java.io.File"
abstract="false"
@@ -181201,6 +181512,17 @@
<parameter name="t" type="android.view.animation.Transformation">
</parameter>
</method>
+<method name="cancel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="computeDurationHint"
return="long"
abstract="false"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 45d7546..9b9cbd5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -84,7 +84,7 @@
import android.os.StatFs;
import android.os.Vibrator;
import android.os.FileUtils.FileStatus;
-import android.storage.StorageManager;
+import android.os.storage.StorageManager;
import android.telephony.TelephonyManager;
import android.text.ClipboardManager;
import android.util.AndroidRuntimeException;
@@ -197,9 +197,9 @@
private File mDatabasesDir;
private File mPreferencesDir;
private File mFilesDir;
-
-
private File mCacheDir;
+ private File mExternalFilesDir;
+ private File mExternalCacheDir;
private static long sInstanceCount = 0;
@@ -438,6 +438,38 @@
}
@Override
+ public File getExternalFilesDir(String type) {
+ synchronized (mSync) {
+ if (mExternalFilesDir == null) {
+ mExternalFilesDir = Environment.getExternalStorageAppFilesDirectory(
+ getPackageName());
+ }
+ if (!mExternalFilesDir.exists()) {
+ try {
+ (new File(Environment.getExternalStorageAndroidDataDir(),
+ ".nomedia")).createNewFile();
+ } catch (IOException e) {
+ }
+ if (!mExternalFilesDir.mkdirs()) {
+ Log.w(TAG, "Unable to create external files directory");
+ return null;
+ }
+ }
+ if (type == null) {
+ return mExternalFilesDir;
+ }
+ File dir = new File(mExternalFilesDir, type);
+ if (!dir.exists()) {
+ if (!dir.mkdirs()) {
+ Log.w(TAG, "Unable to create external media directory " + dir);
+ return null;
+ }
+ }
+ return dir;
+ }
+ }
+
+ @Override
public File getCacheDir() {
synchronized (mSync) {
if (mCacheDir == null) {
@@ -457,7 +489,28 @@
return mCacheDir;
}
-
+ @Override
+ public File getExternalCacheDir() {
+ synchronized (mSync) {
+ if (mExternalCacheDir == null) {
+ mExternalCacheDir = Environment.getExternalStorageAppCacheDirectory(
+ getPackageName());
+ }
+ if (!mExternalCacheDir.exists()) {
+ try {
+ (new File(Environment.getExternalStorageAndroidDataDir(),
+ ".nomedia")).createNewFile();
+ } catch (IOException e) {
+ }
+ if (!mExternalCacheDir.mkdirs()) {
+ Log.w(TAG, "Unable to create external cache directory");
+ return null;
+ }
+ }
+ return mExternalCacheDir;
+ }
+ }
+
@Override
public File getFileStreamPath(String name) {
return makeFilename(getFilesDir(), name);
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 804c8eb..3fd36a37 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -42,7 +42,7 @@
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_REDELIVER_INTENT} instead of
* {@link Service#START_NOT_STICKY}, so that if this service's process
- * is called while it is executing the Intent in
+ * is killed while it is executing the Intent in
* {@link #onHandleIntent(Intent)}, then when later restarted the same Intent
* will be re-delivered to it, to retry its execution.
*/
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5aefe4c..672e5f7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -25,6 +25,7 @@
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.media.MediaScannerConnection.ScanResultListener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -137,7 +138,28 @@
/**
* Return the context of the single, global Application object of the
- * current process.
+ * current process. This generally should only be used if you need a
+ * Context whose lifecycle is separate from the current context, that is
+ * tied to the lifetime of the process rather than the current component.
+ *
+ * <p>Consider for example how this interacts with
+ * {@ #registerReceiver(BroadcastReceiver, IntentFilter)}:
+ * <ul>
+ * <li> <p>If used from an Activity context, the receiver is being registered
+ * within that activity. This means that you are expected to unregister
+ * before the activity is done being destroyed; in fact if you do not do
+ * so, the framework will clean up your leaked registration as it removes
+ * the activity and log an error. Thus, if you use the Activity context
+ * to register a receiver that is static (global to the process, not
+ * associated with an Activity instance) then that registration will be
+ * removed on you at whatever point the activity you used is destroyed.
+ * <li> <p>If used from the Context returned here, the receiver is being
+ * registered with the global state associated with your application. Thus
+ * it will never be unregistered for you. This is necessary if the receiver
+ * is associated with static data, not a particular component. However
+ * using the ApplicationContext elsewhere can easily lead to serious leaks
+ * if you forget to unregister, unbind, etc.
+ * </ul>
*/
public abstract Context getApplicationContext();
@@ -393,11 +415,84 @@
public abstract File getFilesDir();
/**
+ * Returns the absolute path to the directory on the external filesystem
+ * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
+ * Environment.getExternalStorageDirectory()} where the application can
+ * place persistent files it owns. These files are private to the
+ * applications, and not typically visible to the user as media.
+ *
+ * <p>This is like {@link #getFilesDir()} in that these
+ * files will be deleted when the application is uninstalled, however there
+ * are some important differences:
+ *
+ * <ul>
+ * <li>External files are not always available: they will disappear if the
+ * user mounts the external storage on a computer or removes it. See the
+ * APIs on {@link android.os.Environment} for information in the storage state.
+ * <li>There is no security enforced with these files. All applications
+ * can read and write files placed here.
+ * </ul>
+ *
+ * <p>Here is an example of typical code to manipulate a file in
+ * an application's private storage:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+ * private_file}
+ *
+ * <p>If you install a non-null <var>type</var> to this function, the returned
+ * file will be a path to a sub-directory of the given type. Though these files
+ * are not automatically scanned by the media scanner, you can explicitly
+ * add them to the media database with
+ * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[],
+ * ScanResultListener) MediaScannerConnection.scanFile}.
+ * Note that this is not the same as
+ * {@link android.os.Environment#getExternalStoragePublicDirectory
+ * Environment.getExternalStoragePublicDirectory()}, which provides
+ * directories of media shared by all applications. The
+ * directories returned here are
+ * owned by the application, and its contents will be removed when the
+ * application is uninstalled. Unlike
+ * {@link android.os.Environment#getExternalStoragePublicDirectory
+ * Environment.getExternalStoragePublicDirectory()}, the directory
+ * returned here will be automatically created for you.
+ *
+ * <p>Here is an example of typical code to manipulate a picture in
+ * an application's private storage and add it to the media database:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+ * private_picture}
+ *
+ * @param type The type of files directory to return. May be null for
+ * the root of the files directory or one of
+ * the following Environment constants for a subdirectory:
+ * {@link android.os.Environment#DIRECTORY_MUSIC},
+ * {@link android.os.Environment#DIRECTORY_PODCASTS},
+ * {@link android.os.Environment#DIRECTORY_RINGTONES},
+ * {@link android.os.Environment#DIRECTORY_ALARMS},
+ * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+ * {@link android.os.Environment#DIRECTORY_PICTURES}, or
+ * {@link android.os.Environment#DIRECTORY_MOVIES}.
+ *
+ * @return Returns the path of the directory holding application files
+ * on external storage. Returns null if external storage is not currently
+ * mounted so it could not ensure the path exists; you will need to call
+ * this method again when it is available.
+ *
+ * @see #getFilesDir
+ */
+ public abstract File getExternalFilesDir(String type);
+
+ /**
* Returns the absolute path to the application specific cache directory
* on the filesystem. These files will be ones that get deleted first when the
- * device runs low on storage
+ * device runs low on storage.
* There is no guarantee when these files will be deleted.
- *
+ *
+ * <strong>Note: you should not <em>rely</em> on the system deleting these
+ * files for you; you should always have a reasonable maximum, such as 1 MB,
+ * for the amount of space you consume with cache files, and prune those
+ * files when exceeding that space.</strong>
+ *
* @return Returns the path of the directory holding application cache files.
*
* @see #openFileOutput
@@ -407,6 +502,37 @@
public abstract File getCacheDir();
/**
+ * Returns the absolute path to the directory on the external filesystem
+ * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
+ * Environment.getExternalStorageDirectory()} where the application can
+ * place cache files it owns.
+ *
+ * <p>This is like {@link #getCacheDir()} in that these
+ * files will be deleted when the application is uninstalled, however there
+ * are some important differences:
+ *
+ * <ul>
+ * <li>The platform does not monitor the space available in external storage,
+ * and thus will not automatically delete these files. Note that you should
+ * be managing the maximum space you will use for these anyway, just like
+ * with {@link #getCacheDir()}.
+ * <li>External files are not always available: they will disappear if the
+ * user mounts the external storage on a computer or removes it. See the
+ * APIs on {@link android.os.Environment} for information in the storage state.
+ * <li>There is no security enforced with these files. All applications
+ * can read and write files placed here.
+ * </ul>
+ *
+ * @return Returns the path of the directory holding application cache files
+ * on external storage. Returns null if external storage is not currently
+ * mounted so it could not ensure the path exists; you will need to call
+ * this method again when it is available.
+ *
+ * @see #getCacheDir
+ */
+ public abstract File getExternalCacheDir();
+
+ /**
* Returns an array of strings naming the private files associated with
* this Context's application package.
*
@@ -1110,7 +1236,7 @@
* @see #SENSOR_SERVICE
* @see android.hardware.SensorManager
* @see #STORAGE_SERVICE
- * @see android.storage.StorageManager
+ * @see android.os.storage.StorageManager
* @see #VIBRATOR_SERVICE
* @see android.os.Vibrator
* @see #CONNECTIVITY_SERVICE
@@ -1243,11 +1369,11 @@
/**
* Use with {@link #getSystemService} to retrieve a {@link
- * android.storage.StorageManager} for accesssing system storage
+ * android.os.storage.StorageManager} for accesssing system storage
* functions.
*
* @see #getSystemService
- * @see android.storage.StorageManager
+ * @see android.os.storage.StorageManager
*/
public static final String STORAGE_SERVICE = "storage";
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 1b34320..a447108 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -179,11 +179,21 @@
}
@Override
+ public File getExternalFilesDir(String type) {
+ return mBase.getExternalFilesDir(type);
+ }
+
+ @Override
public File getCacheDir() {
return mBase.getCacheDir();
}
@Override
+ public File getExternalCacheDir() {
+ return mBase.getExternalCacheDir();
+ }
+
+ @Override
public File getDir(String name, int mode) {
return mBase.getDir(name, mode);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 54db5e0..2c8c112 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -305,4 +305,5 @@
*/
void updateExternalMediaStatus(boolean mounted);
+ String nextPackageToClean(String lastPackage);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e3b1694..fca8588 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -622,6 +622,13 @@
public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
/**
+ * Action to external storage service to clean out removed apps.
+ * @hide
+ */
+ public static final String ACTION_CLEAN_EXTERNAL_STORAGE
+ = "android.content.pm.CLEAN_EXTERNAL_STORAGE";
+
+ /**
* Determines best place to install an application: either SD or internal FLASH.
* Tweak the algorithm for best results.
* @param appInfo ApplicationInfo object of the package to install.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bac55cc..b31df32 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2138,7 +2138,7 @@
havePerm = true;
}
if (writePermission != null) {
- writePermission = readPermission.intern();
+ writePermission = writePermission.intern();
havePerm = true;
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 540f4445..5d1e7cf 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1880,8 +1880,7 @@
*/
Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
getPath() + "; i.e., NO space for this sql statement in cache: " +
- sql + ". Make sure your sql " +
- "statements are using prepared-sql-statement syntax with '?' for " +
+ sql + ". Please change your sql statements to use '?' for " +
"bindargs, instead of using actual values");
/* increment the number of times this warnings has been printed.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 9491bd4..a9831aa 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,7 +18,7 @@
import java.io.File;
-import android.os.IMountService;
+import android.os.storage.IMountService;
/**
* Provides access to environment variables.
@@ -91,6 +91,14 @@
private static final File EXTERNAL_STORAGE_DIRECTORY
= getDirectory("EXTERNAL_STORAGE", "/sdcard");
+ private static final File EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY
+ = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+ "Android"), "data");
+
+ private static final File EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY
+ = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+ "Android"), "media");
+
private static final File DOWNLOAD_CACHE_DIRECTORY
= getDirectory("DOWNLOAD_CACHE", "/cache");
@@ -102,13 +110,183 @@
}
/**
- * Gets the Android external storage directory.
+ * Gets the Android external storage directory. This directory may not
+ * currently be accessible if it has been mounted by the user on their
+ * computer, has been removed from the device, or some other problem has
+ * happened. You can determine its current state with
+ * {@link #getExternalStorageState()}.
+ *
+ * <p>Here is an example of typical code to monitor the state of
+ * external storage:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+ * monitor_storage}
*/
public static File getExternalStorageDirectory() {
return EXTERNAL_STORAGE_DIRECTORY;
}
/**
+ * Standard directory in which to place any audio files that should be
+ * in the regular list of music for the user.
+ * This may be combined with
+ * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+ * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+ * of directories to categories a particular audio file as more than one
+ * type.
+ */
+ public static String DIRECTORY_MUSIC = "Music";
+
+ /**
+ * Standard directory in which to place any audio files that should be
+ * in the list of podcasts that the user can select (not as regular
+ * music).
+ * This may be combined with {@link #DIRECTORY_MUSIC},
+ * {@link #DIRECTORY_NOTIFICATIONS},
+ * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+ * of directories to categories a particular audio file as more than one
+ * type.
+ */
+ public static String DIRECTORY_PODCASTS = "Podcasts";
+
+ /**
+ * Standard directory in which to place any audio files that should be
+ * in the list of ringtones that the user can select (not as regular
+ * music).
+ * This may be combined with {@link #DIRECTORY_MUSIC},
+ * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
+ * {@link #DIRECTORY_ALARMS} as a series
+ * of directories to categories a particular audio file as more than one
+ * type.
+ */
+ public static String DIRECTORY_RINGTONES = "Ringtones";
+
+ /**
+ * Standard directory in which to place any audio files that should be
+ * in the list of alarms that the user can select (not as regular
+ * music).
+ * This may be combined with {@link #DIRECTORY_MUSIC},
+ * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+ * and {@link #DIRECTORY_RINGTONES} as a series
+ * of directories to categories a particular audio file as more than one
+ * type.
+ */
+ public static String DIRECTORY_ALARMS = "Alarms";
+
+ /**
+ * Standard directory in which to place any audio files that should be
+ * in the list of notifications that the user can select (not as regular
+ * music).
+ * This may be combined with {@link #DIRECTORY_MUSIC},
+ * {@link #DIRECTORY_PODCASTS},
+ * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+ * of directories to categories a particular audio file as more than one
+ * type.
+ */
+ public static String DIRECTORY_NOTIFICATIONS = "Notifications";
+
+ /**
+ * Standard directory in which to place pictures that are available to
+ * the user. Note that this is primarily a convention for the top-level
+ * public directory, as the media scanner will find and collect pictures
+ * in any directory.
+ */
+ public static String DIRECTORY_PICTURES = "Pictures";
+
+ /**
+ * Standard directory in which to place movies that are available to
+ * the user. Note that this is primarily a convention for the top-level
+ * public directory, as the media scanner will find and collect movies
+ * in any directory.
+ */
+ public static String DIRECTORY_MOVIES = "Movies";
+
+ /**
+ * Standard directory in which to place files that have been downloaded by
+ * the user. Note that this is primarily a convention for the top-level
+ * public directory, you are free to download files anywhere in your own
+ * private directories.
+ */
+ public static String DIRECTORY_DOWNLOADS = "Downloads";
+
+ /**
+ * The traditional location for pictures and videos when mounting the
+ * device as a camera. Note that this is primarily a convention for the
+ * top-level public directory, as this convention makes no sense elsewhere.
+ */
+ public static String DIRECTORY_DCIM = "DCIM";
+
+ /**
+ * Get a top-level public external storage directory for placing files of
+ * a particular type. This is where the user will typically place and
+ * manage their own files, so you should be careful about what you put here
+ * to ensure you don't erase their files or get in the way of their own
+ * organization.
+ *
+ * <p>Here is an example of typical code to manipulate a picture on
+ * the public external storage:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+ * public_picture}
+ *
+ * @param type The type of storage directory to return. Should be one of
+ * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
+ * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
+ * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
+ * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
+ * {@link #DIRECTORY_DCIM}. May not be null.
+ *
+ * @return Returns the File path for the directory. Note that this
+ * directory may not yet exist, so you must make sure it exists before
+ * using it such as with {@link File#mkdirs File.mkdirs()}.
+ */
+ public static File getExternalStoragePublicDirectory(String type) {
+ return new File(getExternalStorageDirectory(), type);
+ }
+
+ /**
+ * Returns the path for android-specific data on the SD card.
+ * @hide
+ */
+ public static File getExternalStorageAndroidDataDir() {
+ return EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY;
+ }
+
+ /**
+ * Generates the raw path to an application's data
+ * @hide
+ */
+ public static File getExternalStorageAppDataDirectory(String packageName) {
+ return new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY, packageName);
+ }
+
+ /**
+ * Generates the raw path to an application's media
+ * @hide
+ */
+ public static File getExternalStorageAppMediaDirectory(String packageName) {
+ return new File(EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY, packageName);
+ }
+
+ /**
+ * Generates the path to an application's files.
+ * @hide
+ */
+ public static File getExternalStorageAppFilesDirectory(String packageName) {
+ return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
+ packageName), "files");
+ }
+
+ /**
+ * Generates the path to an application's cache.
+ * @hide
+ */
+ public static File getExternalStorageAppCacheDirectory(String packageName) {
+ return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
+ packageName), "cache");
+ }
+
+ /**
* Gets the Android Download/Cache content directory.
*/
public static File getDownloadCacheDirectory() {
@@ -173,6 +351,8 @@
* Gets the current state of the external storage device.
* Note: This call should be deprecated as it doesn't support
* multiple volumes.
+ *
+ * <p>See {@link #getExternalStorageDirectory()} for an example of its use.
*/
public static String getExternalStorageState() {
try {
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index 3457815..7e99f38 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -52,73 +52,75 @@
public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
| CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
- | DELETE_SELF | MOVE_SELF;
+ | DELETE_SELF | MOVE_SELF;
private static final String LOG_TAG = "FileObserver";
private static class ObserverThread extends Thread {
- private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
- private int m_fd;
+ private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
+ private int m_fd;
- public ObserverThread() {
- super("FileObserver");
- m_fd = init();
- }
+ public ObserverThread() {
+ super("FileObserver");
+ m_fd = init();
+ }
- public void run() {
- observe(m_fd);
- }
+ public void run() {
+ observe(m_fd);
+ }
- public int startWatching(String path, int mask, FileObserver observer) {
- int wfd = startWatching(m_fd, path, mask);
+ public int startWatching(String path, int mask, FileObserver observer) {
+ int wfd = startWatching(m_fd, path, mask);
- Integer i = new Integer(wfd);
- if (wfd >= 0) {
- synchronized (m_observers) {
- m_observers.put(i, new WeakReference(observer));
- }
- }
+ Integer i = new Integer(wfd);
+ if (wfd >= 0) {
+ synchronized (m_observers) {
+ m_observers.put(i, new WeakReference(observer));
+ }
+ }
- return i;
- }
+ return i;
+ }
- public void stopWatching(int descriptor) {
- stopWatching(m_fd, descriptor);
- }
+ public void stopWatching(int descriptor) {
+ stopWatching(m_fd, descriptor);
+ }
- public void onEvent(int wfd, int mask, String path) {
- // look up our observer, fixing up the map if necessary...
- FileObserver observer;
+ public void onEvent(int wfd, int mask, String path) {
+ // look up our observer, fixing up the map if necessary...
+ FileObserver observer = null;
- synchronized (m_observers) {
- WeakReference weak = m_observers.get(wfd);
- observer = (FileObserver) weak.get();
- if (observer == null) {
- m_observers.remove(wfd);
+ synchronized (m_observers) {
+ WeakReference weak = m_observers.get(wfd);
+ if (weak != null) { // can happen with lots of events from a dead wfd
+ observer = (FileObserver) weak.get();
+ if (observer == null) {
+ m_observers.remove(wfd);
+ }
+ }
+ }
+
+ // ...then call out to the observer without the sync lock held
+ if (observer != null) {
+ try {
+ observer.onEvent(mask, path);
+ } catch (Throwable throwable) {
+ Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
+ }
}
}
- // ...then call out to the observer without the sync lock held
- if (observer != null) {
- try {
- observer.onEvent(mask, path);
- } catch (Throwable throwable) {
- Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
- }
- }
- }
-
- private native int init();
- private native void observe(int fd);
- private native int startWatching(int fd, String path, int mask);
- private native void stopWatching(int fd, int wfd);
+ private native int init();
+ private native void observe(int fd);
+ private native int startWatching(int fd, String path, int mask);
+ private native void stopWatching(int fd, int wfd);
}
private static ObserverThread s_observerThread;
static {
- s_observerThread = new ObserverThread();
- s_observerThread.start();
+ s_observerThread = new ObserverThread();
+ s_observerThread.start();
}
// instance
@@ -127,30 +129,30 @@
private int m_mask;
public FileObserver(String path) {
- this(path, ALL_EVENTS);
+ this(path, ALL_EVENTS);
}
public FileObserver(String path, int mask) {
- m_path = path;
- m_mask = mask;
- m_descriptor = -1;
+ m_path = path;
+ m_mask = mask;
+ m_descriptor = -1;
}
protected void finalize() {
- stopWatching();
+ stopWatching();
}
public void startWatching() {
- if (m_descriptor < 0) {
- m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
- }
+ if (m_descriptor < 0) {
+ m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
+ }
}
public void stopWatching() {
- if (m_descriptor >= 0) {
- s_observerThread.stopWatching(m_descriptor);
- m_descriptor = -1;
- }
+ if (m_descriptor >= 0) {
+ s_observerThread.stopWatching(m_descriptor);
+ m_descriptor = -1;
+ }
}
public abstract void onEvent(int event, String path);
diff --git a/core/java/android/os/IMountServiceListener.aidl b/core/java/android/os/IMountServiceListener.aidl
deleted file mode 100644
index 3df64b2..0000000
--- a/core/java/android/os/IMountServiceListener.aidl
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-/**
- * Callback class for receiving events from MountService.
- *
- * @hide
- */
-interface IMountServiceListener {
- /**
- * A sharing method has changed availability state.
- *
- * @param method The share method which has changed.
- * @param available The share availability state.
- */
- void onShareAvailabilityChanged(String method, boolean available);
-
- /**
- * Media has been inserted
- *
- * @param label The volume label.
- * @param path The volume mount path.
- * @param major The backing device major number.
- * @param minor The backing device minor number.
- */
- void onMediaInserted(String label, String path, int major, int minor);
-
- /**
- * Media has been removed
- *
- * @param label The volume label.
- * @param path The volume mount path.
- * @param major The backing device major number.
- * @param minor The backing device minor number.
- * @param clean Indicates if the removal was clean (unmounted first).
- */
- void onMediaRemoved(String label, String path, int major, int minor, boolean clean);
-
- /**
- * Volume state has changed.
- *
- * @param label The volume label.
- * @param path The volume mount path.
- * @param oldState The old state of the volume.
- * @param newState The new state of the volume.
- *
- * Note: State is one of the values returned by Environment.getExternalStorageState()
- */
- void onVolumeStateChanged(String label, String path, String oldState, String newState);
-
-}
diff --git a/core/java/android/os/MountServiceListener.java b/core/java/android/os/MountServiceListener.java
deleted file mode 100644
index a68f464..0000000
--- a/core/java/android/os/MountServiceListener.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-/**
- * Callback class for receiving progress reports during a restore operation. These
- * methods will all be called on your application's main thread.
- * @hide
- */
-public abstract class MountServiceListener {
- /**
- * A sharing method has changed availability state.
- *
- * @param method The share method which has changed.
- * @param available The share availability state.
- */
- void shareAvailabilityChange(String method, boolean available) {
- }
-
- /**
- * Media has been inserted
- *
- * @param label The volume label.
- * @param path The volume mount path.
- * @param major The backing device major number.
- * @param minor The backing device minor number.
- */
- void mediaInserted(String label, String path, int major, int minor) {
- }
-
- /**
- * Media has been removed
- *
- * @param label The volume label.
- * @param path The volume mount path.
- * @param major The backing device major number.
- * @param minor The backing device minor number.
- * @param clean Indicates if the removal was clean (unmounted first).
- */
- void mediaRemoved(String label, String path, int major, int minor, boolean clean) {
- }
-
- /**
- * Volume state has changed.
- *
- * @param label The volume label.
- * @param path The volume mount path.
- * @param oldState The old state of the volume.
- * @param newState The new state of the volume.
- *
- * Note: State is one of the values returned by Environment.getExternalStorageState()
- */
- void volumeStateChange(String label, String path, String oldState, String newState) {
- }
-}
diff --git a/core/java/android/os/MountServiceResultCode.java b/core/java/android/os/MountServiceResultCode.java
deleted file mode 100644
index e71dbf4..0000000
--- a/core/java/android/os/MountServiceResultCode.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import java.io.IOException;
-
-/**
- * Class that provides access to constants returned from MountService APIs
- *
- * {@hide}
- */
-public class MountServiceResultCode
-{
- public static final int OperationSucceeded = 0;
- public static final int OperationFailedInternalError = -1;
- public static final int OperationFailedNoMedia = -2;
- public static final int OperationFailedMediaBlank = -3;
- public static final int OperationFailedMediaCorrupt = -4;
- public static final int OperationFailedVolumeNotMounted = -5;
-}
diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java
index bc76180..b3df522 100644
--- a/core/java/android/os/Power.java
+++ b/core/java/android/os/Power.java
@@ -18,7 +18,7 @@
import java.io.IOException;
import android.os.ServiceManager;
-import android.os.IMountService;
+import android.os.storage.IMountService;
/**
* Class that provides access to some of the power management functions.
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
similarity index 82%
rename from core/java/android/os/IMountService.aidl
rename to core/java/android/os/storage/IMountService.aidl
index a5828f6..84e3f58 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -15,14 +15,15 @@
** limitations under the License.
*/
-package android.os;
+package android.os.storage;
-import android.os.IMountServiceListener;
+import android.os.storage.IMountServiceListener;
/** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
* In particular, the ordering of the methods below must match the
* _TRANSACTION enum in IMountService.cpp
- * @hide
+ * @hide - Applications should use android.os.storage.StorageManager to access
+ * storage functions.
*/
interface IMountService
{
@@ -38,31 +39,19 @@
void unregisterListener(IMountServiceListener listener);
/**
- * Gets an Array of supported share methods
+ * Returns true if a USB mass storage host is connected
*/
- String[] getShareMethodList();
+ boolean isUsbMassStorageConnected();
/**
- * Returns true if the share method is available
+ * Enables / disables USB mass storage.
*/
- boolean getShareMethodAvailable(String method);
+ int setUsbMassStorageEnabled(boolean enable);
/**
- * Shares a volume via the specified method
- * Returns an int consistent with MountServiceResultCode
+ * Returns true if a USB mass storage host is enabled (media is shared)
*/
- int shareVolume(String path, String method);
-
- /**
- * Unshares a volume via the specified method
- * Returns an int consistent with MountServiceResultCode
- */
- int unshareVolume(String path, String method);
-
- /**
- * Returns true if the volume is shared via the specified method.
- */
- boolean getVolumeShared(String path, String method);
+ boolean isUsbMassStorageEnabled();
/**
* Mount external storage at given mount point.
diff --git a/core/java/android/os/storage/IMountServiceListener.aidl b/core/java/android/os/storage/IMountServiceListener.aidl
new file mode 100644
index 0000000..883413a
--- /dev/null
+++ b/core/java/android/os/storage/IMountServiceListener.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+/**
+ * Callback class for receiving events from MountService.
+ *
+ * @hide - Applications should use android.os.storage.IStorageEventListener
+ * for storage event callbacks.
+ */
+interface IMountServiceListener {
+ /**
+ * Detection state of USB Mass Storage has changed
+ *
+ * @param available true if a UMS host is connected.
+ */
+ void onUsbMassStorageConnectionChanged(boolean connected);
+
+ /**
+ * Storage state has changed.
+ *
+ * @param path The volume mount path.
+ * @param oldState The old state of the volume.
+ * @param newState The new state of the volume.
+ *
+ * Note: State is one of the values returned by Environment.getExternalStorageState()
+ */
+ void onStorageStateChanged(String path, String oldState, String newState);
+}
diff --git a/core/java/android/os/storage/MountServiceListener.java b/core/java/android/os/storage/MountServiceListener.java
new file mode 100644
index 0000000..bebb3f6
--- /dev/null
+++ b/core/java/android/os/storage/MountServiceListener.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+/**
+ * Callback class for receiving progress reports during a restore operation. These
+ * methods will all be called on your application's main thread.
+ * @hide
+ */
+public abstract class MountServiceListener {
+ /**
+ * USB Mass storage connection state has changed.
+ *
+ * @param connected True if UMS is connected.
+ */
+ void onUsbMassStorageConnectionChanged(boolean connected) {
+ }
+
+ /**
+ * Storage state has changed.
+ *
+ * @param path The volume mount path.
+ * @param oldState The old state of the volume.
+ * @param newState The new state of the volume.
+ *
+ * @Note: State is one of the values returned by Environment.getExternalStorageState()
+ */
+ void onStorageStateChange(String path, String oldState, String newState) {
+ }
+}
diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java
new file mode 100644
index 0000000..d3d39d6
--- /dev/null
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+/**
+ * Used for receiving notifications from the StorageManager
+ */
+public abstract class StorageEventListener {
+ /**
+ * Called when the detection state of a USB Mass Storage host has changed.
+ * @param connected true if the USB mass storage is connected.
+ */
+ public void onUsbMassStorageConnectionChanged(boolean connected) {
+ }
+
+ /**
+ * Called when storage has changed state
+ * @param path the filesystem path for the storage
+ * @param oldState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
+ * @param newState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
+ */
+ public void onStorageStateChanged(String path, String oldState, String newState) {
+ }
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
new file mode 100644
index 0000000..e421ea5
--- /dev/null
+++ b/core/java/android/os/storage/StorageManager.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * StorageManager is the interface to the systems storage service.
+ * Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
+ * of {@link android.content.Context#STORAGE_SERVICE}.
+ *
+ */
+
+public class StorageManager
+{
+ private static final String TAG = "StorageManager";
+
+ /*
+ * Our internal MountService binder reference
+ */
+ private IMountService mMountService;
+
+ /*
+ * The looper target for callbacks
+ */
+ Looper mTgtLooper;
+
+ /*
+ * Target listener for binder callbacks
+ */
+ private MountServiceBinderListener mBinderListener;
+
+ /*
+ * List of our listeners
+ */
+ private ArrayList<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
+
+ private class MountServiceBinderListener extends IMountServiceListener.Stub {
+ public void onUsbMassStorageConnectionChanged(boolean available) {
+ final int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ mListeners.get(i).sendShareAvailabilityChanged(available);
+ }
+ }
+
+ public void onStorageStateChanged(String path, String oldState, String newState) {
+ final int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ mListeners.get(i).sendStorageStateChanged(path, oldState, newState);
+ }
+ }
+ }
+
+ /**
+ * Private base class for messages sent between the callback thread
+ * and the target looper handler.
+ */
+ private class StorageEvent {
+ public static final int EVENT_UMS_CONNECTION_CHANGED = 1;
+ public static final int EVENT_STORAGE_STATE_CHANGED = 2;
+
+ private Message mMessage;
+
+ public StorageEvent(int what) {
+ mMessage = Message.obtain();
+ mMessage.what = what;
+ mMessage.obj = this;
+ }
+
+ public Message getMessage() {
+ return mMessage;
+ }
+ }
+
+ /**
+ * Message sent on a USB mass storage connection change.
+ */
+ private class UmsConnectionChangedStorageEvent extends StorageEvent {
+ public boolean available;
+
+ public UmsConnectionChangedStorageEvent(boolean a) {
+ super(EVENT_UMS_CONNECTION_CHANGED);
+ available = a;
+ }
+ }
+
+ /**
+ * Message sent on volume state change.
+ */
+ private class StorageStateChangedStorageEvent extends StorageEvent {
+ public String path;
+ public String oldState;
+ public String newState;
+
+ public StorageStateChangedStorageEvent(String p, String oldS, String newS) {
+ super(EVENT_STORAGE_STATE_CHANGED);
+ path = p;
+ oldState = oldS;
+ newState = newS;
+ }
+ }
+
+ /**
+ * Private class containing sender and receiver code for StorageEvents.
+ */
+ private class ListenerDelegate {
+ final StorageEventListener mStorageEventListener;
+ private final Handler mHandler;
+
+ ListenerDelegate(StorageEventListener listener) {
+ mStorageEventListener = listener;
+ mHandler = new Handler(mTgtLooper) {
+ @Override
+ public void handleMessage(Message msg) {
+ StorageEvent e = (StorageEvent) msg.obj;
+
+ if (msg.what == StorageEvent.EVENT_UMS_CONNECTION_CHANGED) {
+ UmsConnectionChangedStorageEvent ev = (UmsConnectionChangedStorageEvent) e;
+ mStorageEventListener.onUsbMassStorageConnectionChanged(ev.available);
+ } else if (msg.what == StorageEvent.EVENT_STORAGE_STATE_CHANGED) {
+ StorageStateChangedStorageEvent ev = (StorageStateChangedStorageEvent) e;
+ mStorageEventListener.onStorageStateChanged(ev.path, ev.oldState, ev.newState);
+ } else {
+ Log.e(TAG, "Unsupported event " + msg.what);
+ }
+ }
+ };
+ }
+
+ StorageEventListener getListener() {
+ return mStorageEventListener;
+ }
+
+ void sendShareAvailabilityChanged(boolean available) {
+ UmsConnectionChangedStorageEvent e = new UmsConnectionChangedStorageEvent(available);
+ mHandler.sendMessage(e.getMessage());
+ }
+
+ void sendStorageStateChanged(String path, String oldState, String newState) {
+ StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);
+ mHandler.sendMessage(e.getMessage());
+ }
+ }
+
+ /**
+ * Constructs a StorageManager object through which an application can
+ * can communicate with the systems mount service.
+ *
+ * @param tgtLooper The {@android.os.Looper} which events will be received on.
+ *
+ * <p>Applications can get instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
+ * of {@link android.content.Context#STORAGE_SERVICE}.
+ *
+ * @hide
+ */
+ public StorageManager(Looper tgtLooper) throws RemoteException {
+ mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
+ if (mMountService == null) {
+ Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
+ return;
+ }
+ mTgtLooper = tgtLooper;
+ mBinderListener = new MountServiceBinderListener();
+ mMountService.registerListener(mBinderListener);
+ }
+
+
+ /**
+ * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
+ *
+ * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
+ *
+ */
+ public void registerListener(StorageEventListener listener) {
+ if (listener == null) {
+ return;
+ }
+
+ synchronized (mListeners) {
+ mListeners.add(new ListenerDelegate(listener));
+ }
+ }
+
+ /**
+ * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
+ *
+ * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
+ *
+ */
+ public void unregisterListener(StorageEventListener listener) {
+ if (listener == null) {
+ return;
+ }
+
+ synchronized (mListeners) {
+ final int size = mListeners.size();
+ for (int i=0 ; i<size ; i++) {
+ ListenerDelegate l = mListeners.get(i);
+ if (l.getListener() == listener) {
+ mListeners.remove(i);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Enables USB Mass Storage (UMS) on the device.
+ * @return an integer value representing the outcome of the operation.
+ * @see android.os.storage.StorageResultCode
+ */
+ public int enableUsbMassStorage() {
+ try {
+ return mMountService.setUsbMassStorageEnabled(true);
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to enable UMS", ex);
+ }
+ return StorageResultCode.OperationFailedInternalError;
+ }
+
+ /**
+ * Disables USB Mass Storage (UMS) on the device.
+ * @return an integer value representing the outcome of the operation.
+ * @see android.os.storage.StorageResultCode
+ */
+ public int disableUsbMassStorage() {
+ try {
+ return mMountService.setUsbMassStorageEnabled(false);
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to disable UMS", ex);
+ }
+ return StorageResultCode.OperationFailedInternalError;
+ }
+
+ /**
+ * Query if a USB Mass Storage (UMS) host is connected.
+ * @return true if UMS host is connected.
+ */
+ public boolean isUsbMassStorageConnected() {
+ try {
+ return mMountService.isUsbMassStorageConnected();
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to get UMS connection state", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Query if a USB Mass Storage (UMS) is enabled on the device.
+ * @return true if UMS host is enabled.
+ */
+ public boolean isUsbMassStorageEnabled() {
+ try {
+ return mMountService.isUsbMassStorageEnabled();
+ } catch (RemoteException rex) {
+ Log.e(TAG, "Failed to get UMS enable state", rex);
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/os/storage/StorageResultCode.java b/core/java/android/os/storage/StorageResultCode.java
new file mode 100644
index 0000000..584f160
--- /dev/null
+++ b/core/java/android/os/storage/StorageResultCode.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+/**
+ * Class that provides access to constants returned from StorageManager
+ * and lower level MountService APIs.
+ */
+public class StorageResultCode
+{
+ /**
+ * Operation succeeded.
+ * @see android.os.storage.StorageManager
+ */
+ public static final int OperationSucceeded = 0;
+
+ /**
+ * Operation failed: Internal error.
+ * @see android.os.storage.StorageManager
+ */
+ public static final int OperationFailedInternalError = -1;
+
+ /**
+ * Operation failed: Missing media.
+ * @see android.os.storage.StorageManager
+ */
+ public static final int OperationFailedNoMedia = -2;
+
+ /**
+ * Operation failed: Media is blank.
+ * @see android.os.storage.StorageManager
+ */
+ public static final int OperationFailedMediaBlank = -3;
+
+ /**
+ * Operation failed: Media is corrupt.
+ * @see android.os.storage.StorageManager
+ */
+ public static final int OperationFailedMediaCorrupt = -4;
+
+ /**
+ * Operation failed: Media not mounted.
+ * @see android.os.storage.StorageManager
+ */
+ public static final int OperationFailedVolumeNotMounted = -5;
+}
diff --git a/core/java/android/storage/StorageEventListener.java b/core/java/android/storage/StorageEventListener.java
deleted file mode 100644
index cd71090..0000000
--- a/core/java/android/storage/StorageEventListener.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.storage;
-
-/**
- * Used for receiving notifications from the StorageManager
- */
-public interface StorageEventListener {
- /**
- * Called when the ability to share a volume has changed.
- * @param method the share-method which has changed.
- * @param available true if the share is available.
- */
- public void onShareAvailabilityChanged(String method, boolean available);
-
- /**
- * Called when media has been inserted
- * @param label the system defined label for the volume.
- * @param path the filesystem path for the volume.
- * @param major the major number of the device.
- * @param minor the minor number of the device.
- */
- public void onMediaInserted(String label, String path, int major, int minor);
-
- /**
- * Called when media has been removed
- * @param label the system defined label for the volume.
- * @param path the filesystem path for the volume.
- * @param major the major number of the device.
- * @param minor the minor number of the device.
- * @param clean the media was removed cleanly.
- */
- public void onMediaRemoved(String label, String path, int major, int minor, boolean clean);
-
- /**
- * Called when a volume has changed state
- * @param label the system defined label for the volume.
- * @param path the filesystem path for the volume.
- * @param oldState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
- * @param newState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
- */
- public void onVolumeStateChanged(String label, String path, String oldState, String newState);
-}
diff --git a/core/java/android/storage/StorageManager.java b/core/java/android/storage/StorageManager.java
deleted file mode 100644
index 924d169..0000000
--- a/core/java/android/storage/StorageManager.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.storage;
-
-import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.Handler;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.IMountService;
-import android.os.IMountServiceListener;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Class that lets you access the device's storage management functions. Get an instance of this
- * class by calling {@link android.content.Context#getSystemService(java.lang.String)
- * Context.getSystemService()} with an argument of {@link android.content.Context#STORAGE_SERVICE}.
- */
-public class StorageManager
-{
- private static final String TAG = "StorageManager";
-
- /*
- * Our internal MountService binder reference
- */
- private IMountService mMountService;
-
- /*
- * The looper target for callbacks
- */
- Looper mTgtLooper;
-
- /*
- * Target listener for binder callbacks
- */
- private MountServiceBinderListener mBinderListener;
-
- /*
- * *static* list of our listeners
- */
- static final ArrayList<ListenerDelegate> sListeners = new ArrayList<ListenerDelegate>();
-
- private class MountServiceBinderListener extends IMountServiceListener.Stub {
- public void onShareAvailabilityChanged(String method, boolean available) {
- final int size = sListeners.size();
- for (int i = 0; i < size; i++) {
- sListeners.get(i).sendShareAvailabilityChanged(method, available);
- }
- }
-
- public void onMediaInserted(String label, String path, int major, int minor) {
- final int size = sListeners.size();
- for (int i = 0; i < size; i++) {
- sListeners.get(i).sendMediaInserted(label, path, major, minor);
- }
- }
-
- public void onMediaRemoved(String label, String path, int major, int minor, boolean clean) {
- final int size = sListeners.size();
- for (int i = 0; i < size; i++) {
- sListeners.get(i).sendMediaRemoved(label, path, major, minor, clean);
- }
- }
-
- public void onVolumeStateChanged(String label, String path, String oldState, String newState) {
- final int size = sListeners.size();
- for (int i = 0; i < size; i++) {
- sListeners.get(i).sendVolumeStateChanged(label, path, oldState, newState);
- }
- }
- }
-
- /**
- * Private base class for messages sent between the callback thread
- * and the target looper handler
- */
- private class StorageEvent {
- public static final int EVENT_SHARE_AVAILABILITY_CHANGED = 1;
- public static final int EVENT_MEDIA_INSERTED = 2;
- public static final int EVENT_MEDIA_REMOVED = 3;
- public static final int EVENT_VOLUME_STATE_CHANGED = 4;
-
- private Message mMessage;
-
- public StorageEvent(int what) {
- mMessage = Message.obtain();
- mMessage.what = what;
- mMessage.obj = this;
- }
-
- public Message getMessage() {
- return mMessage;
- }
- }
-
- /**
- * Message sent on a share availability change.
- */
- private class ShareAvailabilityChangedStorageEvent extends StorageEvent {
- public String method;
- public boolean available;
-
- public ShareAvailabilityChangedStorageEvent(String m, boolean a) {
- super(EVENT_SHARE_AVAILABILITY_CHANGED);
- method = m;
- available = a;
- }
- }
-
- /**
- * Message sent on media insertion
- */
- private class MediaInsertedStorageEvent extends StorageEvent {
- public String label;
- public String path;
- public int major;
- public int minor;
-
- public MediaInsertedStorageEvent(String l, String p, int maj, int min) {
- super(EVENT_MEDIA_INSERTED);
- label = l;
- path = p;
- major = maj;
- minor = min;
- }
- }
-
- /**
- * Message sent on media removal
- */
- private class MediaRemovedStorageEvent extends StorageEvent {
- public String label;
- public String path;
- public int major;
- public int minor;
- public boolean clean;
-
- public MediaRemovedStorageEvent(String l, String p, int maj, int min, boolean c) {
- super(EVENT_MEDIA_REMOVED);
- label = l;
- path = p;
- major = maj;
- minor = min;
- clean = c;
- }
- }
-
- /**
- * Message sent on volume state change
- */
- private class VolumeStateChangedStorageEvent extends StorageEvent {
- public String label;
- public String path;
- public String oldState;
- public String newState;
-
- public VolumeStateChangedStorageEvent(String l, String p, String oldS, String newS) {
- super(EVENT_VOLUME_STATE_CHANGED);
- label = l;
- path = p;
- oldState = oldS;
- newState = newS;
- }
- }
-
- /**
- * Private class containing sender and receiver code for StorageEvents
- */
- private class ListenerDelegate {
- final StorageEventListener mStorageEventListener;
- private final Handler mHandler;
-
- ListenerDelegate(StorageEventListener listener) {
- mStorageEventListener = listener;
- mHandler = new Handler(mTgtLooper) {
- @Override
- public void handleMessage(Message msg) {
- StorageEvent e = (StorageEvent) msg.obj;
-
- if (msg.what == StorageEvent.EVENT_SHARE_AVAILABILITY_CHANGED) {
- ShareAvailabilityChangedStorageEvent ev = (ShareAvailabilityChangedStorageEvent) e;
- mStorageEventListener.onShareAvailabilityChanged(ev.method, ev.available);
- } else if (msg.what == StorageEvent.EVENT_MEDIA_INSERTED) {
- MediaInsertedStorageEvent ev = (MediaInsertedStorageEvent) e;
- mStorageEventListener.onMediaInserted(ev.label, ev.path, ev.major, ev.minor);
- } else if (msg.what == StorageEvent.EVENT_MEDIA_REMOVED) {
- MediaRemovedStorageEvent ev = (MediaRemovedStorageEvent) e;
- mStorageEventListener.onMediaRemoved(ev.label, ev.path, ev.major, ev.minor, ev.clean);
- } else if (msg.what == StorageEvent.EVENT_VOLUME_STATE_CHANGED) {
- VolumeStateChangedStorageEvent ev = (VolumeStateChangedStorageEvent) e;
- mStorageEventListener.onVolumeStateChanged(ev.label, ev.path, ev.oldState, ev.newState);
- } else {
- Log.e(TAG, "Unsupported event " + msg.what);
- }
- }
- };
- }
-
- StorageEventListener getListener() {
- return mStorageEventListener;
- }
-
- void sendShareAvailabilityChanged(String method, boolean available) {
- ShareAvailabilityChangedStorageEvent e = new ShareAvailabilityChangedStorageEvent(method, available);
- mHandler.sendMessage(e.getMessage());
- }
-
- void sendMediaInserted(String label, String path, int major, int minor) {
- MediaInsertedStorageEvent e = new MediaInsertedStorageEvent(label, path, major, minor);
- mHandler.sendMessage(e.getMessage());
- }
-
- void sendMediaRemoved(String label, String path, int major, int minor, boolean clean) {
- MediaRemovedStorageEvent e = new MediaRemovedStorageEvent(label, path, major, minor, clean);
- mHandler.sendMessage(e.getMessage());
- }
-
- void sendVolumeStateChanged(String label, String path, String oldState, String newState) {
- VolumeStateChangedStorageEvent e = new VolumeStateChangedStorageEvent(label, path, oldState, newState);
- mHandler.sendMessage(e.getMessage());
- }
- }
-
- /**
- * {@hide}
- */
- public StorageManager(Looper tgtLooper) throws RemoteException {
- mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
- if (mMountService == null) {
- Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
- return;
- }
- mTgtLooper = tgtLooper;
- mBinderListener = new MountServiceBinderListener();
- mMountService.registerListener(mBinderListener);
- }
-
-
- /**
- * Registers a {@link android.storage.StorageEventListener StorageEventListener}.
- *
- * @param listener A {@link android.storage.StorageEventListener StorageEventListener} object.
- *
- */
- public void registerListener(StorageEventListener listener) {
- if (listener == null) {
- return;
- }
-
- synchronized (sListeners) {
- sListeners.add(new ListenerDelegate(listener));
- }
- }
-
- /**
- * Unregisters a {@link android.storage.StorageEventListener StorageEventListener}.
- *
- * @param listener A {@link android.storage.StorageEventListener StorageEventListener} object.
- *
- */
- public void unregisterListener(StorageEventListener listener) {
- if (listener == null) {
- return;
- }
- synchronized (sListeners) {
- final int size = sListeners.size();
- for (int i=0 ; i<size ; i++) {
- ListenerDelegate l = sListeners.get(i);
- if (l.getListener() == listener) {
- sListeners.remove(i);
- break;
- }
- }
- }
- }
-}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5c465e..889985a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8281,6 +8281,9 @@
* Cancels any animations for this view.
*/
public void clearAnimation() {
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.cancel();
+ }
mCurrentAnimation = null;
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 000e4ce..ad98259 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -256,6 +256,27 @@
}
/**
+ * Cancel the animation. Cancelling an animation invokes the animation
+ * listener, if set, to notify the end of the animation.
+ *
+ * If you cancel an animation manually, you must call {@link #reset()}
+ * before starting the animation again.
+ *
+ * @see #reset()
+ * @see #start()
+ * @see #startNow()
+ */
+ public void cancel() {
+ if (mStarted && !mEnded) {
+ if (mListener != null) mListener.onAnimationEnd(this);
+ mEnded = true;
+ }
+ // Make sure we move the animation to the end
+ mStartTime = Long.MIN_VALUE;
+ mMore = mOneMoreTime = false;
+ }
+
+ /**
* Whether or not the animation has been initialized.
*
* @return Has this animation been initialized.
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 68be08d..9e9cc7e 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -816,6 +816,9 @@
int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
| EditorInfo.IME_FLAG_NO_FULLSCREEN;
switch (type) {
+ case 0: // NORMAL_TEXT_FIELD
+ imeOptions |= EditorInfo.IME_ACTION_GO;
+ break;
case 1: // TEXT_AREA
single = false;
inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
@@ -851,6 +854,7 @@
imeOptions |= EditorInfo.IME_ACTION_GO;
break;
default:
+ imeOptions |= EditorInfo.IME_ACTION_GO;
break;
}
setHint(null);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 52c0e01..8fa8f09 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3379,6 +3379,10 @@
text = "";
}
mWebTextView.setTextAndKeepSelection(text);
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null && imm.isActive(mWebTextView)) {
+ imm.restartInput(mWebTextView);
+ }
}
mWebTextView.requestFocus();
}
@@ -6172,6 +6176,9 @@
// mContentHeight may not be updated yet
y = Math.max(0,
(Math.min(maxHeight, y + viewHeight) - viewHeight));
+ // We need to take into account the visible title height
+ // when scrolling since y is an absolute view position.
+ y = Math.max(0, y - getVisibleTitleHeight());
scrollTo(x, y);
}
break;
diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
index 2b07ae6..7e9bbd1 100644
--- a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
+++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
@@ -24,7 +24,7 @@
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IMountService;
+import android.os.storage.IMountService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 98fb236..24818a8 100755
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -24,7 +24,6 @@
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IMountService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index c110f95..2f48499 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -32,7 +32,7 @@
import android.os.Power;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.IMountService;
+import android.os.storage.IMountService;
import com.android.internal.telephony.ITelephony;
import android.util.Log;
diff --git a/core/java/com/android/internal/app/StorageNotification.java b/core/java/com/android/internal/app/StorageNotification.java
new file mode 100644
index 0000000..8876612
--- /dev/null
+++ b/core/java/com/android/internal/app/StorageNotification.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.storage.IMountService;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageResultCode;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class StorageNotification extends StorageEventListener {
+ private static final String TAG = "StorageNotification";
+
+ /**
+ * Binder context for this service
+ */
+ private Context mContext;
+
+ /**
+ * The notification that is shown when a USB mass storage host
+ * is connected.
+ * <p>
+ * This is lazily created, so use {@link #setUsbStorageNotification()}.
+ */
+ private Notification mUsbStorageNotification;
+
+ /**
+ * The notification that is shown when the following media events occur:
+ * - Media is being checked
+ * - Media is blank (or unknown filesystem)
+ * - Media is corrupt
+ * - Media is safe to unmount
+ * - Media is missing
+ * <p>
+ * This is lazily created, so use {@link #setMediaStorageNotification()}.
+ */
+ private Notification mMediaStorageNotification;
+ private boolean mUmsAvailable;
+ private StorageManager mStorageManager;
+
+ public StorageNotification(Context context) {
+ mContext = context;
+
+ mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+ mUmsAvailable = mStorageManager.isUsbMassStorageConnected();
+ Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable,
+ Environment.getExternalStorageState()));
+ }
+
+ /*
+ * @override com.android.os.storage.StorageEventListener
+ */
+ @Override
+ public void onUsbMassStorageConnectionChanged(boolean connected) {
+ mUmsAvailable = connected;
+ /*
+ * Even though we may have a UMS host connected, we the SD card
+ * may not be in a state for export.
+ */
+ String st = Environment.getExternalStorageState();
+
+ Log.i(TAG, String.format("UMS connection changed to %s (media state %s)", connected, st));
+
+ if (connected && (st.equals(
+ Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) {
+ /*
+ * No card or card being checked = don't display
+ */
+ connected = false;
+ }
+ updateUsbMassStorageNotification(connected);
+ }
+
+ /*
+ * @override com.android.os.storage.StorageEventListener
+ */
+ @Override
+ public void onStorageStateChanged(String path, String oldState, String newState) {
+ Log.i(TAG, String.format(
+ "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
+ if (newState.equals(Environment.MEDIA_SHARED)) {
+ /*
+ * Storage is now shared. Modify the UMS notification
+ * for stopping UMS.
+ */
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+ setUsbStorageNotification(
+ com.android.internal.R.string.usb_storage_stop_notification_title,
+ com.android.internal.R.string.usb_storage_stop_notification_message,
+ com.android.internal.R.drawable.stat_sys_warning, false, true, pi);
+ } else if (newState.equals(Environment.MEDIA_CHECKING)) {
+ /*
+ * Storage is now checking. Update media notification and disable
+ * UMS notification.
+ */
+ setMediaStorageNotification(
+ com.android.internal.R.string.ext_media_checking_notification_title,
+ com.android.internal.R.string.ext_media_checking_notification_message,
+ com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null);
+ updateUsbMassStorageNotification(false);
+ } else if (newState.equals(Environment.MEDIA_MOUNTED)) {
+ /*
+ * Storage is now mounted. Dismiss any media notifications,
+ * and enable UMS notification if connected.
+ */
+ setMediaStorageNotification(0, 0, 0, false, false, null);
+ updateUsbMassStorageNotification(mUmsAvailable);
+ } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) {
+ /*
+ * Storage is now unmounted. We may have been unmounted
+ * because the user is enabling/disabling UMS, in which case we don't
+ * want to display the 'safe to unmount' notification.
+ */
+ if (!mStorageManager.isUsbMassStorageEnabled()) {
+ if (oldState.equals(Environment.MEDIA_SHARED)) {
+ /*
+ * The unmount was due to UMS being enabled. Dismiss any
+ * media notifications, and enable UMS notification if connected
+ */
+ setMediaStorageNotification(0, 0, 0, false, false, null);
+ updateUsbMassStorageNotification(mUmsAvailable);
+ } else {
+ /*
+ * Show safe to unmount media notification, and enable UMS
+ * notification if connected.
+ */
+ setMediaStorageNotification(
+ com.android.internal.R.string.ext_media_safe_unmount_notification_title,
+ com.android.internal.R.string.ext_media_safe_unmount_notification_message,
+ com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
+ updateUsbMassStorageNotification(mUmsAvailable);
+ }
+ } else {
+ /*
+ * The unmount was due to UMS being enabled. Dismiss any
+ * media notifications, and disable the UMS notification
+ */
+ setMediaStorageNotification(0, 0, 0, false, false, null);
+ updateUsbMassStorageNotification(false);
+ }
+ } else if (newState.equals(Environment.MEDIA_NOFS)) {
+ /*
+ * Storage has no filesystem. Show blank media notification,
+ * and enable UMS notification if connected.
+ */
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ setMediaStorageNotification(
+ com.android.internal.R.string.ext_media_nofs_notification_title,
+ com.android.internal.R.string.ext_media_nofs_notification_message,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
+ updateUsbMassStorageNotification(mUmsAvailable);
+ } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) {
+ /*
+ * Storage is corrupt. Show corrupt media notification,
+ * and enable UMS notification if connected.
+ */
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ setMediaStorageNotification(
+ com.android.internal.R.string.ext_media_unmountable_notification_title,
+ com.android.internal.R.string.ext_media_unmountable_notification_message,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
+ updateUsbMassStorageNotification(mUmsAvailable);
+ } else if (newState.equals(Environment.MEDIA_REMOVED)) {
+ /*
+ * Storage has been removed. Show nomedia media notification,
+ * and disable UMS notification regardless of connection state.
+ */
+ setMediaStorageNotification(
+ com.android.internal.R.string.ext_media_nomedia_notification_title,
+ com.android.internal.R.string.ext_media_nomedia_notification_message,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb,
+ true, false, null);
+ updateUsbMassStorageNotification(false);
+ } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) {
+ /*
+ * Storage has been removed unsafely. Show bad removal media notification,
+ * and disable UMS notification regardless of connection state.
+ */
+ setMediaStorageNotification(
+ com.android.internal.R.string.ext_media_badremoval_notification_title,
+ com.android.internal.R.string.ext_media_badremoval_notification_message,
+ com.android.internal.R.drawable.stat_sys_warning,
+ true, true, null);
+ updateUsbMassStorageNotification(false);
+ } else {
+ Log.w(TAG, String.format("Ignoring unknown state {%s}", newState));
+ }
+ }
+
+ /**
+ * Update the state of the USB mass storage notification
+ */
+ void updateUsbMassStorageNotification(boolean available) {
+
+ if (available) {
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+ setUsbStorageNotification(
+ com.android.internal.R.string.usb_storage_notification_title,
+ com.android.internal.R.string.usb_storage_notification_message,
+ com.android.internal.R.drawable.stat_sys_data_usb,
+ false, true, pi);
+ } else {
+ setUsbStorageNotification(0, 0, 0, false, false, null);
+ }
+ }
+
+ /**
+ * Sets the USB storage notification.
+ */
+ private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
+ PendingIntent pi) {
+
+ if (!visible && mUsbStorageNotification == null) {
+ return;
+ }
+
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (notificationManager == null) {
+ return;
+ }
+
+ if (visible) {
+ Resources r = Resources.getSystem();
+ CharSequence title = r.getText(titleId);
+ CharSequence message = r.getText(messageId);
+
+ if (mUsbStorageNotification == null) {
+ mUsbStorageNotification = new Notification();
+ mUsbStorageNotification.icon = icon;
+ mUsbStorageNotification.when = 0;
+ }
+
+ if (sound) {
+ mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+ } else {
+ mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+ }
+
+ mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
+
+ mUsbStorageNotification.tickerText = title;
+ if (pi == null) {
+ Intent intent = new Intent();
+ pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ }
+
+ mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+ }
+
+ final int notificationId = mUsbStorageNotification.icon;
+ if (visible) {
+ notificationManager.notify(notificationId, mUsbStorageNotification);
+ } else {
+ notificationManager.cancel(notificationId);
+ }
+ }
+
+ private synchronized boolean getMediaStorageNotificationDismissable() {
+ if ((mMediaStorageNotification != null) &&
+ ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
+ Notification.FLAG_AUTO_CANCEL))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Sets the media storage notification.
+ */
+ private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
+ boolean dismissable, PendingIntent pi) {
+
+ if (!visible && mMediaStorageNotification == null) {
+ return;
+ }
+
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (notificationManager == null) {
+ return;
+ }
+
+ if (mMediaStorageNotification != null && visible) {
+ /*
+ * Dismiss the previous notification - we're about to
+ * re-use it.
+ */
+ final int notificationId = mMediaStorageNotification.icon;
+ notificationManager.cancel(notificationId);
+ }
+
+ if (visible) {
+ Resources r = Resources.getSystem();
+ CharSequence title = r.getText(titleId);
+ CharSequence message = r.getText(messageId);
+
+ if (mMediaStorageNotification == null) {
+ mMediaStorageNotification = new Notification();
+ mMediaStorageNotification.when = 0;
+ }
+
+ mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+
+ if (dismissable) {
+ mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
+ } else {
+ mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ }
+
+ mMediaStorageNotification.tickerText = title;
+ if (pi == null) {
+ Intent intent = new Intent();
+ pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ }
+
+ mMediaStorageNotification.icon = icon;
+ mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+ }
+
+ final int notificationId = mMediaStorageNotification.icon;
+ if (visible) {
+ notificationManager.notify(notificationId, mMediaStorageNotification);
+ } else {
+ notificationManager.cancel(notificationId);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
index 2b93dbc..cb268b3 100644
--- a/core/java/com/android/internal/app/TetherActivity.java
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -25,7 +25,6 @@
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IMountService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
diff --git a/core/java/com/android/internal/app/UsbStorageActivity.java b/core/java/com/android/internal/app/UsbStorageActivity.java
index 34ae2b4..991f04b 100644
--- a/core/java/com/android/internal/app/UsbStorageActivity.java
+++ b/core/java/com/android/internal/app/UsbStorageActivity.java
@@ -25,8 +25,9 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Environment;
-import android.os.IMountService;
-import android.os.MountServiceResultCode;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageResultCode;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -35,6 +36,7 @@
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
+import android.util.Log;
/**
* This activity is shown to the user for him/her to enable USB mass storage
@@ -42,11 +44,13 @@
* dialog style. It will be launched from a notification.
*/
public class UsbStorageActivity extends Activity {
+ private static final String TAG = "UsbStorageActivity";
private Button mMountButton;
private Button mUnmountButton;
private TextView mBanner;
private TextView mMessage;
private ImageView mIcon;
+ private StorageManager mStorageManager = null;
/** Used to detect when the USB cable is unplugged, so we can call finish() */
private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
@@ -57,11 +61,30 @@
}
}
};
+
+ private StorageEventListener mStorageListener = new StorageEventListener() {
+ @Override
+ public void onStorageStateChanged(String path, String oldState, String newState) {
+ if (newState.equals(Environment.MEDIA_SHARED)) {
+ switchDisplay(true);
+ } else {
+ switchDisplay(false);
+ }
+ }
+ };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (mStorageManager == null) {
+ mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+ if (mStorageManager == null) {
+ Log.w(TAG, "Failed to get StorageManager");
+ }
+ mStorageManager.registerListener(mStorageListener);
+ }
+
setTitle(getString(com.android.internal.R.string.usb_storage_activity_title));
setContentView(com.android.internal.R.layout.usb_storage_activity);
@@ -74,9 +97,11 @@
mMountButton.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v) {
- mountAsUsbStorage();
- // TODO: replace with forthcoming MountService callbacks
- switchDisplay(true);
+ int rc = mStorageManager.enableUsbMassStorage();
+ if (rc != StorageResultCode.OperationSucceeded) {
+ Log.e(TAG, String.format("UMS enable failed (%d)", rc));
+ showSharingError();
+ }
}
});
@@ -84,9 +109,11 @@
mUnmountButton.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v) {
- stopUsbStorage();
- // TODO: replace with forthcoming MountService callbacks
- switchDisplay(false);
+ int rc = mStorageManager.disableUsbMassStorage();
+ if (rc != StorageResultCode.OperationSucceeded) {
+ Log.e(TAG, String.format("UMS disable failed (%d)", rc));
+ showStoppingError();
+ }
}
});
}
@@ -112,19 +139,11 @@
super.onResume();
registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-
- boolean umsOn = false;
try {
- IMountService mountService = IMountService.Stub.asInterface(ServiceManager
- .getService("mount"));
- if (mountService != null) {
- umsOn = mountService.getVolumeShared(
- Environment.getExternalStorageDirectory().getPath(), "ums");
- }
- } catch (android.os.RemoteException exc) {
- // pass
+ switchDisplay(mStorageManager.isUsbMassStorageEnabled());
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to read UMS enable state", ex);
}
- switchDisplay(umsOn);
}
@Override
@@ -134,42 +153,6 @@
unregisterReceiver(mBatteryReceiver);
}
- private void mountAsUsbStorage() {
- IMountService mountService = IMountService.Stub.asInterface(ServiceManager
- .getService("mount"));
- if (mountService == null) {
- showSharingError();
- return;
- }
-
- try {
- if (mountService.shareVolume(
- Environment.getExternalStorageDirectory().getPath(), "ums") !=
- MountServiceResultCode.OperationSucceeded) {
- showSharingError();
- }
- } catch (RemoteException e) {
- showSharingError();
- }
- }
-
- private void stopUsbStorage() {
- IMountService mountService = IMountService.Stub.asInterface(ServiceManager
- .getService("mount"));
- if (mountService == null) {
- showStoppingError();
- return;
- }
-
- try {
- mountService.unshareVolume(
- Environment.getExternalStorageDirectory().getPath(), "ums");
- } catch (RemoteException e) {
- showStoppingError();
- return;
- }
- }
-
private void handleBatteryChanged(Intent intent) {
int pluggedType = intent.getIntExtra("plugged", 0);
if (pluggedType == 0) {
diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png
index 855484a..6a97d5b 100644
--- a/core/res/res/drawable-mdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-mdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_def_app_icon.png b/core/res/res/drawable-mdpi/sym_def_app_icon.png
index 8be3b54..9777d11 100644
--- a/core/res/res/drawable-mdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-mdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
index fad4349..fb5a36f 100644
--- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -16,6 +16,7 @@
package android.database;
+import dalvik.annotation.BrokenTest;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@@ -373,7 +374,9 @@
c.close();
}
- @LargeTest
+ //@LargeTest
+ @BrokenTest("Consistently times out")
+ @Suppress
public void testLoadingThread() throws Exception {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
@@ -398,7 +401,9 @@
c.close();
}
- @LargeTest
+ //@LargeTest
+ @BrokenTest("Consistently times out")
+ @Suppress
public void testLoadingThreadClose() throws Exception {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
@@ -450,9 +455,11 @@
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
final int count = 36799;
+ mDatabase.execSQL("BEGIN Transaction;");
for (int i = 0; i < count; i++) {
mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
}
+ mDatabase.execSQL("COMMIT;");
Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
assertNotNull(c);
@@ -484,9 +491,11 @@
// if cursor window size changed, adjust this value too
final int count = 600; // more than two fillWindow needed
+ mDatabase.execSQL("BEGIN Transaction;");
for (int i = 0; i < count; i++) {
mDatabase.execSQL(sql.toString());
}
+ mDatabase.execSQL("COMMIT;");
Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
assertNotNull(c);
@@ -513,6 +522,7 @@
// if cursor window size changed, adjust this value too
final int count = 600;
+ mDatabase.execSQL("BEGIN Transaction;");
for (int i = 0; i < count; i++) {
StringBuilder sql = new StringBuilder(2100);
sql.append("INSERT INTO test (txt, data) VALUES ('");
@@ -522,6 +532,7 @@
sql.append("');");
mDatabase.execSQL(sql.toString());
}
+ mDatabase.execSQL("COMMIT;");
Cursor c = mDatabase.query("test", new String[]{"txt", "data"}, null, null, null, null, null);
assertNotNull(c);
diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
old mode 100755
new mode 100644
index 61460b1..a935f16
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackLegacy.ttf b/data/fonts/DroidSansFallbackLegacy.ttf
new file mode 100644
index 0000000..61460b1
--- /dev/null
+++ b/data/fonts/DroidSansFallbackLegacy.ttf
Binary files differ
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 965b7dd..2d6152e 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -39,7 +39,6 @@
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
-#include <ui/DisplayInfo.h>
#include <pixelflinger/pixelflinger.h>
#include <GLES/gl.h>
@@ -350,8 +349,8 @@
mServerCblk->connected |= 1<<dpy;
display_cblk_t* dcblk = mServerCblk->displays + dpy;
memset(dcblk, 0, sizeof(display_cblk_t));
- dcblk->w = w;
- dcblk->h = h;
+ dcblk->w = plane.getWidth();
+ dcblk->h = plane.getHeight();
dcblk->format = f;
dcblk->orientation = ISurfaceComposer::eOrientationDefault;
dcblk->xdpi = hw.getDpiX();
@@ -621,14 +620,8 @@
const DisplayHardware& hw(plane.displayHardware());
volatile display_cblk_t* dcblk = mServerCblk->displays + dpy;
dcblk->orientation = orientation;
- if (orientation & eOrientationSwapMask) {
- // 90 or 270 degrees orientation
- dcblk->w = hw.getHeight();
- dcblk->h = hw.getWidth();
- } else {
- dcblk->w = hw.getWidth();
- dcblk->h = hw.getHeight();
- }
+ dcblk->w = plane.getWidth();
+ dcblk->h = plane.getHeight();
mVisibleRegionsDirty = true;
mDirtyRegion.set(hw.bounds());
@@ -1795,13 +1788,47 @@
return mHw ? true : false;
}
-void GraphicPlane::setDisplayHardware(DisplayHardware *hw) {
- mHw = hw;
+int GraphicPlane::getWidth() const {
+ return mWidth;
}
-void GraphicPlane::setTransform(const Transform& tr) {
- mTransform = tr;
- mGlobalTransform = mOrientationTransform * mTransform;
+int GraphicPlane::getHeight() const {
+ return mHeight;
+}
+
+void GraphicPlane::setDisplayHardware(DisplayHardware *hw)
+{
+ mHw = hw;
+
+ // initialize the display orientation transform.
+ // it's a constant that should come from the display driver.
+ int displayOrientation = ISurfaceComposer::eOrientationDefault;
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get("ro.sf.hwrotation", property, NULL) > 0) {
+ //displayOrientation
+ switch (atoi(property)) {
+ case 90:
+ displayOrientation = ISurfaceComposer::eOrientation90;
+ break;
+ case 270:
+ displayOrientation = ISurfaceComposer::eOrientation270;
+ break;
+ }
+ }
+
+ const float w = hw->getWidth();
+ const float h = hw->getHeight();
+ GraphicPlane::orientationToTransfrom(displayOrientation, w, h,
+ &mDisplayTransform);
+ if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) {
+ mDisplayWidth = h;
+ mDisplayHeight = w;
+ } else {
+ mDisplayWidth = w;
+ mDisplayHeight = h;
+ }
+
+ setOrientation(ISurfaceComposer::eOrientationDefault);
}
status_t GraphicPlane::orientationToTransfrom(
@@ -1810,8 +1837,9 @@
float a, b, c, d, x, y;
switch (orientation) {
case ISurfaceComposer::eOrientationDefault:
- a=1; b=0; c=0; d=1; x=0; y=0;
- break;
+ // make sure the default orientation is optimal
+ tr->reset();
+ return NO_ERROR;
case ISurfaceComposer::eOrientation90:
a=0; b=-1; c=1; d=0; x=w; y=0;
break;
@@ -1831,20 +1859,16 @@
status_t GraphicPlane::setOrientation(int orientation)
{
- const DisplayHardware& hw(displayHardware());
- const float w = hw.getWidth();
- const float h = hw.getHeight();
-
- if (orientation == ISurfaceComposer::eOrientationDefault) {
- // make sure the default orientation is optimal
- mOrientationTransform.reset();
- mOrientation = orientation;
- mGlobalTransform = mTransform;
- return NO_ERROR;
- }
-
// If the rotation can be handled in hardware, this is where
// the magic should happen.
+
+ const DisplayHardware& hw(displayHardware());
+ const float w = mDisplayWidth;
+ const float h = mDisplayHeight;
+ mWidth = int(w);
+ mHeight = int(h);
+
+ Transform orientationTransform;
if (UNLIKELY(orientation == 42)) {
float a, b, c, d, x, y;
const float r = (3.14159265f / 180.0f) * 42.0f;
@@ -1853,14 +1877,18 @@
a=co; b=-si; c=si; d=co;
x = si*(h*0.5f) + (1-co)*(w*0.5f);
y =-si*(w*0.5f) + (1-co)*(h*0.5f);
- mOrientationTransform.set(a, b, c, d);
- mOrientationTransform.set(x, y);
+ orientationTransform.set(a, b, c, d);
+ orientationTransform.set(x, y);
} else {
GraphicPlane::orientationToTransfrom(orientation, w, h,
- &mOrientationTransform);
+ &orientationTransform);
+ if (orientation & ISurfaceComposer::eOrientationSwapMask) {
+ mWidth = int(h);
+ mHeight = int(w);
+ }
}
mOrientation = orientation;
- mGlobalTransform = mOrientationTransform * mTransform;
+ mGlobalTransform = mDisplayTransform * orientationTransform;
return NO_ERROR;
}
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index c0ab73d..2b7820c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -116,9 +116,10 @@
bool initialized() const;
void setDisplayHardware(DisplayHardware *);
- void setTransform(const Transform& tr);
status_t setOrientation(int orientation);
int getOrientation() const { return mOrientation; }
+ int getWidth() const;
+ int getHeight() const;
const DisplayHardware& displayHardware() const;
const Transform& transform() const;
@@ -129,10 +130,13 @@
GraphicPlane operator = (const GraphicPlane&);
DisplayHardware* mHw;
- Transform mTransform;
- Transform mOrientationTransform;
Transform mGlobalTransform;
+ Transform mDisplayTransform;
int mOrientation;
+ float mDisplayWidth;
+ float mDisplayHeight;
+ int mWidth;
+ int mHeight;
};
// ---------------------------------------------------------------------------
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index d2596b8..65b67a1 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -57,17 +57,32 @@
};
/**
+ * Interface for notifying clients of the result of scanning a
+ * requested media file.
+ */
+ public interface ScanResultListener {
+ /**
+ * Called to notify the client when the media scanner has finished
+ * scanning a file.
+ * @param path the path to the file that has been scanned.
+ * @param uri the Uri for the file if the scanning operation succeeded
+ * and the file was added to the media database, or null if scanning failed.
+ */
+ public void onScanCompleted(String path, Uri uri);
+ }
+
+ /**
* An interface for notifying clients of MediaScannerConnection
* when a connection to the MediaScanner service has been established
* and when the scanning of a file has completed.
*/
- public interface MediaScannerConnectionClient {
+ public interface MediaScannerConnectionClient extends ScanResultListener {
/**
* Called to notify the client when a connection to the
* MediaScanner service has been established.
*/
public void onMediaScannerConnected();
-
+
/**
* Called to notify the client when the media scanner has finished
* scanning a file.
@@ -136,11 +151,12 @@
/**
* Requests the media scanner to scan a file.
+ * Success or failure of the scanning operation cannot be determined until
+ * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
+ *
* @param path the path to the file to be scanned.
* @param mimeType an optional mimeType for the file.
* If mimeType is null, then the mimeType will be inferred from the file extension.
- * Success or failure of the scanning operation cannot be determined until
- * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
*/
public void scanFile(String path, String mimeType) {
synchronized (this) {
@@ -159,7 +175,67 @@
}
}
}
-
+
+ static class ClientProxy implements MediaScannerConnectionClient {
+ final String[] mPaths;
+ final String[] mMimeTypes;
+ final ScanResultListener mClient;
+ MediaScannerConnection mConnection;
+ int mNextPath;
+
+ ClientProxy(String[] paths, String[] mimeTypes, ScanResultListener client) {
+ mPaths = paths;
+ mMimeTypes = mimeTypes;
+ mClient = client;
+ }
+
+ public void onMediaScannerConnected() {
+ scanNextPath();
+ }
+
+ public void onScanCompleted(String path, Uri uri) {
+ if (mClient != null) {
+ mClient.onScanCompleted(path, uri);
+ }
+ scanNextPath();
+ }
+
+ void scanNextPath() {
+ if (mNextPath >= mPaths.length) {
+ mConnection.disconnect();
+ return;
+ }
+ String mimeType = mMimeTypes != null ? mMimeTypes[mNextPath] : null;
+ mConnection.scanFile(mPaths[mNextPath], mimeType);
+ mNextPath++;
+ }
+ }
+
+ /**
+ * Convenience for constructing a {@link MediaScannerConnection}, calling
+ * {@link #connect} on it, and calling {@link #scanFile} with the given
+ * <var>path</var> and <var>mimeType</var> when the connection is
+ * established.
+ * @param context The caller's Context, required for establishing a connection to
+ * the media scanner service.
+ * Success or failure of the scanning operation cannot be determined until
+ * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
+ * @param paths Array of paths to be scanned.
+ * @param mimeTypes Optional array of MIME types for each path.
+ * If mimeType is null, then the mimeType will be inferred from the file extension.
+ * @param callback Optional callback through which you can receive the
+ * scanned URI and MIME type; If null, the file will be scanned but
+ * you will not get a result back.
+ * @see scanFile(String, String)
+ */
+ public static void scanFile(Context context, String[] paths, String[] mimeTypes,
+ ScanResultListener callback) {
+ ClientProxy client = new ClientProxy(paths, mimeTypes, callback);
+ MediaScannerConnection connection = new MediaScannerConnection(context, client);
+ client.mConnection = connection;
+ connection.connect();
+ }
+
/**
* Part of the ServiceConnection interface. Do not call.
*/
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index d25f7f6..a13b242 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -111,6 +111,7 @@
AwesomePlayer::AwesomePlayer()
: mTimeSource(NULL),
mAudioPlayer(NULL),
+ mFlags(0),
mLastVideoBuffer(NULL),
mVideoBuffer(NULL) {
CHECK_EQ(mClient.connect(), OK);
@@ -167,23 +168,17 @@
reset_l();
- sp<DataSource> dataSource = DataSource::CreateFromURI(uri, headers);
+ mUri = uri;
- if (dataSource == NULL) {
- return UNKNOWN_ERROR;
+ if (headers) {
+ mUriHeaders = *headers;
}
- sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+ // The actual work will be done during preparation in the call to
+ // ::finishSetDataSource_l to avoid blocking the calling thread in
+ // setDataSource for any significant time.
- if (extractor == NULL) {
- return UNKNOWN_ERROR;
- }
-
- if (dataSource->flags() & DataSource::kWantsPrefetching) {
- mPrefetcher = new Prefetcher;
- }
-
- return setDataSource_l(extractor);
+ return OK;
}
status_t AwesomePlayer::setDataSource(
@@ -242,6 +237,10 @@
}
void AwesomePlayer::reset_l() {
+ while (mFlags & PREPARING) {
+ mPreparedCondition.wait(mLock);
+ }
+
cancelPlayerEvents();
mVideoRenderer.clear();
@@ -290,6 +289,9 @@
mSeekTimeUs = 0;
mPrefetcher.clear();
+
+ mUri.setTo("");
+ mUriHeaders.clear();
}
void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
@@ -350,6 +352,14 @@
return OK;
}
+ if (!(mFlags & PREPARED)) {
+ status_t err = prepare_l();
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
mFlags |= PLAYING;
mFlags |= FIRST_FRAME;
@@ -725,7 +735,7 @@
if (latenessUs > 40000) {
// We're more than 40ms late.
- LOGI("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
+ LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
mVideoBuffer->release();
mVideoBuffer = NULL;
@@ -815,30 +825,49 @@
status_t AwesomePlayer::prepare() {
Mutex::Autolock autoLock(mLock);
+ return prepare_l();
+}
+status_t AwesomePlayer::prepare_l() {
+ if (mFlags & PREPARED) {
+ return OK;
+ }
+
+ if (mFlags & PREPARING) {
+ return UNKNOWN_ERROR;
+ }
+
+ mIsAsyncPrepare = false;
status_t err = prepareAsync_l();
if (err != OK) {
return err;
}
- while (mAsyncPrepareEvent != NULL) {
+ while (mFlags & PREPARING) {
mPreparedCondition.wait(mLock);
}
- return OK;
+ return mPrepareResult;
}
status_t AwesomePlayer::prepareAsync() {
Mutex::Autolock autoLock(mLock);
+
+ if (mFlags & PREPARING) {
+ return UNKNOWN_ERROR; // async prepare already pending
+ }
+
+ mIsAsyncPrepare = true;
return prepareAsync_l();
}
status_t AwesomePlayer::prepareAsync_l() {
- if (mAsyncPrepareEvent != NULL) {
- return UNKNOWN_ERROR; // async prepare already pending.
+ if (mFlags & PREPARING) {
+ return UNKNOWN_ERROR; // async prepare already pending
}
+ mFlags |= PREPARING;
mAsyncPrepareEvent = new AwesomeEvent(
this, &AwesomePlayer::onPrepareAsyncEvent);
@@ -847,7 +876,49 @@
return OK;
}
+status_t AwesomePlayer::finishSetDataSource_l() {
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
+
+ if (dataSource == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (dataSource->flags() & DataSource::kWantsPrefetching) {
+ mPrefetcher = new Prefetcher;
+ }
+
+ return setDataSource_l(extractor);
+}
+
void AwesomePlayer::onPrepareAsyncEvent() {
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mUri.size() > 0) {
+ status_t err = finishSetDataSource_l();
+
+ if (err != OK) {
+ if (mIsAsyncPrepare) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ }
+
+ mPrepareResult = err;
+ mFlags &= ~PREPARING;
+ mAsyncPrepareEvent = NULL;
+ mPreparedCondition.broadcast();
+
+ return;
+ }
+ }
+ }
+
sp<Prefetcher> prefetcher;
{
@@ -861,16 +932,21 @@
Mutex::Autolock autoLock(mLock);
- if (mVideoWidth < 0 || mVideoHeight < 0) {
- notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
- } else {
- notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+ if (mIsAsyncPrepare) {
+ if (mVideoWidth < 0 || mVideoHeight < 0) {
+ notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
+ } else {
+ notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+ }
+
+ notifyListener_l(MEDIA_PREPARED);
}
- notifyListener_l(MEDIA_PREPARED);
-
+ mPrepareResult = OK;
+ mFlags &= ~PREPARING;
+ mFlags |= PREPARED;
mAsyncPrepareEvent = NULL;
- mPreparedCondition.signal();
+ mPreparedCondition.broadcast();
}
} // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6274a6c..4458006 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1070,6 +1070,11 @@
metadataKey = kKeyGenre;
break;
}
+ case FOURCC(0xa9, 'g', 'e', 'n'):
+ {
+ metadataKey = kKeyGenre;
+ break;
+ }
case FOURCC('t', 'r', 'k', 'n'):
{
if (size == 16 && flags == 0) {
@@ -1077,11 +1082,22 @@
sprintf(tmp, "%d/%d",
(int)buffer[size - 5], (int)buffer[size - 3]);
- printf("track: %s\n", tmp);
mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
}
break;
}
+ case FOURCC('d', 'i', 's', 'k'):
+ {
+ if (size == 14 && flags == 0) {
+ char tmp[16];
+ sprintf(tmp, "%d/%d",
+ (int)buffer[size - 3], (int)buffer[size - 1]);
+
+ mFileMetaData->setCString(kKeyDiscNumber, tmp);
+ }
+ break;
+ }
+
default:
break;
}
@@ -1093,11 +1109,25 @@
buffer + 8, size - 8);
} else if (metadataKey == kKeyGenre) {
if (flags == 0) {
- // uint8_t
+ // uint8_t genre code, iTunes genre codes are
+ // the standard id3 codes, except they start
+ // at 1 instead of 0 (e.g. Pop is 14, not 13)
+ // We use standard id3 numbering, so subtract 1.
+ int genrecode = (int)buffer[size - 1];
+ genrecode--;
+ if (genrecode < 0) {
+ genrecode = 255; // reserved for 'unknown genre'
+ }
char genre[10];
- sprintf(genre, "%d", (int)buffer[size - 1]);
+ sprintf(genre, "%d", genrecode);
mFileMetaData->setCString(metadataKey, genre);
+ } else if (flags == 1) {
+ // custom genre string
+ buffer[size] = '\0';
+
+ mFileMetaData->setCString(
+ metadataKey, (const char *)buffer + 8);
}
} else {
buffer[size] = '\0';
@@ -1198,7 +1228,7 @@
CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
if (prevSampleRate != sampleRate) {
- LOGW("mpeg4 audio sample rate different from previous setting. "
+ LOGV("mpeg4 audio sample rate different from previous setting. "
"was: %d, now: %d", prevSampleRate, sampleRate);
}
@@ -1208,7 +1238,7 @@
CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount));
if (prevChannelCount != numChannels) {
- LOGW("mpeg4 audio channel count different from previous setting. "
+ LOGV("mpeg4 audio channel count different from previous setting. "
"was: %d, now: %d", prevChannelCount, numChannels);
}
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 0355a82..c6c6f21 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -139,6 +139,7 @@
#define CODEC_LOGI(x, ...) LOGI("[%s] "x, mComponentName, ##__VA_ARGS__)
#define CODEC_LOGV(x, ...) LOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
+#define CODEC_LOGE(x, ...) LOGE("[%s] "x, mComponentName, ##__VA_ARGS__)
struct OMXCodecObserver : public BnOMXObserver {
OMXCodecObserver() {
@@ -1284,7 +1285,8 @@
CHECK_EQ(err, OK);
buffers->removeAt(i);
- } else if (mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
+ } else if (mState != ERROR
+ && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
drainInputBuffer(&buffers->editItemAt(i));
}
@@ -1930,10 +1932,17 @@
srcLength = srcBuffer->range_length();
if (info->mSize < srcLength) {
- LOGE("info->mSize = %d, srcLength = %d",
+ CODEC_LOGE(
+ "Codec's input buffers are too small to accomodate "
+ "buffer read from source (info->mSize = %d, srcLength = %d)",
info->mSize, srcLength);
+
+ srcBuffer->release();
+ srcBuffer = NULL;
+
+ setState(ERROR);
+ return;
}
- CHECK(info->mSize >= srcLength);
memcpy(info->mData,
(const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(),
srcLength);
@@ -2250,7 +2259,7 @@
}
status_t OMXCodec::stop() {
- CODEC_LOGV("stop");
+ CODEC_LOGV("stop mState=%d", mState);
Mutex::Autolock autoLock(mLock);
@@ -2309,6 +2318,8 @@
mSource->stop();
+ CODEC_LOGV("stopped");
+
return OK;
}
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 651b910..a19784b 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -58,6 +58,7 @@
void reset();
status_t prepare();
+ status_t prepare_l();
status_t prepareAsync();
status_t prepareAsync_l();
@@ -84,6 +85,8 @@
PLAYING = 1,
LOOPING = 2,
FIRST_FRAME = 4,
+ PREPARING = 8,
+ PREPARED = 16,
};
mutable Mutex mLock;
@@ -97,6 +100,9 @@
TimeSource *mTimeSource;
+ String8 mUri;
+ KeyedVector<String8, String8> mUriHeaders;
+
sp<MediaSource> mVideoSource;
sp<AwesomeRenderer> mVideoRenderer;
@@ -127,6 +133,8 @@
sp<TimedEventQueue::Event> mAsyncPrepareEvent;
Condition mPreparedCondition;
+ bool mIsAsyncPrepare;
+ status_t mPrepareResult;
void postVideoEvent_l(int64_t delayUs = -1);
void postBufferingEvent_l();
@@ -158,6 +166,7 @@
void onBufferingUpdate();
void onCheckAudioStatus();
void onPrepareAsyncEvent();
+ status_t finishSetDataSource_l();
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index 3d72017..5ec72df 100755
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -5,9 +5,9 @@
<uses-permission android:name="android.permission.ASEC_CREATE"/>
<uses-permission android:name="android.permission.ASEC_DESTROY"/>
<uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application android:process="def.container.service"
- android:label="@string/service_name">
+ <application android:label="@string/service_name">
<service android:name=".DefaultContainerService"
android:enabled="true"
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index d23b7d0..c418ccb 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -3,15 +3,19 @@
import com.android.internal.app.IMediaContainerService;
import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Debug;
+import android.os.Environment;
import android.os.IBinder;
-import android.os.IMountService;
-import android.os.MountServiceResultCode;
+import android.os.storage.IMountService;
+import android.os.storage.StorageResultCode;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.app.IntentService;
import android.app.Service;
import android.util.Log;
@@ -25,7 +29,6 @@
import android.os.FileUtils;
-
/*
* This service copies a downloaded apk to a file passed in as
* a ParcelFileDescriptor or to a newly created container specified
@@ -33,7 +36,7 @@
* based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER
* permission to access apks downloaded via the download manager.
*/
-public class DefaultContainerService extends Service {
+public class DefaultContainerService extends IntentService {
private static final String TAG = "DefContainer";
private static final boolean localLOGV = false;
@@ -78,6 +81,40 @@
}
};
+ public DefaultContainerService() {
+ super("DefaultContainerService");
+ setIntentRedelivery(true);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {
+ IPackageManager pm = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+ String pkg = null;
+ try {
+ while ((pkg=pm.nextPackageToClean(pkg)) != null) {
+ eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg));
+ eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ void eraseFiles(File path) {
+ if (path.isDirectory()) {
+ String[] files = path.list();
+ if (files != null) {
+ for (String file : files) {
+ eraseFiles(new File(path, file));
+ }
+ }
+ }
+ //Log.i(TAG, "Deleting: " + path);
+ path.delete();
+ }
+
public IBinder onBind(Intent intent) {
return mBinder;
}
@@ -158,12 +195,12 @@
int rc = mountService.createSecureContainer(
containerId, mbLen, "vfat", sdEncKey, ownerUid);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, String.format("Container creation failed (%d)", rc));
// XXX: This destroy should not be necessary
rc = mountService.destroySecureContainer(containerId);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, String.format("Container creation-cleanup failed (%d)", rc));
return null;
}
@@ -171,7 +208,7 @@
// XXX: Does this ever actually succeed?
rc = mountService.createSecureContainer(
containerId, mbLen, "vfat", sdEncKey, ownerUid);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, String.format("Container creation retry failed (%d)", rc));
}
}
@@ -226,7 +263,7 @@
private String mountSdDir(String containerId, String key) {
try {
int rc = getMountService().mountSecureContainer(containerId, key, Process.myUid());
- if (rc == MountServiceResultCode.OperationSucceeded) {
+ if (rc == StorageResultCode.OperationSucceeded) {
return getMountService().getSecureContainerPath(containerId);
} else {
Log.e(TAG, String.format("Failed to mount id %s with rc %d ", containerId, rc));
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index e4ee4ae..1625d9f 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -320,14 +320,18 @@
}
tag = parser.getName();
if ("admin".equals(tag)) {
- DeviceAdminInfo dai = findAdmin(
- ComponentName.unflattenFromString(
- parser.getAttributeValue(null, "name")));
- if (dai != null) {
- ActiveAdmin ap = new ActiveAdmin(dai);
- ap.readFromXml(parser);
- mAdminMap.put(ap.info.getComponent(), ap);
- mAdminList.add(ap);
+ String name = parser.getAttributeValue(null, "name");
+ try {
+ DeviceAdminInfo dai = findAdmin(
+ ComponentName.unflattenFromString(name));
+ if (dai != null) {
+ ActiveAdmin ap = new ActiveAdmin(dai);
+ ap.readFromXml(parser);
+ mAdminMap.put(ap.info.getComponent(), ap);
+ mAdminList.add(ap);
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failed loading admin " + name, e);
}
} else if ("failed-password-attempts".equals(tag)) {
mFailedPasswordAttempts = Integer.parseInt(
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 51abfc3..8d45033 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -23,13 +23,14 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
-import android.os.IMountService;
-import android.os.IMountServiceListener;
-import android.os.MountServiceResultCode;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.os.storage.StorageResultCode;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Environment;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UEventObserver;
import android.os.Handler;
@@ -41,11 +42,14 @@
import java.io.FileReader;
/**
- * MountService implements an to the mount service daemon
- * @hide
+ * MountService implements back-end services for platform storage
+ * management.
+ * @hide - Applications should use android.os.storage.StorageManager
+ * to access the MountService.
*/
class MountService extends IMountService.Stub
implements INativeDaemonConnectorCallbacks {
+ private static final boolean LOCAL_LOGD = false;
private static final String TAG = "MountService";
@@ -109,45 +113,35 @@
private PackageManagerService mPms;
private boolean mUmsEnabling;
private ArrayList<MountServiceBinderListener> mListeners;
+ private boolean mBooted;
+ private boolean mReady;
- /**
- * Constructs a new MountService instance
- *
- * @param context Binder context for this service
- */
- public MountService(Context context) {
- mContext = context;
-
- // XXX: This will go away soon in favor of IMountServiceObserver
- mPms = (PackageManagerService) ServiceManager.getService("package");
-
- // Register a BOOT_COMPLETED handler so that we can start
- // our NativeDaemonConnector. We defer the startup so that we don't
- // start processing events before we ought-to
- mContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
-
- mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
- mListeners = new ArrayList<MountServiceBinderListener>();
+ private void waitForReady() {
+ while (mReady == false) {
+ for (int retries = 5; retries > 0; retries--) {
+ if (mReady) {
+ return;
+ }
+ SystemClock.sleep(1000);
+ }
+ Log.w(TAG, "Waiting too long for mReady!");
+ }
}
- BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
- /*
- * Vold does not run in the simulator, so fake out a mounted
- * event to trigger MediaScanner
- */
- if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
- updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED);
- return;
- }
+ mBooted = true;
- Thread thread = new Thread(
- mConnector, NativeDaemonConnector.class.getName());
- thread.start();
+ String path = Environment.getExternalStorageDirectory().getPath();
+ if (getVolumeState(path).equals(Environment.MEDIA_UNMOUNTED)) {
+ int rc = doMountVolume(path);
+ if (rc != StorageResultCode.OperationSucceeded) {
+ Log.e(TAG, String.format("Boot-time mount failed (%d)", rc));
+ }
+ }
}
}
};
@@ -161,7 +155,7 @@
}
public void binderDied() {
- Log.d(TAG, "An IMountServiceListener has died!");
+ if (LOCAL_LOGD) Log.d(TAG, "An IMountServiceListener has died!");
synchronized(mListeners) {
mListeners.remove(this);
mListener.asBinder().unlinkToDeath(this, 0);
@@ -169,7 +163,7 @@
}
}
- int doShareUnshareVolume(String path, String method, boolean enable) {
+ private int doShareUnshareVolume(String path, String method, boolean enable) {
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
// TODO: Add support for multiple share methods
@@ -182,9 +176,13 @@
*/
String vs = getVolumeState(path);
if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
- mUmsEnabling = enable; // Supress unmounted events
- unmountVolume(path);
- mUmsEnabling = false; // Unsupress unmounted events
+ mUmsEnabling = enable; // Override for isUsbMassStorageEnabled()
+ int rc = doUnmountVolume(path);
+ mUmsEnabling = false; // Clear override
+ if (rc != StorageResultCode.OperationSucceeded) {
+ Log.e(TAG, String.format("Failed to unmount before enabling UMS (%d)", rc));
+ return rc;
+ }
}
try {
@@ -192,14 +190,14 @@
"volume %sshare %s %s", (enable ? "" : "un"), path, method));
} catch (NativeDaemonConnectorException e) {
Log.e(TAG, "Failed to share/unshare", e);
- return MountServiceResultCode.OperationFailedInternalError;
+ return StorageResultCode.OperationFailedInternalError;
}
/*
* If we disabled UMS then mount the volume
*/
if (!enable) {
- if (mountVolume(path) != MountServiceResultCode.OperationSucceeded) {
+ if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
Log.e(TAG, String.format(
"Failed to remount %s after disabling share method %s", path, method));
/*
@@ -210,15 +208,19 @@
}
}
- return MountServiceResultCode.OperationSucceeded;
+ return StorageResultCode.OperationSucceeded;
}
- void updatePublicVolumeState(String path, String state) {
+ private void updatePublicVolumeState(String path, String state) {
if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
Log.w(TAG, "Multiple volumes not currently supported");
return;
}
- Log.i(TAG, "State for {" + path + "} = {" + state + "}");
+
+ if (mLegacyState.equals(state)) {
+ Log.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
+ return;
+ }
String oldState = mLegacyState;
mLegacyState = state;
@@ -227,7 +229,7 @@
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
- bl.mListener.onVolumeStateChanged("", path, oldState, state);
+ bl.mListener.onStorageStateChanged(path, oldState, state);
} catch (RemoteException rex) {
Log.e(TAG, "Listener dead");
mListeners.remove(i);
@@ -270,11 +272,7 @@
if (st == VolumeState.NoMedia) {
state = Environment.MEDIA_REMOVED;
} else if (st == VolumeState.Idle) {
- state = null;
- int rc = mountVolume(path);
- if (rc != MountServiceResultCode.OperationSucceeded) {
- Log.e(TAG, String.format("Connection-mount failed (%d)", rc));
- }
+ state = Environment.MEDIA_UNMOUNTED;
} else if (st == VolumeState.Mounted) {
state = Environment.MEDIA_MOUNTED;
Log.i(TAG, "Media already mounted on daemon connection");
@@ -294,23 +292,26 @@
}
try {
- boolean avail = getShareMethodAvailable("ums");
+ boolean avail = doGetShareMethodAvailable("ums");
notifyShareAvailabilityChange("ums", avail);
} catch (Exception ex) {
Log.w(TAG, "Failed to get share availability");
}
+ /*
+ * Now that we've done our initialization, release
+ * the hounds!
+ */
+ mReady = true;
}
}.start();
}
/**
- *
* Callback from NativeDaemonConnector
*/
public boolean onEvent(int code, String raw, String[] cooked) {
Intent in = null;
- // Log.d(TAG, "event {" + raw + "}");
if (code == VoldResponseCode.VolumeStateChange) {
/*
* One of the volumes we're managing has changed state.
@@ -347,34 +348,12 @@
Log.e(TAG, "Failed to parse major/minor", ex);
}
- synchronized (mListeners) {
- for (int i = mListeners.size() -1; i >= 0; i--) {
- MountServiceBinderListener bl = mListeners.get(i);
- try {
- if (code == VoldResponseCode.VolumeDiskInserted) {
- bl.mListener.onMediaInserted(label, path, major, minor);
- } else if (code == VoldResponseCode.VolumeDiskRemoved) {
- bl.mListener.onMediaRemoved(label, path, major, minor, true);
- } else if (code == VoldResponseCode.VolumeBadRemoval) {
- bl.mListener.onMediaRemoved(label, path, major, minor, false);
- } else {
- Log.e(TAG, String.format("Unknown code {%d}", code));
- }
- } catch (RemoteException rex) {
- Log.e(TAG, "Listener dead");
- mListeners.remove(i);
- } catch (Exception ex) {
- Log.e(TAG, "Listener failed", ex);
- }
- }
- }
-
if (code == VoldResponseCode.VolumeDiskInserted) {
new Thread() {
public void run() {
try {
int rc;
- if ((rc = mountVolume(path)) != MountServiceResultCode.OperationSucceeded) {
+ if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
Log.w(TAG, String.format("Insertion mount failed (%d)", rc));
}
} catch (Exception ex) {
@@ -417,7 +396,7 @@
return true;
}
- void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
+ private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
String vs = getVolumeState(path);
Intent in = null;
@@ -471,7 +450,144 @@
}
}
- void notifyShareAvailabilityChange(String method, final boolean avail) {
+ private boolean doGetShareMethodAvailable(String method) {
+ ArrayList<String> rsp = mConnector.doCommand("share status " + method);
+
+ for (String line : rsp) {
+ String []tok = line.split(" ");
+ int code;
+ try {
+ code = Integer.parseInt(tok[0]);
+ } catch (NumberFormatException nfe) {
+ Log.e(TAG, String.format("Error parsing code %s", tok[0]));
+ return false;
+ }
+ if (code == VoldResponseCode.ShareStatusResult) {
+ if (tok[2].equals("available"))
+ return true;
+ return false;
+ } else {
+ Log.e(TAG, String.format("Unexpected response code %d", code));
+ return false;
+ }
+ }
+ Log.e(TAG, "Got an empty response");
+ return false;
+ }
+
+ private int doMountVolume(String path) {
+ int rc = StorageResultCode.OperationSucceeded;
+
+ try {
+ mConnector.doCommand(String.format("volume mount %s", path));
+ } catch (NativeDaemonConnectorException e) {
+ /*
+ * Mount failed for some reason
+ */
+ Intent in = null;
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedNoMedia) {
+ /*
+ * Attempt to mount but no media inserted
+ */
+ rc = StorageResultCode.OperationFailedNoMedia;
+ } else if (code == VoldResponseCode.OpFailedMediaBlank) {
+ /*
+ * Media is blank or does not contain a supported filesystem
+ */
+ updatePublicVolumeState(path, Environment.MEDIA_NOFS);
+ in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
+ rc = StorageResultCode.OperationFailedMediaBlank;
+ } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
+ /*
+ * Volume consistency check failed
+ */
+ updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
+ in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
+ rc = StorageResultCode.OperationFailedMediaCorrupt;
+ } else {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+
+ /*
+ * Send broadcast intent (if required for the failure)
+ */
+ if (in != null) {
+ mContext.sendBroadcast(in);
+ }
+ }
+
+ return rc;
+ }
+
+ private int doUnmountVolume(String path) {
+ if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
+ return VoldResponseCode.OpFailedVolNotMounted;
+ }
+
+ // Notify PackageManager of potential media removal and deal with
+ // return code later on. The caller of this api should be aware or have been
+ // notified that the applications installed on the media will be killed.
+ mPms.updateExternalMediaStatus(false);
+ try {
+ mConnector.doCommand(String.format("volume unmount %s", path));
+ return StorageResultCode.OperationSucceeded;
+ } catch (NativeDaemonConnectorException e) {
+ // Don't worry about mismatch in PackageManager since the
+ // call back will handle the status changes any way.
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedVolNotMounted) {
+ return StorageResultCode.OperationFailedVolumeNotMounted;
+ } else {
+ return StorageResultCode.OperationFailedInternalError;
+ }
+ }
+ }
+
+ private int doFormatVolume(String path) {
+ try {
+ String cmd = String.format("volume format %s", path);
+ mConnector.doCommand(cmd);
+ return StorageResultCode.OperationSucceeded;
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedNoMedia) {
+ return StorageResultCode.OperationFailedNoMedia;
+ } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
+ return StorageResultCode.OperationFailedMediaCorrupt;
+ } else {
+ return StorageResultCode.OperationFailedInternalError;
+ }
+ }
+ }
+
+ private boolean doGetVolumeShared(String path, String method) {
+ String cmd = String.format("volume shared %s %s", path, method);
+ ArrayList<String> rsp = mConnector.doCommand(cmd);
+
+ for (String line : rsp) {
+ String []tok = line.split(" ");
+ int code;
+ try {
+ code = Integer.parseInt(tok[0]);
+ } catch (NumberFormatException nfe) {
+ Log.e(TAG, String.format("Error parsing code %s", tok[0]));
+ return false;
+ }
+ if (code == VoldResponseCode.ShareEnabledResult) {
+ if (tok[2].equals("enabled"))
+ return true;
+ return false;
+ } else {
+ Log.e(TAG, String.format("Unexpected response code %d", code));
+ return false;
+ }
+ }
+ Log.e(TAG, "Got an empty response");
+ return false;
+ }
+
+ private void notifyShareAvailabilityChange(String method, final boolean avail) {
if (!method.equals("ums")) {
Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
return;
@@ -481,7 +597,7 @@
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
- bl.mListener.onShareAvailabilityChanged(method, avail);
+ bl.mListener.onUsbMassStorageConnectionChanged(avail);
} catch (RemoteException rex) {
Log.e(TAG, "Listener dead");
mListeners.remove(i);
@@ -491,22 +607,55 @@
}
}
- Intent intent;
- if (avail) {
- intent = new Intent(Intent.ACTION_UMS_CONNECTED);
- } else {
- intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
+ if (mBooted == true) {
+ Intent intent;
+ if (avail) {
+ intent = new Intent(Intent.ACTION_UMS_CONNECTED);
+ } else {
+ intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
+ }
+ mContext.sendBroadcast(intent);
}
- mContext.sendBroadcast(intent);
}
- void validatePermission(String perm) {
+ private void validatePermission(String perm) {
if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(String.format("Requires %s permission", perm));
}
}
/**
+ * Constructs a new MountService instance
+ *
+ * @param context Binder context for this service
+ */
+ public MountService(Context context) {
+ mContext = context;
+
+ /*
+ * Vold does not run in the simulator, so fake out a mounted
+ * event to trigger MediaScanner
+ */
+ if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
+ updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED);
+ return;
+ }
+
+ // XXX: This will go away soon in favor of IMountServiceObserver
+ mPms = (PackageManagerService) ServiceManager.getService("package");
+
+ mContext.registerReceiver(mBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+
+ mListeners = new ArrayList<MountServiceBinderListener>();
+
+ mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
+ mReady = false;
+ Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
+ thread.start();
+ }
+
+ /**
* Exposed API calls below here
*/
@@ -549,7 +698,7 @@
* the UMS host could have dirty FAT cache entries
* yet to flush.
*/
- if (unshareVolume(path, "ums") != MountServiceResultCode.OperationSucceeded) {
+ if (setUsbMassStorageEnabled(false) != StorageResultCode.OperationSucceeded) {
Log.e(TAG, "UMS disable on shutdown failed");
}
} else if (state.equals(Environment.MEDIA_CHECKING)) {
@@ -577,75 +726,30 @@
/*
* If the media is mounted, then gracefully unmount it.
*/
- if (unmountVolume(path) != MountServiceResultCode.OperationSucceeded) {
+ if (doUnmountVolume(path) != StorageResultCode.OperationSucceeded) {
Log.e(TAG, "Failed to unmount media for shutdown");
}
}
}
- public String[] getShareMethodList() {
- String[] rdata = new String[1];
- rdata[0] = "ums";
- return rdata;
- }
+ public boolean isUsbMassStorageConnected() {
+ waitForReady();
- public boolean getShareMethodAvailable(String method) {
- ArrayList<String> rsp = mConnector.doCommand("share status " + method);
-
- for (String line : rsp) {
- String []tok = line.split(" ");
- int code;
- try {
- code = Integer.parseInt(tok[0]);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, String.format("Error parsing code %s", tok[0]));
- return false;
- }
- if (code == VoldResponseCode.ShareStatusResult) {
- if (tok[2].equals("available"))
- return true;
- return false;
- } else {
- Log.e(TAG, String.format("Unexpected response code %d", code));
- return false;
- }
+ if (mUmsEnabling) {
+ return true;
}
- Log.e(TAG, "Got an empty response");
- return false;
+ return doGetShareMethodAvailable("ums");
}
- public int shareVolume(String path, String method) {
- return doShareUnshareVolume(path, method, true);
+ public int setUsbMassStorageEnabled(boolean enable) {
+ waitForReady();
+
+ return doShareUnshareVolume(Environment.getExternalStorageDirectory().getPath(), "ums", enable);
}
- public int unshareVolume(String path, String method) {
- return doShareUnshareVolume(path, method, false);
- }
-
- public boolean getVolumeShared(String path, String method) {
- String cmd = String.format("volume shared %s %s", path, method);
- ArrayList<String> rsp = mConnector.doCommand(cmd);
-
- for (String line : rsp) {
- String []tok = line.split(" ");
- int code;
- try {
- code = Integer.parseInt(tok[0]);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, String.format("Error parsing code %s", tok[0]));
- return false;
- }
- if (code == VoldResponseCode.ShareEnabledResult) {
- if (tok[2].equals("enabled"))
- return true;
- return false;
- } else {
- Log.e(TAG, String.format("Unexpected response code %d", code));
- return false;
- }
- }
- Log.e(TAG, "Got an empty response");
- return false;
+ public boolean isUsbMassStorageEnabled() {
+ waitForReady();
+ return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
}
/**
@@ -664,113 +768,38 @@
return mLegacyState;
}
-
- /**
- * Attempt to mount external media
- */
public int mountVolume(String path) {
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- int rc = MountServiceResultCode.OperationSucceeded;
- try {
- mConnector.doCommand(String.format("volume mount %s", path));
- } catch (NativeDaemonConnectorException e) {
- /*
- * Mount failed for some reason
- */
- Intent in = null;
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedNoMedia) {
- /*
- * Attempt to mount but no media inserted
- */
- rc = MountServiceResultCode.OperationFailedNoMedia;
- } else if (code == VoldResponseCode.OpFailedMediaBlank) {
- /*
- * Media is blank or does not contain a supported filesystem
- */
- updatePublicVolumeState(path, Environment.MEDIA_NOFS);
- in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
- rc = MountServiceResultCode.OperationFailedMediaBlank;
- } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
- /*
- * Volume consistency check failed
- */
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
- in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
- rc = MountServiceResultCode.OperationFailedMediaCorrupt;
- } else {
- rc = MountServiceResultCode.OperationFailedInternalError;
- }
-
- /*
- * Send broadcast intent (if required for the failure)
- */
- if (in != null) {
- mContext.sendBroadcast(in);
- }
- }
-
- return rc;
+ waitForReady();
+ return doMountVolume(path);
}
- /**
- * Attempt to unmount external media to prepare for eject
- */
public int unmountVolume(String path) {
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ waitForReady();
- // Check if media has been mounted
- String oldState = mLegacyState;
- if (!oldState.equals(Environment.MEDIA_MOUNTED)) {
- return VoldResponseCode.OpFailedVolNotMounted;
- }
- // Notify PackageManager of potential media removal and deal with
- // return code later on. The caller of this api should be aware or have been
- // notified that the applications installed on the media will be killed.
- mPms.updateExternalMediaStatus(false);
- try {
- mConnector.doCommand(String.format("volume unmount %s", path));
- return MountServiceResultCode.OperationSucceeded;
- } catch (NativeDaemonConnectorException e) {
- // Don't worry about mismatch in PackageManager since the
- // call back will handle the status changes any way.
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedVolNotMounted) {
- return MountServiceResultCode.OperationFailedVolumeNotMounted;
- } else {
- return MountServiceResultCode.OperationFailedInternalError;
- }
- }
+ return doUnmountVolume(path);
}
- /**
- * Synchronously formats a volume
- *
- * @param path The volume path to format
- * @return Error code from MountServiceResultCode
- */
public int formatVolume(String path) {
validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+ waitForReady();
- try {
- String cmd = String.format("volume format %s", path);
- mConnector.doCommand(cmd);
- return MountServiceResultCode.OperationSucceeded;
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedNoMedia) {
- return MountServiceResultCode.OperationFailedNoMedia;
- } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
- return MountServiceResultCode.OperationFailedMediaCorrupt;
- } else {
- return MountServiceResultCode.OperationFailedInternalError;
- }
+ return doFormatVolume(path);
+ }
+
+ private void warnOnNotMounted() {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ Log.w(TAG, "getSecureContainerList() called when storage not mounted");
}
}
public String[] getSecureContainerList() {
validatePermission(android.Manifest.permission.ASEC_ACCESS);
+ waitForReady();
+ warnOnNotMounted();
+
try {
return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
} catch (NativeDaemonConnectorException e) {
@@ -781,82 +810,96 @@
public int createSecureContainer(String id, int sizeMb, String fstype,
String key, int ownerUid) {
validatePermission(android.Manifest.permission.ASEC_CREATE);
+ waitForReady();
+ warnOnNotMounted();
- int rc = MountServiceResultCode.OperationSucceeded;
+ int rc = StorageResultCode.OperationSucceeded;
String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
try {
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
- rc = MountServiceResultCode.OperationFailedInternalError;
+ rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
}
public int finalizeSecureContainer(String id) {
validatePermission(android.Manifest.permission.ASEC_CREATE);
+ warnOnNotMounted();
- int rc = MountServiceResultCode.OperationSucceeded;
+ int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.doCommand(String.format("asec finalize %s", id));
} catch (NativeDaemonConnectorException e) {
- rc = MountServiceResultCode.OperationFailedInternalError;
+ rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
}
public int destroySecureContainer(String id) {
validatePermission(android.Manifest.permission.ASEC_DESTROY);
+ waitForReady();
+ warnOnNotMounted();
- int rc = MountServiceResultCode.OperationSucceeded;
+ int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.doCommand(String.format("asec destroy %s", id));
} catch (NativeDaemonConnectorException e) {
- rc = MountServiceResultCode.OperationFailedInternalError;
+ rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
}
public int mountSecureContainer(String id, String key, int ownerUid) {
validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+ waitForReady();
+ warnOnNotMounted();
- int rc = MountServiceResultCode.OperationSucceeded;
+ int rc = StorageResultCode.OperationSucceeded;
String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
try {
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
- rc = MountServiceResultCode.OperationFailedInternalError;
+ rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
}
public int unmountSecureContainer(String id) {
validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+ waitForReady();
+ warnOnNotMounted();
- int rc = MountServiceResultCode.OperationSucceeded;
+ int rc = StorageResultCode.OperationSucceeded;
String cmd = String.format("asec unmount %s", id);
try {
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
- rc = MountServiceResultCode.OperationFailedInternalError;
+ rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
}
public int renameSecureContainer(String oldId, String newId) {
validatePermission(android.Manifest.permission.ASEC_RENAME);
+ waitForReady();
+ warnOnNotMounted();
- int rc = MountServiceResultCode.OperationSucceeded;
+ int rc = StorageResultCode.OperationSucceeded;
String cmd = String.format("asec rename %s %s", oldId, newId);
try {
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
- rc = MountServiceResultCode.OperationFailedInternalError;
+ rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
}
public String getSecureContainerPath(String id) {
validatePermission(android.Manifest.permission.ASEC_ACCESS);
+ waitForReady();
+ warnOnNotMounted();
+
ArrayList<String> rsp = mConnector.doCommand("asec path " + id);
for (String line : rsp) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index d989b6b..ad8ab84 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -50,6 +50,7 @@
import android.os.Power;
import android.os.Process;
import android.os.RemoteException;
+import android.os.storage.StorageManager;
import android.os.SystemProperties;
import android.os.Vibrator;
import android.provider.Settings;
@@ -406,6 +407,10 @@
mSound.setUsesWakeLock(context);
mToastQueue = new ArrayList<ToastRecord>();
mHandler = new WorkerHandler();
+
+ StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+ sm.registerListener(new com.android.internal.app.StorageNotification(context));
+
mStatusBarService = statusBar;
statusBar.setNotificationCallbacks(mNotificationCallbacks);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index f27ef8e..b1e5d32 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -74,7 +74,7 @@
import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.MountServiceResultCode;
+import android.os.storage.StorageResultCode;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.ServiceManager;
@@ -148,6 +148,10 @@
static final int SCAN_NEW_INSTALL = 1<<4;
static final int SCAN_NO_PATHS = 1<<5;
+ static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ "com.android.defcontainer",
+ "com.android.defcontainer.DefaultContainerService");
+
final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
Process.THREAD_PRIORITY_BACKGROUND);
final PackageHandler mHandler;
@@ -298,6 +302,7 @@
static final int END_COPY = 4;
static final int INIT_COPY = 5;
static final int MCS_UNBIND = 6;
+ static final int START_CLEANING_PACKAGE = 7;
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
private ServiceConnection mDefContainerConn = new ServiceConnection() {
@@ -328,9 +333,7 @@
case INIT_COPY: {
InstallArgs args = (InstallArgs) msg.obj;
args.createCopyFile();
- Intent service = new Intent().setComponent(new ComponentName(
- "com.android.defcontainer",
- "com.android.defcontainer.DefaultContainerService"));
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
if (mContainerService != null) {
// No need to add to pending list. Use remote stub directly
handleStartCopy(args);
@@ -405,6 +408,15 @@
}
break;
}
+ case START_CLEANING_PACKAGE: {
+ String packageName = (String)msg.obj;
+ synchronized (mPackages) {
+ if (!mSettings.mPackagesToBeCleaned.contains(packageName)) {
+ mSettings.mPackagesToBeCleaned.add(packageName);
+ }
+ }
+ startCleaningPackages();
+ } break;
}
}
@@ -2823,6 +2835,9 @@
mSettings.insertPackageSettingLP(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
+ // Make sure we don't accidentally delete its data.
+ mSettings.mPackagesToBeCleaned.remove(pkgName);
+
int N = pkg.providers.size();
StringBuilder r = null;
int i;
@@ -4018,7 +4033,47 @@
}
}
}
+
+ public String nextPackageToClean(String lastPackage) {
+ synchronized (mPackages) {
+ if (!mMediaMounted) {
+ // If the external storage is no longer mounted at this point,
+ // the caller may not have been able to delete all of this
+ // packages files and can not delete any more. Bail.
+ return null;
+ }
+ if (lastPackage != null) {
+ mSettings.mPackagesToBeCleaned.remove(lastPackage);
+ }
+ return mSettings.mPackagesToBeCleaned.size() > 0
+ ? mSettings.mPackagesToBeCleaned.get(0) : null;
+ }
+ }
+ void schedulePackageCleaning(String packageName) {
+ mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName));
+ }
+
+ void startCleaningPackages() {
+ synchronized (mPackages) {
+ if (!mMediaMounted) {
+ return;
+ }
+ if (mSettings.mPackagesToBeCleaned.size() <= 0) {
+ return;
+ }
+ }
+ Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);
+ intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
+ IActivityManager am = ActivityManagerNative.getDefault();
+ if (am != null) {
+ try {
+ am.startService(null, intent, null);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
private final class AppDirObserver extends FileObserver {
public AppDirObserver(String path, int mask, boolean isrom) {
super(path, mask);
@@ -5224,7 +5279,6 @@
* persisting settings for later use
* sending a broadcast if necessary
*/
-
private boolean deletePackageX(String packageName, boolean sendBroadCast,
boolean deleteCodeAndResources, int flags) {
PackageRemovedInfo info = new PackageRemovedInfo();
@@ -5316,6 +5370,7 @@
File dataDir = new File(pkg.applicationInfo.dataDir);
dataDir.delete();
}
+ schedulePackageCleaning(packageName);
synchronized (mPackages) {
if (outInfo != null) {
outInfo.removedUid = mSettings.removePackageLP(packageName);
@@ -5328,7 +5383,7 @@
mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
}
// Save settings now
- mSettings.writeLP ();
+ mSettings.writeLP();
}
}
@@ -6817,6 +6872,10 @@
final HashMap<String, BasePermission> mPermissionTrees =
new HashMap<String, BasePermission>();
+ // Packages that have been uninstalled and still need their external
+ // storage data deleted.
+ final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+
private final StringBuilder mReadMessages = new StringBuilder();
private static final class PendingPackage extends PackageSettingBase {
@@ -7380,6 +7439,14 @@
serializer.endTag(null, "shared-user");
}
+ if (mPackagesToBeCleaned.size() > 0) {
+ for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
+ serializer.startTag(null, "cleaning-package");
+ serializer.attribute(null, "name", mPackagesToBeCleaned.get(i));
+ serializer.endTag(null, "cleaning-package");
+ }
+ }
+
serializer.endTag(null, "packages");
serializer.endDocument();
@@ -7412,7 +7479,7 @@
}
void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg)
- throws java.io.IOException {
+ throws java.io.IOException {
serializer.startTag(null, "updated-package");
serializer.attribute(null, "name", pkg.name);
serializer.attribute(null, "codePath", pkg.codePathString);
@@ -7450,7 +7517,7 @@
}
void writePackage(XmlSerializer serializer, final PackageSetting pkg)
- throws java.io.IOException {
+ throws java.io.IOException {
serializer.startTag(null, "package");
serializer.attribute(null, "name", pkg.name);
serializer.attribute(null, "codePath", pkg.codePathString);
@@ -7640,6 +7707,11 @@
readPreferredActivitiesLP(parser);
} else if(tagName.equals("updated-package")) {
readDisabledSysPackageLP(parser);
+ } else if (tagName.equals("cleaning-package")) {
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ mPackagesToBeCleaned.add(name);
+ }
} else {
Log.w(TAG, "Unknown element under <packages>: "
+ parser.getName());
@@ -7765,7 +7837,7 @@
}
private void readDisabledSysPackageLP(XmlPullParser parser)
- throws XmlPullParserException, IOException {
+ throws XmlPullParserException, IOException {
String name = parser.getAttributeValue(null, "name");
String codePathStr = parser.getAttributeValue(null, "codePath");
String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
@@ -8295,17 +8367,17 @@
int rc = mountService.createSecureContainer(
pkgName, mbLen, "vfat", sdEncKey, Process.SYSTEM_UID);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, String.format("Failed to create container (%d)", rc));
rc = mountService.destroySecureContainer(pkgName);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, String.format("Failed to cleanup container (%d)", rc));
return null;
}
rc = mountService.createSecureContainer(
pkgName, mbLen, "vfat", sdEncKey, Process.SYSTEM_UID);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, String.format("Failed to create container (2nd try) (%d)", rc));
return null;
}
@@ -8325,7 +8397,7 @@
int rc = getMountService().mountSecureContainer(pkgName, sdEncKey, ownerUid);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " rc : " + rc);
return null;
}
@@ -8336,7 +8408,7 @@
private boolean unMountSdDir(String pkgName) {
// STOPSHIP unmount directory
int rc = getMountService().unmountSecureContainer(pkgName);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, "Failed to unmount : " + pkgName + " with rc " + rc);
return false;
}
@@ -8360,7 +8432,7 @@
private boolean finalizeSdDir(String pkgName) {
int rc = getMountService().finalizeSecureContainer(pkgName);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.i(TAG, "Failed to finalize container for pkg : " + pkgName);
return false;
}
@@ -8369,7 +8441,7 @@
private boolean destroySdDir(String pkgName) {
int rc = getMountService().destroySecureContainer(pkgName);
- if (rc != MountServiceResultCode.OperationSucceeded) {
+ if (rc != StorageResultCode.OperationSucceeded) {
Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
return false;
}
@@ -8435,19 +8507,21 @@
}
public void updateExternalMediaStatus(final boolean mediaStatus) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
- mediaStatus+", mMediaMounted=" + mMediaMounted);
- if (mediaStatus == mMediaMounted) {
- return;
- }
- mMediaMounted = mediaStatus;
- // Queue up an async operation since the package installation may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- updateExternalMediaStatusInner(mediaStatus);
+ synchronized (mPackages) {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
+ mediaStatus+", mMediaMounted=" + mMediaMounted);
+ if (mediaStatus == mMediaMounted) {
+ return;
}
- });
+ mMediaMounted = mediaStatus;
+ // Queue up an async operation since the package installation may take a little while.
+ mHandler.post(new Runnable() {
+ public void run() {
+ mHandler.removeCallbacks(this);
+ updateExternalMediaStatusInner(mediaStatus);
+ }
+ });
+ }
}
void updateExternalMediaStatusInner(boolean mediaStatus) {
@@ -8505,6 +8579,7 @@
if (mediaStatus) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
loadMediaPackages(processCids, uidArr);
+ startCleaningPackages();
} else {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
unloadMediaPackages(processCids, uidArr);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 1e7dd99..1ccae86 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -41,7 +41,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.IMountService;
+import android.os.storage.IMountService;
import android.os.IPowerManager;
import android.os.LocalPowerManager;
import android.os.Power;
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 88aadbd..b57e543 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5465,7 +5465,6 @@
curFocus = mCurrentFocus;
// cache the paused state at ctor time as well
if (theFocus == null || theFocus.mToken == null) {
- Log.i(TAG, "focus " + theFocus + " mToken is null at event dispatch!");
focusPaused = false;
} else {
focusPaused = theFocus.mToken.paused;
@@ -5478,7 +5477,7 @@
+ " fin=" + finished + " gfw=" + gotFirstWindow
+ " ed=" + eventDispatching + " tts=" + timeToSwitch
+ " wf=" + wasFrozen + " fp=" + focusPaused
- + " mcf=" + mCurrentFocus + "}}";
+ + " mcf=" + curFocus + "}}";
}
};
private DispatchState mDispatchState = null;
@@ -5651,10 +5650,11 @@
synchronized (this) {
Log.w(TAG, "Key dispatching timed out sending to " +
(targetWin != null ? targetWin.mAttrs.getTitle()
- : "<null>"));
+ : "<null>: no window ready for key dispatch"));
// NOSHIP debugging
- Log.w(TAG, "Dispatch state: " + mDispatchState);
- Log.w(TAG, "Current state: " + new DispatchState(nextKey, targetWin));
+ Log.w(TAG, "Previous dispatch state: " + mDispatchState);
+ Log.w(TAG, "Current dispatch state: " +
+ new DispatchState(nextKey, targetWin));
// END NOSHIP
//dump();
if (targetWin != null) {
diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java
index 57b22f8..ffd757c 100644
--- a/test-runner/android/test/mock/MockContext.java
+++ b/test-runner/android/test/mock/MockContext.java
@@ -158,11 +158,21 @@
}
@Override
+ public File getExternalFilesDir(String type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public File getCacheDir() {
throw new UnsupportedOperationException();
}
@Override
+ public File getExternalCacheDir() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public File getDir(String name, int mode) {
throw new UnsupportedOperationException();
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index a1370ea..3a4d38c 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -16,7 +16,7 @@
package com.android.unit_tests;
-import android.os.IMountService.Stub;
+import android.os.storage.IMountService.Stub;
import android.net.Uri;
import android.os.FileUtils;
@@ -54,8 +54,8 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IMountService;
-import android.os.MountServiceResultCode;
+import android.os.storage.IMountService;
+import android.os.storage.StorageResultCode;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
@@ -662,7 +662,7 @@
try {
String mPath = Environment.getExternalStorageDirectory().toString();
int ret = getMs().mountVolume(mPath);
- return ret == MountServiceResultCode.OperationSucceeded;
+ return ret == StorageResultCode.OperationSucceeded;
} catch (RemoteException e) {
return false;
}
@@ -675,7 +675,7 @@
try {
String mPath = Environment.getExternalStorageDirectory().toString();
int ret = getMs().unmountVolume(mPath);
- return ret == MountServiceResultCode.OperationSucceeded;
+ return ret == StorageResultCode.OperationSucceeded;
} catch (RemoteException e) {
return true;
}
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index cbb5203..558b587 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -39,7 +39,7 @@
mRequireLocalization(false), mPseudolocalize(false),
mUTF8(false), mEncodingSpecified(false), mValues(false),
mCompressionMethod(0), mOutputAPKFile(NULL), mManifestPackageNameOverride(NULL),
- mAssetSourceDir(NULL), mProguardFile(NULL),
+ mAutoAddOverlay(false), mAssetSourceDir(NULL), mProguardFile(NULL),
mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
mRClassDir(NULL), mResourceIntermediatesDir(NULL),
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
@@ -90,6 +90,8 @@
void setOutputAPKFile(const char* val) { mOutputAPKFile = val; }
const char* getManifestPackageNameOverride() const { return mManifestPackageNameOverride; }
void setManifestPackageNameOverride(const char * val) { mManifestPackageNameOverride = val; }
+ bool getAutoAddOverlay() { return mAutoAddOverlay; }
+ void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
/*
* Input options.
@@ -181,6 +183,7 @@
bool mJunkPath;
const char* mOutputAPKFile;
const char* mManifestPackageNameOverride;
+ bool mAutoAddOverlay;
const char* mAssetSourceDir;
const char* mProguardFile;
const char* mAndroidManifestFile;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 6675ac2..6d0a351 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -60,6 +60,7 @@
" [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n"
" [--max-sdk-version VAL] [--app-version VAL] \\\n"
" [--app-version-name TEXT] [--custom-package VAL] [--utf16] \\\n"
+ " [--auto-add-overlay] \\\n"
" [-I base-package [-I base-package ...]] \\\n"
" [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n"
" [-S resource-sources [-S resource-sources ...]] "
@@ -136,6 +137,8 @@
" inserts android:versionName in to manifest.\n"
" --custom-package\n"
" generates R.java into a different package.\n"
+ " --auto-add-overlay\n"
+ " Automatically add resources that are only in overlays.\n"
" --utf16\n"
" changes default encoding for resources to UTF-16. Only useful when API\n"
" level is set to 7 or higher where the default encoding is UTF-8.\n");
@@ -445,6 +448,8 @@
goto bail;
}
bundle.setManifestPackageNameOverride(argv[0]);
+ } else if (strcmp(cp, "-auto-add-overlay") == 0) {
+ bundle.setAutoAddOverlay(true);
} else {
fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
wantUsage = true;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index a9cbd11..b682702 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1591,7 +1591,7 @@
sourcePos.file.striing(), sourcePos.line, String8(type).string());
}
#endif
- if (overlay && !hasBagOrEntry(package, type, name)) {
+ if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) {
bool canAdd = false;
sp<Package> p = mPackages.valueFor(package);
if (p != NULL) {
@@ -3279,12 +3279,13 @@
const SourcePos& sourcePos,
const ResTable_config* config,
bool doSetIndex,
- bool overlay)
+ bool overlay,
+ bool autoAddOverlay)
{
int pos = -1;
sp<ConfigList> c = mConfigs.valueFor(entry);
if (c == NULL) {
- if (overlay == true && mCanAddEntries.indexOf(entry) < 0) {
+ if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
sourcePos.error("Resource at %s appears in overlay but not"
" in the base package; use <add-resource> to add.\n",
String8(entry).string());
@@ -3596,7 +3597,7 @@
if (t == NULL) {
return NULL;
}
- return t->getEntry(name, sourcePos, config, doSetIndex, overlay);
+ return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
}
sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index cfa75a71..60d0901 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -425,7 +425,8 @@
const SourcePos& pos,
const ResTable_config* config = NULL,
bool doSetIndex = false,
- bool overlay = false);
+ bool overlay = false,
+ bool autoAddOverlay = false);
const SourcePos& getFirstPublicSourcePos() const { return *mFirstPublicSourcePos; }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index b670eee..57b5d4e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -928,6 +928,12 @@
}
@Override
+ public File getExternalCacheDir() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
public ContentResolver getContentResolver() {
if (mContentResolver == null) {
mContentResolver = new BridgeContentResolver(this);
@@ -960,6 +966,12 @@
}
@Override
+ public File getExternalFilesDir(String type) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
public String getPackageCodePath() {
// TODO Auto-generated method stub
return null;