Android7.0 Vold 程式工作機制分析之由Kernel發起掛載請求
PS:本文已同步至我的部落格:點選開啟
一、MountService簡介
MountService是一個系統服務,負責與Vold程式通訊的,執行在SystemServer程式,當收到Vold的掛載訊息後,會通過廣播的方式通知上層應用.
它是在SystemServer的startOtherServices方法裡啟動的
SystemServer路徑——————/frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
......
//如果不為低階工廠測試
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!disableStorage &&!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
try {
//啟動MountService
mSystemServiceManager.startService(MOUNT_SERVICE_CLASS);
mountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
} catch (Throwable e) {
reportWtf("starting Mount Service", e);
}
}
}
......
}複製程式碼
這裡並不打算詳細介紹MountService的啟動過程,只需關注它的構造方法即可(在啟動時會呼叫構造方法)
MountService路徑——————/frameworks/base/services/core/java/com/android/server/MountService.java
class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
......
public MountService(Context context) {
......
//建立並啟動一個帶訊息迴圈的MountService工作執行緒
HandlerThread hthread = new HandlerThread(TAG);
hthread.start();
//為MountService工作執行緒建立一個Handler
mHandler = new MountServiceHandler(hthread.getLooper());
......
/*
NativeDaemonConnector用於Socket通訊,第二個引數“vold”表示將和Vold通訊,也就是
和CL模組中的那個socket建立通訊連線。第一個引數為INativeDaemonConnectorCallbacks
介面。它提供兩個回撥函式:
onDaemonConnected:當NativeDaemonConnector連線上Vold後回撥。
onEvent:當NativeDaemonConnector收到來自Vold的資料後回撥。
*/
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
null);
//建立執行緒名為"VoldConnector"的執行緒,用於跟vold通訊
mConnectorThread = new Thread(mConnector, VOLD_TAG);
......
}複製程式碼
主要做了以下幾件事
①建立並啟動一個帶訊息迴圈的MountService工作執行緒 HandlerThread
②為MountService工作執行緒建立一個Handler
③建立執行緒名為”VoldConnector”的執行緒,用於跟vold通訊
在MountService的start方法啟動這個執行緒
private void start() {
mConnectorThread.start();
}複製程式碼
這個VoldConnector執行緒在run方法裡會呼叫一個方法listenToSocket,監聽Vold程式發過來的Socket訊息
NativeDaemonConnector路徑——/frameworks/base/services/core/java/com/android/server/NativeDaemonConnector.java
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
......
@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);
while (true) {
try {
//監聽Vold程式發過來的socket訊息
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
SystemClock.sleep(5000);
}
}
}
......
}複製程式碼
在listenToSocket方法裡會建立Socket連線,把接收的訊息解析成NativeDaemonEvent事件,通過mCallbackHandler處理這個事件
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
//建立與"/dev/socket/vold"的socket連線
socket.connect(address);
......
try {
//解析成 NativeDaemonEvent事件
final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
//當響應碼區間為[600,700),則傳送訊息交由mCallbackHandler處理
Message msg = mCallbackHandler.obtainMessage( event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
......
}
}複製程式碼
關於MountService的簡介先到這裡,下面分析Kernel發起掛載請求的流程.
二、Kernel發起掛載請求的流程
上一篇文章Android7.0 Vold 程式工作機制分析之整體流程已經講了,Kernel是通過Netlink 進行通訊的,Netlink 是一種特殊的Socket。
以下是上一篇文章的Vold 程式啟動時序圖(縮放瀏覽器可以放大檢視或者在新標籤頁開啟)
這裡以SD卡插入,Kernel掛載請求為例,流程從NetlinkListener的onDataAvailable開始
1.NetlinkListener::onDataAvailable
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -1;
......
NetlinkEvent *evt = new NetlinkEvent();
//解析獲得NetlinkEvent例項
if (evt->decode(mBuffer, count, mFormat)) {
//傳入NetlinkEvent例項
onEvent(evt);
}
......
}複製程式碼
解析獲得NetlinkEvent例項,呼叫onEvent方法,onEvent由子類NetlinkHandler實現
2.NetlinkHandler::onEvent
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
//獲取VolumeManager 單例物件
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();
if (!strcmp(subsys, "block")) {
//呼叫VolumeManager 的handleBlockEvent方法
vm->handleBlockEvent(evt);
}
}複製程式碼
獲取VolumeManager 的單例然後呼叫handleBlockEvent方法
3.vm->handleBlockEvent
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
switch (evt->getAction()) {
case NetlinkEvent::Action::kAdd: {
for (auto source : mDiskSources) {
if (source->matches(eventPath)) {
......
auto disk = new android::vold::Disk(eventPath, device,source->getNickname(), flags);
//呼叫disk 的create方法
disk->create();
mDisks.push_back(std::shared_ptr(disk));
break;
}
}
break;
}
case NetlinkEvent::Action::kChange: {
......
break;
}
case NetlinkEvent::Action::kRemove: {
......
break;
}
......
}
}複製程式碼
SD卡插入為add事件。那麼呼叫了disk->create()方法。
4.disk->create()
路徑:
disk.cpp———————–system/vold/disk.cpp
status_t Disk::create() {
CHECK(!mCreated);
mCreated = true;
//呼叫notifyEvent方法
notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
readMetadata();
//讀取分割槽資料,建立Volume
readPartitions();
return OK;
}複製程式碼
在create方法裡呼叫notifyEvent通知DiskCreated事件,然後還有readMetadata方法建立Volume.所有這裡也要分兩條線5和6
5. Disk::notifyEvent
void Disk::notifyEvent(int event) {
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,getId().c_str(), false);
}複製程式碼
獲取單例VolumeManager物件,然後獲取到SocketListener物件呼叫sendBroadcast方法
sendBroadcast方法的實現如下
在safelist列表中新增SocketClient,然後呼叫sendMsg方法
5. 1 SocketListener::sendBroadcast
void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
SocketClientCollection safeList;
//首先新增所有活動的SockClient到安全列表中
safeList.clear();
for (i = mClients->begin(); i != mClients->end(); ++i) {
SocketClient* c = *i;
c->incRef();
//新增
safeList.push_back(c);
}
while (!safeList.empty()) {
/* Pop the first item from the list */
i = safeList.begin();
SocketClient* c = *i;
safeList.erase(i);
//呼叫SockClient的sendMSg方法傳送訊息
if (c->sendMsg(code, msg, addErrno, false)) {
SLOGW("Error sending broadcast (%s)", strerror(errno));
}
c->decRef();
}
}複製程式碼
SockClient
路徑:
SockClient.cpp————————system/core/libsysutils/src/SockClient.cpp
呼叫sendMsg方法經過層層跳轉,到sendDataLockedv方法中,往Socket中寫入資訊
5.2 sendDataLockedv
int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
......
for (;;) {
ssize_t rc = TEMP_FAILURE_RETRY(
writev(mSocket, iov + current, iovcnt - current));
......
}複製程式碼
寫入到Socket之後,SystemServer中的MountService會收到,就是前面講到的NativeDaemonConnector的listenToSocket方法
5.3 NativeDaemonConnector::listenToSocket
在listenToSocket方法裡會建立Socket連線,把接收的訊息解析成NativeDaemonEvent事件,通過mCallbackHandler處理這個事件
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
//建立與"/dev/socket/vold"的socket連線
socket.connect(address);
......
try {
//解析成 NativeDaemonEvent事件
final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
//當響應碼區間為[600,700),則傳送訊息交由mCallbackHandler處理
Message msg = mCallbackHandler.obtainMessage( event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
......
}
}複製程式碼
mCallbackHandler處理事件,HandleMessage方法如下
5.4 NativeDaemonConnector::handleMessage
@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));
}
}
......
return true;
}複製程式碼
在handleMessage方法裡會回撥MountService的onEvent方法
5.5 MountService::onEvent
@Override
public boolean onEvent(int code, String raw, String[] cooked) {
synchronized (mLock) {
return onEventLocked(code, raw, cooked);
}
}複製程式碼
交給onEventLocked處理了
5.6 MountService::onEventLocked
private boolean onEventLocked(int code, String raw, String[] cooked) {
switch (code) {
//處理DISK_CREATED建立成功事件
case VoldResponseCode.DISK_CREATED: {
if (cooked.length != 3) break;
final String id = cooked[1];
int flags = Integer.parseInt(cooked[2]);
if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
|| mForceAdoptable) {
flags |= DiskInfo.FLAG_ADOPTABLE;
}
mDisks.put(id, new DiskInfo(id, flags));
break;
}
......
//這個事件就是之前4.disk->create()方法走第6條線最後回到這裡處理
//處理VOLUME_CREATED建立成功事件
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;
}
......
}
return true;
}複製程式碼
小結
在這裡處理了DISK_CREATED建立成功事件,至此這個事件傳遞就完了.我畫一張UML圖
(縮放瀏覽器可以放大檢視或者在新標籤頁開啟)
下面介紹的是從第6條線開始的流程
6 Disk::readPartitions()
在這個方法裡會讀取分割槽資料,然後建立Volume
status_t Disk::readPartitions() {
......
if (table == Table::kMbr) {
......
//6.2 建立PublicVolume
createPublicVolume(partDevice);
break;
}
} else if (table == Table::kGpt) {
......
if (!strcasecmp(typeGuid, kGptBasicData)) {
createPublicVolume(partDevice);
} else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {
createPrivateVolume(partDevice, partGuid);
}
}
}
}
if (table == Table::kUnknown || !foundParts) {
......
if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {
createPublicVolume(mDevice);
}
}
......
}複製程式碼
6.1 Disk::createPublicVolume()
void Disk::createPublicVolume(dev_t device) {
//獲取PublicVolume例項
auto vol = std::shared_ptr(new PublicVolume(device));
if (mJustPartitioned) {
LOG(DEBUG) << "Device just partitioned; silently formatting";
vol->setSilent(true);
vol->create();
vol->format("auto");
vol->destroy();
vol->setSilent(false);
}
mVolumes.push_back(vol);
vol->setDiskId(getId());
//呼叫VolumeBase的create方法
vol->create();
} 複製程式碼
6.2 VolumeBase::create()
status_t VolumeBase::create() {
mCreated = true;
status_t res = doCreate();
//向VolumeManager傳送VolumeCreated命令
notifyEvent(ResponseCode::VolumeCreated,StringPrintf("%d "%s" "%s"", mType, mDiskId.c_str(), mPartGuid.c_str()));
//設定已解除安裝狀態
setState(State::kUnmounted);
return res;
}複製程式碼
在這個方法裡通知VolumeManager建立了Volume,這個方法之後的步驟就和第5步以後一樣的了,就不重複介紹了.
之後走到5.6 MountService::onEventLocked方法裡的case VoldResponseCode.VOLUME_CREATED:處理,呼叫onVolumeCreatedLocked(vol)方法
6.3 MountService::onVolumeCreatedLocked()
class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
......
private void onVolumeCreatedLocked(VolumeInfo vol) {
......
if (vol.type == VolumeInfo.TYPE_EMULATED) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
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;
//handler傳送訊息
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
......
}
}複製程式碼
之後走到Handler處理H_VOLUME_MOUNT訊息
6.4 MountService::handleMessage()
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 {
//執行mount(掛載)命令
mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
vol.mountUserId);
} catch (NativeDaemonConnectorException ignored) {
}
break;
}
......
}複製程式碼
會呼叫NativeDaemonConnector的execute方法,經過跳轉到executeForList方法
6.5 NativeDaemonConnector::executeForList()
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
......
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
......
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);
}
}
......
}複製程式碼
往Socket寫入輸出流之後,Vold中FrameWorkListener的onDataAvailable會收到
6.6 FrameWorkListener::onDataAvailable()
bool FrameworkListener::onDataAvailable(SocketClient *c) {
char buffer[CMD_BUF_SIZE];
int len;
//讀取socket訊息
len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
.....
int i;
for (i = 0; i < len; i++) {
if (buffer[i] == ` `) {
//根據訊息內容 派發命令
dispatchCommand(c, buffer + offset);
offset = i + 1;
}
}
return true;
}複製程式碼
在onDataAvailable方法裡會先讀取Socket訊息,然後分發命令
6.7 FrameWorkListener::dispatchCommand()
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
......
//執行對應的訊息
for (i = mCommands->begin(); i != mCommands->end(); ++i) {
FrameworkCommand *c = *i;
//匹配命令
if (!strcmp(argv[0], c->getCommand())) {
//執行命令
if (c->runCommand(cli, argc, argv)) {
SLOGW("Handler `%s` error (%s)", c->getCommand(), strerror(errno));
}
goto out;
}
}
......
}複製程式碼
會呼叫FrameworkCommand 的runCommand方法,之前在CommandListener的構造方法裡註冊的這些指令,就是FrameWorkCommand型別,如下
FrameworkListener.cpp
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
//新增元素
mCommands->push_back(cmd);
}複製程式碼
CommandListener.cpp
CommandListener::CommandListener() :FrameworkListener("vold", true) {
//註冊多條指令
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd());
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new StorageCmd());
registerCmd(new FstrimCmd());
registerCmd(new AppFuseCmd());
}複製程式碼
這裡插入SD卡是掛載指令,即VolumeCmd指令,會進入到VolumeCmd的runCommand方法
6.8 CommandListener::runCommand()
CommandListener.cpp
int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) {
......
} else if (cmd == "mount" && argc > 2) {
// mount [volId] [flags] [user]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
vol->setMountFlags(mountFlags);
vol->setMountUserId(mountUserId);
//執行真正的掛載操作
int res = vol->mount();
if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
vm->setPrimary(vol);
}
//傳送應答訊息給MountService
return sendGenericOkFail(cli, res);
......
}
}複製程式碼
會執行實際的mount操作
6.9 vol->mount()
vol是VolumeBase的例項,VolumeBase的mount方法由具體的子類EmulatedVolume、PublicVolume、PrivateVolume等實現
執行操作之後會傳送應答訊息給MountService.
status_t VolumeBase::mount() {
......
setState(State::kChecking);
//doMount由子類實現實際掛載操作
status_t res = doMount();
if (res == OK) {
setState(State::kMounted);
} else {
setState(State::kUnmountable);
}
return res;
}複製程式碼
6.10 PublicVolume->doMount()
PublicVolume.cpp
status_t PublicVolume::doMount() {
......
}複製程式碼
三、小結
至此,這個掛載操作就完成了.時序圖如下:
下一篇講解從上層MountService發起掛載請求的流程.