diff options
| -rw-r--r-- | core/java/android/os/Environment.java | 45 | ||||
| -rw-r--r-- | core/java/android/os/storage/IMountService.java | 14 | ||||
| -rw-r--r-- | core/java/android/os/storage/StorageManager.java | 27 | ||||
| -rw-r--r-- | core/java/android/os/storage/StorageVolume.aidl | 19 | ||||
| -rw-r--r-- | core/java/android/os/storage/StorageVolume.java | 147 | ||||
| -rwxr-xr-x | core/res/res/values/attrs.xml | 17 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 50 | ||||
| -rwxr-xr-x | core/res/res/values/strings.xml | 8 | ||||
| -rw-r--r-- | core/res/res/xml/storage_list.xml | 40 | ||||
| -rw-r--r-- | docs/html/guide/topics/usb/adk.jd | 61 | ||||
| -rw-r--r-- | media/java/android/media/MediaScanner.java | 3 | ||||
| -rw-r--r-- | media/java/android/mtp/MtpStorage.java | 13 | ||||
| -rw-r--r-- | media/jni/android_mtp_MtpServer.cpp | 12 | ||||
| -rw-r--r-- | media/mtp/MtpStorage.cpp | 7 | ||||
| -rw-r--r-- | media/mtp/MtpStorage.h | 5 | ||||
| -rw-r--r-- | services/java/com/android/server/MountService.java | 109 |
16 files changed, 461 insertions, 116 deletions
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index e308c2c48a8d..1f3f6d9565b6 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -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) { diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 27da3c344880..c2dc8aeefc43 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -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; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 234057b8632e..6fd1d002fa84 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -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; + } } diff --git a/core/java/android/os/storage/StorageVolume.aidl b/core/java/android/os/storage/StorageVolume.aidl new file mode 100644 index 000000000000..d68991790519 --- /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 000000000000..d79f6c8f1792 --- /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 71a8b2a82dc1..004b7555f53f 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 27c7a4d1a602..49bbd824418c 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 8ef9a3b247b4..b713b518e739 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 000000000000..944bb3a94209 --- /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> diff --git a/docs/html/guide/topics/usb/adk.jd b/docs/html/guide/topics/usb/adk.jd index 44d7fc22edab..8aaa65c9fd0b 100644 --- a/docs/html/guide/topics/usb/adk.jd +++ b/docs/html/guide/topics/usb/adk.jd @@ -6,7 +6,9 @@ page.title=Android Open Accessory Development Kit <h2>In this document</h2> <ol> + <li><a href="#components">ADK Components</a></li> <li> + <a href="#getting-started">Getting Started with the ADK</a> <ol> @@ -54,16 +56,28 @@ page.title=Android Open Accessory Development Kit </li> </ol> - <h2>Where to Buy</h2> + + + <h2>See also</h2> + + <ol> + <li><a href="http://www.youtube.com/watch?v=s7szcpXf2rE">Google I/O Session Video</a></li> + <li><a href="{@docRoot}guide/topics/usb/accessory.html">USB Accessory Dev Guide</a></li> + </ol> + + <h2>Where to buy</h2> <ol> <li><a href= - "http://www.rt-shop.sakura.ne.jp/rt-shop/index.php?main_page=product_info&products_id=2731&language=en"> + "http://www.rt-net.jp/shop/index.php?main_page=product_info&cPath=3_4&products_id=1"> RT Corp</a></li> <li><a href= "http://www.microchip.com/android"> Microchip</a></li> + + <li><a href="https://store.diydrones.com/ProductDetails.asp?ProductCode=BR-PhoneDrone"> + DIY Drones</a></li> </ol> </div> </div> @@ -79,7 +93,7 @@ page.title=Android Open Accessory Development Kit released Android-powered devices are only capable of acting as a USB device and cannot initiate connections with external USB devices. Android Open Accessory support overcomes this limitation and allows you to build accessories that can interact with an assortment of Android-powered - devices by allowing the accessory initiate the connection.</p> + devices by allowing the accessory to initiate the connection.</p> <p class="note"><strong>Note:</strong> Accessory mode is ultimately dependent on the device's hardware and not all devices will support accessory mode. Devices that support accessory mode can @@ -87,20 +101,29 @@ page.title=Android Open Accessory Development Kit Android manifest. For more information, see the <a href= "{@docRoot}guide/topics/usb/accessory.html#manifest">USB Accessory</a> Developer Guide.</p> + <p>The following list of distributers are currently producing Android Open Accessory compatible + development boards:</p> + + <ul> + <li><a href="http://www.rt-net.jp/shop/index.php?main_page=product_info&cPath=3_4&products_id=1"> + RT Corp</a> provides an Arduino-compatible board based on the Android ADK board design.</li> + <li><a href="http://www.microchip.com/android">Microchip</a> provides a A PIC based USB + microcontroller board.</li> + <li><a href="https://store.diydrones.com/ProductDetails.asp?ProductCode=BR-PhoneDrone">DIY + Drones</a> provides an Arduino-compatible board geared towards RC (radio controlled) and UAV + (unmanned aerial vehicle) enthusiasts.</li> + </ul> + + <p>We expect more hardware distributers to create a variety of kits, so please stay tuned for + further developments.</p> + + <h2 id="components">ADK Components</h2> <p>The Android Open Accessory Development Kit (ADK) provides an implementation of an Android USB accessory that is based on the <a href="http://www.arduino.cc/">Arduino open source electronics prototyping platform</a>, the accessory's hardware design files, code that implements the accessory's firmware, and the Android application that interacts with the accessory. The hardware - design files and code are contained in the <a href= - "https://dl-ssl.google.com/android/adk/adk_release_0506.zip">ADK package download</a>. You can - <a href= - "http://www.rt-shop.sakura.ne.jp/rt-shop/index.php?main_page=product_info&products_id=2731&language=en">buy - the hardware components</a> of the ADK if you do not already have them. There is also a <a href= - "http://www.microchip.com/android"> - PIC based USB microcontroller</a> that is not based on the ADK design, but that you can still use - to create your own Android open accessories. We expect more hardware distributers to create a - variety of kits, so please stay tuned for further developments.</p> - + design files and firmware code are contained in the <a href= + "https://dl-ssl.google.com/android/adk/adk_release_0512.zip">ADK package download</a>.</p> <p>The main hardware and software components of the ADK include:</p> <ul> @@ -138,7 +161,6 @@ page.title=Android Open Accessory Development Kit how to setup communication with the device.</li> <li>Other third party libraries to support the ADK board's functionality: - <ul> <li><a href="http://www.arduino.cc/playground/Main/CapSense">CapSense library</a></li> @@ -150,11 +172,12 @@ page.title=Android Open Accessory Development Kit <li><a href="http://www.arduino.cc/playground/Code/Spi">Spi library</a></li> <li><a href="http://www.arduino.cc/en/Reference/Wire">Wire library</a></li> + + <li>An Android application, DemoKit, that communicates with the ADK board and shield. The + source for this project is in the <code>app/</code> directory.</li> </ul> </li> - <li>An Android application, DemoKit, that communicates with the ADK board and shield. The - source for this project is in the <code>app/</code> directory.</li> </ul> <h2 id="getting-started">Getting Started with the ADK</h2> @@ -172,7 +195,7 @@ page.title=Android Open Accessory Development Kit libraries to sense human capacitance. This is needed for the capacative button that is located on the ADK shield.</li> - <li><a href="">The ADK package</a>: contains the firmware for the ADK board and hardware design + <li><a href="https://dl-ssl.google.com/android/adk/adk_release_0512.zip">The ADK package</a>: contains the firmware for the ADK board and hardware design files for the ADK board and shield.</li> </ul> @@ -190,7 +213,7 @@ page.title=Android Open Accessory Development Kit otherwise.</p> </li> - <li><a href="https://dl-ssl.google.com/android/adk/adk_release_0506.zip">Download</a> and + <li><a href="https://dl-ssl.google.com/android/adk/adk_release_0512.zip">Download</a> and extract the ADK package to a directory of your choice. You should have an <code>app</code>, <code>firmware</code>, and <code>hardware</code> directories.</li> @@ -419,7 +442,7 @@ page.title=Android Open Accessory Development Kit mode, the accessory cannot discern whether the device supports accessory mode and is not in that state, or if the device does not support accessory mode at all. This is because devices that support accessory mode but aren't in it initially report the device's manufacturer vendor ID and - product ID, and not the special Google ones. In either case, the accessory should try to start + product ID, and not the special Android Open Accessory ones. In either case, the accessory should try to start the device into accessory mode to figure out if the device supports it. The following steps explain how to do this:</p> diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index d1eb3884968e..55b004517958 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -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; diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index 33146e7dadb4..21a18ca46f03 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -29,12 +29,15 @@ public class MtpStorage { private final String mPath; private final String mDescription; private final long mReserveSpace; + private final boolean mRemovable; - public MtpStorage(int id, String path, String description, long reserveSpace) { + public MtpStorage(int id, String path, String description, + long reserveSpace, boolean removable) { mStorageId = id; mPath = path; mDescription = description; mReserveSpace = reserveSpace; + mRemovable = removable; } /** @@ -86,4 +89,12 @@ public class MtpStorage { return mReserveSpace; } + /** + * Returns true if the storage is removable. + * + * @return is removable + */ + public final boolean isRemovable() { + return mRemovable; + } } diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index c55189f3491d..4f6bb153170a 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -47,6 +47,7 @@ static jfieldID field_MtpStorage_storageId; static jfieldID field_MtpStorage_path; static jfieldID field_MtpStorage_description; static jfieldID field_MtpStorage_reserveSpace; +static jfieldID field_MtpStorage_removable; static Mutex sMutex; @@ -245,11 +246,13 @@ android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage) jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path); jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description); jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace); + jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable); const char *pathStr = env->GetStringUTFChars(path, NULL); const char *descriptionStr = env->GetStringUTFChars(description, NULL); - MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace); + MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, + reserveSpace, removable); thread->addStorage(storage); env->ReleaseStringUTFChars(path, pathStr); @@ -318,7 +321,12 @@ int register_android_mtp_MtpServer(JNIEnv *env) } field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J"); if (field_MtpStorage_reserveSpace == NULL) { - LOGE("Can't find MtpStorage.mStorageId"); + LOGE("Can't find MtpStorage.mReserveSpace"); + return -1; + } + field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z"); + if (field_MtpStorage_removable == NULL) { + LOGE("Can't find MtpStorage.mRemovable"); return -1; } clazz_MtpStorage = (jclass)env->NewGlobalRef(clazz); diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp index fff0b5f27ccd..fef80664bda3 100644 --- a/media/mtp/MtpStorage.cpp +++ b/media/mtp/MtpStorage.cpp @@ -33,12 +33,13 @@ namespace android { MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, - const char* description, uint64_t reserveSpace) + const char* description, uint64_t reserveSpace, bool removable) : mStorageID(id), mFilePath(filePath), mDescription(description), mMaxCapacity(0), - mReserveSpace(reserveSpace) + mReserveSpace(reserveSpace), + mRemovable(removable) { LOGV("MtpStorage id: %d path: %s\n", id, filePath); } @@ -47,7 +48,7 @@ MtpStorage::~MtpStorage() { } int MtpStorage::getType() const { - return MTP_STORAGE_FIXED_RAM; + return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM); } int MtpStorage::getFileSystemType() const { diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h index d6ad25f33abb..3e4f40d0aa04 100644 --- a/media/mtp/MtpStorage.h +++ b/media/mtp/MtpStorage.h @@ -33,10 +33,12 @@ private: uint64_t mMaxCapacity; // amount of free space to leave unallocated uint64_t mReserveSpace; + bool mRemovable; public: MtpStorage(MtpStorageID id, const char* filePath, - const char* description, uint64_t reserveSpace); + const char* description, uint64_t reserveSpace, + bool removable); virtual ~MtpStorage(); inline MtpStorageID getStorageID() const { return mStorageID; } @@ -47,6 +49,7 @@ public: uint64_t getFreeSpace(); const char* getDescription() const; inline const char* getPath() const { return (const char *)mFilePath; } + inline bool isRemovable() const { return mRemovable; } }; }; // namespace android diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index a100f1f34dd8..376d42f74c15 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -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; } |