|
1. 簡介 為了能把應用程序安裝到SD卡和TF卡上,Android系統默認是不支持的,它只有一個asec mount點: /mnt/secure/asec,在我的系統中,此mount點由/mnt/sdcard給佔用了,所以TF卡就支持不了。為了解決此問題,除了把代碼讀明白之外,無其它的辦法。為了方便理解下面的描述,先看下Vold(管理外設熱插拔)的系統框架圖:
關於相關類圖,引用其他仁兄的圖:
2. 從U盤安裝應用程序流程
- PackageInstallerActivity.java::initiateInstall->
- ::startInstallConfirm->
-
- InstallAppProgress:nCreate->
- InstallAppProgress::initView->
- PackageManager::installPackage->
- PackageManagerService::installPackage->
- installPackageWithVerification->
- mHandler.sendMessage(msg)->
- PackageHandler::handleMessage->
- PackageHandler::doHandleMessage->
- HandlerParams::startCopy->
- InstallParams::handleStartCopy-> (根據installLocation確認安裝位置,只有針對/data分區有lowThreshold)
- SdInstallArgs::copyApk-> (關鍵的地方,下面詳解)
- InstallParams::handleReturnCode->
- PackageManagerService::processPendingInstall->
- PackageManagerService::installPackageLI-> //args, res分析
- PackageManagerService::installNewPackageLI-> // or replacePackageLI
- PackageManagerService::scanPackageLI-> (KEY)->parsePackage(解析AndroidManifest.xml)
- Installer::install (mInstaller.install)-> (Socket)--->
- installd(真正做事的地方)
複製代碼
2.1
- SdInstallArgs::copyApkSdInstallArgs::copyApk->
- DefaultContainerService::copyResourceInner->
- PackageHelper::createSdDir->
- MountService::createSecureContainer->
- (cmd = String.format(new Locale("en", "Us"),
- "asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);)
- NativeDaemonConnector::doCommandLocked->(向"vold" socket發送命令給vold,並等待結果)
- NativeDaemonConnector::sendCommandLocked-> (Socket)
- FrameworkListener:nDataAvailable-> (receive message) "buffer=asec list"
- 或"buffer=asec create smdl2tmp1 19 fat caf791d0426d682e5b2aafc5439d55a9 10023"
- FrameworkListener::dispatchCommand->
- CommandListener::AsecCmd::runCommand->
複製代碼
(所有與Volume::SEC_ASECDIR相關的代碼都需要修改)
VolumeManager::createAsec (決定安裝文件的路徑Volume::SEC_ASECDIR)
2.2 複雜的mount關係 詳細代碼參見Volume.cpp::
代碼默認不把sdcard && udisk mount為asec,為此需要把SD卡或TF卡mount到asec掛載點,當然系統默認只有一個/mnt/secure/asec,你可以在init.rc中創建一個/mnt/secure/asecsd或/mnt/secure/asectf,並執行mount即可。
‧ /mnt/secure: 下的mount點為物理外設的mount點
‧/mnt/asec: 為公共掛載點,它是一個tmpfs文件系統的掛載點,安裝到SD或TF上的應用程序都可以被mount到此目錄下的mount點
複雜的mount關係如下所示:
1) /dev/block/vold/31:9 mount to /mnt/secure/staging
2) /mnt/secure/staging/.android_secure mount to /mnt/secure/asec (MS_BIND)
3) tmpfs mount to /mnt/secure/staging/.android_secure (只讀0字節tmpfs,把它隱藏起來, obscuring the asec image directory from non root users)
4) 把/mnt/secure/staging下的子目錄(.android_secure)移動到/mnt/sdcard下
註:MS_BIND: 執行bind掛載,使文件或者子目錄樹在文件系統內的另一個點上可視。
搞這麼複雜的目的只有一個:只有root用戶才能查看/mnt/secure/asec下的文件,當然把TF卡取出來,在Widows上可以看到.android_secure目錄下的.asec文件。
2.3 把APK安裝到SD卡或TF卡上留下的東東 當把APK安裝到SD卡或TF卡上時,將在以下地方留下它的東東:
1) /mnt/secure/asec: (安裝之後產生的.asec文件,它才是真正的內容)
-rwxrwxr-x system sdcard_rw 20257280 2013-04-07 06:14 com.myarrow.test-1.asec
2) /mnt/asec/com.myarrow.test-1: (是被mount上去的,它是一個tmpfs,並不真正佔用flash空間)
dr-xr-xr-x system root 2013-04-07 06:14 lib
-r-xr-xr-x system root 4973096 2013-04-07 06:14 pkg.apk
3) /data/data/com.myarrow.test: (只是一個鏈接,不真正佔用flash空間)
lrwxrwxrwx system system 2013-04-09 06:51 lib -> /mnt/asec/com.myarrow.test-1/lib
4) /data/dalvik-cache: (包含apk包中的dex文件,它佔用data分區的flash空間, 所以data分區必須預留空間)
-rw-r--r-- system app_58 36608 2013-04-07 06:14 mnt@asec@com.myarrow.test-1@pkg.apk@classes.dex
3. 需要修改的主要文件 1) Volume.cpp
修改其中的mountVol/unmountVol,把SD或TF卡也執行bindmount
2) DefaultContainerService.java
修改isUnderExternalThreshold,當空間不足時,看看SD或TF卡上是否有足夠的空間
3) VolumeManager.cpp
擴展與Volume::SEC_ASECDIR相關的地方,因為我們增加了一個Volume::SEC_SD_ASECDIR
4. Mount和Unmoun asec t流程
1) Mount:
- MountService.java (notifyVolumeStateChange/onEvent)->
- MountService.jvav (updatePublicVolumeState) ->
- PackageManagerService.java (updateExternalMediaStatus) ->
- (updateExternalMediaStatusInner) ->
- (loadMediaPackages) ->
- SdInstallArgs.doPreInstall->
- PackageHelper.java (mountSdDir) ->
- MountService.java(mountSecureContainer)->
- CommandListener.cpp(CommandListener::AsecCmd::runCommand)
複製代碼
2) Unmount:
- MountService.java (notifyVolumeStateChange/onEvent)->
- MountService.jvav (updatePublicVolumeState) ->
- PackageManagerService.java (updateExternalMediaStatus) ->
- (updateExternalMediaStatusInner) ->
- (unloadMediaPackages) ->(send UPDATED_MEDIA_STATUS)
- PackageHandler.doHandleMessage (get UPDATED_MEDIA_STATUS)->
- unloadAllContainers ->
- (SdInstallArgs.doPostDeleteLI)->
- PackageHelper.java (unMountSdDir) ->
- MountService.java (unmountSecureContainer)->
- CommandListener.cpp(CommandListener::AsecCmd::runCommand)
複製代碼
5. copyResourceInner其詳細代碼如下所示:
- private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName) {
- // Make sure the sdcard is mounted.
- String status = Environment.getExternalStorageState();
- if (!status.equals(Environment.MEDIA_MOUNTED)) {
- Slog.w(TAG, "Make sure sdcard is mounted.");
- return null;
- }
-
- // The .apk file
- String codePath = packageURI.getPath();
- File codeFile = new File(codePath);
-
- // Calculate size of container needed to hold base APK.
- int sizeMb;
- try {
- sizeMb = calculateContainerSize(codeFile);
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "File does not exist when trying to copy " + codeFile.getPath());
- return null;
- }
-
- // Create new container
- final String newCachePath;
- if ((newCachePath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid())) == null) {
- Slog.e(TAG, "Failed to create container " + newCid);
- return null;
- }
-
- /*if (localLOGV)*/ {
- Slog.i(TAG, "Created container for " + newCid + " at path : " + newCachePath);
- }
-
- final File resFile = new File(newCachePath, resFileName);
- if (FileUtils.copyFile(new File(codePath), resFile)) {
- /*if (localLOGV)*/ {
- Slog.i(TAG, "Copied " + codePath + " to " + resFile);
- }
- } else {
- Slog.e(TAG, "Failed to copy " + codePath + " to " + resFile);
- // Clean up container
- PackageHelper.destroySdDir(newCid);
- return null;
- }
-
- final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
- if (sharedLibraryDir.mkdir()) {
- int ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(codeFile, sharedLibraryDir);
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
- Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath());
- PackageHelper.destroySdDir(newCid);
- return null;
- }
- } else {
- Slog.e(TAG, "Could not create native lib directory: " + sharedLibraryDir.getPath());
- PackageHelper.destroySdDir(newCid);
- return null;
- }
-
- if (!PackageHelper.finalizeSdDir(newCid)) {
- Slog.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath);
- // Clean up container
- PackageHelper.destroySdDir(newCid);
- return null;
- }
-
- if (localLOGV) {
- Slog.i(TAG, "Finalized container " + newCid);
- }
-
- if (PackageHelper.isContainerMounted(newCid)) {
- if (localLOGV) {
- Slog.i(TAG, "Unmounting " + newCid + " at path " + newCachePath);
- }
-
- // Force a gc to avoid being killed.
- Runtime.getRuntime().gc();
- PackageHelper.unMountSdDir(newCid);
- } else {
- if (localLOGV) {
- Slog.i(TAG, "Container " + newCid + " not mounted");
- }
- }
-
- return newCachePath;
- }
複製代碼
其主要功能為:
1)創建.asec文件
2)copy apk
3)copy lib
4)final .asec文件
其詳細log信息如下:
- D/VoldVolumeManager( 85): createAsec(367) call createImageFile
- E/Vold ( 85): createImageFile(236) call creat
- E/Vold ( 85): createImageFile(241) call ftruncate
- E/Vold ( 85): createImageFile(247) END
- D/VoldVolumeManager( 85): createAsec(374) call asecHash
- D/VoldVolumeManager( 85): createAsec(383) call Loop::create
- D/VoldVolumeManager( 85): createAsec(397) call Devmapper::create
- D/VoldVolumeManager( 85): createAsec(414) call open
- D/VoldVolumeManager( 85): createAsec(436) write sb
- D/VoldVolumeManager( 85): createAsec(453) Fat::format
- D/VoldVolumeManager( 85): createAsec(477) Fat::doMount
- D/VoldVolumeManager( 85): createAsec(491) End
-
- ****copy apk and lib*****
-
- D/VoldVolumeManager( 85): finalizeAsec(502) start
- D/VoldVolumeManager( 85): finalizeAsec(520) End
- D/VoldVolumeManager( 85): renameAsec:538 Volume::SEC_ASECDIR start
- D/VoldVolumeManager( 85): renameAsec:538 Volume::SEC_ASECDIR start
複製代碼
|
|