Mounting encrypted OBB files has never worked reliably across devices, partly due to its reliance on Twofish encryption support in the kernel. This is because Twofish support (CONFIG_CRYPTO_TWOFISH) has never been required or even recommended for Android. It has never been enabled in GKI, but even before GKI it wasn't required or recommended. Moreover, this is now the only Android feature that still uses dm-crypt (CONFIG_DM_CRYPT), and some devices don't have that enabled either. Therefore, it appears that this feature is unused. That's perhaps not surprising, considering that the documentation for OBBs (https://developer.android.com/google/play/expansion-files) says that they are deprecated, and also it explains OBBs as being app files that are opaque to the platform; the ability of the platform to mount OBBs that happen to be in a particular format is never mentioned. That means that OBB mounting is probably rarely used even with unencrypted OBBs. Finally, the usefulness of OBBs having their own encryption layer (in addition to what the platform already provides via FBE) is not clear either, especially with such an unusual choice of cipher. To avoid the confusion that is being caused by having the broken code for mounting encrypted OBBs still sitting around, let's remove it. Test: atest StorageManagerTest # on Cuttlefish Test: atest StorageManagerIntegrationTest # on Cuttlefish Bug: 216475849 Change-Id: I6e6a6462ab8343299dc5e0145b87dc28b16b0bc1
561 lines
20 KiB
C++
561 lines
20 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#define LOG_TAG "IMountService"
|
|
|
|
#include <storage/IMountService.h>
|
|
#include <binder/Parcel.h>
|
|
|
|
namespace android {
|
|
|
|
enum {
|
|
TRANSACTION_registerListener = IBinder::FIRST_CALL_TRANSACTION,
|
|
TRANSACTION_unregisterListener,
|
|
TRANSACTION_isUsbMassStorageConnected,
|
|
TRANSACTION_setUsbMassStorageEnabled,
|
|
TRANSACTION_isUsbMassStorageEnabled,
|
|
TRANSACTION_mountVolume,
|
|
TRANSACTION_unmountVolume,
|
|
TRANSACTION_formatVolume,
|
|
TRANSACTION_getStorageUsers,
|
|
TRANSACTION_getVolumeState,
|
|
TRANSACTION_createSecureContainer,
|
|
TRANSACTION_finalizeSecureContainer,
|
|
TRANSACTION_destroySecureContainer,
|
|
TRANSACTION_mountSecureContainer,
|
|
TRANSACTION_unmountSecureContainer,
|
|
TRANSACTION_isSecureContainerMounted,
|
|
TRANSACTION_renameSecureContainer,
|
|
TRANSACTION_getSecureContainerPath,
|
|
TRANSACTION_getSecureContainerList,
|
|
TRANSACTION_shutdown,
|
|
TRANSACTION_finishMediaUpdate,
|
|
TRANSACTION_mountObb,
|
|
TRANSACTION_unmountObb,
|
|
TRANSACTION_isObbMounted,
|
|
TRANSACTION_getMountedObbPath,
|
|
TRANSACTION_isExternalStorageEmulated,
|
|
TRANSACTION_decryptStorage,
|
|
TRANSACTION_encryptStorage,
|
|
};
|
|
|
|
class BpMountService: public BpInterface<IMountService>
|
|
{
|
|
public:
|
|
explicit BpMountService(const sp<IBinder>& impl)
|
|
: BpInterface<IMountService>(impl)
|
|
{
|
|
}
|
|
|
|
virtual void registerListener(const sp<IMountServiceListener>& listener)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeStrongBinder(IInterface::asBinder(listener));
|
|
if (remote()->transact(TRANSACTION_registerListener, data, &reply) != NO_ERROR) {
|
|
ALOGD("registerListener could not contact remote\n");
|
|
return;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("registerListener caught exception %d\n", err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
virtual void unregisterListener(const sp<IMountServiceListener>& listener)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeStrongBinder(IInterface::asBinder(listener));
|
|
if (remote()->transact(TRANSACTION_unregisterListener, data, &reply) != NO_ERROR) {
|
|
ALOGD("unregisterListener could not contact remote\n");
|
|
return;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("unregisterListener caught exception %d\n", err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
virtual bool isUsbMassStorageConnected()
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
if (remote()->transact(TRANSACTION_isUsbMassStorageConnected, data, &reply) != NO_ERROR) {
|
|
ALOGD("isUsbMassStorageConnected could not contact remote\n");
|
|
return false;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("isUsbMassStorageConnected caught exception %d\n", err);
|
|
return false;
|
|
}
|
|
return reply.readInt32() != 0;
|
|
}
|
|
|
|
virtual void setUsbMassStorageEnabled(const bool enable)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeInt32(enable != 0);
|
|
if (remote()->transact(TRANSACTION_setUsbMassStorageEnabled, data, &reply) != NO_ERROR) {
|
|
ALOGD("setUsbMassStorageEnabled could not contact remote\n");
|
|
return;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("setUsbMassStorageEnabled caught exception %d\n", err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
virtual bool isUsbMassStorageEnabled()
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
if (remote()->transact(TRANSACTION_isUsbMassStorageEnabled, data, &reply) != NO_ERROR) {
|
|
ALOGD("isUsbMassStorageEnabled could not contact remote\n");
|
|
return false;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("isUsbMassStorageEnabled caught exception %d\n", err);
|
|
return false;
|
|
}
|
|
return reply.readInt32() != 0;
|
|
}
|
|
|
|
int32_t mountVolume(const String16& mountPoint)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(mountPoint);
|
|
if (remote()->transact(TRANSACTION_mountVolume, data, &reply) != NO_ERROR) {
|
|
ALOGD("mountVolume could not contact remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("mountVolume caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t unmountVolume(const String16& mountPoint, const bool force, const bool removeEncryption)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(mountPoint);
|
|
data.writeInt32(force ? 1 : 0);
|
|
data.writeInt32(removeEncryption ? 1 : 0);
|
|
if (remote()->transact(TRANSACTION_unmountVolume, data, &reply) != NO_ERROR) {
|
|
ALOGD("unmountVolume could not contact remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("unmountVolume caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t formatVolume(const String16& mountPoint)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(mountPoint);
|
|
if (remote()->transact(TRANSACTION_formatVolume, data, &reply) != NO_ERROR) {
|
|
ALOGD("formatVolume could not contact remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("formatVolume caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t getStorageUsers(const String16& mountPoint, int32_t** users)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(mountPoint);
|
|
if (remote()->transact(TRANSACTION_getStorageUsers, data, &reply) != NO_ERROR) {
|
|
ALOGD("getStorageUsers could not contact remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("getStorageUsers caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
int32_t numUsersI = reply.readInt32();
|
|
uint32_t numUsers;
|
|
if (numUsersI < 0) {
|
|
ALOGW("Number of users is negative: %d\n", numUsersI);
|
|
numUsers = 0;
|
|
} else {
|
|
numUsers = static_cast<uint32_t>(numUsersI);
|
|
}
|
|
*users = (int32_t*)malloc(sizeof(int32_t)*numUsers);
|
|
for (size_t i = 0; i < numUsers; i++) {
|
|
**users++ = reply.readInt32();
|
|
}
|
|
return static_cast<int32_t>(numUsers);
|
|
}
|
|
|
|
int32_t getVolumeState(const String16& mountPoint)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(mountPoint);
|
|
if (remote()->transact(TRANSACTION_getVolumeState, data, &reply) != NO_ERROR) {
|
|
ALOGD("getVolumeState could not contact remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("getVolumeState caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t createSecureContainer(const String16& id, const int32_t sizeMb, const String16& fstype,
|
|
const String16& key, const int32_t ownerUid)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(id);
|
|
data.writeInt32(sizeMb);
|
|
data.writeString16(fstype);
|
|
data.writeString16(key);
|
|
data.writeInt32(ownerUid);
|
|
if (remote()->transact(TRANSACTION_createSecureContainer, data, &reply) != NO_ERROR) {
|
|
ALOGD("createSecureContainer could not contact remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("createSecureContainer caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t finalizeSecureContainer(const String16& id)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(id);
|
|
if (remote()->transact(TRANSACTION_finalizeSecureContainer, data, &reply) != NO_ERROR) {
|
|
ALOGD("finalizeSecureContainer couldn't call remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("finalizeSecureContainer caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t destroySecureContainer(const String16& id)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(id);
|
|
if (remote()->transact(TRANSACTION_destroySecureContainer, data, &reply) != NO_ERROR) {
|
|
ALOGD("destroySecureContainer couldn't call remote");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("destroySecureContainer caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t mountSecureContainer(const String16& id, const String16& key, const int32_t ownerUid)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(id);
|
|
data.writeString16(key);
|
|
data.writeInt32(ownerUid);
|
|
// Assume read-only
|
|
data.writeInt32(1);
|
|
if (remote()->transact(TRANSACTION_mountSecureContainer, data, &reply) != NO_ERROR) {
|
|
ALOGD("mountSecureContainer couldn't call remote");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode(); // What to do...
|
|
if (err < 0) {
|
|
ALOGD("mountSecureContainer caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t unmountSecureContainer(const String16& id, const bool force)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(id);
|
|
data.writeInt32(force ? 1 : 0);
|
|
if (remote()->transact(TRANSACTION_getSecureContainerPath, data, &reply) != NO_ERROR) {
|
|
ALOGD("unmountSecureContainer couldn't call remote");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode(); // What to do...
|
|
if (err < 0) {
|
|
ALOGD("unmountSecureContainer caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
bool isSecureContainerMounted(const String16& id)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(id);
|
|
if (remote()->transact(TRANSACTION_isSecureContainerMounted, data, &reply) != NO_ERROR) {
|
|
ALOGD("isSecureContainerMounted couldn't call remote");
|
|
return false;
|
|
}
|
|
int32_t err = reply.readExceptionCode(); // What to do...
|
|
if (err < 0) {
|
|
ALOGD("isSecureContainerMounted caught exception %d\n", err);
|
|
return false;
|
|
}
|
|
return reply.readInt32() != 0;
|
|
}
|
|
|
|
int32_t renameSecureContainer(const String16& oldId, const String16& newId)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(oldId);
|
|
data.writeString16(newId);
|
|
if (remote()->transact(TRANSACTION_renameSecureContainer, data, &reply) != NO_ERROR) {
|
|
ALOGD("renameSecureContainer couldn't call remote");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode(); // What to do...
|
|
if (err < 0) {
|
|
ALOGD("renameSecureContainer caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
bool getSecureContainerPath(const String16& id, String16& path)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(id);
|
|
if (remote()->transact(TRANSACTION_getSecureContainerPath, data, &reply) != NO_ERROR) {
|
|
ALOGD("getSecureContainerPath couldn't call remote");
|
|
return false;
|
|
}
|
|
int32_t err = reply.readExceptionCode(); // What to do...
|
|
if (err < 0) {
|
|
ALOGD("getSecureContainerPath caught exception %d\n", err);
|
|
return false;
|
|
}
|
|
path = reply.readString16();
|
|
return true;
|
|
}
|
|
|
|
int32_t getSecureContainerList(const String16& id, String16*& containers)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(id);
|
|
if (remote()->transact(TRANSACTION_getSecureContainerList, data, &reply) != NO_ERROR) {
|
|
ALOGD("getSecureContainerList couldn't call remote");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("getSecureContainerList caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
const int32_t numStrings = reply.readInt32();
|
|
containers = new String16[numStrings];
|
|
for (int i = 0; i < numStrings; i++) {
|
|
containers[i] = reply.readString16();
|
|
}
|
|
return numStrings;
|
|
}
|
|
|
|
void shutdown(const sp<IMountShutdownObserver>& observer)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeStrongBinder(IInterface::asBinder(observer));
|
|
if (remote()->transact(TRANSACTION_shutdown, data, &reply) != NO_ERROR) {
|
|
ALOGD("shutdown could not contact remote\n");
|
|
return;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("shutdown caught exception %d\n", err);
|
|
return;
|
|
}
|
|
reply.readExceptionCode();
|
|
}
|
|
|
|
void finishMediaUpdate()
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
if (remote()->transact(TRANSACTION_finishMediaUpdate, data, &reply) != NO_ERROR) {
|
|
ALOGD("finishMediaUpdate could not contact remote\n");
|
|
return;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("finishMediaUpdate caught exception %d\n", err);
|
|
return;
|
|
}
|
|
reply.readExceptionCode();
|
|
}
|
|
|
|
void mountObb(const String16& rawPath, const String16& canonicalPath,
|
|
const sp<IObbActionListener>& token, int32_t nonce, const sp<ObbInfo>& obbInfo)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(rawPath);
|
|
data.writeString16(canonicalPath);
|
|
data.writeStrongBinder(IInterface::asBinder(token));
|
|
data.writeInt32(nonce);
|
|
obbInfo->writeToParcel(&data);
|
|
if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
|
|
ALOGD("mountObb could not contact remote\n");
|
|
return;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("mountObb caught exception %d\n", err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void unmountObb(const String16& filename, const bool force,
|
|
const sp<IObbActionListener>& token, const int32_t nonce)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(filename);
|
|
data.writeInt32(force ? 1 : 0);
|
|
data.writeStrongBinder(IInterface::asBinder(token));
|
|
data.writeInt32(nonce);
|
|
if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) {
|
|
ALOGD("unmountObb could not contact remote\n");
|
|
return;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("unmountObb caught exception %d\n", err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool isObbMounted(const String16& filename)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(filename);
|
|
if (remote()->transact(TRANSACTION_isObbMounted, data, &reply) != NO_ERROR) {
|
|
ALOGD("isObbMounted could not contact remote\n");
|
|
return false;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("isObbMounted caught exception %d\n", err);
|
|
return false;
|
|
}
|
|
return reply.readInt32() != 0;
|
|
}
|
|
|
|
bool getMountedObbPath(const String16& filename, String16& path)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(filename);
|
|
if (remote()->transact(TRANSACTION_getMountedObbPath, data, &reply) != NO_ERROR) {
|
|
ALOGD("getMountedObbPath could not contact remote\n");
|
|
return false;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("getMountedObbPath caught exception %d\n", err);
|
|
return false;
|
|
}
|
|
path = reply.readString16();
|
|
return true;
|
|
}
|
|
|
|
int32_t decryptStorage(const String16& password)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(password);
|
|
if (remote()->transact(TRANSACTION_decryptStorage, data, &reply) != NO_ERROR) {
|
|
ALOGD("decryptStorage could not contact remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("decryptStorage caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
|
|
int32_t encryptStorage(const String16& password)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
|
data.writeString16(password);
|
|
if (remote()->transact(TRANSACTION_encryptStorage, data, &reply) != NO_ERROR) {
|
|
ALOGD("encryptStorage could not contact remote\n");
|
|
return -1;
|
|
}
|
|
int32_t err = reply.readExceptionCode();
|
|
if (err < 0) {
|
|
ALOGD("encryptStorage caught exception %d\n", err);
|
|
return err;
|
|
}
|
|
return reply.readInt32();
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_META_INTERFACE(MountService, "android.os.storage.IStorageManager")
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
}
|