<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                這一節將分析一個實際案例,即插入一張SD卡引發的事件及其處理過程。在分析之前,還是應先介紹MountService。 1. MountService的介紹 有些應用程序需要檢測外部存儲卡的插入/拔出事件,這些事件是由MountService通過Intent廣播發出的,例如外部存儲卡插入后,MountService就會發送ACTION_MEDIA_MOUNTED消息。從某種意義上說,可把MountService看成是Java世界的Vold。來簡單認識一下這個MountService,它的代碼如下所示: **MountService.java** ~~~ class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks { //MountService實現了INativeDaemonConnectorCallbacks接口 ...... public MountService(Context context) { mContext = context; ...... //創建一個HandlerThread,在第5章中曾介紹過。 mHandlerThread = new HandlerThread("MountService"); mHandlerThread.start(); /* 創建一個Handler,這個Handler使用HandlerThread的Looper,也就是說,派發給該Handler 的消息將在另外一個線程中處理。可回顧第5章的內容,以加深印象。 */ mHandler = new MountServiceHandler(mHandlerThread.getLooper()); ...... /* NativeDaemonConnector用于Socket通信,第二個參數“vold”表示將和Vold通信,也就是 和CL模塊中的那個socket建立通信連接。第一個參數為INativeDaemonConnectorCallbacks 接口。它提供兩個回調函數: onDaemonConnected:當NativeDaemonConnector連接上Vold后回調。 onEvent:當NativeDaemonConnector收到來自Vold的數據后回調。 */ mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector"); mReady= false; //再啟動一個線程用于和Vold通信。 Threadthread = new Thread(mConnector, NativeDaemonConnector.class.getName()); thread.start(); } ...... } ~~~ MountService通過NativeDaemonConnector和Vold的CL模塊建立通信連接,這部分內容比較簡單,讀者可自行研究。下面來分析SD卡插入后所引發的一連串處理。 2. 設備插入事件的處理 (1)Vold處理Uevent事件 在插入SD卡后,Vold的NM模塊接收到Uevent消息,假設此消息的內容是前面介紹Uevent知識時使用的add消息,它的內容如下所示: **SD卡插入的Uevent消息** ~~~ add@/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0 ACTION=add //add表示設備插入動作,另外還有remove和change等動作 //DEVPATH表示該設備位于/sys目錄中的設備路徑 DEVPATH=/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0 /* SUBSYSTEM表示該設備屬于哪一類設備,block為塊設備,磁盤也屬于這一類設備,另外還有 character(字符)設備等類型。 */ SUBSYSTEM=block MAJOR=179//MAJOR和MINOR分別表示該設備的主次設備號,二者聯合起來可以標識一個設備 MINOR=0 DEVNAME=mmcblk0 DEVTYPE=disk//設備Type為disk NPARTS=3 //表示該SD卡上的分區,我的SD卡上有三塊分區 SEQNUM=1357//序號 ~~~ 根據前文分析可知,NM模塊中的NetlinkHandler會處理此消息,請回顧一下相關代碼: **NetlinkHandler.cpp** ~~~ void NetlinkHandler::onEvent(NetlinkEvent *evt){ VolumeManager *vm = VolumeManager::Instance(); constchar *subsys = evt->getSubsystem(); ...... //根據上面Uevent消息的內容可知,它的subsystem對應為block,所以我們會走下面這個if分支 if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); //調用VM的handleBlockEvent } ...... } ~~~ **VolumeManager.cpp** ~~~ voidVolumeManager::handleBlockEvent(NetlinkEvent *evt) { constchar *devpath = evt->findParam("DEVPATH"); VolumeCollection::iterator it; boolhit = false; for(it = mVolumes->begin(); it != mVolumes->end(); ++it) { //調用各個Volume的handleBlockEvent if(!(*it)->handleBlockEvent(evt)) { hit = true; break; } } ...... } ~~~ 我的G7手機只有一個Volume,其實際類型就是之前介紹過的DirectVolume。請看它是怎么對待這個Uevent消息的,代碼如下所示: **DirectVolume.cpp** ~~~ int DirectVolume::handleBlockEvent(NetlinkEvent*evt) { constchar *dp = evt->findParam("DEVPATH"); PathCollection::iterator it; 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); //內部調用mknod函數創建設備節點 if (createDeviceNode(nodepath, major, minor)) { SLOGE("Error makingdevice node '%s' (%s)", nodepath, strerror(errno)); } if (!strcmp(devtype, "disk")) { //對應Uevent消息的DEVTYPE值為disk,所以走這個分支 handleDiskAdded(dp, evt); } else { //處理DEVTYPE為Partition的情況 handlePartitionAdded(dp,evt); } } else if (action == NetlinkEvent::NlActionRemove) { //對應Uevent的ACTION值為remove ...... } else if (action == NetlinkEvent::NlActionChange) { //對應Uevent的ACTION值為change ...... } ...... return 0; } } errno= ENODEV; return-1; } ~~~ 插入SD卡,首先收到的Uevent消息中DEVTYPE的值是“disk”,這將導致DirectVolume的handleDiskInserted被調用。下面來看它的工作。 **DirectVolume.cpp** ~~~ void DirectVolume::handleDiskAdded(const char*devpath, NetlinkEvent *evt) { mDiskMajor = atoi(evt->findParam("MAJOR")); mDiskMinor = atoi(evt->findParam("MINOR")); constchar *tmp = evt->findParam("NPARTS"); if(tmp) { mDiskNumParts = atoi(tmp);//這個disk上的分區個數。 } else{ ...... mDiskNumParts = 1; } charmsg[255]; intpartmask = 0; inti; /* Partmask會記錄這個Disk上分區加載的情況,前面曾介紹過,如果一個Disk有多個分區, 它后續則會收到多個分區的Uevent消息。 */ for (i= 1; i <= mDiskNumParts; i++) { partmask |= (1 << i); } mPendingPartMap = partmask; if(mDiskNumParts == 0) { ......//如果沒有分區,則設置Volume的狀態為Idle。 setState(Volume::State_Idle); }else { ......//如果還有分區未加載,則設置Volume狀態為Pending setState(Volume::State_Pending); } /* 設置通知內容,snprintf調用完畢后msg的值為: “Volume sdcard/mnt/sdcard disk inserted (179:0)” */ snprintf(msg, sizeof(msg), "Volume %s %s disk inserted(%d:%d)", getLabel(), getMountpoint(), mDiskMajor, mDiskMinor); /* getBroadcaster函數返回setBroadcaster函數設置的那個Broadcaster,也就是CL對象。 然后調用CL對象的sendBroadcast給MountService發送消息,注意它的第一個參數是ResponseCode::VolumeDiskInserted。 */ mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted, msg, false); } ~~~ handleDiskAdded把Uevent消息做一些轉換后發送給了MountService,實際上可認為CL模塊和MountService通信使用的是另外一套協議。那么,MountService會做什么處理呢? (2)MountService的處理 **MountService.javaonEvent函數** ~~~ publicboolean onEvent(int code, String raw, String[] cooked) { Intent in = null; //關于onEvent函數,MountService的介紹中曾提過,當NativeDaemonConnector收到 //來自vold的數據后都會調用這個onEvent函數。 ...... if(code == VoldResponseCode.VolumeStateChange) { ...... }else if (code == VoldResponseCode.ShareAvailabilityChange) { ...... }else if ((code == VoldResponseCode.VolumeDiskInserted) || (code ==VoldResponseCode.VolumeDiskRemoved) || (code ==VoldResponseCode.VolumeBadRemoval)) { final String label = cooked[2]; //label值為”sdcard” final String path = cooked[3]; //path值為”/mnt/sdcard” int major = -1; int minor = -1; try { String devComp = cooked[6].substring(1, cooked[6].length() -1); String[] devTok = devComp.split(":"); major = Integer.parseInt(devTok[0]); minor = Integer.parseInt(devTok[1]); } catch (Exception ex) { ...... } if (code == VoldResponseCode.VolumeDiskInserted) { //收到handleDiskAdded發送的VolumeDiskInserted消息了 //然后單獨啟動一個線程來處理這個消息。 new Thread() { public void run() { try { int rc; //調用doMountVolume處理。 if ((rc =doMountVolume(path)) != StorageResultCode.OperationSucceeded) { } } catch (Exception ex){ ...... } } }.start(); } ~~~ doMountVolume函數的代碼如下所示: **MountService.java** ~~~ private int doMountVolume(String path) { int rc = StorageResultCode.OperationSucceeded; try { //通過NativeDaemonConnector給Vold發送請求,請求內容為: //volume mount /mnt/sdcard mConnector.doCommand(String.format("volume mount %s", path)); }catch (NativeDaemonConnectorException e) { ......//異常處理 } ~~~ 走了一大圈,最后又回到Vold了。CL模塊將收到這個來自MountService的請求,請求的內容為字符串“volume mount/mnt/sdcard”,其中的volume表示命令的名字,CL會根據這個名字找到VolumeCmd對象,并交給它處理這個命令。 (3)Vold處理MountService的命令 Vold處理MountService命令的代碼如下所示: **CommandListener.cppVolumeCmd類** ~~~ intCommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) { ...... VolumeManager *vm = VolumeManager::Instance(); int rc= 0; if(!strcmp(argv[1], "list")) { return vm->listVolumes(cli); } elseif (!strcmp(argv[1], "debug")) { ...... } elseif (!strcmp(argv[1], "mount")) { ......//調用VM模塊的mountVolume來處理mount命令,參數是“/mnt/sdcard” rc= vm->mountVolume(argv[2]); } elseif (!strcmp(argv[1], "unmount")) { ...... rc= vm->unmountVolume(argv[2], force); } elseif (!strcmp(argv[1], "format")) { ...... rc = vm->formatVolume(argv[2]); } elseif (!strcmp(argv[1], "share")) { ...... rc= vm->shareVolume(argv[2], argv[3]); } elseif (!strcmp(argv[1], "unshare")) { ...... rc= vm->unshareVolume(argv[2], argv[3]); } ...... if(!rc) { //發送處理結果給MountService cli->sendMsg(ResponseCode::CommandOkay, "volume operationsucceeded", false); } ...... return0; } ~~~ 看mountVolume函數: **VolumeManager.cpp** ~~~ int VolumeManager::mountVolume(const char*label) { /* 根據label找到對應的Volume。label這個參數的名字含義上有些歧義,根據loopupVolume 的實現來看,它其實比較的是Volume的掛載路徑,也就是vold.fstab中指定的那個 /mnt/sdcard。而vold.fstab中指定的label叫sdcard。 */ Volume*v = lookupVolume(label); ...... returnv->mountVol();//mountVol由Volume類實現。 } ~~~ 找到對應的DirectVolume后,也就找到了代表真實存儲卡的對象。它是如何處理這個命令的呢?代碼如下所示: **Volume.cpp** ~~~ int Volume::mountVol() { dev_tdeviceNodes[4]; int n,i, rc = 0; charerrmsg[255]; ...... //getMountpoint返回掛載路徑,即/mnt/sdcard //isMountpointMounted判斷這個路徑是不是已經被mount了 if(isMountpointMounted(getMountpoint())) { setState(Volume::State_Mounted);//設置狀態為State_Mounted return 0;//如果已經被mount了,則直接返回 } n =getDeviceNodes((dev_t *) &deviceNodes, 4); ...... for (i= 0; i < n; i++) { char devicePath[255]; sprintf(devicePath, "/dev/block/vold/%d:%d",MAJOR(deviceNodes[i]), MINOR(deviceNodes[i])); ...... errno = 0; setState(Volume::State_Checking); //默認SD卡為FAT分區,只有這樣,當加載為磁盤的時候才能被Windows識別。 if(Fat::check(devicePath)) { ...... return -1; } /* 先把設備mount到/mnt/secure/staging,這樣/mnt/secure/staging下的內容 就是該設備的存儲內容了 */ errno = 0; if(Fat::doMount(devicePath, "/mnt/secure/staging", false, false, 1000, 1015, 0702,true)) { ...... continue; } /* 下面這個函數會把存儲卡中的autorun.inf文件找出來并刪掉,這個文件就是“臭名昭著”的 自動運行文件,在Windows系統上,把SD卡掛載為磁盤后,雙擊這個磁盤就會自動運行 這個文件,很多病毒和木馬都是通過它傳播的。為了安全起見,要把這個文件刪掉。 */ protectFromAutorunStupidity(); //①下面這個函數比較有意思,需要看看: if(createBindMounts()) { ...... return -1; } //將存儲卡mount路徑從/mnt/secure/staging移到/mnt/sdcard if(doMoveMount("/mnt/secure/staging", getMountpoint(), false)) { ...... return -1; } //②設置狀態為State_Mounted,這個函數將發送狀態信息給MountService setState(Volume::State_Mounted); mCurrentlyMountedKdev = deviceNodes[i]; return 0; } ...... setState(Volume::State_Idle); return-1; } ~~~ 上面代碼中有個比較有意思的函數,就是createBindMounts,其代碼如下所示: **Volume.cpp** ~~~ int Volume::createBindMounts() { unsigned long flags; /*‘ 將/mnt/secure/staging/android_secure目錄名改成 /mnt/secure/staging/.android_secure,SEC_STG_SECIMGDIR的值就是 /mnt/secure/staging/.android_secure,也就是把它變成Linux平臺上的隱藏目錄 */ if(!access("/mnt/secure/staging/android_secure", R_OK | X_OK)&& access(SEC_STG_SECIMGDIR, R_OK | X_OK)) { if(rename("/mnt/secure/staging/android_secure", SEC_STG_SECIMGDIR)) { SLOGE("Failed to rename legacy asec dir (%s)",strerror(errno)); } } ...... /* 使用mount命令的bind選項,可將/mnt/secure/staging/.android_secure這個目錄 掛載到/mnt/secure/asec目錄下。/mnt/secure/asec目錄是一個secure container, 目前主要用來保存一些安裝在SD卡上的APP信息。APP2SD是Android 2.2引入的新機制,它 支持將APP安裝在SD卡上,這樣可以節約內部的存儲空間。 mount的bind選項允許將文件系統的一個目錄掛載到另外一個目錄下。讀者可以通過man mount 查詢具體信息。 */ if(mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) { ...... return -1; } ...... /* 將tempfs設備掛載到/mnt/secure/staging/.android_secure目錄,這樣之前 .android_secure目錄中的內容就只能通過/mnt/secure/asec訪問了。由于那個目錄只能 由root訪問,所以可以起到安全和保護的作用。 */ if(mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY,"size=0,mode=000,uid=0,gid=0")){ ...... umount("/mnt/asec_secure"); return -1; } return0; } ~~~ createBindMounts的作用就是將存儲卡上的.android_secure目錄掛載到/mnt/secure/asec目錄下,同時對.android_secure進行一些特殊處理,這樣,沒有權限的用戶就不能更改或破壞.android_secure目錄中的內容了,因此它起到了一定的保護作用。 在手機上,受保護的目錄內容,只能在用adb shell登錄后,進入/mnt/secure/asec目錄來查看。注意,這個asec目錄的內容就是.android_secure未掛載tmpfs時的內容(亦即它保存著那些安裝在存儲卡上的應用程序的信息)。另外,可把SD卡拔出來,通過讀卡器直接插到臺式機上,此時,這些信息就能在.android_secure目錄中被直接看到了。 (4)MountService處理狀態通知 volume的mountVol完成相關工作后,就通過下面的函數,發送信息給MountService: ~~~ setState(Volume::State_Mounted); //感興趣的讀者可自行分析此函數的實現。 ~~~ MountService依然會在onEvent函數中收到這個消息。 **MountService.java** ~~~ public boolean onEvent(int code, String raw,String[] cooked) { Intent in = null; ...... if(code == VoldResponseCode.VolumeStateChange) { /* 狀態變化由notifyVolumeStateChange函數處理,由于Volume的狀態 被置成Mounted,所以下面這個notifyVolumeStateChange會發送 ACTION_MEDIA_MOUNTED這個廣播。我們就不再分析這個函數了,讀者 可自行研究。 */ notifyVolumeStateChange( cooked[2], cooked[3],Integer.parseInt(cooked[7]), Integer.parseInt(cooked[10])); } ~~~ 實例分析就到這里。中間略去了一些處理內容,例如對分區的處理等,讀者可自行研讀,相信已沒有太大難度了。另外,在上述處理過程中,稍微難懂的是mountVol這個函數在掛載方面的處理過程。用圖9-6來總結一下這個處理過程: :-: ![](http://img.blog.csdn.net/20150802164428372?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖9-6 SD卡插入事件處理流程圖 由上圖可知,Vold在安全性上還是做了一定考慮的。如果沒有特殊需要,讀者了解上面這些知識也就夠了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看