MountServicepublic class MountService extends IMountService.Stub implements Watchdog.Monitor, INativeDaemonConnectorCallbacksMountService implements back-end services for platform storage
management. |
Fields Summary |
---|
static MountService | sSelf | private static final boolean | LOCAL_LOGD | private static final boolean | DEBUG_UNMOUNT | private static final boolean | DEBUG_EVENTS | private static final boolean | DEBUG_OBB | private static final boolean | WATCHDOG_ENABLE | private static final String | TAG | private static final String | VOLD_TAG | private static final int | MAX_CONTAINERSMaximum number of ASEC containers allowed to be mounted. | public static final String[] | CRYPTO_TYPESList of crypto types.
These must match CRYPT_TYPE_XXX in cryptfs.h AND their
corresponding commands in CommandListener.cpp | private final android.content.Context | mContext | private final NativeDaemonConnector | mConnector | private final Object | mVolumesLock | private android.os.storage.StorageVolume | mEmulatedTemplateWhen defined, base template for user-specific {@link StorageVolume}. | private final ArrayList | mVolumes | private final HashMap | mVolumesByPathMap from path to {@link StorageVolume} | private final HashMap | mVolumeStatesMap from path to state | private volatile boolean | mSystemReady | private com.android.server.pm.PackageManagerService | mPms | private boolean | mUmsEnabling | private boolean | mUmsAvailable | private final ArrayList | mListeners | private final CountDownLatch | mConnectedSignal | private final CountDownLatch | mAsecsScanned | private boolean | mSendUmsConnectedOnBoot | private final HashSet | mAsecMountSetPrivate hash of currently mounted secure containers.
Used as a lock in methods to manipulate secure containers. | private static final int | CRYPTO_ALGORITHM_KEY_SIZEThe size of the crypto algorithm key in bits for OBB files. Currently
Twofish is used which takes 128-bit keys. | private static final int | PBKDF2_HASH_ROUNDSThe number of times to run SHA1 in the PBKDF2 function for OBB files.
1024 is reasonably secure and not too slow. | private final Map | mObbMountsMounted OBB tracking information. Used to track the current state of all
OBBs. | private final Map | mObbPathToStateMapMap from raw paths to {@link ObbState}. | private final ObbActionHandler | mObbActionHandler | private static final int | OBB_RUN_ACTION | private static final int | OBB_MCS_BOUND | private static final int | OBB_MCS_UNBIND | private static final int | OBB_MCS_RECONNECT | private static final int | OBB_FLUSH_MOUNT_STATE | static final android.content.ComponentName | DEFAULT_CONTAINER_COMPONENT | private final DefaultContainerConnection | mDefContainerConn | private com.android.internal.app.IMediaContainerService | mContainerService | private static final String | LAST_FSTRIM_FILE | private final File | mLastMaintenanceFile | private long | mLastMaintenance | private static final int | H_UNMOUNT_PM_UPDATE | private static final int | H_UNMOUNT_PM_DONE | private static final int | H_UNMOUNT_MS | private static final int | H_SYSTEM_READY | private static final int | H_FSTRIM | private static final int | RETRY_UNMOUNT_DELAY | private static final int | MAX_UNMOUNT_RETRIES | private final android.os.Handler | mHandler | private final android.content.BroadcastReceiver | mUserReceiver | private final android.content.BroadcastReceiver | mUsbReceiver | private static final String | TAG_STORAGE_LIST | private static final String | TAG_STORAGE |
Constructors Summary |
---|
public MountService(android.content.Context context)Constructs a new MountService instance
sSelf = this;
mContext = context;
synchronized (mVolumesLock) {
readStorageListLocked();
}
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
HandlerThread hthread = new HandlerThread(TAG);
hthread.start();
mHandler = new MountServiceHandler(hthread.getLooper());
// Watch for user changes
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
userFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
// Watch for USB changes on primary volume
final StorageVolume primary = getPrimaryPhysicalVolume();
if (primary != null && primary.allowMassStorage()) {
mContext.registerReceiver(
mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
}
// Add OBB Action Handler to MountService thread.
mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
// Initialize the last-fstrim tracking if necessary
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
if (!mLastMaintenanceFile.exists()) {
// Not setting mLastMaintenance here means that we will force an
// fstrim during reboot following the OTA that installs this code.
try {
(new FileOutputStream(mLastMaintenanceFile)).close();
} catch (IOException e) {
Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
}
} else {
mLastMaintenance = mLastMaintenanceFile.lastModified();
}
/*
* Create the connection to vold with a maximum queue of twice the
* amount of containers we'd ever expect to have. This keeps an
* "asec list" from blocking a thread repeatedly.
*/
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
null);
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
// Add ourself to the Watchdog monitors if enabled.
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
}
|
Methods Summary |
---|
private void | addObbStateLocked(com.android.server.MountService$ObbState obbState)
final IBinder binder = obbState.getBinder();
List<ObbState> obbStates = mObbMounts.get(binder);
if (obbStates == null) {
obbStates = new ArrayList<ObbState>();
mObbMounts.put(binder, obbStates);
} else {
for (final ObbState o : obbStates) {
if (o.rawPath.equals(obbState.rawPath)) {
throw new IllegalStateException("Attempt to add ObbState twice. "
+ "This indicates an error in the MountService logic.");
}
}
}
obbStates.add(obbState);
try {
obbState.link();
} catch (RemoteException e) {
/*
* The binder died before we could link it, so clean up our state
* and return failure.
*/
obbStates.remove(obbState);
if (obbStates.isEmpty()) {
mObbMounts.remove(binder);
}
// Rethrow the error so mountObb can get it
throw e;
}
mObbPathToStateMap.put(obbState.rawPath, obbState);
| private void | addVolumeLocked(android.os.storage.StorageVolume volume)
Slog.d(TAG, "addVolumeLocked() " + volume);
mVolumes.add(volume);
final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
if (existing != null) {
throw new IllegalStateException(
"Volume at " + volume.getPath() + " already exists: " + existing);
}
| public static java.lang.String | buildObbPath(java.lang.String canonicalPath, int userId, boolean forVold)
// TODO: allow caller to provide Environment for full testing
// TODO: extend to support OBB mounts on secondary external storage
// Only adjust paths when storage is emulated
if (!Environment.isExternalStorageEmulated()) {
return canonicalPath;
}
String path = canonicalPath.toString();
// First trim off any external storage prefix
final UserEnvironment userEnv = new UserEnvironment(userId);
// /storage/emulated/0
final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
// /storage/emulated_legacy
final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
.getAbsolutePath();
if (path.startsWith(externalPath)) {
path = path.substring(externalPath.length() + 1);
} else if (path.startsWith(legacyExternalPath)) {
path = path.substring(legacyExternalPath.length() + 1);
} else {
return canonicalPath;
}
// Handle special OBB paths on emulated storage
final String obbPath = "Android/obb";
if (path.startsWith(obbPath)) {
path = path.substring(obbPath.length() + 1);
if (forVold) {
return new File(Environment.getEmulatedStorageObbSource(), path).getAbsolutePath();
} else {
final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
.getAbsolutePath();
}
}
// Handle normal external storage paths
if (forVold) {
return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
} else {
return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
}
| public int | changeEncryptionPassword(int type, java.lang.String password)Set the password for encrypting the master key.
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
waitForReady();
if (DEBUG_EVENTS) {
Slog.i(TAG, "changing encryption password...");
}
try {
NativeDaemonEvent event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
new SensitiveArg(toHex(password)));
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
}
| public void | clearPassword()
if (!isReady()) {
return;
}
final NativeDaemonEvent event;
try {
event = mConnector.execute("cryptfs", "clearpw");
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
| private void | copyLocaleFromMountService()
String systemLocale;
try {
systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
} catch (RemoteException e) {
return;
}
if (TextUtils.isEmpty(systemLocale)) {
return;
}
Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
Locale locale = Locale.forLanguageTag(systemLocale);
Configuration config = new Configuration();
config.setLocale(locale);
try {
ActivityManagerNative.getDefault().updateConfiguration(config);
} catch (RemoteException e) {
Slog.e(TAG, "Error setting system locale from mount service", e);
}
// Temporary workaround for http://b/17945169.
Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
SystemProperties.set("persist.sys.language", locale.getLanguage());
SystemProperties.set("persist.sys.country", locale.getCountry());
| private void | createEmulatedVolumeForUserLocked(android.os.UserHandle user)Create and add new {@link StorageVolume} for given {@link UserHandle}
using {@link #mEmulatedTemplate} as template.
if (mEmulatedTemplate == null) {
throw new IllegalStateException("Missing emulated volume multi-user template");
}
final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
final File path = userEnv.getExternalStorageDirectory();
final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
volume.setStorageId(0);
addVolumeLocked(volume);
if (mSystemReady) {
updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
} else {
// Place stub status for early callers to find
mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
volume.setState(Environment.MEDIA_MOUNTED);
}
| public int | createSecureContainer(java.lang.String id, int sizeMb, java.lang.String fstype, java.lang.String key, int ownerUid, boolean external)
validatePermission(android.Manifest.permission.ASEC_CREATE);
waitForReady();
warnOnNotMounted();
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
ownerUid, external ? "1" : "0");
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
if (rc == StorageResultCode.OperationSucceeded) {
synchronized (mAsecMountSet) {
mAsecMountSet.add(id);
}
}
return rc;
| public int | decryptStorage(java.lang.String password)
if (TextUtils.isEmpty(password)) {
throw new IllegalArgumentException("password cannot be empty");
}
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
waitForReady();
if (DEBUG_EVENTS) {
Slog.i(TAG, "decrypting storage...");
}
final NativeDaemonEvent event;
try {
event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
final int code = Integer.parseInt(event.getMessage());
if (code == 0) {
// Decrypt was successful. Post a delayed message before restarting in order
// to let the UI to clear itself
mHandler.postDelayed(new Runnable() {
public void run() {
try {
mConnector.execute("cryptfs", "restart");
} catch (NativeDaemonConnectorException e) {
Slog.e(TAG, "problem executing in background", e);
}
}
}, 1000); // 1 second
}
return code;
} catch (NativeDaemonConnectorException e) {
// Decryption failed
return e.getCode();
}
| public int | destroySecureContainer(java.lang.String id, boolean force)
validatePermission(android.Manifest.permission.ASEC_DESTROY);
waitForReady();
warnOnNotMounted();
/*
* Force a GC to make sure AssetManagers in other threads of the
* system_server are cleaned up. We have to do this since AssetManager
* instances are kept as a WeakReference and it's possible we have files
* open on the external storage.
*/
Runtime.getRuntime().gc();
int rc = StorageResultCode.OperationSucceeded;
try {
final Command cmd = new Command("asec", "destroy", id);
if (force) {
cmd.appendArg("force");
}
mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageBusy) {
rc = StorageResultCode.OperationFailedStorageBusy;
} else {
rc = StorageResultCode.OperationFailedInternalError;
}
}
if (rc == StorageResultCode.OperationSucceeded) {
synchronized (mAsecMountSet) {
if (mAsecMountSet.contains(id)) {
mAsecMountSet.remove(id);
}
}
}
return rc;
| private int | doFormatVolume(java.lang.String path)
try {
mConnector.execute("volume", "format", path);
return StorageResultCode.OperationSucceeded;
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedNoMedia) {
return StorageResultCode.OperationFailedNoMedia;
} else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
return StorageResultCode.OperationFailedMediaCorrupt;
} else {
return StorageResultCode.OperationFailedInternalError;
}
}
| private boolean | doGetVolumeShared(java.lang.String path, java.lang.String method)
final NativeDaemonEvent event;
try {
event = mConnector.execute("volume", "shared", path, method);
} catch (NativeDaemonConnectorException ex) {
Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
return false;
}
if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
return event.getMessage().endsWith("enabled");
} else {
return false;
}
| private int | doMountVolume(java.lang.String path)
int rc = StorageResultCode.OperationSucceeded;
final StorageVolume volume;
synchronized (mVolumesLock) {
volume = mVolumesByPath.get(path);
}
if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {
Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume.");
return StorageResultCode.OperationFailedInternalError;
}
if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
try {
mConnector.execute("volume", "mount", path);
} catch (NativeDaemonConnectorException e) {
/*
* Mount failed for some reason
*/
String action = null;
int code = e.getCode();
if (code == VoldResponseCode.OpFailedNoMedia) {
/*
* Attempt to mount but no media inserted
*/
rc = StorageResultCode.OperationFailedNoMedia;
} else if (code == VoldResponseCode.OpFailedMediaBlank) {
if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
/*
* Media is blank or does not contain a supported filesystem
*/
updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
action = Intent.ACTION_MEDIA_NOFS;
rc = StorageResultCode.OperationFailedMediaBlank;
} else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
/*
* Volume consistency check failed
*/
updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
action = Intent.ACTION_MEDIA_UNMOUNTABLE;
rc = StorageResultCode.OperationFailedMediaCorrupt;
} else {
rc = StorageResultCode.OperationFailedInternalError;
}
/*
* Send broadcast intent (if required for the failure)
*/
if (action != null) {
sendStorageIntent(action, volume, UserHandle.ALL);
}
}
return rc;
| private void | doShareUnshareVolume(java.lang.String path, java.lang.String method, boolean enable)
// TODO: Add support for multiple share methods
if (!method.equals("ums")) {
throw new IllegalArgumentException(String.format("Method %s not supported", method));
}
try {
mConnector.execute("volume", enable ? "share" : "unshare", path, method);
} catch (NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to share/unshare", e);
}
| private int | doUnmountVolume(java.lang.String path, boolean force, boolean removeEncryption)
if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
return VoldResponseCode.OpFailedVolNotMounted;
}
/*
* Force a GC to make sure AssetManagers in other threads of the
* system_server are cleaned up. We have to do this since AssetManager
* instances are kept as a WeakReference and it's possible we have files
* open on the external storage.
*/
Runtime.getRuntime().gc();
// Redundant probably. But no harm in updating state again.
mPms.updateExternalMediaStatus(false, false);
try {
final Command cmd = new Command("volume", "unmount", path);
if (removeEncryption) {
cmd.appendArg("force_and_revert");
} else if (force) {
cmd.appendArg("force");
}
mConnector.execute(cmd);
// We unmounted the volume. None of the asec containers are available now.
synchronized (mAsecMountSet) {
mAsecMountSet.clear();
}
return StorageResultCode.OperationSucceeded;
} catch (NativeDaemonConnectorException e) {
// Don't worry about mismatch in PackageManager since the
// call back will handle the status changes any way.
int code = e.getCode();
if (code == VoldResponseCode.OpFailedVolNotMounted) {
return StorageResultCode.OperationFailedStorageNotMounted;
} else if (code == VoldResponseCode.OpFailedStorageBusy) {
return StorageResultCode.OperationFailedStorageBusy;
} else {
return StorageResultCode.OperationFailedInternalError;
}
}
| protected void | dump(java.io.FileDescriptor fd, java.io.PrintWriter writer, java.lang.String[] args)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
synchronized (mObbMounts) {
pw.println("mObbMounts:");
pw.increaseIndent();
final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
.iterator();
while (binders.hasNext()) {
Entry<IBinder, List<ObbState>> e = binders.next();
pw.println(e.getKey() + ":");
pw.increaseIndent();
final List<ObbState> obbStates = e.getValue();
for (final ObbState obbState : obbStates) {
pw.println(obbState);
}
pw.decreaseIndent();
}
pw.decreaseIndent();
pw.println();
pw.println("mObbPathToStateMap:");
pw.increaseIndent();
final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
while (maps.hasNext()) {
final Entry<String, ObbState> e = maps.next();
pw.print(e.getKey());
pw.print(" -> ");
pw.println(e.getValue());
}
pw.decreaseIndent();
}
synchronized (mVolumesLock) {
pw.println();
pw.println("mVolumes:");
pw.increaseIndent();
for (StorageVolume volume : mVolumes) {
pw.println(volume);
pw.increaseIndent();
pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
pw.decreaseIndent();
}
pw.decreaseIndent();
}
pw.println();
pw.println("mConnection:");
pw.increaseIndent();
mConnector.dump(fd, pw, args);
pw.decreaseIndent();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
pw.println();
pw.print("Last maintenance: ");
pw.println(sdf.format(new Date(mLastMaintenance)));
| public int | encryptStorage(int type, java.lang.String password)
if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
throw new IllegalArgumentException("password cannot be empty");
}
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
waitForReady();
if (DEBUG_EVENTS) {
Slog.i(TAG, "encrypting storage...");
}
try {
mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
new SensitiveArg(toHex(password)));
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
}
return 0;
| public int | finalizeSecureContainer(java.lang.String id)
validatePermission(android.Manifest.permission.ASEC_CREATE);
warnOnNotMounted();
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("asec", "finalize", id);
/*
* Finalization does a remount, so no need
* to update mAsecMountSet
*/
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
| public void | finishMediaUpdate()
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
| public int | fixPermissionsSecureContainer(java.lang.String id, int gid, java.lang.String filename)
validatePermission(android.Manifest.permission.ASEC_CREATE);
warnOnNotMounted();
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("asec", "fixperms", id, gid, filename);
/*
* Fix permissions does a remount, so no need to update
* mAsecMountSet
*/
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
| public int | formatVolume(java.lang.String path)
validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
waitForReady();
return doFormatVolume(path);
| private java.lang.String | fromHex(java.lang.String hexPassword)
if (hexPassword == null) {
return null;
}
try {
byte[] bytes = Hex.decodeHex(hexPassword.toCharArray());
return new String(bytes, StandardCharsets.UTF_8);
} catch (DecoderException e) {
return null;
}
| public int | getEncryptionState()
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
waitForReady();
final NativeDaemonEvent event;
try {
event = mConnector.execute("cryptfs", "cryptocomplete");
return Integer.parseInt(event.getMessage());
} catch (NumberFormatException e) {
// Bad result - unexpected.
Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
return ENCRYPTION_STATE_ERROR_UNKNOWN;
} catch (NativeDaemonConnectorException e) {
// Something bad happened.
Slog.w(TAG, "Error in communicating with cryptfs in validating");
return ENCRYPTION_STATE_ERROR_UNKNOWN;
}
| public java.lang.String | getField(java.lang.String field)Gets a field from the crypto header.
waitForReady();
final NativeDaemonEvent event;
try {
final String[] contents = NativeDaemonEvent.filterMessageList(
mConnector.executeForList("cryptfs", "getfield", field),
VoldResponseCode.CryptfsGetfieldResult);
String result = new String();
for (String content : contents) {
result += content;
}
return result;
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
| public java.lang.String | getMountedObbPath(java.lang.String rawPath)
Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
waitForReady();
warnOnNotMounted();
final ObbState state;
synchronized (mObbPathToStateMap) {
state = mObbPathToStateMap.get(rawPath);
}
if (state == null) {
Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
return null;
}
final NativeDaemonEvent event;
try {
event = mConnector.execute("obb", "path", state.voldPath);
event.checkCode(VoldResponseCode.AsecPathResult);
return event.getMessage();
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) {
return null;
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
}
| public java.lang.String | getPassword()
if (!isReady()) {
return new String();
}
final NativeDaemonEvent event;
try {
event = mConnector.execute("cryptfs", "getpw");
return fromHex(event.getMessage());
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
| public int | getPasswordType()Get the type of encryption used to encrypt the master key.
waitForReady();
final NativeDaemonEvent event;
try {
event = mConnector.execute("cryptfs", "getpwtype");
for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
if (CRYPTO_TYPES[i].equals(event.getMessage()))
return i;
}
throw new IllegalStateException("unexpected return from cryptfs");
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
| private android.os.storage.StorageVolume | getPrimaryPhysicalVolume()
synchronized (mVolumesLock) {
for (StorageVolume volume : mVolumes) {
if (volume.isPrimary() && !volume.isEmulated()) {
return volume;
}
}
}
return null;
| public java.lang.String | getSecureContainerFilesystemPath(java.lang.String id)
validatePermission(android.Manifest.permission.ASEC_ACCESS);
waitForReady();
warnOnNotMounted();
final NativeDaemonEvent event;
try {
event = mConnector.execute("asec", "fspath", id);
event.checkCode(VoldResponseCode.AsecPathResult);
return event.getMessage();
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) {
Slog.i(TAG, String.format("Container '%s' not found", id));
return null;
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
}
| public java.lang.String[] | getSecureContainerList()
validatePermission(android.Manifest.permission.ASEC_ACCESS);
waitForReady();
warnOnNotMounted();
try {
return NativeDaemonEvent.filterMessageList(
mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
} catch (NativeDaemonConnectorException e) {
return new String[0];
}
| public java.lang.String | getSecureContainerPath(java.lang.String id)
validatePermission(android.Manifest.permission.ASEC_ACCESS);
waitForReady();
warnOnNotMounted();
final NativeDaemonEvent event;
try {
event = mConnector.execute("asec", "path", id);
event.checkCode(VoldResponseCode.AsecPathResult);
return event.getMessage();
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) {
Slog.i(TAG, String.format("Container '%s' not found", id));
return null;
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
}
| public int[] | getStorageUsers(java.lang.String path)
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
try {
final String[] r = NativeDaemonEvent.filterMessageList(
mConnector.executeForList("storage", "users", path),
VoldResponseCode.StorageUsersListResult);
// FMT: <pid> <process name>
int[] data = new int[r.length];
for (int i = 0; i < r.length; i++) {
String[] tok = r[i].split(" ");
try {
data[i] = Integer.parseInt(tok[0]);
} catch (NumberFormatException nfe) {
Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
return new int[0];
}
}
return data;
} catch (NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to retrieve storage users list", e);
return new int[0];
}
| private boolean | getUmsEnabling()
synchronized (mListeners) {
return mUmsEnabling;
}
| public android.os.storage.StorageVolume[] | getVolumeList()
final int callingUserId = UserHandle.getCallingUserId();
final boolean accessAll = (mContext.checkPermission(
android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
synchronized (mVolumesLock) {
final ArrayList<StorageVolume> filtered = Lists.newArrayList();
for (StorageVolume volume : mVolumes) {
final UserHandle owner = volume.getOwner();
final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
if (accessAll || ownerMatch) {
filtered.add(volume);
}
}
return filtered.toArray(new StorageVolume[filtered.size()]);
}
| public java.lang.String | getVolumeState(java.lang.String mountPoint)
synchronized (mVolumesLock) {
String state = mVolumeStates.get(mountPoint);
if (state == null) {
Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
state = Environment.MEDIA_REMOVED;
} else {
throw new IllegalArgumentException();
}
}
return state;
}
| private void | handleSystemReady()
// Snapshot current volume states since it's not safe to call into vold
// while holding locks.
final HashMap<String, String> snapshot;
synchronized (mVolumesLock) {
snapshot = new HashMap<String, String>(mVolumeStates);
}
for (Map.Entry<String, String> entry : snapshot.entrySet()) {
final String path = entry.getKey();
final String state = entry.getValue();
if (state.equals(Environment.MEDIA_UNMOUNTED)) {
int rc = doMountVolume(path);
if (rc != StorageResultCode.OperationSucceeded) {
Slog.e(TAG, String.format("Boot-time mount failed (%d)",
rc));
}
} else if (state.equals(Environment.MEDIA_SHARED)) {
/*
* Bootstrap UMS enabled state since vold indicates
* the volume is shared (runtime restart while ums enabled)
*/
notifyVolumeStateChange(null, path, VolumeState.NoMedia,
VolumeState.Shared);
}
}
// Push mounted state for all emulated storage
synchronized (mVolumesLock) {
for (StorageVolume volume : mVolumes) {
if (volume.isEmulated()) {
updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
}
}
}
/*
* If UMS was connected on boot, send the connected event
* now that we're up.
*/
if (mSendUmsConnectedOnBoot) {
sendUmsIntent(true);
mSendUmsConnectedOnBoot = false;
}
/*
* Start scheduling nominally-daily fstrim operations
*/
MountServiceIdler.scheduleIdlePass(mContext);
| private boolean | hasUserRestriction(java.lang.String restriction)
UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
return um.hasUserRestriction(restriction, Binder.getCallingUserHandle());
| public boolean | isExternalStorageEmulated()
return mEmulatedTemplate != null;
| public boolean | isObbMounted(java.lang.String rawPath)
Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
synchronized (mObbMounts) {
return mObbPathToStateMap.containsKey(rawPath);
}
| private boolean | isReady()
try {
return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return false;
}
| public boolean | isSecureContainerMounted(java.lang.String id)
validatePermission(android.Manifest.permission.ASEC_ACCESS);
waitForReady();
warnOnNotMounted();
synchronized (mAsecMountSet) {
return mAsecMountSet.contains(id);
}
| private boolean | isUidOwnerOfPackageOrSystem(java.lang.String packageName, int callerUid)
if (callerUid == android.os.Process.SYSTEM_UID) {
return true;
}
if (packageName == null) {
return false;
}
final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
if (DEBUG_OBB) {
Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
packageUid + ", callerUid = " + callerUid);
}
return callerUid == packageUid;
| public boolean | isUsbMassStorageConnected()
waitForReady();
if (getUmsEnabling()) {
return true;
}
synchronized (mListeners) {
return mUmsAvailable;
}
| public boolean | isUsbMassStorageEnabled()
waitForReady();
final StorageVolume primary = getPrimaryPhysicalVolume();
if (primary != null) {
return doGetVolumeShared(primary.getPath(), "ums");
} else {
return false;
}
| public long | lastMaintenance()
return mLastMaintenance;
| public static java.lang.String | maybeTranslatePathForVold(java.lang.String path, java.io.File[] appPaths, java.io.File[] voldPaths)Translate the given path from an app-visible path to a vold-visible path,
but only if it's under the given whitelisted paths.
if (appPaths.length != voldPaths.length) {
throw new IllegalStateException("Paths must be 1:1 mapping");
}
for (int i = 0; i < appPaths.length; i++) {
final String appPath = appPaths[i].getAbsolutePath() + "/";
if (path.startsWith(appPath)) {
path = new File(voldPaths[i], path.substring(appPath.length()))
.getAbsolutePath();
if (!path.endsWith("/")) {
path = path + "/";
}
return path;
}
}
return null;
| public int | mkdirs(java.lang.String callingPkg, java.lang.String appPath)
final int userId = UserHandle.getUserId(Binder.getCallingUid());
final UserEnvironment userEnv = new UserEnvironment(userId);
// Validate that reported package name belongs to caller
final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
Context.APP_OPS_SERVICE);
appOps.checkPackage(Binder.getCallingUid(), callingPkg);
try {
appPath = new File(appPath).getCanonicalPath();
} catch (IOException e) {
Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
return -1;
}
if (!appPath.endsWith("/")) {
appPath = appPath + "/";
}
// Try translating the app path into a vold path, but require that it
// belong to the calling package.
String voldPath = maybeTranslatePathForVold(appPath,
userEnv.buildExternalStorageAppDataDirs(callingPkg),
userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
if (voldPath != null) {
try {
mConnector.execute("volume", "mkdirs", voldPath);
return 0;
} catch (NativeDaemonConnectorException e) {
return e.getCode();
}
}
voldPath = maybeTranslatePathForVold(appPath,
userEnv.buildExternalStorageAppObbDirs(callingPkg),
userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
if (voldPath != null) {
try {
mConnector.execute("volume", "mkdirs", voldPath);
return 0;
} catch (NativeDaemonConnectorException e) {
return e.getCode();
}
}
voldPath = maybeTranslatePathForVold(appPath,
userEnv.buildExternalStorageAppMediaDirs(callingPkg),
userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
if (voldPath != null) {
try {
mConnector.execute("volume", "mkdirs", voldPath);
return 0;
} catch (NativeDaemonConnectorException e) {
return e.getCode();
}
}
throw new SecurityException("Invalid mkdirs path: " + appPath);
| public void | monitor(){@inheritDoc}
if (mConnector != null) {
mConnector.monitor();
}
| public void | mountObb(java.lang.String rawPath, java.lang.String canonicalPath, java.lang.String key, android.os.storage.IObbActionListener token, int nonce)
Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
Preconditions.checkNotNull(token, "token cannot be null");
final int callingUid = Binder.getCallingUid();
final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
final ObbAction action = new MountObbAction(obbState, key, callingUid);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
if (DEBUG_OBB)
Slog.i(TAG, "Send to OBB handler: " + action.toString());
| public int | mountSecureContainer(java.lang.String id, java.lang.String key, int ownerUid, boolean readOnly)
validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
waitForReady();
warnOnNotMounted();
synchronized (mAsecMountSet) {
if (mAsecMountSet.contains(id)) {
return StorageResultCode.OperationFailedStorageMounted;
}
}
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
readOnly ? "ro" : "rw");
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code != VoldResponseCode.OpFailedStorageBusy) {
rc = StorageResultCode.OperationFailedInternalError;
}
}
if (rc == StorageResultCode.OperationSucceeded) {
synchronized (mAsecMountSet) {
mAsecMountSet.add(id);
}
}
return rc;
| public int | mountVolume(java.lang.String path)
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
return doMountVolume(path);
| private void | notifyShareAvailabilityChange(boolean avail)
synchronized (mListeners) {
mUmsAvailable = avail;
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
bl.mListener.onUsbMassStorageConnectionChanged(avail);
} catch (RemoteException rex) {
Slog.e(TAG, "Listener dead");
mListeners.remove(i);
} catch (Exception ex) {
Slog.e(TAG, "Listener failed", ex);
}
}
}
if (mSystemReady == true) {
sendUmsIntent(avail);
} else {
mSendUmsConnectedOnBoot = avail;
}
final StorageVolume primary = getPrimaryPhysicalVolume();
if (avail == false && primary != null
&& Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
final String path = primary.getPath();
/*
* USB mass storage disconnected while enabled
*/
new Thread("MountService#AvailabilityChange") {
@Override
public void run() {
try {
int rc;
Slog.w(TAG, "Disabling UMS after cable disconnect");
doShareUnshareVolume(path, "ums", false);
if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
Slog.e(TAG, String.format(
"Failed to remount {%s} on UMS enabled-disconnect (%d)",
path, rc));
}
} catch (Exception ex) {
Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
}
}
}.start();
}
| private void | notifyVolumeStateChange(java.lang.String label, java.lang.String path, int oldState, int newState)
final StorageVolume volume;
final String state;
synchronized (mVolumesLock) {
volume = mVolumesByPath.get(path);
state = getVolumeState(path);
}
if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
String action = null;
if (oldState == VolumeState.Shared && newState != oldState) {
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
}
if (newState == VolumeState.Init) {
} else if (newState == VolumeState.NoMedia) {
// NoMedia is handled via Disk Remove events
} else if (newState == VolumeState.Idle) {
/*
* Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
* if we're in the process of enabling UMS
*/
if (!state.equals(
Environment.MEDIA_BAD_REMOVAL) && !state.equals(
Environment.MEDIA_NOFS) && !state.equals(
Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
action = Intent.ACTION_MEDIA_UNMOUNTED;
}
} else if (newState == VolumeState.Pending) {
} else if (newState == VolumeState.Checking) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
action = Intent.ACTION_MEDIA_CHECKING;
} else if (newState == VolumeState.Mounted) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
action = Intent.ACTION_MEDIA_MOUNTED;
} else if (newState == VolumeState.Unmounting) {
action = Intent.ACTION_MEDIA_EJECT;
} else if (newState == VolumeState.Formatting) {
} else if (newState == VolumeState.Shared) {
if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
/* Send the media unmounted event first */
updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
action = Intent.ACTION_MEDIA_SHARED;
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
} else if (newState == VolumeState.SharedMnt) {
Slog.e(TAG, "Live shared mounts not supported yet!");
return;
} else {
Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
}
if (action != null) {
sendStorageIntent(action, volume, UserHandle.ALL);
}
| public boolean | onCheckHoldWakeLock(int code)Callback from NativeDaemonConnector
return false;
| public void | onDaemonConnected()Callback from NativeDaemonConnector
/*
* Since we'll be calling back into the NativeDaemonConnector,
* we need to do our work in a new thread.
*/
new Thread("MountService#onDaemonConnected") {
@Override
public void run() {
/**
* Determine media state and UMS detection status
*/
try {
final String[] vols = NativeDaemonEvent.filterMessageList(
mConnector.executeForList("volume", "list", "broadcast"),
VoldResponseCode.VolumeListResult);
for (String volstr : vols) {
String[] tok = volstr.split(" ");
// FMT: <label> <mountpoint> <state>
String path = tok[1];
String state = Environment.MEDIA_REMOVED;
final StorageVolume volume;
synchronized (mVolumesLock) {
volume = mVolumesByPath.get(path);
}
int st = Integer.parseInt(tok[2]);
if (st == VolumeState.NoMedia) {
state = Environment.MEDIA_REMOVED;
} else if (st == VolumeState.Idle) {
state = Environment.MEDIA_UNMOUNTED;
} else if (st == VolumeState.Mounted) {
state = Environment.MEDIA_MOUNTED;
Slog.i(TAG, "Media already mounted on daemon connection");
} else if (st == VolumeState.Shared) {
state = Environment.MEDIA_SHARED;
Slog.i(TAG, "Media shared on daemon connection");
} else {
throw new Exception(String.format("Unexpected state %d", st));
}
if (state != null) {
if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
updatePublicVolumeState(volume, state);
}
}
} catch (Exception e) {
Slog.e(TAG, "Error processing initial volume state", e);
final StorageVolume primary = getPrimaryPhysicalVolume();
if (primary != null) {
updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
}
}
/*
* Now that we've done our initialization, release
* the hounds!
*/
mConnectedSignal.countDown();
// On an encrypted device we can't see system properties yet, so pull
// the system locale out of the mount service.
if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
copyLocaleFromMountService();
}
// Let package manager load internal ASECs.
mPms.scanAvailableAsecs();
// Notify people waiting for ASECs to be scanned that it's done.
mAsecsScanned.countDown();
}
}.start();
| public boolean | onEvent(int code, java.lang.String raw, java.lang.String[] cooked)Callback from NativeDaemonConnector
if (DEBUG_EVENTS) {
StringBuilder builder = new StringBuilder();
builder.append("onEvent::");
builder.append(" raw= " + raw);
if (cooked != null) {
builder.append(" cooked = " );
for (String str : cooked) {
builder.append(" " + str);
}
}
Slog.i(TAG, builder.toString());
}
if (code == VoldResponseCode.VolumeStateChange) {
/*
* One of the volumes we're managing has changed state.
* Format: "NNN Volume <label> <path> state changed
* from <old_#> (<old_str>) to <new_#> (<new_str>)"
*/
notifyVolumeStateChange(
cooked[2], cooked[3], Integer.parseInt(cooked[7]),
Integer.parseInt(cooked[10]));
} else if (code == VoldResponseCode.VolumeUuidChange) {
// Format: nnn <label> <path> <uuid>
final String path = cooked[2];
final String uuid = (cooked.length > 3) ? cooked[3] : null;
final StorageVolume vol = mVolumesByPath.get(path);
if (vol != null) {
vol.setUuid(uuid);
}
} else if (code == VoldResponseCode.VolumeUserLabelChange) {
// Format: nnn <label> <path> <label>
final String path = cooked[2];
final String userLabel = (cooked.length > 3) ? cooked[3] : null;
final StorageVolume vol = mVolumesByPath.get(path);
if (vol != null) {
vol.setUserLabel(userLabel);
}
} else if ((code == VoldResponseCode.VolumeDiskInserted) ||
(code == VoldResponseCode.VolumeDiskRemoved) ||
(code == VoldResponseCode.VolumeBadRemoval)) {
// FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
// FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
// FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
String action = null;
final String label = cooked[2];
final String path = cooked[3];
int major = -1;
int minor = -1;
try {
String devComp = cooked[6].substring(1, cooked[6].length() -1);
String[] devTok = devComp.split(":");
major = Integer.parseInt(devTok[0]);
minor = Integer.parseInt(devTok[1]);
} catch (Exception ex) {
Slog.e(TAG, "Failed to parse major/minor", ex);
}
final StorageVolume volume;
final String state;
synchronized (mVolumesLock) {
volume = mVolumesByPath.get(path);
state = mVolumeStates.get(path);
}
if (code == VoldResponseCode.VolumeDiskInserted) {
new Thread("MountService#VolumeDiskInserted") {
@Override
public void run() {
try {
int rc;
if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
}
} catch (Exception ex) {
Slog.w(TAG, "Failed to mount media on insertion", ex);
}
}
}.start();
} else if (code == VoldResponseCode.VolumeDiskRemoved) {
/*
* This event gets trumped if we're already in BAD_REMOVAL state
*/
if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
return true;
}
/* Send the media unmounted event first */
if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
action = Intent.ACTION_MEDIA_REMOVED;
} else if (code == VoldResponseCode.VolumeBadRemoval) {
if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
/* Send the media unmounted event first */
updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
action = Intent.ACTION_MEDIA_BAD_REMOVAL;
} else if (code == VoldResponseCode.FstrimCompleted) {
EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
} else {
Slog.e(TAG, String.format("Unknown code {%d}", code));
}
if (action != null) {
sendStorageIntent(action, volume, UserHandle.ALL);
}
} else {
return false;
}
return true;
| private void | readStorageListLocked()
mVolumes.clear();
mVolumeStates.clear();
Resources resources = mContext.getResources();
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);
String path = a.getString(
com.android.internal.R.styleable.Storage_mountPoint);
int descriptionId = a.getResourceId(
com.android.internal.R.styleable.Storage_storageDescription, -1);
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);
boolean allowMassStorage = a.getBoolean(
com.android.internal.R.styleable.Storage_allowMassStorage, false);
// resource parser does not support longs, so XML value is in megabytes
long maxFileSize = a.getInt(
com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
Slog.d(TAG, "got storage path: " + path + " description: " + description +
" primary: " + primary + " removable: " + removable +
" emulated: " + emulated + " mtpReserve: " + mtpReserve +
" allowMassStorage: " + allowMassStorage +
" maxFileSize: " + maxFileSize);
if (emulated) {
// For devices with emulated storage, we create separate
// volumes for each known user.
mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
true, mtpReserve, false, maxFileSize, null);
final UserManagerService userManager = UserManagerService.getInstance();
for (UserInfo user : userManager.getUsers(false)) {
createEmulatedVolumeForUserLocked(user.getUserHandle());
}
} else {
if (path == null || description == null) {
Slog.e(TAG, "Missing storage path or description in readStorageList");
} else {
final StorageVolume volume = new StorageVolume(new File(path),
descriptionId, primary, removable, emulated, mtpReserve,
allowMassStorage, maxFileSize, null);
addVolumeLocked(volume);
// Until we hear otherwise, treat as unmounted
mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
volume.setState(Environment.MEDIA_UNMOUNTED);
}
}
a.recycle();
}
}
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// Compute storage ID for each physical volume; emulated storage is
// always 0 when defined.
int index = isExternalStorageEmulated() ? 1 : 0;
for (StorageVolume volume : mVolumes) {
if (!volume.isEmulated()) {
volume.setStorageId(index++);
}
}
parser.close();
}
| public void | registerListener(android.os.storage.IMountServiceListener listener)Exposed API calls below here
synchronized (mListeners) {
MountServiceBinderListener bl = new MountServiceBinderListener(listener);
try {
listener.asBinder().linkToDeath(bl, 0);
mListeners.add(bl);
} catch (RemoteException rex) {
Slog.e(TAG, "Failed to link to listener death");
}
}
| private void | removeObbStateLocked(com.android.server.MountService$ObbState obbState)
final IBinder binder = obbState.getBinder();
final List<ObbState> obbStates = mObbMounts.get(binder);
if (obbStates != null) {
if (obbStates.remove(obbState)) {
obbState.unlink();
}
if (obbStates.isEmpty()) {
mObbMounts.remove(binder);
}
}
mObbPathToStateMap.remove(obbState.rawPath);
| private void | removeVolumeLocked(android.os.storage.StorageVolume volume)
Slog.d(TAG, "removeVolumeLocked() " + volume);
mVolumes.remove(volume);
mVolumesByPath.remove(volume.getPath());
mVolumeStates.remove(volume.getPath());
| public int | renameSecureContainer(java.lang.String oldId, java.lang.String newId)
validatePermission(android.Manifest.permission.ASEC_RENAME);
waitForReady();
warnOnNotMounted();
synchronized (mAsecMountSet) {
/*
* Because a mounted container has active internal state which cannot be
* changed while active, we must ensure both ids are not currently mounted.
*/
if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
return StorageResultCode.OperationFailedStorageMounted;
}
}
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("asec", "rename", oldId, newId);
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
| public int | resizeSecureContainer(java.lang.String id, int sizeMb, java.lang.String key)
validatePermission(android.Manifest.permission.ASEC_CREATE);
waitForReady();
warnOnNotMounted();
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
return rc;
| void | runIdleMaintenance(java.lang.Runnable callback)
mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
| public void | runMaintenance()
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
runIdleMaintenance(null);
| private void | sendStorageIntent(java.lang.String action, android.os.storage.StorageVolume volume, android.os.UserHandle user)
final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
mContext.sendBroadcastAsUser(intent, user);
| private void | sendUmsIntent(boolean c)
mContext.sendBroadcastAsUser(
new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
UserHandle.ALL);
| public void | setField(java.lang.String field, java.lang.String contents)Set a field in the crypto header.
waitForReady();
final NativeDaemonEvent event;
try {
event = mConnector.execute("cryptfs", "setfield", field, contents);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
| private void | setUmsEnabling(boolean enable)
synchronized (mListeners) {
mUmsEnabling = enable;
}
| public void | setUsbMassStorageEnabled(boolean enable)
waitForReady();
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
final StorageVolume primary = getPrimaryPhysicalVolume();
if (primary == null) return;
// TODO: Add support for multiple share methods
/*
* If the volume is mounted and we're enabling then unmount it
*/
String path = primary.getPath();
String vs = getVolumeState(path);
String method = "ums";
if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
// Override for isUsbMassStorageEnabled()
setUmsEnabling(enable);
UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
// Clear override
setUmsEnabling(false);
}
/*
* If we disabled UMS then mount the volume
*/
if (!enable) {
doShareUnshareVolume(path, method, enable);
if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
Slog.e(TAG, "Failed to remount " + path +
" after disabling share method " + method);
/*
* Even though the mount failed, the unshare didn't so don't indicate an error.
* The mountVolume() call will have set the storage state and sent the necessary
* broadcasts.
*/
}
}
| public void | shutdown(android.os.storage.IMountShutdownObserver observer)
validatePermission(android.Manifest.permission.SHUTDOWN);
Slog.i(TAG, "Shutting down");
synchronized (mVolumesLock) {
// Get all volumes to be unmounted.
MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
mVolumeStates.size());
for (String path : mVolumeStates.keySet()) {
String state = mVolumeStates.get(path);
if (state.equals(Environment.MEDIA_SHARED)) {
/*
* If the media is currently shared, unshare it.
* XXX: This is still dangerous!. We should not
* be rebooting at *all* if UMS is enabled, since
* the UMS host could have dirty FAT cache entries
* yet to flush.
*/
setUsbMassStorageEnabled(false);
} else if (state.equals(Environment.MEDIA_CHECKING)) {
/*
* If the media is being checked, then we need to wait for
* it to complete before being able to proceed.
*/
// XXX: @hackbod - Should we disable the ANR timer here?
int retries = 30;
while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
try {
Thread.sleep(1000);
} catch (InterruptedException iex) {
Slog.e(TAG, "Interrupted while waiting for media", iex);
break;
}
state = Environment.getExternalStorageState();
}
if (retries == 0) {
Slog.e(TAG, "Timed out waiting for media to check");
}
}
if (state.equals(Environment.MEDIA_MOUNTED)) {
// Post a unmount message.
ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
} else if (observer != null) {
/*
* Count down, since nothing will be done. The observer will be
* notified when we are done so shutdown sequence can continue.
*/
mountShutdownLatch.countDown();
Slog.i(TAG, "Unmount completed: " + path +
", result code: " + StorageResultCode.OperationSucceeded);
}
}
}
| public void | systemReady()
mSystemReady = true;
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
| private java.lang.String | toHex(java.lang.String password)
if (password == null) {
return new String();
}
byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
return new String(Hex.encodeHex(bytes));
| public void | unmountObb(java.lang.String rawPath, boolean force, android.os.storage.IObbActionListener token, int nonce)
Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
final ObbState existingState;
synchronized (mObbPathToStateMap) {
existingState = mObbPathToStateMap.get(rawPath);
}
if (existingState != null) {
// TODO: separate state object from request data
final int callingUid = Binder.getCallingUid();
final ObbState newState = new ObbState(
rawPath, existingState.canonicalPath, callingUid, token, nonce);
final ObbAction action = new UnmountObbAction(newState, force);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
if (DEBUG_OBB)
Slog.i(TAG, "Send to OBB handler: " + action.toString());
} else {
Slog.w(TAG, "Unknown OBB mount at " + rawPath);
}
| public int | unmountSecureContainer(java.lang.String id, boolean force)
validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
waitForReady();
warnOnNotMounted();
synchronized (mAsecMountSet) {
if (!mAsecMountSet.contains(id)) {
return StorageResultCode.OperationFailedStorageNotMounted;
}
}
/*
* Force a GC to make sure AssetManagers in other threads of the
* system_server are cleaned up. We have to do this since AssetManager
* instances are kept as a WeakReference and it's possible we have files
* open on the external storage.
*/
Runtime.getRuntime().gc();
int rc = StorageResultCode.OperationSucceeded;
try {
final Command cmd = new Command("asec", "unmount", id);
if (force) {
cmd.appendArg("force");
}
mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageBusy) {
rc = StorageResultCode.OperationFailedStorageBusy;
} else {
rc = StorageResultCode.OperationFailedInternalError;
}
}
if (rc == StorageResultCode.OperationSucceeded) {
synchronized (mAsecMountSet) {
mAsecMountSet.remove(id);
}
}
return rc;
| public void | unmountVolume(java.lang.String path, boolean force, boolean removeEncryption)
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
String volState = getVolumeState(path);
if (DEBUG_UNMOUNT) {
Slog.i(TAG, "Unmounting " + path
+ " force = " + force
+ " removeEncryption = " + removeEncryption);
}
if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
Environment.MEDIA_REMOVED.equals(volState) ||
Environment.MEDIA_SHARED.equals(volState) ||
Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
// Media already unmounted or cannot be unmounted.
// TODO return valid return code when adding observer call back.
return;
}
UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
| public void | unregisterListener(android.os.storage.IMountServiceListener listener)
synchronized (mListeners) {
for(MountServiceBinderListener bl : mListeners) {
if (bl.mListener.asBinder() == listener.asBinder()) {
mListeners.remove(mListeners.indexOf(bl));
listener.asBinder().unlinkToDeath(bl, 0);
return;
}
}
}
| private void | updatePublicVolumeState(android.os.storage.StorageVolume volume, java.lang.String state)
final String path = volume.getPath();
final String oldState;
synchronized (mVolumesLock) {
oldState = mVolumeStates.put(path, state);
volume.setState(state);
}
if (state.equals(oldState)) {
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
state, state, path));
return;
}
Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
// Tell PackageManager about changes to primary volume state, but only
// when not emulated.
if (volume.isPrimary() && !volume.isEmulated()) {
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
mPms.updateExternalMediaStatus(false, false);
/*
* Some OBBs might have been unmounted when this volume was
* unmounted, so send a message to the handler to let it know to
* remove those from the list of mounted OBBS.
*/
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
OBB_FLUSH_MOUNT_STATE, path));
} else if (Environment.MEDIA_MOUNTED.equals(state)) {
mPms.updateExternalMediaStatus(true, false);
}
}
synchronized (mListeners) {
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
bl.mListener.onStorageStateChanged(path, oldState, state);
} catch (RemoteException rex) {
Slog.e(TAG, "Listener dead");
mListeners.remove(i);
} catch (Exception ex) {
Slog.e(TAG, "Listener failed", ex);
}
}
}
| private void | validatePermission(java.lang.String perm)
if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(String.format("Requires %s permission", perm));
}
| private void | validateUserRestriction(java.lang.String restriction)
if (hasUserRestriction(restriction)) {
throw new SecurityException("User has restriction " + restriction);
}
| public int | verifyEncryptionPassword(java.lang.String password)Validate a user-supplied password string with cryptfs
// Only the system process is permitted to validate passwords
if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
throw new SecurityException("no permission to access the crypt keeper");
}
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
if (TextUtils.isEmpty(password)) {
throw new IllegalArgumentException("password cannot be empty");
}
waitForReady();
if (DEBUG_EVENTS) {
Slog.i(TAG, "validating encryption password...");
}
final NativeDaemonEvent event;
try {
event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
}
| void | waitForAsecScan()
waitForLatch(mAsecsScanned);
| private void | waitForLatch(java.util.concurrent.CountDownLatch latch)
for (;;) {
try {
if (latch.await(5000, TimeUnit.MILLISECONDS)) {
return;
} else {
Slog.w(TAG, "Thread " + Thread.currentThread().getName()
+ " still waiting for MountService ready...");
}
} catch (InterruptedException e) {
Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
}
}
| private void | waitForReady()
waitForLatch(mConnectedSignal);
| private void | warnOnNotMounted()
final StorageVolume primary = getPrimaryPhysicalVolume();
if (primary != null) {
boolean mounted = false;
try {
mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
} catch (IllegalArgumentException e) {
}
if (!mounted) {
Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
}
}
|
|