APK安裝流程概述




. APK安裝簡介


APKAndroid Package的縮寫。


Android應用安裝有如下四種方式:

1.系統應用安裝――開機時完成,沒有安裝界面;

2.網絡下載應用安裝――通過market應用完成,沒有安裝界面;

3.ADB工具安裝――沒有安裝界面;

4.第三方應用安裝――通過SD卡里的APK文件安裝,有安裝界面,由packageinstaller.apk應用處理安裝及卸載過程的界面。

 


應用安裝涉及到如下幾個目錄:

  • system/app----------------系統自帶的應用程序,獲得adb root權限才能刪除;
  • data/app-------------------用戶程序安裝的目錄,安裝時把apk 文件復制到此目錄;
  • data/data-------------------存放應用程序的數據;
  • data/dalvik-cache---------將apk中的dex文件安裝到dalvik-cache目錄下(dex文件是dalvik虛擬機的可執行文件,其大小約為原始apk文件大小的四分之一)。



. 系統應用安裝

 

1. 了解須知:

1. 對於在/system/app/data/app目錄下的APK文件,在PackageManagerService的啟動過程中,會掃描安裝

2.PackageManagerServicesystem_server啟動,它全面負責應用包的安裝,卸載,權限檢查等工作。

3.在每次開機的時 候,PackageManagerService都會在其構造函數中,對指定的目錄的APK進行掃描。對於沒有安裝的APK文件會觸發安裝過程。



2. 實現原理:

(1). 開機啟動PackageManagerService,通過SystemServer.startBootstrapServices()啟動。

1 public static PackageManagerService main(Context context, Installer installer,
2             boolean factoryTest, boolean onlyCore) {
3         PackageManagerService m = HwServiceFactory.getHuaweiPackageManagerService(context, installer,
4                 factoryTest, onlyCore);
5         ServiceManager.addService("package", m);
6         return m;
7     }

 

 

(2). PackageManagerService初始化,執行構造方法,分為六個重要步驟。

 

第一步:創建Settings對象,添加shareUserId

 1         mSettings = new Settings(mPackages);
 2         mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
 3                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
 4         mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
 5                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
 6         mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
 7                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
 8         mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
 9                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
10         mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
11                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
12         mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
13                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

 


第二步:創建應用安裝器Installer,來源是PackageManagerService參數之一。

 

第三步:構造SystemConfig,讀取/system/etc/permissions/*.xml” 資源,獲取mSystemPermissions(系統權限),mGlobalGidsGroup-ids),mAvailableFeatures(系統支持的features)屬性。

執行順序:

com.android.server.pm.PackageManagerService#PackageManagerService

--> com.android.server.SystemConfig#getInstance

--> com.android.server.SystemConfig#SystemConfig

--> com.android.server.SystemConfig#readPermissions

 

1    SystemConfig systemConfig = SystemConfig.getInstance();
2    mGlobalGids = systemConfig.getGlobalGids();
3    mSystemPermissions = systemConfig.getSystemPermissions();
4    mAvailableFeatures = systemConfig.getAvailableFeatures();

 

第四步:創建系統消息處理線程。

1   mHandlerThread = new ServiceThread(TAG,
2             Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
3   mHandlerThread.start();
4   mHandler = new PackageHandler(mHandlerThread.getLooper());
5   Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

 

第五步:執行com.android.server.pm.Settings#readLPw, 讀取安裝包信息,並解析成對應的數據結構,包括以下重要文件:

  • packages.xml:記錄系統中所有安裝的應用信息,包括基本信息、簽名和權限。

  • packages-backup.xmlpackages.xml文件的備份。

  • packages.list:保存普通應用的數據目錄和uid等信息。

  • packages-stopped.xml:記錄系統中被強制停止運行的應用信息。系統在強制停止某個應用時,會講應用的信息記錄到該文件中。

  • packages-stopped-backup.xmlpacakges-stopped.xml文件的備份。

這幾個目錄在創建Settings對象的時候,就已經被封裝成對應的File文件。

packages-backup.xmlpackages.xml的備份文件。在每次寫packages.xml文件的時候,都會將舊的 packages.xml文件先備份,這樣做是為了防止寫文件過程中文件以外損壞,還能從舊的文件中恢復。

package- restrictions.xml保存着受限制的APP的狀態,比如某個APP處於disable狀態,或者某個APP具有更高的優先級等。



第六步:執行PackageManagerService#scanDirLI

監控和掃描系統包安裝目錄:

  • /system/framework 系統庫
  • /system/app 默認的系統應用
  • /vendor/app 廠商定制的應用

 

掃描非系統apk信息:

  • /data/app/
  • /system/preloadapp/
  • /data/app-private/

 

跟蹤掃描安裝過程

構建PackageParser對象

      調用PackageManagerService#scanPackageLI(xxx) 方法。

 

構建一個PackageParser.Package對象並返回

  調用PackageParser#parsePackage(java.io.File, int) 方法,掃描APK安裝包的AndroidManifest.xml文件和提取證書信息,以此信息構建一個PackageParser.Package對象,並將其返回;


PackageParser.Package對象的信息保存到PackageManagerService

其中包括ContentProvider,Activity,Service,BroadcastReceiver


構建PackageSetting 對象

執行以下代碼:

.PackageManagerService#scanPackageLI(xxx)

--> .PackageManagerService#scanPackageDirtyLI

構建PackageSetting 對象,這個對象中保存的信息最后會通過writeLPr寫入到/data/system/packages.xml文件中去。



以上幾個步驟可以用兩個圖代替:

----------------------------------------------------------------------------------------------------------------------------------------------------------------



----------------------------------------------------------------------------------------------------------------------------------------------------------------





調用mInstaller.createUserData()函數創建數據目錄

調用PackageManagerService#createDataDirsLI方法,installd發送消息,為應用程序創建對應的數據目錄,如果已經存在,也會重新創建一遍。

 

 

調用mInstaller.install()函數完成APK安裝

 1  private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
 2         int[] users = sUserManager.getUserIds();
 3         int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
 4         if (res < 0) {
 5             return res;
 6         }
 7         for (int user : users) {
 8             if (user != 0) {
 9                 res = mInstaller.createUserData(volumeUuid, packageName,
10                         UserHandle.getUid(user, uid), user, seinfo);
11                 if (res < 0) {
12                     return res;
13                 }
14             }
15         }
16         return res;
17     }

 

Installer.install()函數和createUserData()進行完成了命令組裝工作,在組裝完命令之后,將命令傳遞給InstallerConnectio.java處理。

 1 public int install(String uuid, String name, int uid, int gid, String seinfo) {
 2         StringBuilder builder = new StringBuilder("install");
 3         builder.append(' ');
 4         builder.append(escapeNull(uuid));
 5         builder.append(' ');
 6         builder.append(name);
 7         builder.append(' ');
 8         builder.append(uid);
 9         builder.append(' ');
10         builder.append(gid);
11         builder.append(' ');
12         builder.append(seinfo != null ? seinfo : "!");
13         return mInstaller.execute(builder.toString());
14     }

 

通過分析InstallerConnection.java得到以下結論:

1. InstallerConnection連接一個名為Installd的服務

2. Install具體的命令有Installd完成



以下是InstallerConnection連接Installd服務的代碼

 1 private boolean connect() {
 2         if (mSocket != null) {
 3             return true;
 4         }
 5         Slog.i(TAG, "connecting...");
 6         try {
 7             mSocket = new LocalSocket();
 8 
 9             LocalSocketAddress address = new LocalSocketAddress("installd",
10                     LocalSocketAddress.Namespace.RESERVED);
11 
12             mSocket.connect(address);
13 
14             mIn = mSocket.getInputStream();
15             mOut = mSocket.getOutputStream();
16         } catch (IOException ex) {
17             disconnect();
18             return false;
19         }
20         return true;
21     }

 


Installed介紹

Installd是一個native進程,該進程啟動一個socket,然后處理來自Installer的命令。



PackageManagerService通過套接字的方式訪問installd服務進程,在Android啟動腳本init.rc中通過服務配置啟動了installd服務進程。

1     service installd /system/bin/installd
2     class main
3     socket installd stream 600 system system

通過以上配置,init進程就會啟動installd服務進程了。


Installed 進程的入口是main函數,該函數首先初始化一些變量就安裝目錄,然后從環境變量中取得installd套件字的句柄值,然后進入監聽此socket,當客戶端發送過來請求時,接收客戶端的請求,並讀取客戶端發送過來的命令數據,並根據讀取的客戶端命令來執行命令操作。

1 int main(const int argc __unused, char *argv[]) {
 2     char buf[BUFFER_MAX];
 3     struct sockaddr addr;
 4     socklen_t alen;
 5     int lsocket, s;
 6     int selinux_enabled = (is_selinux_enabled() > 0);
 7 
 8     setenv("ANDROID_LOG_TAGS", "*:v", 1);
 9     android::base::InitLogging(argv);
10 
11     ALOGI("installd firing up\n");
12 
13     union selinux_callback cb;
14     cb.func_log = log_callback;
15     selinux_set_callback(SELINUX_CB_LOG, cb);
16 
17     if (initialize_globals() < 0) {
18         ALOGE("Could not initialize globals; exiting.\n");
19         exit(1);
20     }
21 
22     if (initialize_directories() < 0) {
23         ALOGE("Could not create directories; exiting.\n");
24         exit(1);
25     }
26 
27     if (selinux_enabled && selinux_status_open(true) < 0) {
28         ALOGE("Could not open selinux status; exiting.\n");
29         exit(1);
30     }
31 
32     lsocket = android_get_control_socket(SOCKET_PATH);
33     if (lsocket < 0) {
34         ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
35         exit(1);
36     }
37     if (listen(lsocket, 5)) {
38         ALOGE("Listen on socket failed: %s\n", strerror(errno));
39         exit(1);
40     }
41     fcntl(lsocket, F_SETFD, FD_CLOEXEC);
42 
43     for (;;) {
44         alen = sizeof(addr);
45         s = accept(lsocket, &addr, &alen);
46         if (s < 0) {
47             ALOGE("Accept failed: %s\n", strerror(errno));
48             continue;
49         }
50         fcntl(s, F_SETFD, FD_CLOEXEC);
51 
52         ALOGI("new connection\n");
53         for (;;) {
54             unsigned short count;
55             if (readx(s, &count, sizeof(count))) {
56                 ALOGE("failed to read size\n");
57                 break;
58             }
59             if ((count < 1) || (count >= BUFFER_MAX)) {
60                 ALOGE("invalid size %d\n", count);
61                 break;
62             }
63             if (readx(s, buf, count)) {
64                 ALOGE("failed to read command\n");
65                 break;
66             }
67             buf[count] = 0;
68             if (selinux_enabled && selinux_status_updated() > 0) {
69                 selinux_android_seapp_context_reload();
70             }
71             if (execute(s, buf)) break;
72         }
73         ALOGI("closing connection\n");
74         close(s);
75     }
76 
77     return 0;
78 }

 


main函數調用execute函數,執行客戶發送過來的請求命令。

 1 static int execute(int s, char cmd[BUFFER_MAX])
 2 {
 3     char reply[REPLY_MAX];
 4     char *arg[TOKEN_MAX+1];
 5     unsigned i;
 6     unsigned n = 0;
 7     unsigned short count;
 8     int ret = -1;
 9 
10     // ALOGI("execute('%s')\n", cmd);
11 
12         /* default reply is "" */
13     reply[0] = 0;
14 
15         /* n is number of args (not counting arg[0]) */
16     arg[0] = cmd;
17     while (*cmd) {
18         if (isspace(*cmd)) {
19             *cmd++ = 0;
20             n++;
21             arg[n] = cmd;
22             if (n == TOKEN_MAX) {
23                 ALOGE("too many arguments\n");
24                 goto done;
25             }
26         }
27         if (*cmd) {
28           cmd++;
29         }
30     }
31 
32     for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
33         if (!strcmp(cmds[i].name,arg[0])) {
34             if (n != cmds[i].numargs) {
35                 ALOGE("%s requires %d arguments (%d given)\n",
36                      cmds[i].name, cmds[i].numargs, n);
37             } else {
38                 ret = cmds[i].func(arg + 1, reply);
39             }
40             goto done;
41         }
42     }
43     ALOGE("unsupported command '%s'\n", arg[0]);
44 
45 done:
46     if (reply[0]) {
47         n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
48     } else {
49         n = snprintf(cmd, BUFFER_MAX, "%d", ret);
50     }
51     if (n > BUFFER_MAX) n = BUFFER_MAX;
52     count = n;
53 
54     // ALOGI("reply: '%s'\n", cmd);
55     if (writex(s, &count, sizeof(count))) return -1;
56     if (writex(s, cmd, count)) return -1;
57     return 0;
58 }

 

installd服務可執行的命令:

1 struct cmdinfo cmds[] = {
 2     { "ping",                 0, do_ping },
 3     { "install",              5, do_install },
 4     { "dexopt",               9, do_dexopt },
 5     { "markbootcomplete",     1, do_mark_boot_complete },
 6     { "movedex",              3, do_move_dex },
 7     { "rmdex",                2, do_rm_dex },
 8     { "remove",               3, do_remove },
 9     { "rename",               2, do_rename },
10     { "fixuid",               4, do_fixuid },
11     { "freecache",            2, do_free_cache },
12     { "rmcache",              3, do_rm_cache },
13     { "rmcodecache",          3, do_rm_code_cache },
14     { "getsize",              8, do_get_size },
15     { "rmuserdata",           3, do_rm_user_data },
16     { "cpcompleteapp",        6, do_cp_complete_app },
17     { "movefiles",            0, do_movefiles },
18     { "linklib",              4, do_linklib },
19     { "mkuserdata",           5, do_mk_user_data },
20     { "mkuserconfig",         1, do_mk_user_config },
21     { "rmuser",               2, do_rm_user },
22     { "idmap",                3, do_idmap },
23     { "restorecondata",       4, do_restorecon_data },
24     { "createoatdir",         2, do_create_oat_dir },
25     { "rmpackagedir",         1, do_rm_package_dir },
26     { "linkfile",             3, do_link_file }
27 };

 

應用程序安裝

1 static int do_install(char **arg, char reply[REPLY_MAX] __unused)
2 {
3     return install(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), arg[4]); /* uuid, pkgname, uid, gid, seinfo */
4 }

 

do_install 函數直接調用frameworks\base\cmds\installd\commands.c中的install函數來安裝

1 int install(const char *uuid, const char *pkgname, uid_t uid, gid_t gid, const char *seinfo)
 2 {
 3     if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
 4         ALOGE("invalid uid/gid: %d %d\n", uid, gid);
 5         return -1;
 6     }
 7 
 8     std::string _pkgdir(create_data_user_package_path(uuid, 0, pkgname));
 9     const char* pkgdir = _pkgdir.c_str();
10 
11     if (mkdir(pkgdir, 0751) < 0) {
12         ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
13         return -1;
14     }
15     if (chmod(pkgdir, 0751) < 0) {
16         ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
17         unlink(pkgdir);
18         return -1;
19     }
20 
21     if (selinux_android_setfilecon(pkgdir, pkgname, seinfo, uid) < 0) {
22         ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
23         unlink(pkgdir);
24         return -errno;
25     }
26 
27     if (chown(pkgdir, uid, gid) < 0) {
28         ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
29         unlink(pkgdir);
30         return -1;
31     }
32 
33     return 0;
34 }

 



. PackageInstaller 安裝apk

 

PackageInstaller 本身就是一個apk,代碼位置在/packages/apps/PackageInstaller/”,用於顯示安裝應用的界面的一個apk。安裝過程其實是通過PackageManagerService 調用Installer來完成的。



安裝過程中涉及到的類文件:

PackageInstallerActivity.java

在文件管理器里點擊apk后就會調用該類,主要用於顯示要安裝的apk的一些權限信息

InstallAppProgress.java

當看完所有權限后,點安裝后就會調用該類,用於顯示安裝進度,這時候PackageManagerService就在默默的安裝應用。

ApplicationPackageManager.java

這是類是PackageManager的子類,我們使用mContext.getPackageManager得到的其實就是ApplicationPackageManager的對象,它爹PackageManager是個抽象類,對外的方法都定義在里面。

PackageParser.java

解析app,主要解析apk中的AndroidManifest.xml,解析里面的四大組件以及權限信息放入內存里,最后寫到packages.xmlpackage.list/data/system下)中。

AssetManager.java

AndroidManifest.xmlapp中拿出來給PackageParser.java去解析。

DefaultContainerService.java

這個服務用於檢查存儲狀態,得到合適的安裝位置。

Installer.java

PackageManagerService調用它去執行安裝,他會把PackageManagerService傳過來的數據封裝成命令,然后讓底層的Installer去執行。

PackageManagerService.java

管理app的安裝、移動、卸載、查詢等。



實現原理:

1. 點擊文件管理器中的apk時,文件管理器會啟動PackageInstallerPackageInstallerActivity界面,並且將apk的信息通過intent傳遞給PackageInstallerActivity



2. PackageInstaller啟動過后會檢查是否開啟未知來源,未開啟就需要先進入設置設置后,方可繼續安裝;

1      @Override
2     protected void onCreate(Bundle icicle) {
3         ......
4         mPm = getPackageManager();
5         boolean requestFromUnknownSource =  isInstallRequestFromUnknownSource(intent);
6         ......
7         initiateInstall();
8     }

 

之后會依次調用initiateInstall()->startInstallConfirm();

initiateInstall方法負責檢查是否已經安裝過,是否是系統應用等;

startInstallConfirm負責初始化界面,顯示權限信息;

當點擊安裝按鈕時,啟動安裝,切換界面到InstallAppProgress

 1 private void startInstall() {
 2         // Start subactivity to actually install the application
 3         Intent newIntent = new Intent();
 4         newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
 5                 mPkgInfo.applicationInfo);
 6         newIntent.setData(mPackageURI);
 7         newIntent.setClass(this, InstallAppProgress.class);
 8         ........
 9         startActivity(newIntent);
10         finish();
11     }

 


3. InstallAppProgress中會調用initView去初始化界面並調用ApplicationPackageManagerinstallPackageWithVerificationAndEncryption方法來安裝.

1 @Override
2     public void onCreate(Bundle icicle) {
3         ......
4         initView();
5     }

 

 1 public void initView() {
 2         ......
 3         if ("package".equals(mPackageURI.getScheme())) {
 4             try {
 5                 pm.installExistingPackage(mAppInfo.packageName);
 6                 observer.packageInstalled(mAppInfo.packageName,
 7                         PackageManager.INSTALL_SUCCEEDED);
 8             } catch (PackageManager.NameNotFoundException e) {
 9                 observer.packageInstalled(mAppInfo.packageName,
10                         PackageManager.INSTALL_FAILED_INVALID_APK);
11             }
12         } else {
13             pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
14                     installerPackageName, verificationParams, null);
15         }
16     }

 


4. ApplicationPackageManagerinstallPackageWithVerificationAndEncryption里也是調用PMSinstallPackage 方法.

1 @Override
2     public void installPackageWithVerificationAndEncryption(Uri packageURI,
3             IPackageInstallObserver observer, int flags, String installerPackageName,
4             VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { 
5         installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
6                 installerPackageName, verificationParams, encryptionParams);
7     }

 

1 private void installCommon(Uri packageURI,
 2             PackageInstallObserver observer, int flags, String installerPackageName,
 3             VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {  
 4         if (!"file".equals(packageURI.getScheme())) {
 5             throw new UnsupportedOperationException("Only file:// URIs are supported");
 6         }
 7         if (encryptionParams != null) {
 8             throw new UnsupportedOperationException("ContainerEncryptionParams not supported");
 9         }
10 
11         final String originPath = packageURI.getPath();
12         try {
13             mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
14                     verificationParams, null);
15         } catch (RemoteException ignored) {
16         }
17     }

 


5. .installPackage() 方法里,首先會獲取設置中的用戶安裝位置,並且會把InstallParams對象和安裝位置flag封裝到Message里,然后發出一個消息。

1     @Override
2     public void installPackage(String originPath, IPackageInstallObserver2 observer,
3             int installFlags, String installerPackageName, VerificationParams verificationParams,
4             String packageAbiOverride) {
5         installPackageAsUser(originPath, observer, installFlags, installerPackageName,
6                 verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
7     }


1 @Override
 2 public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
 3         int installFlags, String installerPackageName, VerificationParams verificationParams,
 4         String packageAbiOverride, int userId) {
 5 
 6     ......
 7 
 8     final Message msg = mHandler.obtainMessage(INIT_COPY);
 9     msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
10             null, verificationParams, user, packageAbiOverride, null);
11     mHandler.sendMessage(msg);  
12 }

 


6. PackageManagerService.PackageHandler#doHandleMessage 處理INIT_COPYMCS_BOUN消息。

如果msg.what INIT_COPY

連接DefaultContainerService服務把我們要安裝的信息放到HandlerParams的一個ListmPendingInstalls然后發送MCS_BOUND消息。

如果msg.what MCS_BOUN

則通過 “HandlerParams params = mPendingInstalls.get(0)” 讀取出我們要安裝的包信息,然后清除該包信息,如果還有其他包就繼續發MCS_BOUND這個消息,循環,直到都安裝完了。

然后執行PackageManagerService.HandlerParams#startCopy



7. 執行HandlerParams#startCopy

1 final boolean startCopy() {
 2             boolean res;
 3             try {
 4                 if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
 5 
 6                 if (++mRetries > MAX_RETRIES) {
 7                     Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
 8                     mHandler.sendEmptyMessage(MCS_GIVE_UP);
 9                     handleServiceError();
10                     return false;
11                 } else {
12                     handleStartCopy();
13                     Slog.i(TAG, "Apk copy done");
14                     res = true;
15                 }
16             } catch (RemoteException e) {
17                 if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
18                 mHandler.sendEmptyMessage(MCS_RECONNECT);
19                 res = false;
20             }
21             handleReturnCode();
22             return res;
23         }

 

startCopy有兩個重要的方法:handleStartCopy handleReturnCode


調用handleStartCopy

handleStartCopy方法中會檢查應用是否能安裝;

不合法則返回FAILEDCODE,接着會調用DefaultContainerServicegetMinimalPackageInfo方法,該方法用於獲取存儲狀態,返回合適的安裝位置;經過一系列的判斷,如果返回碼是INSTALL_SUCCEEDED,那接下來就會調用InstallParamscopyApk如果安裝到內置,調用的就是FileInstallArgscopyApk方法;如安裝到外置就調用AsecInstallArgscopyApk方法;AsecInstallArgsFileInstallArgs都是InstallParams的子類。

copyApk方法中會依次調用FileInstallArgs createCopyFile->PackageManagerServicecreateTempPackageFile方法去創建臨時文件。


handleStartCopy有兩個作用:

1. final InstallArgs args = createInstallArgs(this);

2. 返回ret標識是否安裝成功的



調用handleReturnCode

1 @Override
2 void handleReturnCode() {
3     if (mArgs != null) {
4         processPendingInstall(mArgs, mRet);
5     }
6 }

 

也就是調用installPackageLI(args, true, res)

1        if (replace) {
2             replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
3                     installerPackageName, volumeUuid, res);
4         } else {
5             installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
6                     args.user, installerPackageName, volumeUuid, res);
7         }

 

如果是第一次安裝,則執行installNewPackageLI方法。

之后的代碼和PackageManagerService安裝系統軟件一樣了。

PackageManagerService#installNewPackageLI

-->PackageManagerService#scanPackageLI(android.content.pm.PackageParser.Package, int, int, long, android.os.UserHandle)

-->PackageManagerService#scanPackageDirtyLI

-->PackageManagerService#createDataDirsLI

 

 1 private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
 2         int[] users = sUserManager.getUserIds();
 3         int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
 4         if (res < 0) {
 5             return res;
 6         }
 7         for (int user : users) {
 8             if (user != 0) {
 9                 res = mInstaller.createUserData(volumeUuid, packageName,
10                         UserHandle.getUid(user, uid), user, seinfo);
11                 if (res < 0) {
12                     return res;
13                 }
14             }
15         }
16         return res;
17     }

 

調用InstallercreateUserDatainstall方法,連接底層的Installed服務來安裝。

備注: 關於adb工具安裝apk的文章, 可以參考 http://blog.csdn.net/gaugamela/article/details/52691084





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM