Android7.0 SD卡挂载流程
这篇文章是对上层T卡挂载的流程总结,涉及到的主要类介绍:
MountService:Android Binder服务,运行在system_server进程,用于跟Vold进行消息通信,比如 MountService 向 Vold 发送挂载SD卡的命令,或者接收到来自 Vold 的外设热插拔事件。
NativeDaemonConnector(Client端)和 Vold的CL模块(Server端)建立socket通信。
StorageManager存储管理类。
1. MountService启动
MountService运行在system_server进程,在系统启动到阶段
在 SystemServer.java--->startOtherServices()中:
/*
* NotificationManagerService is dependant on MountService,
* (for media / usb notifications) so we must start MountService first.
*/
mSystemServiceManager.startService(MOUNT_SERVICE_CLASS);
mountService = IMountService.Stub.asInterface(
ServiceManager.getService("mount"));
private static final String MOUNT_SERVICE_CLASS=
"com.android.server.MountService$Lifecycle";
mSystemServiceManager.startService(MOUNT_SERVICE_CLASS)完成创建MOUNT_SERVICE_CLASS所指类的Lifecycle对象,将该对象添加SystemServiceManager的 mServices 服务列表,最后调用Lifecycle的onStart()方法.
Lifecycle是MountService的一个内部类:
public static class Lifecycle extends SystemService {
private MountService mMountService;
private String oldDefaultPath = "";
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
Slog.d(TAG, "MountService onStart");
sSelf.isBootingPhase = true;
mMountService = new MountService(getContext());
publishBinderService("mount", mMountService);
mMountService.start();
oldDefaultPath = sSelf.getDefaultPath();
Slog.d(TAG, "get Default path onStart default path=" + oldDefaultPath);
}
......
}
在Lifecycle的onStart()创建MountService对象
2. NativeDaemonConnector启动
NativeDaemonConnector实现了Runnable, Handler.Callback, Watchdog.Monitor三个接口
在MountService的构造方法中创建NativeDaemonConnector对象:
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
null);
mConnector.setDebug(true);
mConnector.setWarnIfHeld(mLock);
mConnectorThread = new Thread(mConnector, VOLD_TAG);
创建名为mConnectorThread的线程,用于和vold的进行通信
@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);
while (true) {
try {
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
SystemClock.sleep(5000);
}
}
}
在run()中通过while(true)不断调用listenToSocket()来监听vold的socket
MountService向vold发送信息
1. onBootPhase
MountService与NDC都启动,那么接下来到系统启动到达阶段PHASE_ACTIVITY_MANAGER_READY,则调用到onBootPhase方法。
@Override
public void onBootPhase(int phase) {
Slog.d(TAG, "MountService onBootPhase");
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mMountService.systemReady();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
mMountService.bootCompleted();
}
if (phase == SystemService.PHASE_BOOT_COMPLETED) {
Slog.d(TAG, "MountService onBootPhase: PHASE_BOOT_COMPLETED");
sSelf.isBootingPhase = false;
if (!oldDefaultPath.contains("emulated") && !"".equals(oldDefaultPath)) {
Slog.d(TAG, "set defaut path to " + oldDefaultPath);
sSelf.setDefaultPath(oldDefaultPath);
sSelf.updateDefaultPathIfNeed();
}
}
}
调用MountService.systemReady方法,该方法主要是通过mHandler发送消息。
private void systemReady() {
mSystemReady = true;
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
}
mHandler = new MountServiceHandler(hthread.getLooper());
到此system_server主线程通过handler向线程”MountService”发送H_SYSTEM_READY消息,接下来进入线程”MountService”的MountServiceHandler对象的handleMessage()来处理相关的消息。
2. 调用handleSystemReady
private void handleSystemReady() {
initIfReadyAndConnected();
resetIfReadyAndConnected();
/*
* If UMS was connected on boot
* send the connected broadcast when system ready
*/
if (mSendUmsConnectedOnBoot) {
sendUmsIntent(true);
mSendUmsConnectedOnBoot = false;
}
// Start scheduling nominally-daily fstrim operations
MountServiceIdler.scheduleIdlePass(mContext);
}
3. 调用resetIfReadyAndConnected()调用NDC.execute
private void resetIfReadyAndConnected() {
Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
+ ", mDaemonConnected=" + mDaemonConnected);
if (mSystemReady && mDaemonConnected) {
final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
killMediaProvider(users);
final int[] systemUnlockedUsers;
synchronized (mLock) {
systemUnlockedUsers = mSystemUnlockedUsers;
mDisks.clear();
mVolumes.clear();
addInternalVolumeLocked();
}
try {
mConnector.execute("volume", "reset");
// Tell vold about all existing and started users
for (UserInfo user : users) {
mConnector.execute("volume", "user_added", user.id, user.serialNumber);
}
for (int userId : systemUnlockedUsers) {
mConnector.execute("volume", "user_started", userId);
}
} catch (NativeDaemonConnectorException e) {
Slog.w(TAG, "Failed to reset vold", e);
}
}
}
4. NDC.execute
public NativeDaemonEvent execute(String cmd, Object... args)
throws NativeDaemonConnectorException {
return execute(DEFAULT_TIMEOUT, cmd, args);
}
public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
final NativeDaemonEvent[] events = executeForList(timeoutMs, cmd, args);
if (events.length != 1) {
throw new NativeDaemonConnectorException(
"Expected exactly one response, but received " + events.length);
}
return events[0];
}
MountService接收vold信息
1. NDC.listenToSocket()
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
socket.connect(address);
InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}
mCallbacks.onDaemonConnected();
FileDescriptor[] fdList = null;
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;
while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
if (count < 0) {
loge("got " + count + " reading with start = " + start);
break;
}
fdList = socket.getAncillaryFileDescriptors();
// Add our starting point to the count and reset the start.
count += start;
start = 0;
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
// Note - do not log this raw message since it may contain
// sensitive data
final String rawEvent = new String(
buffer, start, i - start, StandardCharsets.UTF_8);
boolean releaseWl = false;
try {
final NativeDaemonEvent event =
NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
log("RCV <- {" + event + "}");
if (event.isClassUnsolicited()) {
// TODO: migrate to sending NativeDaemonEvent instances
if (mCallbacks.onCheckHoldWakeLock(event.getCode())
&& mWakeLock != null) {
mWakeLock.acquire();
releaseWl = true;
}
Message msg = mCallbackHandler.obtainMessage(
event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
if (mCallbackHandler.sendMessage(msg)) {
releaseWl = false;
}
} else {
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message " + e);
} finally {
if (releaseWl) {
mWakeLock.release();
}
}
start = i + 1;
}
}
通过handler消息机制,由mCallbackHandler处理,先来看看其初始化:
@Override
public boolean handleMessage(Message msg) {
final String event = (String) msg.obj;
final int start = uptimeMillisInt();
final int sent = msg.arg1;
try {
if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
log(String.format("Unhandled event '%s'", event));
}
} catch (Exception e) {
loge("Error handling '" + event + "': " + e);
} finally {
if (mCallbacks.onCheckHoldWakeLock(msg.what) && mWakeLock != null) {
mWakeLock.release();
}
final int end = uptimeMillisInt();
if (start > sent && start - sent > WARN_EXECUTE_DELAY_MS) {
loge(String.format("NDC event {%s} processed too late: %dms", event, start - sent));
}
if (end > start && end - start > WARN_EXECUTE_DELAY_MS) {
loge(String.format("NDC event {%s} took too long: %dms", event, end - start));
}
}
return true;
}
onEevent
是接口InativeDaemonConnectorCallbacks中的一个抽象方法,其在MountService实现
2. MS.onEvent
@Override
public boolean onEvent(int code, String raw, String[] cooked) {
synchronized (mLock) {
return onEventLocked(code, raw, cooked);
}
}
MountService.onEvent调用了onEventLocked,onEventLocked增加了同步锁,用于多线程并发访问处理,根据vold发送过来的不同响应码采取不同的处理流程
3. MS. onEventLocked
当收到的响应码是650时 case VoldResponseCode.VOLUME_CREATED: {
Slog.d(TAG, "VOLUME_CREATED");
final String id = cooked[1];
final int type = Integer.parseInt(cooked[2]);
final String diskId = TextUtils.nullIfEmpty(cooked[3]);
final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
final DiskInfo disk = mDisks.get(diskId);
final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
mVolumes.put(id, vol);
onVolumeCreatedLocked(vol);
Slog.d(TAG, "create volumeInfo=" + mVolumes.get(id));
break;
}
将T卡信息put到VolumeInfo中,T卡挂载成功,同时调用onVolumeCreatedLocked()方法
private void onVolumeCreatedLocked(VolumeInfo vol) {
if (mPms.isOnlyCoreApps()) {
Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
return;
}
Slog.d(TAG, "onVolumeCreatedLocked, volumeInfo=" + vol);
if (vol.type == VolumeInfo.TYPE_EMULATED) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
Slog.d(TAG, "privateVol=" + privateVol);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
&& VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
} else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
}
......
}
case H_VOLUME_MOUNT: {
Slog.i(TAG, "H_VOLUME_MOUNT");
final VolumeInfo vol = (VolumeInfo) msg.obj;
if (isMountDisallowed(vol)) {
Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
break;
}
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
vol.mountUserId);
} catch (NativeDaemonConnectorException ignored) {
rc = ignored.getCode();
Slog.w(TAG, "mount volume fail, ignored=" + ignored);
}
if (rc == StorageResultCode.OperationSucceeded) {
VolumeInfo curVol = null;
synchronized (mLock) {
curVol = mVolumes.get(vol.getId());
}
if (isShowDefaultPathDialog(curVol)) {
showDefaultPathDialog(curVol);
}
} else {
// if mount fail, for other volume mount operation
// should not marked as disk insert
isDiskInsert = false;
Slog.w(TAG, "mount volume fail, vol=" + vol
+ ", return code=" + rc);
}
break;
}
4. T卡状态的改变
当MS. onEventLocked收到的响应码是651时
case VoldResponseCode.VOLUME_STATE_CHANGED: {
Slog.d(TAG, "VOLUME_STATE_CHANGED");
if (cooked.length != 3) break;
final VolumeInfo vol = mVolumes.get(cooked[1]);
if (vol != null) {
final int oldState = vol.state;
final int newState = Integer.parseInt(cooked[2]);
vol.state = newState;
onVolumeStateChangedLocked(vol, oldState, newState);
}
break;
}
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
Slog.d(TAG, "onVolumeStateChangedLocked"
+ ", oldState=" + VolumeInfo.getEnvironmentForState(oldState)
+ ", newState=" + VolumeInfo.getEnvironmentForState(newState)
+ ", volumeInfo=" + vol);
// Remember that we saw this volume so we're ready to accept user
// metadata, or so we can annoy them when a private volume is ejected
if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
VolumeRecord rec = mRecords.get(vol.fsUuid);
if (rec == null) {
rec = new VolumeRecord(vol.type, vol.fsUuid);
rec.partGuid = vol.partGuid;
rec.createdMillis = System.currentTimeMillis();
if (vol.type == VolumeInfo.TYPE_PRIVATE) {
rec.nickname = vol.disk.getDescription();
}
mRecords.put(rec.fsUuid, rec);
writeSettingsLocked();
} else {
// Handle upgrade case where we didn't store partition GUID
if (TextUtils.isEmpty(rec.partGuid)) {
rec.partGuid = vol.partGuid;
writeSettingsLocked();
}
}
}
mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = vol.clone();
args.argi2 = oldState;
args.argi3 = newState;
obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
}
private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
throws RemoteException {
switch (what) {
case MSG_STORAGE_STATE_CHANGED: {
callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
(String) args.arg3);
break;
}
case MSG_VOLUME_STATE_CHANGED: {
callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
break;
}
case MSG_VOLUME_RECORD_CHANGED: {
callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
break;
}
case MSG_VOLUME_FORGOTTEN: {
callback.onVolumeForgotten((String) args.arg1);
break;
}
case MSG_DISK_SCANNED: {
callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
break;
}
case MSG_DISK_DESTROYED: {
callback.onDiskDestroyed((DiskInfo) args.arg1);
break;
}
case MSG_UMS_CONNECTION_CHANGED: {
callback.onUsbMassStorageConnectionChanged((boolean)args.arg1);
break;
}
}
}
可以看到callback是一个接口,下面就看下这个接口是在哪儿实现的:
StorageEventListenerDelegate是StorageManager的内部类,继承
IMountServiceListener.Stub
private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements
Handler.Callback {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
private static final int MSG_VOLUME_RECORD_CHANGED = 3;
private static final int MSG_VOLUME_FORGOTTEN = 4;
private static final int MSG_DISK_SCANNED = 5;
private static final int MSG_DISK_DESTROYED = 6;
final StorageEventListener mCallback;
final Handler mHandler;
public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
mCallback = callback;
mHandler = new Handler(looper, this);
}
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = vol;
args.argi2 = oldState;
args.argi3 = newState;
mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
}
case MSG_VOLUME_STATE_CHANGED:
mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
args.recycle();
return true;
可以看到又调用了StorageEventListener这个接口,就是通个这个接口其他进程T卡状态变了!
在Settings中我们可以看到,实现了这个接口:
private final StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
if (isInteresting(vol)) {
refresh();
}
}
@Override
public void onDiskDestroyed(DiskInfo disk) {
refresh();
}
/// M: ALPS02316229 refresh UI when plug in or out SD card.
@Override
public void onDiskScanned(DiskInfo disk, int volumeCount) {
refresh();
}
};
Settings的T卡状态的监听就是通过StorageEventListener监听实现的
5. T卡的广播
再回过头看onVolumeStateChangedLocked()
if (!Objects.equals(oldStateEnv, newStateEnv)) {
// Kick state changed event towards all started users. Any users
// started after this point will trigger additional
// user-specific broadcasts.
for (int userId : mSystemUnlockedUsers) {
if (vol.isVisibleForRead(userId)) {
final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Slog.d(TAG, "notify callbacks StorageStateChanged, storageVolume=" + userVol);
mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
newStateEnv);
}
}
}
case H_VOLUME_BROADCAST: {
Slog.i(TAG, "H_VOLUME_BROADCAST");
final StorageVolume userVol = (StorageVolume) msg.obj;
final String envState = userVol.getState();
Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
+ userVol.getOwner());
final String action = VolumeInfo.getBroadcastForEnvironment(envState);
if (action != null) {
final Intent intent = new Intent(action,
Uri.fromFile(userVol.getPathFile()));
intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Slog.i(TAG, "sendBroadcastAsUser, intent=" + intent
+ ", userVol=" + userVol);
mContext.sendBroadcastAsUser(intent, userVol.getOwner());
}
break;
}
重点看下这个action,是用VolumeInfo.getBroadcastForEnvironment中获取到的
public static @Nullable String getBroadcastForEnvironment(String envState) {
return sEnvironmentToBroadcast.get(envState);
}
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE);
sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED);
sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL);