MountService是Android系统中众多systemserver中一员,其工作主要是与native service Vold共同管理Android系统中的磁盘模块。
MountService:Android Binder服务端,运行在system_server进程,用于跟Vold进行消息通信,比如MountService向Vold发送挂载SD卡的命令,或者接收到来自Vold的外设热插拔事件。MountService作为Binder服务端,那么相应的Binder客户端便是StorageManager,通过binder IPC与MountService交互。
和众多service一样,mountservice(后简称MS)在systemserver:startOtherServices中启动
private static final String MOUNT_SERVICE_CLASS = "com.android.server.MountService$Lifecycle";
try {
/*
* 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"));
}
MS由SystemServiceManager(后简称SSM)通过startService启动,那么可知会调用MS中Lifecycle类的onStart方法,先看看这个。
public static class Lifecycle extends SystemService {
private MountService mMountService;
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
mMountService = new MountService(getContext());
publishBinderService("mount", mMountService); //通知注册Binder服务
mMountService.start();
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mMountService.systemReady();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
mMountService.bootCompleted();
}
}
@Override
public void onSwitchUser(int userHandle) {
mMountService.mCurrentUserId = userHandle;
}
@Override
public void onUnlockUser(int userHandle) {
mMountService.onUnlockUser(userHandle);
}
@Override
public void onCleanupUser(int userHandle) {
mMountService.onCleanupUser(userHandle);
}
}
Lifecycle 是MS中的内部类,并且继承了SystemService (之前讲过),需要实现一些SystemService 必须要实现的方法。这里我们重点看onStart方法。方法中构建了MS对象,并且发布binder(需要理解binder相关机制)。然后调用了MS的start方法。
看看MS中的start();
private void start() {
mConnectorThread.start();
mCryptConnectorThread.start();
}
mConnectorThread和mCryptConnectorThread 都是Thread对象,根据名字,可以看到在SystemServer中startService在MS中启动了两个线程,分别是连接线程和Crypt连接线程。
这里的两个线程对象都已经实例过了,应该是在刚刚onStart中的构造里做的。
frameworks/base/services/core/java/com/android/server/MountService.java
public MountService(Context context) {
sSelf = this;
mContext = context;
//FgThread线程名为“"android.fg",创建IMountServiceListener回调方法
mCallbacks = new Callbacks(FgThread.get().getLooper());
mLockPatternUtils = new LockPatternUtils(mContext);
//获取PackageManagerService的Client端对象,管理服务
mPms = (PackageManagerService) ServiceManager.getService("package");
//创建“MountService”线程,处理消息
HandlerThread hthread = new HandlerThread(TAG);
hthread.start();
//MountService中的消息处理运行在hthread线程中
mHandler = new MountServiceHandler(hthread.getLooper());
//IoThread线程名为"android.io",创建OBB操作的handler
mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
//判断/data/system/last-fstrim文件,不存在则创建,存在则更新最后修改时间
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();
}
mSettingsFile = new AtomicFile(
new File(Environment.getDataSystemDirectory(), "storage.xml"));
synchronized (mLock) {
readSettingsLocked();
}
//将MountServiceInternalImpl登记到sLocalServiceObjects
LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);
/*
* 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.
*/
//创建用于VoldConnector的NativeDaemonConnector对象
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25, null);
mConnector.setDebug(true);
//创建线程名为"VoldConnector"的线程,用于跟vold通信
mConnector.setWarnIfHeld(mLock);
mConnectorThread = new Thread(mConnector, VOLD_TAG);
//创建用于CryptdConnector的NativeDaemonConnector对象
// Reuse parameters from first connector since they are tested and safe
mCryptConnector = new NativeDaemonConnector(this, "cryptd",
MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
mCryptConnector.setDebug(true);
//创建线程名为"CryptdConnector"的线程,用于加密
mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
//注册监听用户添加、删除的广播
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
userFilter.addAction(Intent.ACTION_USER_REMOVED);
userFilter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
synchronized (mLock) {
addInternalVolumeLocked();
}
// Add ourself to the Watchdog monitors if enabled.
//默认为false
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
}
initMTKFeature();
}
其中MountService完成的主要任务如下:
1、创建ICallbacks回调方法,FgThread线程名为”android.fg”;
3、创建并启动线程名为”MountService”的handlerThread用来处理消息;
4、创建OBB操作的handler,IoThread线程名为”android.io“;
5、创建NativeDaemonConnector对象;
6、创建并启动线程名为”VoldConnector”的线程;
7、创建并启动线程名为”CryptdConnector”的线程;
8、注册监听用户添加、删除的广播;
到这里,MS的创建和启动已经分析完成,接下来看看它的简要工作模式:
MS在创建启动后是,依靠着system_server主线程和其余三个子线程((VoldConnector, MountService, CryptdConnector)一直勤勤恳恳的工作
这里拿mount流程作个简要分析。
需要mount的信息是由vold native层发来的,那么肯定在MS中需要有一个地方在时刻监听,也就是之前说的socket通讯,这个工作由NativeDaemonConnector完成。
在MS构造中通过NativeDaemonConnector创建了一个连接对象,这个就是用来和Vold通信的,
再看看构造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);
// Reuse parameters from first connector since they are tested and safe
mCryptConnector = new NativeDaemonConnector(this, "cryptd",
MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
mCryptConnector.setDebug(true);
mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
注意,这里给NativeDaemonConnector传参的是this,这个this是指什么,我们先看看MS的类声明
class MountService extends IMountService.Stub
implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
MS继承了 IMountService.Stub,实现了INativeDaemonConnectorCallbacks等接口。
那么这个this无疑就是它自己了,但它实现了这些接口等信息都包含在里面了,因此在NativeDaemonConnector构造中,可以看看,这个this就是INativeDaemonConnectorCallbacks 类型。
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) {
this(callbacks, socket, responseQueueSize, logTag, maxLogSize, wl,
FgThread.get().getLooper());
}
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl,
Looper looper) {
mCallbacks = callbacks;
mSocket = socket;
mResponseQueue = new ResponseQueue(responseQueueSize);
mWakeLock = wl;
if (mWakeLock != null) {
mWakeLock.setReferenceCounted(true);
}
mLooper = looper;
mSequenceNumber = new AtomicInteger(0);
TAG = logTag != null ? logTag : "NativeDaemonConnector";
mLocalLog = new LocalLog(maxLogSize);
}
接下来看,这个NativeDaemonConnector在MS构造中创建的对象被作为参数传给了mConnectorThread线程做参数,这个mConnectorThread还熟悉吗?刚刚之前提过的,在MS start方法中启动。那这里实际上就是NativeDaemonConnector线程类,应该可以这么叫吧。
启动后,NativeDaemonConnector的run方法也应该跑起来了。
@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方法中,mCallbackHandler创建了一个Handler对象,可知,这个应该就是处理vold传递来的信息的。而在创建对象后,开启了线程做的事,一个死循环。实现由listenToSocket担当。
//./frameworks/base/services/core/java/com/android/server/NativeDaemonConnector.java
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
//创建Vold socket
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
//向服务端发起连接请求
socket.connect(address);
//获取连接的socket中得到输入输出流
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) {
//从socket输出流中读取数据
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;
//解析读取到的数据,得到NativeDaemonEvent
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 + "}");
//如果命令码code >= 600 && code < 700
if (event.isClassUnsolicited()) {
//将读取到的事件发送到VoldConnector.CallbackHandler线程中处理
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队列
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message " + e);
} finally {
if (releaseWl) {
mWakeLock.release();
}
}
start = i + 1;
}
}
if (start == 0) {
log("RCV incomplete");
}
......
}
以上代码中主要完成了两项工作:
(1)当vold返回的响应吗不在区间[600,700),则将该事件添加到mResponseQueue,并且触发响应事件所对应的请求事件不再阻塞到ResponseQueue.poll,那么线程继续往下执行;
(2)当返回的响应码区间为[600,700):则发送消息交由mCallbackHandler处理,向线程android.fg发送Handler消息,该线程收到后回调NativeDaemonConnector的handleMessage来处理。
这里接收消息处理后然后又发送消息给mCallbackHandler处理,那接下来看看mCallbackHandler是如何处理对应的消息。
@Override
public boolean handleMessage(Message msg) {
......
if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
log(String.format("Unhandled event '%s'", event));
}
......
}
这里省去了大量代码,留下了这句mCallbacks.onEvent,这个mCallbacks是什么,可还记得NativeDaemonConnector构造中曾经从MS中传递进来一个INativeDaemonConnectorCallbacks类型的对象,没错,它在就是NativeDaemonConnector中就是mCallbacks。也就是说这里调用了INativeDaemonConnectorCallbacks对象的onEvent。那么也就是在MS中调用。这样一来,又回到了MS
//./frameworks/base/services/core/java/com/android/server/MountService.java
public boolean onEvent(int code, String raw, String[] cooked) {
synchronized (mLock) {
return onEventLocked(code, raw, cooked);
}
}
private boolean onEventLocked(int code, String raw, String[] cooked) {
//处理各种设备挂载等的命令
......
switch (code) {
case VoldResponseCode.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);
break;
}
......
}
这里仅仅留下了VoldResponseCode.VOLUME_CREATED命令的处理。
VoldResponseCode类声明在MS中。说到相关指令的时候,这里提下MS收到vold指令分为两种情况:
(1)当MountService向Vold发送命令后,将接收到Vold的响应消息;
(2)当外部存储设备发生热插拔时,kernel将通过netlink方式通知Vold,Vold进程经过一系列处理后最终还是要将uevent事件消息发送给MountService,Vold发送uevent的过程。
/*
* Internal vold response code constants
*/
class VoldResponseCode {
/*
* 100 series - Requestion action was initiated; expect another reply
* before proceeding with a new command.
* 100系列-请求行动已启动; 在继续执行新命令之前,需要其他答复。
*/
public static final int VolumeListResult = 110;
public static final int AsecListResult = 111;
public static final int StorageUsersListResult = 112;
public static final int CryptfsGetfieldResult = 113;
/*
* 200 series - Requestion action has been successfully completed.
* 200系列-请求操作已成功完成。
*/
public static final int ShareStatusResult = 210;
public static final int AsecPathResult = 211;
public static final int ShareEnabledResult = 212;
/*
* 400 series - Command was accepted, but the requested action
* did not take place.
* 400系列-接受命令,但未执行所请求的操作。
*/
public static final int OpFailedNoMedia = 401;
public static final int OpFailedMediaBlank = 402;
public static final int OpFailedMediaCorrupt = 403;
public static final int OpFailedVolNotMounted = 404;
public static final int OpFailedStorageBusy = 405;
public static final int OpFailedStorageNotFound = 406;
/*
* 600 series - Unsolicited broadcasts.
* 600系列-不请自来的广播。差不多就是kernel通过vold 主动上报的,由NativeDaemonConnector->listenToSocket循环监听的
*/
public static final int DISK_CREATED = 640;
public static final int DISK_SIZE_CHANGED = 641;
public static final int DISK_LABEL_CHANGED = 642;
public static final int DISK_SCANNED = 643;
public static final int DISK_SYS_PATH_CHANGED = 644;
public static final int DISK_DESTROYED = 649;
public static final int VOLUME_CREATED = 650;
public static final int VOLUME_STATE_CHANGED = 651;
public static final int VOLUME_FS_TYPE_CHANGED = 652;
public static final int VOLUME_FS_UUID_CHANGED = 653;
public static final int VOLUME_FS_LABEL_CHANGED = 654;
public static final int VOLUME_PATH_CHANGED = 655;
public static final int VOLUME_INTERNAL_PATH_CHANGED = 656;
public static final int VOLUME_DESTROYED = 659;
public static final int MOVE_STATUS = 660;
public static final int BENCHMARK_RESULT = 661;
public static final int TRIM_RESULT = 662;
}
回到MS->onEventLocked:VOLUME_CREATED 中
最终调用了,onVolumeCreatedLocked方法,执行实际操作。
private void onVolumeCreatedLocked(VolumeInfo vol) {
.......
if (vol.type == VolumeInfo.TYPE_EMULATED) {
......
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
&& VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
.......
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
} else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
.......
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
}
} else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
......
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
} else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
} else {
Slog.d(TAG, "Skipping automatic mounting of " + vol);
}
}
看起来onVolumeCreatedLocked做了如下的事情:
1.判断是主存储(私有内部)还是公有的存储。
2.不管是哪种,都通过mHandler分发H_VOLUME_MOUNT的消息,这个mHandler是什么?
还记得MS构造中
mHandler = new MountServiceHandler(hthread.getLooper());
此处的mHandler即是MountServiceHandler对象。
class MountServiceHandler extends Handler {
public MountServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
.......
case H_VOLUME_MOUNT: {
final VolumeInfo vol = (VolumeInfo) msg.obj;
if (isMountDisallowed(vol)) {
Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
break;
}
try {
mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
vol.mountUserId);
} catch (NativeDaemonConnectorException ignored) {
}
break;
}
......
}
}
}
可以看到,MS对H_VOLUME_MOUNT消息的处理,最终是调用了mConnector.execute。mConnector这个应该清楚了吧,它就是NativeDaemonConnector对象,在MS构造中创建的。
这样又回到了NativeDaemonConnector里,真是来来回回,百转千回呀。
frameworks\base\services\core\java\com\android\server\NativeDaemonConnector.java
public NativeDaemonEvent execute(String cmd, Object... args)
throws NativeDaemonConnectorException {
//设置超时时间:DEFAULT_TIMEOUT = 1 * 60 * 1000
return execute(DEFAULT_TIMEOUT, cmd, args);
}
public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
//调用executeForList发送命令
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];
}
在executeForList中首先判断命令的合法性,并对命令进行处理后写入到socket输出流mOutputStream中。
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
loge("Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ Integer.toHexString(System.identityHashCode(mWarnIfHeld)));
}
//获取数据传输的开始时间
final long startTime = SystemClock.elapsedRealtime();
final ArrayList events = Lists.newArrayList();
final StringBuilder rawBuilder = new StringBuilder();
final StringBuilder logBuilder = new StringBuilder();
//mSequenceNumber初始化值为0,每执行一次该方法则进行加1操作
final int sequenceNumber = mSequenceNumber.incrementAndGet();
//对指令进行处理
makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
final String rawCmd = rawBuilder.toString();
final String logCmd = logBuilder.toString();
//eg:VoldConnector: SND -> {13 volume unmount public:179,129}
log("SND -> {" + logCmd + "}");
synchronized (mDaemonLock) {
if (mOutputStream == null) {
throw new NativeDaemonConnectorException("missing output stream");
} else {
try {
//将命令写入到socket输出流
mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new NativeDaemonConnectorException("problem sending command", e);
}
}
}
NativeDaemonEvent event = null;
do {
//设置指令接收时间60S,超过60S跳出循环
event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd);
if (event == null) {
loge("timed-out waiting for response to " + logCmd);
throw new NativeDaemonTimeoutException(logCmd, event);
}
if (VDBG) log("RMV <- {" + event + "}");
events.add(event);
//当收到的事件响应码属于[100,200)区间,则继续等待后续事件上报,否则跳出循环
} while (event.isClassContinue());
//获取数据传输的结束时间,并判断是否超过WARN_EXECUTE_DELAY_MS=0.5S
final long endTime = SystemClock.elapsedRealtime();
if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");
}
if (event.isClassClientError()) {
throw new NativeDaemonArgumentException(logCmd, event);
}
if (event.isClassServerError()) {
throw new NativeDaemonFailureException(logCmd, event);
}
return events.toArray(new NativeDaemonEvent[events.size()]);
}
以上executeForList是MountService指令传输中的重要函数,其重主要完成的任务总结如下:
(1) 带执行的命令mSequenceNumber执行加1操作;
(2) 将cmd写入到socket的输出流;
(3) 通过循环与poll机制阻塞等待底层响应该操作完成的结果;
有两个情况会跳出循环:
a、当超过1分钟未收到vold相应事件的响应码,则跳出阻塞等待;
b、当收到底层的响应码,且响应码不属于[100,200)区间,则跳出循环。
(4)对于执行时间超过500ms的时间,则额外输出以NDC Command开头的log信息。
MountService向vold发送消息后,便阻塞在MountService线程的NDC.execute()方法,当MonutService接收到vold返回的消息,且消息响应吗不属于区间[600,700)则添加事件到ResponseQueue,从而唤醒阻塞的MountService继续执行。
上面分析过程中,涉及比较多,因为单单断章取义是不太容易把事情讲清理顺的。这里画图做下总结,算是理下思路。
Android vold进程三 MountService介绍