MountService、vold(4)大容量ストレージ、MountServiceのメッセージメカニズムに深く入り込む(and 5.1)
23767 ワード
大容量ストレージのインタフェースStorageManagerのコードは次のとおりです.
MountServiceでのインタフェース
次にdoShareUnshareVolumeインタフェースを見てみましょう
大容量オープンを実行すると、まずメッセージが送信され、MountServiceのメッセージメカニズムも分析されます.
MountServiceのメッセージを分析し、H_を送信します.UNMOUNT_PM_UPDATEメッセージの後、handlerは処理し、そのUnmountCallBackオブジェクトをmForceUnmountsリストに追加します.
hander処理H_UNMOUNT_PM_UPDATEメッセージ、最後にPMSのupdateExternalMediaStatusインタフェースを呼び出します.その役割を分析しないで、UPDATEDを送信したのを見てみましょう.MEDIA_STATUSというニュース
PMSのhandler処理を見て、MountServiceのfinishMediaUpdateメソッドを呼び出しました
MountServiceで最後にH_を送信しましたUNMOUNT_PM_DONEメッセージ
次はhandler対H_UNMOUNT_PM_DONEメッセージの処理
H_UNMOUNT_MSメッセージは、ucbを実行するhandleFinished関数です.
前に大容量のnewを開いたUmsEnableCallBackオブジェクトを見てみましょう
そのhandleFinishedインタフェースはdoShareUnshareVolumeを呼び出し、voldにshareコマンドを送信します.
MountServiceでは、Unmountと大容量ストレージがオンの場合にのみ、このメッセージが送信されます.この2つの動作は他のpidを許さず,このvolumeの下のpathを用いることを説明する.voldでは、Unmountの場合もこのpathを使用するpidをすべてkillします.しかし、大容量ストレージを開くと、どのpidがこのvolumeのpathを使用しているかはチェックされません.
次に、大容量ストレージvoldとMountServiceのインタラクションを開く方法を見てみましょう.
voldはMountServiceからshareコマンドを受け取り、CommandListenerからコマンドを配布し、最後にVolumeManagement::shareVolume
MountServiceはOnEventでvoldからStateを受信しましたSharedステータスではnotifyVolumeStateChange関数が呼び出されます.updatePublicVolumeStateという関数では各listenerに通知されます.
voldのunshareに対する処理を見てみると、その経路の下に0と書いて、上位のStateを送信しただけです.Idleステータス
MountServiceはまずブロードキャストを送信し、Volumeの状態をMEDIA_に設定します.UNMOUNTED
大容量がオンになっているかどうかを確認
voldはVolumeの状態がStateかどうかを見るShared
これで大容量でMountServiceに格納された解析は終了しますが、UsbDeviceManagerでもこの内容があるので、次回分析しましょう!
MountServiceのメッセージメカニズムは参考になり、メッセージメカニズムを利用してエラー遅延処理を行うことができます.
public void enableUsbMassStorage() {
try {
mMountService.setUsbMassStorageEnabled(true);
} catch (Exception ex) {
Log.e(TAG, "Failed to enable UMS", ex);
}
}
/**
* Disables USB Mass Storage (UMS) on the device.
*
* @hide
*/
public void disableUsbMassStorage() {
try {
mMountService.setUsbMassStorageEnabled(false);
} catch (Exception ex) {
Log.e(TAG, "Failed to disable UMS", ex);
}
}
MountServiceでのインタフェース
public void setUsbMassStorageEnabled(boolean enable) {
waitForReady();
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
final StorageVolume primary = getPhysicalVolume();
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);// , sd
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.
*/
}
}
}
次にdoShareUnshareVolumeインタフェースを見てみましょう
private void doShareUnshareVolume(String path, 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);
}
}
大容量オープンを実行すると、まずメッセージが送信され、MountServiceのメッセージメカニズムも分析されます.
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);
}
MountServiceのメッセージを分析し、H_を送信します.UNMOUNT_PM_UPDATEメッセージの後、handlerは処理し、そのUnmountCallBackオブジェクトをmForceUnmountsリストに追加します.
class MountServiceHandler extends Handler {
ArrayList mForceUnmounts = new ArrayList();
boolean mUpdatingStatus = false;
MountServiceHandler(Looper l) {
super(l);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case H_UNMOUNT_PM_UPDATE: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
mForceUnmounts.add(ucb);
if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
// Register only if needed.
if (!mUpdatingStatus) {
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
mUpdatingStatus = true;
mPms.updateExternalMediaStatus(false, true);
}
break;
}
case H_UNMOUNT_PM_DONE: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
mUpdatingStatus = false;
int size = mForceUnmounts.size();
int sizeArr[] = new int[size];
int sizeArrN = 0;
// Kill processes holding references first
ActivityManagerService ams = (ActivityManagerService)
ServiceManager.getService("activity");
for (int i = 0; i < size; i++) {
UnmountCallBack ucb = mForceUnmounts.get(i);
String path = ucb.path;
boolean done = false;
if (!ucb.force) {
done = true;
} else {
int pids[] = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;
} else {
// Eliminate system process here?
ams.killPids(pids, "unmount media", true);
// Confirm if file references have been freed.
pids = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;
}
}
}
if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
// Retry again
Slog.i(TAG, "Retrying to kill storage users again");
mHandler.sendMessageDelayed(
mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
ucb.retries++),
RETRY_UNMOUNT_DELAY);
} else {
if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
Slog.i(TAG, "Failed to unmount media inspite of " +
MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
}
sizeArr[sizeArrN++] = i;
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
ucb));
}
}
// Remove already processed elements from list.
for (int i = (sizeArrN-1); i >= 0; i--) {
mForceUnmounts.remove(sizeArr[i]);
}
break;
}
case H_UNMOUNT_MS: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
ucb.handleFinished();
break;
}
hander処理H_UNMOUNT_PM_UPDATEメッセージ、最後にPMSのupdateExternalMediaStatusインタフェースを呼び出します.その役割を分析しないで、UPDATEDを送信したのを見てみましょう.MEDIA_STATUSというニュース
@Override
public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
throw new SecurityException("Media status can only be updated by the system");
}
// reader; this apparently protects mMediaMounted, but should probably
// be a different lock in that case.
synchronized (mPackages) {
Log.i(TAG, "Updating external media status from "
+ (mMediaMounted ? "mounted" : "unmounted") + " to "
+ (mediaStatus ? "mounted" : "unmounted"));
if (DEBUG_SD_INSTALL)
Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus
+ ", mMediaMounted=" + mMediaMounted);
if (mediaStatus == mMediaMounted) {
final Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1
: 0, -1);
mHandler.sendMessage(msg);//
return;
}
mMediaMounted = mediaStatus;
}
// Queue up an async operation since the package installation may take a
// little while.
mHandler.post(new Runnable() {
public void run() {
updateExternalMediaStatusInner(mediaStatus, reportStatus, true);
}
});
}
PMSのhandler処理を見て、MountServiceのfinishMediaUpdateメソッドを呼び出しました
case UPDATED_MEDIA_STATUS: {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS");
boolean reportStatus = msg.arg1 == 1;
boolean doGc = msg.arg2 == 1;
if (DEBUG_SD_INSTALL) Log.i(TAG, "reportStatus=" + reportStatus + ", doGc = " + doGc);
if (doGc) {
// Force a gc to clear up stale containers.
Runtime.getRuntime().gc();
}
if (msg.obj != null) {
@SuppressWarnings("unchecked")
Set args = (Set) msg.obj;
if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading all containers");
// Unload containers
unloadAllContainers(args);
}
if (reportStatus) {
try {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Invoking MountService call back");
PackageHelper.getMountService().finishMediaUpdate();
} catch (RemoteException e) {
Log.e(TAG, "MountService not running?");
}
}
} break;
MountServiceで最後にH_を送信しましたUNMOUNT_PM_DONEメッセージ
public void finishMediaUpdate() {
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
次はhandler対H_UNMOUNT_PM_DONEメッセージの処理
case H_UNMOUNT_PM_DONE: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
mUpdatingStatus = false;
int size = mForceUnmounts.size();
int sizeArr[] = new int[size];
int sizeArrN = 0;
// Kill processes holding references first
ActivityManagerService ams = (ActivityManagerService)
ServiceManager.getService("activity");
for (int i = 0; i < size; i++) {// mForceUnmounts
UnmountCallBack ucb = mForceUnmounts.get(i);
String path = ucb.path;
boolean done = false;
if (!ucb.force) {//ucb force true volume , kill
done = true;
} else {
int pids[] = getStorageUsers(path);
if (pids == null || pids.length == 0) {// pid, volume
done = true;
} else {
// Eliminate system process here?
ams.killPids(pids, "unmount media", true);
// Confirm if file references have been freed.
pids = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;// done true
}
}
}
if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
// Retry again
Slog.i(TAG, "Retrying to kill storage users again");
mHandler.sendMessageDelayed(// done true, , MAX_UNMOUNT_RETRIES
mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
ucb.retries++),
RETRY_UNMOUNT_DELAY);
} else {
if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
Slog.i(TAG, "Failed to unmount media inspite of " +
MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
}
sizeArr[sizeArrN++] = i;
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
ucb));// H_UNMOUNT_MS
}
}
// Remove already processed elements from list.
for (int i = (sizeArrN-1); i >= 0; i--) {
mForceUnmounts.remove(sizeArr[i]);
}
break;
}
H_UNMOUNT_MSメッセージは、ucbを実行するhandleFinished関数です.
case H_UNMOUNT_MS: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
ucb.handleFinished();
break;
}
前に大容量のnewを開いたUmsEnableCallBackオブジェクトを見てみましょう
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);
}
そのhandleFinishedインタフェースはdoShareUnshareVolumeを呼び出し、voldにshareコマンドを送信します.
class UmsEnableCallBack extends UnmountCallBack {
final String method;
UmsEnableCallBack(String path, String method, boolean force) {
super(path, force, false);
this.method = method;
}
@Override
void handleFinished() {
super.handleFinished();
doShareUnshareVolume(path, method, true);
}
}
MountServiceでは、Unmountと大容量ストレージがオンの場合にのみ、このメッセージが送信されます.この2つの動作は他のpidを許さず,このvolumeの下のpathを用いることを説明する.voldでは、Unmountの場合もこのpathを使用するpidをすべてkillします.しかし、大容量ストレージを開くと、どのpidがこのvolumeのpathを使用しているかはチェックされません.
次に、大容量ストレージvoldとMountServiceのインタラクションを開く方法を見てみましょう.
voldはMountServiceからshareコマンドを受け取り、CommandListenerからコマンドを配布し、最後にVolumeManagement::shareVolume
int VolumeManager::shareVolume(const char *label, const char *method) {
Volume *v = lookupVolume(label);// volume
if (!v) {
errno = ENOENT;
return -1;
}
/*
* Eventually, we'll want to support additional share back-ends,
* some of which may work while the media is mounted. For now,
* we just support UMS
*/
if (strcmp(method, "ums")) {
errno = ENOSYS;
return -1;
}
if (v->getState() == Volume::State_NoMedia) {
errno = ENODEV;
return -1;
}
if (v->getState() != Volume::State_Idle) {
// You need to unmount manually befoe sharing
errno = EBUSY;
return -1;
}
if (mVolManagerDisabled) {
errno = EBUSY;
return -1;
}
dev_t d = v->getShareDevice();
if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
// This volume does not support raw disk access
errno = EINVAL;
return -1;
}
int fd;
char nodepath[255];
int written = snprintf(nodepath,//nodepath
sizeof(nodepath), "/dev/block/vold/%d:%d",
major(d), minor(d));//
if ((written < 0) || (size_t(written) >= sizeof(nodepath))) {
SLOGE("shareVolume failed: couldn't construct nodepath");
return -1;
}
if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
return -1;
}
if (write(fd, nodepath, strlen(nodepath)) < 0) {// "/sys/class/android_usb/android0/f_mass_storage/lun/file"
SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
close(fd);
return -1;
}
close(fd);
v->handleVolumeShared();// State_Shared, MountService。 setState(Volume::State_Shared);
if (mUmsSharingCount++ == 0) {
FILE* fp;
mSavedDirtyRatio = -1; // in case we fail
if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
char line[16];
if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) {
fprintf(fp, "%d
", mUmsDirtyRatio);
} else {
SLOGE("Failed to read dirty_ratio (%s)", strerror(errno));
}
fclose(fp);
} else {
SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
}
}
return 0;
}
MountServiceはOnEventでvoldからStateを受信しましたSharedステータスではnotifyVolumeStateChange関数が呼び出されます.updatePublicVolumeStateという関数では各listenerに通知されます.
} 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);// MEDIA_UNMOUNTED,
sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
updatePublicVolumeState(volume, Environment.MEDIA_SHARED);// MEDIA_SHARED
action = Intent.ACTION_MEDIA_SHARED;
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
}
voldのunshareに対する処理を見てみると、その経路の下に0と書いて、上位のStateを送信しただけです.Idleステータス
int VolumeManager::unshareVolume(const char *label, const char *method) {
Volume *v = lookupVolume(label);
if (!v) {
errno = ENOENT;
return -1;
}
if (strcmp(method, "ums")) {
errno = ENOSYS;
return -1;
}
if (v->getState() != Volume::State_Shared) {
errno = EINVAL;
return -1;
}
int fd;
if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {//
SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
return -1;
}
char ch = 0;
if (write(fd, &ch, 1) < 0) {// 0
SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
close(fd);
return -1;
}
close(fd);
v->handleVolumeUnshared();// setState(Volume::State_Idle);
if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) {
FILE* fp;
if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
fprintf(fp, "%d
", mSavedDirtyRatio);
fclose(fp);
} else {
SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
}
mSavedDirtyRatio = -1;
}
return 0;
}
MountServiceはまずブロードキャストを送信し、Volumeの状態をMEDIA_に設定します.UNMOUNTED
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;
}
}
大容量がオンになっているかどうかを確認
private boolean doGetVolumeShared(String path, 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;
}
}
voldはVolumeの状態がStateかどうかを見るShared
int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
Volume *v = lookupVolume(label);
if (!v) {
errno = ENOENT;
return -1;
}
if (strcmp(method, "ums")) {
errno = ENOSYS;
return -1;
}
if (v->getState() != Volume::State_Shared) {
*enabled = false;
} else {
*enabled = true;
}
return 0;
}
これで大容量でMountServiceに格納された解析は終了しますが、UsbDeviceManagerでもこの内容があるので、次回分析しましょう!
MountServiceのメッセージメカニズムは参考になり、メッセージメカニズムを利用してエラー遅延処理を行うことができます.