把APK安装到SD卡和TF卡实现方案

来源:互联网 发布:淘宝店标志图片大全 编辑:程序博客网 时间:2024/06/11 04:10

转:http://blog.csdn.net/myarrow/article/details/8778621#comments

1. 简介

    为了能把应用程序安装到SD卡和TF卡上,Android系统默认是不支持的,它只有一个asec mount点: /mnt/secure/asec,在我的系统中,此mount点由/mnt/sdcard给占用了,所以TF卡就支持不了。为了解决此问题,除了把代码读明白之外,无其它的办法。为了方便理解下面的描述,先看下Vold(管理外设热插拔)的系统框架图:


关于相关类图,引用其他仁兄的图:


2. 从U盘安装应用程序流程

PackageInstallerActivity.java::initiateInstall->
                             ::startInstallConfirm->
                   
InstallAppProgress::onCreate->
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::copyApk

SdInstallArgs::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::onDataAvailable-> (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

其详细代码如下所示:

[cpp] view plaincopy
  1. private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName) {  
  2.     // Make sure the sdcard is mounted.  
  3.     String status = Environment.getExternalStorageState();  
  4.     if (!status.equals(Environment.MEDIA_MOUNTED)) {  
  5.         Slog.w(TAG, "Make sure sdcard is mounted.");  
  6.         return null;  
  7.     }  
  8.   
  9.     // The .apk file  
  10.     String codePath = packageURI.getPath();  
  11.     File codeFile = new File(codePath);  
  12.   
  13.     // Calculate size of container needed to hold base APK.  
  14.     int sizeMb;  
  15.     try {  
  16.         sizeMb = calculateContainerSize(codeFile);  
  17.     } catch (FileNotFoundException e) {  
  18.         Slog.w(TAG, "File does not exist when trying to copy " + codeFile.getPath());  
  19.         return null;  
  20.     }  
  21.   
  22.     // Create new container  
  23.     final String newCachePath;  
  24.     if ((newCachePath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid())) == null) {  
  25.         Slog.e(TAG, "Failed to create container " + newCid);  
  26.         return null;  
  27.     }  
  28.   
  29.     /*if (localLOGV)*/ {  
  30.         Slog.i(TAG, "Created container for " + newCid + " at path : " + newCachePath);  
  31.     }  
  32.   
  33.     final File resFile = new File(newCachePath, resFileName);  
  34.     if (FileUtils.copyFile(new File(codePath), resFile)) {  
  35.         /*if (localLOGV)*/ {  
  36.             Slog.i(TAG, "Copied " + codePath + " to " + resFile);  
  37.         }  
  38.     } else {  
  39.         Slog.e(TAG, "Failed to copy " + codePath + " to " + resFile);  
  40.         // Clean up container  
  41.         PackageHelper.destroySdDir(newCid);  
  42.         return null;  
  43.     }  
  44.   
  45.     final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);  
  46.     if (sharedLibraryDir.mkdir()) {  
  47.         int ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(codeFile, sharedLibraryDir);  
  48.         if (ret != PackageManager.INSTALL_SUCCEEDED) {  
  49.             Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath());  
  50.             PackageHelper.destroySdDir(newCid);  
  51.             return null;  
  52.         }  
  53.     } else {  
  54.         Slog.e(TAG, "Could not create native lib directory: " + sharedLibraryDir.getPath());  
  55.         PackageHelper.destroySdDir(newCid);  
  56.         return null;  
  57.     }  
  58.   
  59.     if (!PackageHelper.finalizeSdDir(newCid)) {  
  60.         Slog.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath);  
  61.         // Clean up container  
  62.         PackageHelper.destroySdDir(newCid);  
  63.         return null;  
  64.     }  
  65.   
  66.     if (localLOGV) {  
  67.         Slog.i(TAG, "Finalized container " + newCid);  
  68.     }  
  69.   
  70.     if (PackageHelper.isContainerMounted(newCid)) {  
  71.         if (localLOGV) {  
  72.             Slog.i(TAG, "Unmounting " + newCid + " at path " + newCachePath);  
  73.         }  
  74.   
  75.         // Force a gc to avoid being killed.  
  76.         Runtime.getRuntime().gc();  
  77.         PackageHelper.unMountSdDir(newCid);  
  78.     } else {  
  79.         if (localLOGV) {  
  80.             Slog.i(TAG, "Container " + newCid + " not mounted");  
  81.         }  
  82.     }  
  83.   
  84.     return newCachePath;  
  85. }  


其主要功能为:

1)创建.asec文件

2)copy apk

3)copy lib

4)final .asec文件

其详细log信息如下:

[cpp] view plaincopy
  1. D/VoldVolumeManager(   85):  createAsec(367) call createImageFile     
  2. E/Vold    (   85): createImageFile(236) call creat                    
  3. E/Vold    (   85): createImageFile(241) call ftruncate                
  4. E/Vold    (   85): createImageFile(247) END                           
  5. D/VoldVolumeManager(   85): createAsec(374) call asecHash             
  6. D/VoldVolumeManager(   85): createAsec(383) call Loop::create         
  7. D/VoldVolumeManager(   85): createAsec(397) call Devmapper::create    
  8. D/VoldVolumeManager(   85): createAsec(414) call open                 
  9. D/VoldVolumeManager(   85): createAsec(436) write sb                  
  10. D/VoldVolumeManager(   85): createAsec(453) Fat::format               
  11. D/VoldVolumeManager(   85): createAsec(477) Fat::doMount              
  12. D/VoldVolumeManager(   85): createAsec(491) End                       
  13.                                                                       
  14. ****copy apk and lib*****                                             
  15.                                                                       
  16. D/VoldVolumeManager(   85): finalizeAsec(502) start                   
  17. D/VoldVolumeManager(   85): finalizeAsec(520) End                     
  18. D/VoldVolumeManager(   85): renameAsec:538 Volume::SEC_ASECDIR start  
  19. D/VoldVolumeManager(   85): renameAsec:538 Volume::SEC_ASECDIR start  
0 0
原创粉丝点击