編輯:關於Android編程
看了很長時間Vold存儲模塊的相關知識,也深入的研究一段時間的Android源碼,打算把自己看過的經驗之貼、參考資料和自己的一些見解,以帖子的形式發出來,供有興趣的同仁們參考,有不對的地方請指正,我們相互交流。
從上圖中可知:
· Vold中的NetlinkManager模塊接收來自Linux內核的uevent消息。例如SD卡的插拔等動作都會引起Kernel向NM發送uevent消息。
· NetlinkManager將這些消息轉發給VolumeManager模塊。VolumeManager會對應做一些操作,然後把相關信息通過CommandListener發送給MountService,MountService根據收到的消息會發送相關的處理命令給VolumeManager做進一步的處理。例如待SD卡插入後,VolumeManager會將來自NetlinkManager的“Disk Insert”消息發送給MountService,而後MountService則發送“Mount”指令給Vold,指示它掛載這個SD卡。
· CommandListener模塊內部封裝了一個Socket用於跨進程通信。它在Vold進程中屬於監聽端(即是服務端),而它的連接端(即客戶端)則是MountService。它一方面接收來自MountService的控制命令(例如卸載存儲卡、格式化存儲卡等),另一方面VolumeManager和NetlinkManager模塊又會通過它,將一些信息發送給MountService。
下面來初步的認識Vold,代碼在system\vold\main.cpp
int main() { mkdir("/dev/block/vold", 0755); //創建一個目錄/dev/block/vold //創建一個VolumeManager對象,該對象為單例模式 if (!(vm = VolumeManager::Instance())) { SLOGE("Unable to create VolumeManager"); exit(1); }; //創建一個NetlinkManager對象,該對象為單例模式 if (!(nm = NetlinkManager::Instance())) { SLOGE("Unable to create NetlinkManager"); exit(1); }; //創建一個CommandListener對象 cl = new CommandListener(); vm->setBroadcaster((SocketListener *) cl); nm->setBroadcaster((SocketListener *) cl); //啟動VolumeManager if (vm->start()) { SLOGE("Unable to start VolumeManager (%s)", strerror(errno)); exit(1); } //根據fstab配置文件初始化VolumeManager if (process_config(vm)) { SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno)); } //啟動NetlinkManager對象 if (nm->start()) { SLOGE("Unable to start NetlinkManager (%s)", strerror(errno)); exit(1); } //通過往/sys/block目錄下對應的uevent文件寫“add\n”來觸發內核發送Uevent消息 coldboot("/sys/block"); //啟動CommandListener if (cl->startListener()) { SLOGE("Unable to start CommandListener (%s)", strerror(errno)); exit(1); } }
上圖中的虛線為啟動是的調用流程。 (1) class NetlinkManager(在其start函數中創建了NetlinkHandler對象,並把創建的socket作為參數)
(2)class NetlinkHandler: public NetlinkListener(實現了onEvent) (3) class NetlinkListener : public SocketListener (實現了onDataAvailable) (4) class SocketListener(實現了runListener,在一個線程中通過select查看哪些socket有數據,通過onDataAvailable來讀取數據)
int NetlinkManager::start() { struct sockaddr_nl nladdr; int sz = 64 * 1024; int on = 1; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = getpid(); nladdr.nl_groups = 0xffffffff; // 創建一個socket用於內核空間和用戶空間的異步通信,監控系統的hotplug事件 if ((mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) { SLOGE("Unable to create uevent socket: %s", strerror(errno)); return -1; } if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) { SLOGE("Unable to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno)); return -1; } if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno)); return -1; } if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { SLOGE("Unable to bind uevent socket: %s", strerror(errno)); return -1; } // 利用新創建的socket實例化一個NetlinkHandler類對象,NetlinkHandler繼承了類NetlinkListener, // NetlinkListener又繼承了類SocketListener mHandler = new NetlinkHandler(mSock); if (mHandler->start()) { //啟動NetlinkHandler SLOGE("Unable to start NetlinkHandler: %s", strerror(errno)); return -1; } return 0; }NetlinkHandler構造
NetlinkHandler::NetlinkHandler(int listenerSocket) : NetlinkListener(listenerSocket) { }繼承父類
NetlinkListener::NetlinkListener(int socket) : SocketListener(socket, false) { mFormat = NETLINK_FORMAT_ASCII; }這裡又是構造了一個SockListener的實例,傳入了上面創建的socket標識。
接著調用的start()函數,也是最終實現在SockListener的startListener()。
int NetlinkHandler::start() { return this->startListener(); } int SocketListener::startListener() { if (!mSocketName && mSock == -1) { SLOGE("Failed to start unbound listener"); errno = EINVAL; return -1; } else if (mSocketName) { if ((mSock = android_get_control_socket(mSocketName)) < 0) { SLOGE("Obtaining file descriptor socket '%s' failed: %s", mSocketName, strerror(errno)); return -1; } } if (mListen && listen(mSock, 4) < 0) { SLOGE("Unable to listen on socket (%s)", strerror(errno)); return -1; } else if (!mListen) mClients->push_back(new SocketClient(mSock, false)); if (pipe(mCtrlPipe)) { SLOGE("pipe failed (%s)", strerror(errno)); return -1; } if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { SLOGE("pthread_create (%s)", strerror(errno)); return -1; } return 0; } void *SocketListener::threadStart(void *obj) { SocketListener *me = reinterpret_castSocketListener::runListener是線程真正執行的函數:mListen成員用來判定是否監聽套接字,Netlink套接字屬於udp套接字,非監聽套接字,該函數的主要功能體現在,如果該套接字有數據到來,就調用函數onDataAvailable讀取數據(obj); me->runListener(); pthread_exit(NULL); return NULL; } void SocketListener::runListener() { SocketClientCollection *pendingList = new SocketClientCollection(); while(1) { // 死循環,一直監聽 SocketClientCollection::iterator it; fd_set read_fds; int rc = 0; int max = -1; FD_ZERO(&read_fds); //清空文件描述符集read_fds if (mListen) { max = mSock; FD_SET(mSock, &read_fds); //添加文件描述符到文件描述符集read_fds } FD_SET(mCtrlPipe[0], &read_fds); //添加管道的讀取端文件描述符到read_fds if (mCtrlPipe[0] > max) max = mCtrlPipe[0]; pthread_mutex_lock(&mClientsLock); //對容器mClients的操作需要加鎖 for (it = mClients->begin(); it != mClients->end(); ++it) { int fd = (*it)->getSocket(); FD_SET(fd, &read_fds); ////遍歷容器mClients的所有成員,調用內聯函數getSocket()獲取文件描述符,並添加到文件描述符集read_fds if (fd > max) max = fd; } pthread_mutex_unlock(&mClientsLock); // 等待文件描述符中某一文件描述符或者說socket有數據到來 if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { if (errno == EINTR) continue; SLOGE("select failed (%s)", strerror(errno)); sleep(1); continue; } else if (!rc) continue; if (FD_ISSET(mCtrlPipe[0], &read_fds)) break; if (mListen && FD_ISSET(mSock, &read_fds)) { //監聽套接字處理 struct sockaddr addr; socklen_t alen; int c; do { alen = sizeof(addr); c = accept(mSock, &addr, &alen); //接收鏈接請求,建立連接,如果成功c即為建立鏈接後的數據交換套接字,將其添加到mClient容器 } while (c < 0 && errno == EINTR); if (c < 0) { SLOGE("accept failed (%s)", strerror(errno)); sleep(1); continue; } pthread_mutex_lock(&mClientsLock); mClients->push_back(new SocketClient(c, true)); pthread_mutex_unlock(&mClientsLock); } /* Add all active clients to the pending list first */ pendingList->clear(); pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { int fd = (*it)->getSocket(); if (FD_ISSET(fd, &read_fds)) { pendingList->push_back(*it); } } pthread_mutex_unlock(&mClientsLock); /* Process the pending list, since it is owned by the thread, * there is no need to lock it */ while (!pendingList->empty()) { //非監聽套接字處理 /* Pop the first item from the list */ it = pendingList->begin(); SocketClient* c = *it; pendingList->erase(it); /* Process it, if false is returned and our sockets are * connection-based, remove and destroy it */ // ****** onDataAvailable在NetlinkListener中實現********* if (!onDataAvailable(c) && mListen) { /* Remove the client from our array */ pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { if (*it == c) { mClients->erase(it); break; } } pthread_mutex_unlock(&mClientsLock); /* Remove our reference to the client */ c->decRef(); } } } delete pendingList; }
bool NetlinkListener::onDataAvailable(SocketClient *cli) { int socket = cli->getSocket(); ssize_t count; uid_t uid = -1; count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv( socket, mBuffer, sizeof(mBuffer), &uid)); //從socket中讀取event信息 if (count < 0) { if (uid > 0) LOG_EVENT_INT(65537, uid); SLOGE("recvmsg failed (%s)", strerror(errno)); return false; } NetlinkEvent *evt = new NetlinkEvent(); if (!evt->decode(mBuffer, count, mFormat)) { //調用NetlinkEvent解析event SLOGE("Error decoding NetlinkEvent"); } else { onEvent(evt); //傳遞event給子類NetlinkHandler處理 } delete evt; return true; }到NetlinkHandler.cpp中:
void NetlinkHandler::onEvent(NetlinkEvent *evt) { VolumeManager *vm = VolumeManager::Instance(); const char *subsys = evt->getSubsystem(); if (!subsys) { SLOGW("No subsystem found in netlink event"); return; } if (!strcmp(subsys, "block")) { //EVENT為block類型 int action = evt->getAction(); vm->updatePullOutState(evt); vm->setHotPlug(true); vm->handleBlockEvent(evt); //把event交給VolumeManager處理 vm->setHotPlug(false); }
調用Instance創建一個VM對象。
調用setBroadcaster設置CL對象
調用start啟動VM。
調用process_config配置VM。
VolumeManager *VolumeManager::Instance() { if (!sInstance) sInstance = new VolumeManager(); return sInstance; }
VolumeManager::VolumeManager() { mDebug = false; mVolumes = new VolumeCollection(); //一個容器,保存Volume的集合 mActiveContainers = new AsecIdCollection(); mBroadcaster = NULL; //指向socketlisten,用於發送掛載事件 mUmsSharingCount = 0; mSavedDirtyRatio = -1; mUmsDirtyRatio = dirtyRatio(); mVolManagerDisabled = 0; mIsHotPlug= false; mUseBackupContainers =false; mIsFirstBoot = false; mIpoState = State_Ipo_Start; }
int VolumeManager::start() { return 0; //VM的啟動什麼也沒有做 }
static int process_config(VolumeManager *vm) { char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)]; fstab = fs_mgr_read_fstab(fstab_filename); //讀取fstab文件 if (!fstab) { SLOGE("failed to open %s\n", fstab_filename); return -1; } /* Loop through entries looking for ones that vold manages */ for (i = 0; i < fstab->num_entries; i++) { if (fs_mgr_is_voldmanaged(&fstab->recs[i])) { DirectVolume *dv = NULL; flags = 0; /* Set any flags that might be set for this volume */ if (fs_mgr_is_nonremovable(&fstab->recs[i])) { flags |= VOL_NONREMOVABLE; } if (fs_mgr_is_encryptable(&fstab->recs[i])) { flags |= VOL_ENCRYPTABLE; } /* Only set this flag if there is not an emulated sd card */ if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) && !strcmp(fstab->recs[i].fs_type, "vfat")) { flags |= VOL_PROVIDES_ASEC; } dv = new DirectVolume(vm, &(fstab->recs[i]), flags); if (dv->addPath(fstab->recs[i].blk_device)) { SLOGE("Failed to add devpath %s to volume %s", fstab->recs[i].blk_device, fstab->recs[i].label); goto out_fail; } vm->addVolume(dv); //添加到volumemanager容器 } } }fstab配置文件
# Android fstab file. ## The filesystem that contains the filesystem checker binary (typically /system) cannot # specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK /devices/platform/mtk-msdc.0/mmc_host auto vfat defaults voldmanaged=sdcard0:emmc@fat,noemulatedsd /devices/platform/mtk-msdc.1/mmc_host auto vfat defaults voldmanaged=sdcard1:auto
DirectVolume::DirectVolume(VolumeManager *vm,const char *label, const char*mount_point, int partIdx) : Volume(vm, label, mount_point) {//初始化基類 /* 注意其中的參數: label為”sdcard”,mount_point為”/mnt/sdcard”,partIdx為1 */ mPartIdx = partIdx; //PathCollection定義為typedef android::ListaddPath把和某個存儲卡接口相關的設備路徑與這個DirectVolume綁定到一起,並且這個設備路徑和Uevent中的DEVPATH是對應的,這樣就可以根據Uevent的DEVPATH找到是哪個存儲卡的DirectVolume發生了變動。當然手機上目前只有一個存儲卡接口,所以Vold也只有一個DirectVolume。PathCollection //其實就是一個字符串list mPaths= new PathCollection(); for(int i = 0; i < MAX_PARTITIONS; i++) mPartMinors[i] = -1; mPendingPartMap = 0; mDiskMajor = -1; //存儲設備的主設備號 mDiskMinor = -1; //存儲設備的次設備號,一個存儲設備將由主次兩個設備號標識。 mDiskNumParts = 0; //設置狀態為NoMedia setState(Volume::State_NoMedia); } //再來看addPath函數,它主要目的是添加設備在sysfs中的路徑, int DirectVolume::addPath(const char *path) { mPaths->push_back(strdup(path)); return0; }
void VolumeManager::handleBlockEvent(NetlinkEvent*evt) { constchar *devpath = evt->findParam("DEVPATH"); /* 前面在process_config中構造的DirectVolume對象保存在了mVolumes中,它的定義如下: typedef android::ListVolumeCollection,也是一個列表。 注意它保存的是Volume指針,而我們的DirectVolume是從Volume派生的 */ VolumeCollection::iterator it; boolhit = false; for (it = mVolumes->begin(); it !=mVolumes->end(); ++it) { //調用每個Volume的handleBlockEvent事件,實際上將調用 //DirectVolume的handleBlockEvent函數。 if(!(*it)->handleBlockEvent(evt)) { hit = true; break; } } }
int DirectVolume::handleBlockEvent(NetlinkEvent*evt) { constchar *dp = evt->findParam("DEVPATH"); PathCollection::iterator it; //將Uevent的DEVPATH和addPath添加的路徑進行對比,判斷屬不屬於自己管理的范圍。 for(it = mPaths->begin(); it != mPaths->end(); ++it) { if(!strncmp(dp, *it, strlen(*it))) { int action = evt->getAction(); const char *devtype = evt->findParam("DEVTYPE"); if (action == NetlinkEvent::NlActionAdd) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); char nodepath[255]; snprintf(nodepath, sizeof(nodepath),"/dev/block/vold/%d:%d", major, minor); //創建設備節點 if (createDeviceNode(nodepath, major, minor)) { ...... } if (!strcmp(devtype, "disk")) { handleDiskAdded(dp, evt);//添加一個磁盤 } else { /* 對於有分區的SD卡,先收到上面的“disk”消息,然後每個分區就會收到 一個分區添加消息。 */ handlePartitionAdded(dp,evt); } } else if (action == NetlinkEvent::NlActionRemove) { ...... } else if (action == NetlinkEvent::NlActionChange) { ...... } ...... return 0; } } errno= ENODEV; return-1; }
cl = new CommandListener(); vm->setBroadcaster((SocketListener *) cl); nm->setBroadcaster((SocketListener *) cl); ... /* * Now that we're up, we can respond to commands */ if (cl->startListener()) { SLOGE("Unable to start CommandListener (%s)", strerror(errno)); exit(1); }構造函數/system/vold/CommandListener.cpp
CommandListener::CommandListener() : FrameworkListener("vold", true) { registerCmd(new DumpCmd()); registerCmd(new VolumeCmd()); registerCmd(new AsecCmd()); registerCmd(new ObbCmd()); registerCmd(new StorageCmd()); registerCmd(new XwarpCmd()); registerCmd(new CryptfsCmd()); registerCmd(new FstrimCmd()); //M{ #ifndef MTK_EMULATOR_SUPPORT registerCmd(new USBCmd()); #endif registerCmd(new CDROMCmd()); //}M #if defined (ENG_BUILD_ENG) registerCmd(new SilkRoad()); #endif }構造一個父類FrameworkListener
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : SocketListener(socketName, true, withSeq) { init(socketName, withSeq); }
void FrameworkListener::init(const char *socketName, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; mCommandCount = 0; mWithSeq = withSeq; }
SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) { init(socketName, -1, listen, useCmdNum); }
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) { mListen = listen; mSocketName = socketName; mSock = socketFd; mUseCmdNum = useCmdNum; pthread_mutex_init(&mClientsLock, NULL); mClients = new SocketClientCollection(); }
void FrameworkListener::registerCmd(FrameworkCommand *cmd) { mCommands->push_back(cmd); }
CommandListener::VolumeCmd::VolumeCmd() : VoldCommand("volume") { }
VoldCommand::VoldCommand(const char *cmd) : FrameworkCommand(cmd) { }
FrameworkCommand::FrameworkCommand(const char *cmd) { mCommand = cmd; }
將volume這個command注冊到mCommands這個容器中之後,目的是當FrameworkListerer從SlocketListener接收到command的時候,會依據mCommands 中的command進行解析篩選判斷分發,調用對應的command執行類
int SocketListener::startListener() { if (!mSocketName && mSock == -1) { SLOGE("Failed to start unbound listener"); errno = EINVAL; return -1; } else if (mSocketName) { if ((mSock = android_get_control_socket(mSocketName)) < 0) {//獲取socket的文件描述符,這裡是獲取Vold這個socket的 SLOGE("Obtaining file descriptor socket '%s' failed: %s", mSocketName, strerror(errno)); return -1; } SLOGV("got mSock = %d for %s", mSock, mSocketName); } if (mListen && listen(mSock, 4) < 0) { SLOGE("Unable to listen on socket (%s)", strerror(errno)); return -1; } else if (!mListen)//是否正常監聽socket mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); if (pipe(mCtrlPipe)) {//新建管道,保存文件描述符到數組 SLOGE("pipe failed (%s)", strerror(errno)); return -1; } if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//開了一個線程來處理 SLOGE("pthread_create (%s)", strerror(errno)); return -1; } return 0; }開啟線程用於監聽
void *SocketListener::threadStart(void *obj) { SocketListener *me = reinterpret_cast對socket監聽處理(obj);//threadStart為static函數,上面開線程創建的時候傳了this,這裡需要轉換一個一樣bit位的SocketListener指針 me->runListener();//SocketListener真正的執行函數 pthread_exit(NULL); return NULL; }
void SocketListener::runListener() { SocketClientCollection *pendingList = new SocketClientCollection();//暫存mClients中的SocketClient while(1) { SocketClientCollection::iterator it; fd_set read_fds; int rc = 0; int max = -1; FD_ZERO(&read_fds);//清空文件描述符集read_fds if (mListen) { max = mSock; FD_SET(mSock, &read_fds);//如果正常的監聽,這裡就把之前獲得的vold的文件描述符添加進去 } FD_SET(mCtrlPipe[0], &read_fds);//添加管道讀取端的文件描述符 if (mCtrlPipe[0] > max) max = mCtrlPipe[0]; pthread_mutex_lock(&mClientsLock);//加鎖操作,多線程安全 for (it = mClients->begin(); it != mClients->end(); ++it) {//遍歷mClients,獲取fd 添加到read_fds int fd = (*it)->getSocket(); FD_SET(fd, &read_fds); if (fd > max) max = fd; } pthread_mutex_unlock(&mClientsLock);//解鎖 SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName); //linux下socket編程的select,這裡檢測read_fds集合裡面是否有可讀的,也就是有沒有數據過來,沒有數據的文件描述符會從read_fds中被剔除,這裡的select設置time out為NULL,阻塞操作,直到read_fds集合中描述符有變化 if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { if (errno == EINTR) continue; SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max); sleep(1); continue; } else if (!rc) continue; if (FD_ISSET(mCtrlPipe[0], &read_fds))//如果匿名管道有數據可讀,就退出 break; if (mListen && FD_ISSET(mSock, &read_fds)) {//如果是正常監聽的 mListen 為true,然後mSock這個描述符有可讀數據,就創建鏈接,新建異步處理的SocketClient加入到mClients容器,這裡的mSock 是vold這個套接字的描述符 struct sockaddr addr; socklen_t alen; int c; do { alen = sizeof(addr); c = accept(mSock, &addr, &alen); SLOGV("%s got %d from accept", mSocketName, c); } while (c < 0 && errno == EINTR); if (c < 0) { SLOGE("accept failed (%s)", strerror(errno)); sleep(1); continue; } pthread_mutex_lock(&mClientsLock); mClients->push_back(new SocketClient(c, true, mUseCmdNum)); pthread_mutex_unlock(&mClientsLock); } /* Add all active clients to the pending list first */ pendingList->clear(); pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) {//把上面有請求建立鏈接的Client加入到pendingList 容器中,後面處理 int fd = (*it)->getSocket(); if (FD_ISSET(fd, &read_fds)) { pendingList->push_back(*it); } } pthread_mutex_unlock(&mClientsLock); /* Process the pending list, since it is owned by the thread, * there is no need to lock it */ while (!pendingList->empty()) {//遍歷處理 /* Pop the first item from the list */ it = pendingList->begin(); SocketClient* c = *it; pendingList->erase(it); /* Process it, if false is returned and our sockets are * connection-based, remove and destroy it */ if (!onDataAvailable(c) && mListen) {//調用到FrameworkListener 中onDataAvailable處理Socket事件 /* Remove the client from our array */ SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { if (*it == c) { mClients->erase(it);//處理完成之後,從容器中移除這次的監聽到的SocketClient break; } } pthread_mutex_unlock(&mClientsLock); /* Remove our reference to the client */ c->decRef(); } } } delete pendingList; }SocketListener的核心處理函數是onDataAvailable
bool FrameworkListener::onDataAvailable(SocketClient *c) { char buffer[255]; int len; len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));//讀Socket 內容保存到buffer if (len < 0) { SLOGE("read() failed (%s)", strerror(errno)); return false; } else if (!len) return false; int offset = 0; int i; for (i = 0; i < len; i++) { if (buffer[i] == '\0') {//一次傳入一個字符串 //分發命令,最終會調用對應命令對象的runCommand進行函數處理。 dispatchCommand(c, buffer + offset); offset = i + 1; } } return true; }調用dispatchcommand
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { FrameworkCommandCollection::iterator i; int argc = 0; char *argv[FrameworkListener::CMD_ARGS_MAX]; char tmp[255]; ...//解析判斷command buffer for (i = mCommands->begin(); i != mCommands->end(); ++i) {//遍歷之前register到FrameworkListener中的FrameworkCommand容器 FrameworkCommand *c = *i; if (!strcmp(argv[0], c->getCommand())) {//其中有注冊“Volume”,如果這裡是Volume開頭的command,那麼就調用,Volume構造的時候所構造的父類FrameworkCommand中的runCommand函數 if (c->runCommand(cli, argc, argv)) { SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno)); } goto out; } } }以Volume這個runcommand為例 /system/vold/CommandListener.cpp中
int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) { dumpArgs(argc, argv, -1); if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false); return 0; } VolumeManager *vm = VolumeManager::Instance();//獲取已經存在的VolumeManager實例 ... else if (!strcmp(argv[1], "mount")) {//判斷command 內容 if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount", false); return 0; } rc = vm->mountVolume(argv[2]);//交給VolumeManager來對Volume進行操作 } ... }
public MountService(Context context) { mContext = context; synchronized (mVolumesLock) { readStorageListLocked(); // 解析/frameworks/base/core/res/res/xml/storage_list.xml保存volume到 MountService的list :mVolumes中 } // XXX: This will go away soon in favor of IMountServiceObserver mPms = (PackageManagerService) ServiceManager.getService("package"); mHandlerThread = new HandlerThread("MountService"); mHandlerThread.start(); mHandler = new MountServiceHandler(mHandlerThread.getLooper());//新建消息處理handler // 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(mHandlerThread.getLooper()); /* * 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);//創建 vold 的監聽接收,用於接收system中Vold的socket消息 Thread thread = new Thread(mConnector, VOLD_TAG); thread.start();//啟動線程,NativeDaemonConnector實現了Runnable接口,實現在 run中 // Add ourself to the Watchdog monitors if enabled. if (WATCHDOG_ENABLE) { Watchdog.getInstance().addMonitor(this); } }
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, int responseQueueSize, String logTag, int maxLogSize) { mCallbacks = callbacks; //回調 mSocket = socket; // socket名稱 mResponseQueue = new ResponseQueue(responseQueueSize);//構建一個響應隊列 mSequenceNumber = new AtomicInteger(0); TAG = logTag != null ? logTag : "NativeDaemonConnector"; }開啟監測線程的run方法
public void run() { HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler"); //TAG 為 VoldConnector,這裡新建一個名為VoldConnector.CallbackHandler的消息處理線程,用於下面接收到vold 的socket之後的處理 thread.start(); mCallbackHandler = new Handler(thread.getLooper(), this); //創建handler 用於分發消息 while (true) { try { listenToSocket();// while 循環 監聽socket } catch (Exception e) { loge("Error in NativeDaemonConnector: " + e); SystemClock.sleep(5000); } } }
private void listenToSocket() throws IOException { LocalSocket socket = null; try { socket = new LocalSocket(); //創建本地socket LocalSocketAddress address = new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED);//獲得服務端vold socket的地址 socket.connect(address);//連接 InputStream inputStream = socket.getInputStream(); synchronized (mDaemonLock) { mOutputStream = socket.getOutputStream(); }//獲取輸入輸出流 mCallbacks.onDaemonConnected();//回調,在MountService中執行,初始化一些Volume狀態信息 byte[] buffer = new byte[BUFFER_SIZE]; int start = 0; while (true) { int count = inputStream.read(buffer, start, BUFFER_SIZE - start);//讀取數據到buffer if (count < 0) {//連接斷開,跳出當前while ,外部while循環 重新調用該函數連接 loge("got " + count + " reading with start = " + start); break; } // 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) { final String rawEvent = new String( buffer, start, i - start, Charsets.UTF_8); log("RCV <- {" + rawEvent + "}"); try { final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent( rawEvent); //解析成event 保存 if (event.isClassUnsolicited()) { //判斷event的code范圍 code >= 600 && code < 700 // TODO: migrate to sending NativeDaemonEvent instances mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( //發送消息,把event交給handle來分發處理 event.getCode(), event.getRawEvent())); } else { mResponseQueue.add(event.getCmdNumber(), event);//加入到響應隊列 } } catch (IllegalArgumentException e) { log("Problem parsing message: " + rawEvent + " - " + e); } start = i + 1; } }在NativeDaemonConnector中的handle處理為:
public boolean handleMessage(Message msg) { String event = (String) msg.obj; try { if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) { //回調到MountService 的onEent函數 log(String.format("Unhandled event '%s'", event)); } } catch (Exception e) { loge("Error handling '" + event + "': " + e); } return true; }MountService中onEvent
public boolean onEvent(int code, String raw, String[] cooked) { 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) { //根據 Vold的Code 執行 /* * One of the volumes we're managing has changed state. * Format: "NNN Volume
private int doMountVolume(String path) { int rc = StorageResultCode.OperationSucceeded; final StorageVolume volume; synchronized (mVolumesLock) { volume = mVolumesByPath.get(path); } if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path); try { mConnector.execute("volume", "mount", path);// 調用到NativeDaemonConnector中的execute }NativeDaemonConnector的execute
public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args) throws NativeDaemonConnectorException { final ArrayListevents = Lists.newArrayList(); final int sequenceNumber = mSequenceNumber.incrementAndGet(); final StringBuilder cmdBuilder = new StringBuilder(Integer.toString(sequenceNumber)).append(' '); final long startTime = SystemClock.elapsedRealtime(); makeCommand(cmdBuilder, cmd, args); //轉換制作成標准的Command final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */ log("SND -> {" + logCmd + "}"); cmdBuilder.append('\0'); final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */ synchronized (mDaemonLock) { if (mOutputStream == null) { throw new NativeDaemonConnectorException("missing output stream"); } else { try { mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8)); //通過在listenToSocket中獲取的輸出流,寫入轉換好的sentCmd } catch (IOException e) { throw new NativeDaemonConnectorException("problem sending command", e); } } } }
本文給大家介紹在Android中如何實現頂部導航菜單左右滑動效果,具體內容如下第一種解決方案: 實現原理是使用android-support-v4.jar包中ViewPa
好吧,先給大家展示效果圖:xml文件:<LinearLayout xmlns:android=http://schemas.android.com/apk/res/
概述 Android事件傳遞機制也是Android系統中比較重要的一塊,事件類型有很多種,這裡主要討論TouchEvent的事件在framework層的傳遞處理機制。因
先看下效果吧public class WuliuView extends View { private int mMaginTop; //物流信息