am 0a93d875: Merge "StorageManager: Clean up and generalize storage configuration resources" into honeycomb-mr2

* commit '0a93d875527c52183080b63a0b19d567856952af':
  StorageManager:  Clean up and generalize storage configuration resources
This commit is contained in:
Mike Lockwood
2011-05-16 12:36:29 -07:00
committed by Android Git Automerger
11 changed files with 389 additions and 90 deletions

View File

@@ -20,6 +20,7 @@ import java.io.File;
import android.content.res.Resources;
import android.os.storage.IMountService;
import android.os.storage.StorageVolume;
import android.util.Log;
/**
@@ -35,7 +36,25 @@ public class Environment {
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 @@ public class Environment {
* <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 @@ public class Environment {
* 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) {

View File

@@ -20,7 +20,9 @@ import android.os.Binder;
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 @@ public interface IMountService extends IInterface {
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 @@ public interface IMountService extends IInterface {
}
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 @@ public interface IMountService extends IInterface {
/**
* Returns list of all mountable volumes.
*/
public String[] getVolumeList() throws RemoteException;
public Parcelable[] getVolumeList() throws RemoteException;
}

View File

@@ -19,6 +19,7 @@ package android.os.storage;
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 @@ public class StorageManager
* 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;
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -94,52 +94,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. -->

View File

@@ -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>

View File

@@ -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>

View File

@@ -1147,8 +1147,7 @@ public class MediaScanner
mGenresUri = Genres.getContentUri(volumeName);
mPlaylistsUri = Playlists.getContentUri(volumeName);
mCaseInsensitivePaths = !mContext.getResources().getBoolean(
com.android.internal.R.bool.config_caseSensitiveExternalStorage);
mCaseInsensitivePaths = true;
if (!Process.supportsProcesses()) {
// Simulator uses host file system, so it should be case sensitive.
mCaseInsensitivePaths = false;

View File

@@ -17,6 +17,7 @@
package com.android.server;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.util.XmlUtils;
import com.android.server.am.ActivityManagerService;
import android.Manifest;
@@ -28,6 +29,9 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.res.ObbInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
@@ -36,6 +40,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -46,8 +51,14 @@ import android.os.storage.IMountShutdownObserver;
import android.os.storage.IObbActionListener;
import android.os.storage.OnObbStateChangeListener;
import android.os.storage.StorageResultCode;
import android.os.storage.StorageVolume;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Slog;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -145,6 +156,8 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
private Context mContext;
private NativeDaemonConnector mConnector;
private final ArrayList<StorageVolume> mVolumes = new ArrayList<StorageVolume>();
private StorageVolume mPrimaryVolume;
private final HashMap<String, String> mVolumeStates = new HashMap<String, String>();
private String mExternalStoragePath;
private PackageManagerService mPms;
@@ -1068,6 +1081,74 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
}
}
// Storage list XML tags
private static final String TAG_STORAGE_LIST = "StorageList";
private static final String TAG_STORAGE = "storage";
private void readStorageList(Resources resources) {
int id = com.android.internal.R.xml.storage_list;
XmlResourceParser parser = resources.getXml(id);
AttributeSet attrs = Xml.asAttributeSet(parser);
try {
XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
while (true) {
XmlUtils.nextElement(parser);
String element = parser.getName();
if (element == null) break;
if (TAG_STORAGE.equals(element)) {
TypedArray a = resources.obtainAttributes(attrs,
com.android.internal.R.styleable.Storage);
CharSequence path = a.getText(
com.android.internal.R.styleable.Storage_mountPoint);
CharSequence description = a.getText(
com.android.internal.R.styleable.Storage_storageDescription);
boolean primary = a.getBoolean(
com.android.internal.R.styleable.Storage_primary, false);
boolean removable = a.getBoolean(
com.android.internal.R.styleable.Storage_removable, false);
boolean emulated = a.getBoolean(
com.android.internal.R.styleable.Storage_emulated, false);
int mtpReserve = a.getInt(
com.android.internal.R.styleable.Storage_mtpReserve, 0);
Slog.d(TAG, "got storage path: " + path + " description: " + description +
" primary: " + primary + " removable: " + removable +
" emulated: " + emulated + " mtpReserve: " + mtpReserve);
if (path == null || description == null) {
Slog.e(TAG, "path or description is null in readStorageList");
} else {
StorageVolume volume = new StorageVolume(path.toString(),
description.toString(), removable, emulated, mtpReserve);
if (primary) {
if (mPrimaryVolume == null) {
mPrimaryVolume = volume;
} else {
Slog.e(TAG, "multiple primary volumes in storage list");
}
}
if (mPrimaryVolume == volume) {
// primay volume must be first
mVolumes.add(0, volume);
} else {
mVolumes.add(volume);
}
}
a.recycle();
}
}
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
parser.close();
}
}
/**
* Constructs a new MountService instance
*
@@ -1075,13 +1156,16 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
*/
public MountService(Context context) {
mContext = context;
Resources resources = context.getResources();
readStorageList(resources);
mExternalStoragePath = Environment.getExternalStorageDirectory().getPath();
mEmulateExternalStorage = context.getResources().getBoolean(
com.android.internal.R.bool.config_emulateExternalStorage);
if (mEmulateExternalStorage) {
Slog.d(TAG, "using emulated external storage");
mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED);
if (mPrimaryVolume != null) {
mExternalStoragePath = mPrimaryVolume.getPath();
mEmulateExternalStorage = mPrimaryVolume.isEmulated();
if (mEmulateExternalStorage) {
Slog.d(TAG, "using emulated external storage");
mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED);
}
}
// XXX: This will go away soon in favor of IMountServiceObserver
@@ -1753,13 +1837,12 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
}
}
public String[] getVolumeList() {
synchronized(mVolumeStates) {
Set<String> volumes = mVolumeStates.keySet();
String[] result = new String[volumes.size()];
int i = 0;
for (String volume : volumes) {
result[i++] = volume;
public Parcelable[] getVolumeList() {
synchronized(mVolumes) {
int size = mVolumes.size();
Parcelable[] result = new Parcelable[size];
for (int i = 0; i < size; i++) {
result[i] = mVolumes.get(i);
}
return result;
}