TShopping

 找回密碼
 註冊
搜索
查看: 1241|回復: 0

[分享] 把APK安裝到SD卡和TF卡實現方案

  [複製鏈接]
發表於 2013-8-23 17:35:01 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 
1. 簡介    為了能把應用程序安裝到SD卡和TF卡上,Android系統默認是不支持的,它只有一個asec mount點: /mnt/secure/asec,在我的系統中,此mount點由/mnt/sdcard給佔用了,所以TF卡就支持不了。為了解決此問題,除了把代碼讀明白之外,無其它的辦法。為了方便理解下面的描述,先看下Vold(管理外設熱插拔)的系統框架圖:


關於相關類圖,引用其他仁兄的圖:


2. 從U盤安裝應用程序流程
  1. PackageInstallerActivity.java::initiateInstall->
  2.                              ::startInstallConfirm->
  3.                   
  4. InstallAppProgress:nCreate->
  5. InstallAppProgress::initView->

  6. PackageManager::installPackage->

  7. PackageManagerService::installPackage->
  8. installPackageWithVerification->
  9.        mHandler.sendMessage(msg)->

  10. PackageHandler::handleMessage->
  11.   PackageHandler::doHandleMessage->
  12.   HandlerParams::startCopy->
  13.      InstallParams::handleStartCopy-> (根據installLocation確認安裝位置,只有針對/data分區有lowThreshold)
  14.        SdInstallArgs::copyApk-> (關鍵的地方,下面詳解)
  15.      InstallParams::handleReturnCode->
  16.        PackageManagerService::processPendingInstall->
  17.          PackageManagerService::installPackageLI-> //args, res分析
  18.            PackageManagerService::installNewPackageLI->  // or replacePackageLI
  19.              PackageManagerService::scanPackageLI-> (KEY)->parsePackage(解析AndroidManifest.xml)
  20.                Installer::install (mInstaller.install)-> (Socket)--->
  21.                installd(真正做事的地方)
複製代碼

2.1
  1. SdInstallArgs::copyApkSdInstallArgs::copyApk->
  2. DefaultContainerService::copyResourceInner->
  3. PackageHelper::createSdDir->
  4. MountService::createSecureContainer->
  5.       (cmd = String.format(new Locale("en", "Us"),
  6.        "asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);)
  7. NativeDaemonConnector::doCommandLocked->(向"vold" socket發送命令給vold,並等待結果)
  8. NativeDaemonConnector::sendCommandLocked-> (Socket)

  9. FrameworkListener:nDataAvailable-> (receive message)  "buffer=asec list"
  10.                                   或"buffer=asec create smdl2tmp1 19 fat caf791d0426d682e5b2aafc5439d55a9 10023"
  11. FrameworkListener::dispatchCommand->
  12. 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:

  1. MountService.java (notifyVolumeStateChange/onEvent)->
  2. MountService.jvav (updatePublicVolumeState) ->
  3. PackageManagerService.java (updateExternalMediaStatus) ->
  4.                           (updateExternalMediaStatusInner) ->
  5.                            (loadMediaPackages) ->
  6.                            SdInstallArgs.doPreInstall->
  7. PackageHelper.java (mountSdDir) ->
  8. MountService.java(mountSecureContainer)->
  9. CommandListener.cpp(CommandListener::AsecCmd::runCommand)

複製代碼

2) Unmount:
  1. MountService.java (notifyVolumeStateChange/onEvent)->
  2. MountService.jvav (updatePublicVolumeState) ->
  3. PackageManagerService.java (updateExternalMediaStatus) ->
  4.                           (updateExternalMediaStatusInner) ->
  5.                            (unloadMediaPackages) ->(send UPDATED_MEDIA_STATUS)
  6.                            PackageHandler.doHandleMessage (get UPDATED_MEDIA_STATUS)->
  7.                            unloadAllContainers ->
  8.                            (SdInstallArgs.doPostDeleteLI)->
  9. PackageHelper.java (unMountSdDir) ->
  10. MountService.java (unmountSecureContainer)->
  11. CommandListener.cpp(CommandListener::AsecCmd::runCommand)

複製代碼


5.  copyResourceInner
其詳細代碼如下所示:


    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信息如下:





    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  
    複製代碼




 

臉書網友討論
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-8 22:03 , Processed in 0.056185 second(s), 22 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表