StorageManager: Clean up and generalize storage configuration resources
Replace config_emulateExternalStorage, config_externalStorageRemovable,
config_externalStoragePaths, config_externalStorageDescriptions and
config_mtpReserveSpaceMegabytes resources with an XML resource file
to describe the external storages that are available.
Add android.os.storage.StorageVolume class
StorageManager.getVolumeList() now returns an array of StorageVolume
Change-Id: I06ce1451ebf08b82f0ee825d56d59ebf72eacd3d
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e308c2c..1f3f6d9 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -20,6 +20,7 @@
import android.content.res.Resources;
import android.os.storage.IMountService;
+import android.os.storage.StorageVolume;
import android.util.Log;
/**
@@ -35,7 +36,25 @@
private static final Object mLock = new Object();
- private volatile static Boolean mIsExternalStorageEmulated = null;
+ private volatile static StorageVolume mPrimaryVolume = null;
+
+ private static StorageVolume getPrimaryVolume() {
+ if (mPrimaryVolume == null) {
+ synchronized (mLock) {
+ if (mPrimaryVolume == null) {
+ try {
+ IMountService mountService = IMountService.Stub.asInterface(ServiceManager
+ .getService("mount"));
+ Parcelable[] volumes = mountService.getVolumeList();
+ mPrimaryVolume = (StorageVolume)volumes[0];
+ } catch (Exception e) {
+ Log.e(TAG, "couldn't talk to MountService", e);
+ }
+ }
+ }
+ }
+ return mPrimaryVolume;
+ }
/**
* Gets the Android root directory.
@@ -416,9 +435,8 @@
* <p>See {@link #getExternalStorageDirectory()} for more information.
*/
public static boolean isExternalStorageRemovable() {
- if (isExternalStorageEmulated()) return false;
- return Resources.getSystem().getBoolean(
- com.android.internal.R.bool.config_externalStorageRemovable);
+ StorageVolume volume = getPrimaryVolume();
+ return (volume != null && volume.isRemovable());
}
/**
@@ -435,23 +453,8 @@
* android.content.ComponentName, boolean)} for additional details.
*/
public static boolean isExternalStorageEmulated() {
- if (mIsExternalStorageEmulated == null) {
- synchronized (mLock) {
- if (mIsExternalStorageEmulated == null) {
- boolean externalStorageEmulated;
- try {
- IMountService mountService = IMountService.Stub.asInterface(ServiceManager
- .getService("mount"));
- externalStorageEmulated = mountService.isExternalStorageEmulated();
- mIsExternalStorageEmulated = Boolean.valueOf(externalStorageEmulated);
- } catch (Exception e) {
- Log.e(TAG, "couldn't talk to MountService", e);
- return false;
- }
- }
- }
- }
- return mIsExternalStorageEmulated;
+ StorageVolume volume = getPrimaryVolume();
+ return (volume != null && volume.isEmulated());
}
static File getDirectory(String variableName, String defaultPath) {
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 27da3c3..c2dc8ae 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -20,7 +20,9 @@
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.storage.StorageVolume;
/**
* WARNING! Update IMountService.h and IMountService.cpp if you change this
@@ -638,15 +640,15 @@
return _result;
}
- public String[] getVolumeList() throws RemoteException {
+ public Parcelable[] getVolumeList() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
- String[] _result;
+ Parcelable[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0);
_reply.readException();
- _result = _reply.readStringArray();
+ _result = _reply.readParcelableArray(StorageVolume.class.getClassLoader());
} finally {
_reply.recycle();
_data.recycle();
@@ -1024,9 +1026,9 @@
}
case TRANSACTION_getVolumeList: {
data.enforceInterface(DESCRIPTOR);
- String[] result = getVolumeList();
+ Parcelable[] result = getVolumeList();
reply.writeNoException();
- reply.writeStringArray(result);
+ reply.writeParcelableArray(result, 0);
return true;
}
}
@@ -1207,5 +1209,5 @@
/**
* Returns list of all mountable volumes.
*/
- public String[] getVolumeList() throws RemoteException;
+ public Parcelable[] getVolumeList() throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 234057b..6fd1d00 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -19,6 +19,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
@@ -545,12 +546,34 @@
* Returns list of all mountable volumes.
* @hide
*/
- public String[] getVolumeList() {
+ public StorageVolume[] getVolumeList() {
try {
- return mMountService.getVolumeList();
+ Parcelable[] list = mMountService.getVolumeList();
+ if (list == null) return new StorageVolume[0];
+ int length = list.length;
+ StorageVolume[] result = new StorageVolume[length];
+ for (int i = 0; i < length; i++) {
+ result[i] = (StorageVolume)list[i];
+ }
+ return result;
} catch (RemoteException e) {
Log.e(TAG, "Failed to get volume list", e);
return null;
}
}
+
+ /**
+ * Returns list of paths for all mountable volumes.
+ * @hide
+ */
+ public String[] getVolumePaths() {
+ StorageVolume[] volumes = getVolumeList();
+ if (volumes == null) return null;
+ int count = volumes.length;
+ String[] paths = new String[count];
+ for (int i = 0; i < count; i++) {
+ paths[i] = volumes[i].getPath();
+ }
+ return paths;
+ }
}
diff --git a/core/java/android/os/storage/StorageVolume.aidl b/core/java/android/os/storage/StorageVolume.aidl
new file mode 100644
index 0000000..d689917
--- /dev/null
+++ b/core/java/android/os/storage/StorageVolume.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011, 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;
+
+parcelable StorageVolume;
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
new file mode 100644
index 0000000..d79f6c8
--- /dev/null
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2011 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.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * A class representing a storage volume
+ * @hide
+ */
+public class StorageVolume implements Parcelable {
+
+ private static final String TAG = "StorageVolume";
+
+ private final String mPath;
+ private final String mDescription;
+ private final boolean mRemovable;
+ private final boolean mEmulated;
+ private final int mMtpReserveSpace;
+
+ public StorageVolume(String path, String description,
+ boolean removable, boolean emulated,
+ int mtpReserveSpace) {
+ mPath = path;
+ mDescription = description;
+ mRemovable = removable;
+ mEmulated = emulated;
+ mMtpReserveSpace = mtpReserveSpace;
+ }
+
+ /**
+ * Returns the mount path for the volume.
+ *
+ * @return the mount path
+ */
+ public String getPath() {
+ return mPath;
+ }
+
+ /**
+ * Returns a user visible description of the volume.
+ *
+ * @return the volume description
+ */
+ public String getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Returns true if the volume is removable.
+ *
+ * @return is removable
+ */
+ public boolean isRemovable() {
+ return mRemovable;
+ }
+
+ /**
+ * Returns true if the volume is emulated.
+ *
+ * @return is removable
+ */
+ public boolean isEmulated() {
+ return mEmulated;
+ }
+
+ /**
+ * Number of megabytes of space to leave unallocated by MTP.
+ * MTP will subtract this value from the free space it reports back
+ * to the host via GetStorageInfo, and will not allow new files to
+ * be added via MTP if there is less than this amount left free in the storage.
+ * If MTP has dedicated storage this value should be zero, but if MTP is
+ * sharing storage with the rest of the system, set this to a positive value
+ * to ensure that MTP activity does not result in the storage being
+ * too close to full.
+ *
+ * @return MTP reserve space
+ */
+ public int getMtpReserveSpace() {
+ return mMtpReserveSpace;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof StorageVolume && mPath != null) {
+ StorageVolume volume = (StorageVolume)obj;
+ return (mPath.equals(volume.mPath));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mPath.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return mPath;
+ }
+
+ public static final Parcelable.Creator<StorageVolume> CREATOR =
+ new Parcelable.Creator<StorageVolume>() {
+ public StorageVolume createFromParcel(Parcel in) {
+ String path = in.readString();
+ String description = in.readString();
+ int removable = in.readInt();
+ int emulated = in.readInt();
+ int mtpReserveSpace = in.readInt();
+ return new StorageVolume(path, description,
+ removable == 1, emulated == 1, mtpReserveSpace);
+ }
+
+ public StorageVolume[] newArray(int size) {
+ return new StorageVolume[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mPath);
+ parcel.writeString(mDescription);
+ parcel.writeInt(mRemovable ? 1 : 0);
+ parcel.writeInt(mEmulated ? 1 : 0);
+ parcel.writeInt(mMtpReserveSpace);
+ }
+}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 71a8b2a..004b755 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4878,4 +4878,21 @@
<!-- Y coordinate of the icon hot spot. -->
<attr name="hotSpotY" format="float" />
</declare-styleable>
+
+ <declare-styleable name="Storage">
+ <!-- path to mount point for the storage -->
+ <attr name="mountPoint" format="string" />
+ <!-- user visible description of the storage -->
+ <attr name="storageDescription" format="string" />
+ <!-- true if the storage is the primary external storage -->
+ <attr name="primary" format="boolean" />
+ <!-- true if the storage is removable -->
+ <attr name="removable" format="boolean" />
+ <!-- true if the storage is emulated via the FUSE sdcard daemon -->
+ <attr name="emulated" format="boolean" />
+ <!-- number of megabytes of storage MTP should reserve for free storage
+ (used for emulated storage that is shared with system's data partition) -->
+ <attr name="mtpReserve" format="integer" />
+ </declare-styleable>
+
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 27c7a4d..49bbd82 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -89,52 +89,10 @@
when there's no network connection. If the scan doesn't timeout, use zero -->
<integer name="config_radioScanningTimeout">0</integer>
- <!-- Set to true if the location returned Environment.getExternalStorageDirectory()
- is actually a subdirectory of the internal storage.
- If this is set then Environment.getExternalStorageState() will always return
- MEDIA_MOUNTED and Intent.ACTION_MEDIA_MOUNTED will be broadcast at boot time
- for backward compatibility with apps that require external storage. -->
- <bool name="config_emulateExternalStorage">false</bool>
-
- <!-- Set to true if external storage is case sensitive.
- Typically external storage is FAT, which is case insensitive. -->
- <bool name="config_caseSensitiveExternalStorage">false</bool>
-
- <!-- A product with no SD card == not removable. -->
- <bool name="config_externalStorageRemovable" product="nosdcard">false</bool>
- <!-- Configures whether the primary external storage device is
- removable. For example, if external storage is on an SD card,
- it is removable; if it is built in to the device, it is not removable.
- The default product has external storage on an SD card, which is
- removable. -->
- <bool name="config_externalStorageRemovable" product="default">true</bool>
-
- <!-- List of mount points for external storage devices.
- The first item on the list should be the primary external storage and should match the
- value returned by Environment.getExternalStorageDirectory (/mnt/sdcard).
- MTP storage IDs will be generated based on the position of the mountpoint in this list:
- 0x00010001 - ID for primary external storage (/mnt/sdcard)
- 0x00020001 - ID for first secondary external storage
- 0x00030001 - ID for second secondary external storage
- etc. -->
- <string-array translatable="false" name="config_externalStoragePaths">
- <item>"/mnt/sdcard"</item>
- </string-array>
-
- <!-- User visible descriptions of the volumes in the config_externalStoragePaths array. -->
- <string-array translatable="true" name="config_externalStorageDescriptions">
- <item>"SD card"</item>
- </string-array>
-
- <!-- Number of megabytes of space to leave unallocated by MTP.
- MTP will subtract this value from the free space it reports back
- to the host via GetStorageInfo, and will not allow new files to
- be added via MTP if there is less than this amount left free in the storage.
- If MTP has dedicated storage this value should be zero, but if MTP is
- sharing storage with the rest of the system, set this to a positive value
- to ensure that MTP activity does not result in the storage being
- too close to full. -->
- <integer name="config_mtpReserveSpaceMegabytes">0</integer>
+ <!-- Storage lists for default and nosdcard products.
+ Both have a single SD card storage, but the storage is not removable in the nosdcard case -->
+ <integer name="config_storageListId" product="nosdcard">@xml/storage_list_nosdcard</integer>
+ <integer name="config_storageListId" product="default">@xml/storage_list</integer>
<!-- XXXXX NOTE THE FOLLOWING RESOURCES USE THE WRONG NAMING CONVENTION.
Please don't copy them, copy anything else. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8ef9a3b..b713b51 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2808,4 +2808,12 @@
<!-- Description of the button to decrement the NumberPicker value. [CHAR LIMIT=NONE] -->
<string name="number_picker_decrement_button">Decrement</string>
+ <!-- Storage description for internal storage. [CHAR LIMIT=NONE] -->
+ <string name="storage_internal">Internal Storage</string>
+
+ <!-- Storage description for the SD card. [CHAR LIMIT=NONE] -->
+ <string name="storage_sd_card">SD Card</string>
+
+ <!-- Storage description for USB storage. [CHAR LIMIT=NONE] -->
+ <string name="storage_usb">USB storage</string>
</resources>
diff --git a/core/res/res/xml/storage_list.xml b/core/res/res/xml/storage_list.xml
new file mode 100644
index 0000000..944bb3a
--- /dev/null
+++ b/core/res/res/xml/storage_list.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2011, 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.
+*/
+-->
+
+<!-- The <device> element should contain one or more <storage> elements.
+ Exactly one of these should have the attribute primary="true".
+ This storage will be the primary external storage and should have mountPoint="/mnt/sdcard".
+ Each storage should have both a mountPoint and storageDescription attribute.
+ The following attributes are optional:
+
+ primary: (boolean) this storage is the primary external storage
+ removable: (boolean) this is removable storage (for example, a real SD card)
+ emulated: (boolean) the storage is emulated via the FUSE sdcard daemon
+ mtpReserve: (integer) number of megabytes of storage MTP should reserve for free storage
+ (used for emulated storage that is shared with system's data partition)
+
+ A storage should not have both emulated and removable set to true
+-->
+
+<StorageList xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- removable is not set in nosdcard product -->
+ <storage android:mountPoint="/mnt/sdcard"
+ android:storageDescription="@string/storage_usb"
+ android:primary="true" />
+</StorageList>