王尘宇王尘宇

研究百度干SEO做推广变成一个被互联网搞的人

说一说APK的安装流程

那时他们一同上看呵呵,控制系统是是不是把应用领域加装到你的智能手机上的。

出口处

当他们点选两个 APK 展开加装时,会弹出下列介面

这是 Android Framework 提供更多的应用领域软件加装程序,网页为:PackageInstallerActivity

packagecom.android.packageinstaller;publicclassPackageInstallerActivityextendsActivity{publicvoidonClick(View v){if(v == mOk) {if(mOk.isEnabled()) {//...省略一些细节startInstall();// 开始加装}        }elseif(v == mCancel) {// Cancel and finish}    }privatevoidstartInstall(){// Start subactivity to actually install the applicationIntent newIntent =newIntent();

        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,

                mPkgInfo.applicationInfo);

        newIntent.setData(mPackageURI);

        newIntent.setClass(this, InstallInstalling.class);        ...// newIntent.putExtra 其它参数startActivity(newIntent);

        finish();

    }

}

在这个网页点选加装时,会把加装包的信息通过 Intent 传递到 InstallInstalling 这个 Activity. InstallInstalling 的作用主要是向 PMS 发送包信息以及处理回调。

InstallInstalling.onCreate

进来新网页,当然是先从 onCreate 开始了

protectedvoidonCreate(@Nullable Bundle savedInstanceState){

    ApplicationInfo appInfo = getIntent()

                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);

    mPackageURI = getIntent().getData();

    ...// 根据 mPackageURI 创建两个对应的 FilefinalFile sourceFile =newFile(mPackageURI.getPath());// 显示应用领域信息 icon, 应用领域名或包名PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,          sourceFile), R.id.app_snippet);// 创建、组装 SessionParams,它用来携带会话的参数PackageInstaller.SessionParams params =newPackageInstaller.SessionParams(

                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);

    params.installFlags = PackageManager.INSTALL_FULL_APP;

    ...

    params.installerPackageName =

            getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);

    File file =newFile(mPackageURI.getPath());// 对 APK 展开轻量级的解析,并将解析的结果赋值给 SessionParams 相关字段PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file,0);

    params.setAppPackageName(pkg.packageName);

    params.setInstallLocation(pkg.installLocation);

    params.setSize(

            PackageHelper.calculateInstalledSize(pkg,false, params.abiOverride));// 向 InstallEventReceiver 注册两个观察者返回两个新的 mInstallId// InstallEventReceiver 是两个 BroadcastReceiver,可以通过 EventResultPersister 接收到所有的加装事件// 这里事件会回调给 this::launchFinishBasedOnResultmInstallId = InstallEventReceiver                .addObserver(this, EventResultPersister.GENERATE_NEW_ID,this::launchFinishBasedOnResult);try{// PackageInstaller 的 createSession// 方法内部会通过 IPackageInstaller 与 PackageInstallerService展开进程间通信,// 最终调用的是 PackageInstallerService 的 createSession 方法来创建并返回 mSessionIdmSessionId = getPackageManager().getPackageInstaller().createSession(params);    }catch(IOException e) {        launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,null);

    }

   

  

}

InstallInstalling.onResume

接下来是 onResume, 通过 InstallingAsyncTask 做一些异步工作

protectedvoidonResume(){super.onResume();// This is the first onResume in a single life of the activityif(mInstallingTask ==null) {

        PackageInstaller installer = getPackageManager().getPackageInstaller();

        PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);if(sessionInfo !=null&& !sessionInfo.isActive()) {            mInstallingTask =newInstallingAsyncTask();

            mInstallingTask.execute();

        }else{// we will receive a broadcast when the install is finishedmCancelButton.setEnabled(false);            setFinishOnTouchOutside(false);

        }

    }

}

InstallingAsyncTask

privatefinalclassInstallingAsyncTaskextendsAsyncTask<Void,Void,PackageInstaller.Session>{@OverrideprotectedPackageInstaller.SessiondoInBackground(Void... params){

            PackageInstaller.Session session;

            session = getPackageManager().getPackageInstaller().openSession(mSessionId);

            session.setStagingProgress(0);            File file =newFile(mPackageURI.getPath());            OutputStream out = session.openWrite("PackageInstaller",0, sizeBytes)            InputStream in =newFileInputStream(file)longsizeBytes = file.length();byte[] buffer =newbyte[1024*1024];while(true) {intnumRead = in.read(buffer);if(numRead == -1) {                    session.fsync(out);break;                }// 将 APK 文件通过 IO 流的形式写入到 PackageInstaller.Session 中out.write(buffer,0, numRead);if(sizeBytes >0) {floatfraction = ((float) numRead / (float) sizeBytes);

                    session.addProgress(fraction);

                }

             }returnsession;         }@OverrideprotectedvoidonPostExecute(PackageInstaller.Session session){             Intent broadcastIntent =newIntent(BROADCAST_ACTION);

            broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);

            broadcastIntent.setPackage(

                    getPackageManager().getPermissionControllerPackageName());

            broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

            PendingIntent pendingIntent = PendingIntent.getBroadcast(

                    InstallInstalling.this,

                    mInstallId,

                    broadcastIntent,

                    PendingIntent.FLAG_UPDATE_CURRENT);// 调用 PackageInstaller.Session 的 commit 方法,展开加装session.commit(pendingIntent.getIntentSender()); 

        }

    }

上看下 PackageInstaller.Session 里的实现

publicstaticclassSessionimplementsCloseable{privateIPackageInstallerSession mSession;publicvoidcommit(@NonNull IntentSender statusReceiver){try{            mSession.commit(statusReceiver,false);        }catch(RemoteException e) {throwe.rethrowFromSystemServer();

        }

    }

}

mSession 的类型为 IPackageInstallerSession,这说明要通过 IPackageInstallerSession 来展开进程间的通信,最终会调用PackageInstallerSession 的 commit 方法,剧透呵呵在这个类执行完后,就会进入鼎鼎大名的 PMS 去真正的执行加装了 :

publicclassPackageInstallerSessionextendsIPackageInstallerSession.Stub{publicvoidcommit(@NonNull IntentSender statusReceiver,booleanforTransfer){// 将包的信息封装为 PackageInstallObserverAdapterfinalPackageInstallObserverAdapter adapter =newPackageInstallObserverAdapter(

          mContext, statusReceiver, sessionId,

          isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId);

        mRemoteObserver = adapter.getBinder();// 通过 Handler 处理消息事件mHandler.obtainMessage(MSG_COMMIT).sendToTarget();    }privatefinalHandler.Callback mHandlerCallback =newHandler.Callback() {@OverridepublicbooleanhandleMessage(Message msg){switch(msg.what) {caseMSG_COMMIT:                    commitLocked();break;

            }

        }

    };privatefinalPackageManagerService mPm;privatevoidcommitLocked()throwsPackageManagerException{

        mPm.installStage(mPackageName, stageDir, ...);

    }

  

}

mPm 就是控制系统服务 PackageManagerService。installStage 方法就是正式开始 apk 的加装过程。这个过程包括两大步:拷贝加装包、装载代码

拷贝加装包

继续看 installStage 的代码

// PackageManagerService.javavoidinstallStage(String packageName, File stagedDir,...){finalMessage msg = mHandler.obtainMessage(INIT_COPY);// 把之前传入的 sessionParams 加装信息,及其它信息封装成 InstallParamsfinalInstallParams params =newInstallParams(origin,null, observer,

        sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,

        verificationInfo, user, sessionParams.abiOverride,

        sessionParams.grantedRuntimePermissions, signingDetails, installReason);

    mHandler.sendMessage(msg);

}

发送的消息 INIT_COPY 从名字上就知道是去初始化复制

classPackageHandlerextendsHandler{voiddoHandleMessage(Message msg){switch(msg.what) {caseINIT_COPY: {                HandlerParamsparams= (HandlerParams) msg.obj;// 调用 connectToService 方法连接加装 apk 的 Service 服务。if(!connectToService()) {return;                }else{// Once we bind to the service, the first// pending request will be processed.mPendingInstalls.add(idx,params);

                }

            }

        }

    }privatebooleanconnectToService(){// 通过隐式 Intent 绑定 Service,实际绑定的 Service 是 DefaultContainerServiceIntent service =newIntent().setComponent(DEFAULT_CONTAINER_COMPONENT);if(mContext.bindServiceAsUser(service, mDefContainerConn,

                 Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {

             mBound =true;returntrue;         }returnfalse;

   }

}

当绑定 Service 成功之后,会在 mDefContainerConn 的 onServiceConnection 方法中发送两个绑定操作的 Message,如下所示:

classDefaultContainerConnectionimplementsServiceConnection{publicvoidonServiceConnected(ComponentName name, IBinder service){

        final IMediaContainerService imcs = IMediaContainerService.Stub

                .asInterface(Binder.allowBlocking(service));

        mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));

    }

}// MCS_BOUND 还是在前面的 PackageHandler 处理,直接截取相关代码{    HandlerParamsparams= mPendingInstalls.get(0);if(params.startCopy()) {if(mPendingInstalls.size() >0) {            mPendingInstalls.remove(0);

        }

    }

}

mPendingInstalls 是两个等待队列,里面保存所有需要加装的 apk 解析出来的 HandlerParams 参数(前面在 INIT_COPY 处理时 add),从 mPendingInstalls 中取出第两个需要加装的 HandlerParams 对象,并调用其 startCopy 方法,在 startCopy 方法中会继续调用两个抽象方法 handleStartCopy 处理加装请求。通过之前的分析,他们知道 HandlerParams 实际类型是 InstallParams 类型,因此最终调用的是 InstallParams 的 handlerStartCopy 方法,这是整个加装包拷贝的核心。

classInstallParamsextendsHandlerParams{publicvoidhandleStartCopy()throwsRemoteException{if(origin.staged) {// 设置加装标志位,决定是加装在智能手机内部存储空间还是 sdcard 中if(origin.file !=null) {

                installFlags |= PackageManager.INSTALL_INTERNAL;

                installFlags &= ~PackageManager.INSTALL_EXTERNAL;

            } 

        }// 判断加装位置finalbooleanonSd = (installFlags & PackageManager.INSTALL_EXTERNAL) !=0;finalbooleanonInt = (installFlags & PackageManager.INSTALL_INTERNAL) !=0;finalbooleanephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) !=0;finalInstallArgs args = createInstallArgs(this);// ...ret = args.copyApk(mContainerService,true);    }privateInstallArgscreateInstallArgs(InstallParams params){if(params.move !=null) {returnnewMoveInstallArgs(params);        }else{returnnewFileInstallArgs(params);

        }

    }

}

正常的业务流程下,createInstallArgs 返回的是 FileInstallArgs 对象

FileInstallArgs 的 copyApk 方法

intcopyApk(IMediaContainerService imcs,booleantemp)throwsRemoteException{returndoCopyApk(imcs, temp);}privateintdoCopyApk(IMediaContainerService imcs,booleantemp)throwsRemoteException{// 创建存储加装包的目标路径,实际上是 /data/app/ 应用领域包名目录finalFile tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);finalIParcelFileDescriptorFactory target =newIParcelFileDescriptorFactory.Stub() {@OverridepublicParcelFileDescriptoropen(String name,intmode)throwsRemoteException{finalFile file =newFile(codeFile, name);finalFileDescriptor fd = Os.open(file.getAbsolutePath(),                    O_RDWR | O_CREAT,0644);            Os.chmod(file.getAbsolutePath(),0644);returnnewParcelFileDescriptor(fd);

        }

    };// 调用服务的 copyPackage 方法将加装包 apk 拷贝到目标路径中;ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);// 将 apk 中的动态库 .so 文件也拷贝到目标路径中。ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,

                        abiOverride);

}

这里的 IMediaContainerService imcs 就是之前连接上的 DefaultContainerService

DefaultContainerService

copyPackage 方法本质上就是执行 IO 流操作,具体如下:

// new IMediaContainerService.Stub()publicintcopyPackage(String packagePath, IParcelFileDescriptorFactory target){    PackageLite pkg =null;    final File packageFile =newFile(packagePath);    pkg = PackageParser.parsePackageLite(packageFile,0);returncopyPackageInner(pkg, target);}// DefaultContainerServiceprivateintcopyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target){    copyFile(pkg.baseCodePath, target,"base.apk");if(!ArrayUtils.isEmpty(pkg.splitNames)) {for(inti =0; i < pkg.splitNames.length; i++) {            copyFile(pkg.splitCodePaths[i], target,"split_"+ pkg.splitNames[i] +".apk");

        }

    }returnPackageManager.INSTALL_SUCCEEDED;}privatevoidcopyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName){    InputStreamin=null;    OutputStreamout=null;try{in=newFileInputStream(sourcePath);out=newParcelFileDescriptor.AutoCloseOutputStream(

                target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));

        FileUtils.copy(in,out);    }finally{        IoUtils.closeQuietly(out);        IoUtils.closeQuietly(in);

    }

}

最终加装包在 data/app 目录下以 base.apk 的方式保存,至此加装包拷贝工作就已经完成。

装载代码

加装包拷贝完成,就要开始真正加装了。代码回到上述的 HandlerParams 中的 startCopy 方法:

privateabstractclassHandlerParams{finalbooleanstartCopy(){

        ...

        handleStartCopy();

        handleReturnCode();

    }

}classInstallParamsextendsHandlerParams{@OverridevoidhandleReturnCode(){// If mArgs is null, then MCS couldnt be reached. When it// reconnects, it will try again to install. At that point, this// will succeed.if(mArgs !=null) {

            processPendingInstall(mArgs, mRet);

        }

    }privatevoidprocessPendingInstall(finalInstallArgs args,finalintcurrentStatus){         mHandler.post(newRunnable() {publicvoidrun(){                 PackageInstalledInfo res =newPackageInstalledInfo();if(res.returnCode == PackageManager.INSTALL_SUCCEEDED) {// 预加装操作,主要是检查加装包的状态,确保加装环境正常,如果加装环境有问题会清理拷贝文件args.doPreInstall(res.returnCode);synchronized(mInstallLock) {// 加装阶段installPackageTracedLI(args, res);

                    }

                    args.doPostInstall(res.returnCode, res.uid);

                }

                ...

             }

         }

    }

}

installPackageLI

installPackageTracedLI 方法中添加跟踪 Trace,然后调用 installPackageLI 方法展开加装。这个方法有 600 行,取部分关键代码:

privatevoidinstallPackageLI(InstallArgs args, PackageInstalledInfo res){

    ...

    PackageParser pp =newPackageParser();finalPackageParser.Package pkg;// 1. parsePackagepkg = pp.parsePackage(tmpPackageFile, parseFlags);// 2. 校验加装包签名finalKeySetManagerService ksms = mSettings.mKeySetManagerService;if(ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {if(!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {            res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"Package "+ pkg.packageName +" upgrade keys do not match the "+"previously installed version");return;

        }

    }// 3. 设置相关权限,生成、移植权限intN = pkg.permissions.size();for(inti = N-1; i >=0; i--) {finalPackageParser.Permission perm = pkg.permissions.get(i);

        ...

    }// 4. 生成加装包Abi(Application binary interface,应用领域二进制接口)try{

        String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?

            args.abiOverride : pkg.cpuAbiOverride);finalbooleanextractNativeLibs = !pkg.isLibrary();

        derivePackageAbi(pkg, abiOverride, extractNativeLibs);

    }catch(PackageManagerException pme) {        res.setError(INSTALL_FAILED_INTERNAL_ERROR,"Error deriving application ABI");return;    }// 5. 冻结 APK,执行替换加装 或 新加装,try(PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,"installPackageLI")) {if(replace) {

            replacePackageLIF(pkg, parseFlags, scanFlags, args.user,

                    installerPackageName, res, args.installReason);

        }else{privatevoidinstallNewPackageLIF((pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,

                    args.user, installerPackageName, volumeUuid, res, args.installReason);

        }

    }// 5. 优化dex文件(实际为 dex2oat 操作,用来将 apk 中的 dex 文件转换为 oat 文件)if(performDexopt) {           mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,null/* instructionSets */,

                  getOrCreateCompilerPackageStats(pkg),

                  mDexManager.getPackageUseInfoOrDefault(pkg.packageName),

                  dexoptOptions); 

    }

    ...

}

最后他们上看呵呵 installNewPackageLIF

privatevoidinstallNewPackageLIF(PackageParser.Package pkg,final@ParseFlagsintparseFlags,final@ScanFlagsintscanFlags,...){// 继续扫描解析 apk 加装包文件,保存 apk 相关信息到 PMS 中,并创建 apk 的 data 目录,具体路径为 /data/data/应用领域包名PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,        System.currentTimeMillis(), user);// 更新控制系统设置中的应用领域信息,比如应用领域的权限信息updateSettingsLI(newPackage, installerPackageName,null, res, user, installReason);if(res.returnCode == PackageManager.INSTALL_SUCCEEDED) {// 加装然后准备 APP 数据prepareAppDataAfterInstallLIF(newPackage);     }else{// 如果加装失败,则将加装包以及各种缓存文件删除deletePackageLIF(pkgName, UserHandle.ALL,false,null,                 PackageManager.DELETE_KEEP_DATA, res.removedInfo,true,null);

     }

}

prepareAppDataAfterInstallLIF 还会有一系列的调用

prepareAppDataAfterInstallLIF()

-> prepareAppDataLIF()

-> prepareAppDataLeafLIF()

-> mInstaller.createAppData(...)finalInstaller mInstaller;privatevoid prepareAppDataLeafLIF(...) {// 最终调用 控制系统服务 Installer 加装ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,

                    appId, seInfo, app.targetSdkVersion);     

}publicclassInstallerextendsSystemService{

   ...

}

至此整个 apk 的加装过程结束,实际上加装成功之后,还会发送两个 App 加装成功的广播 ACTION_PACKAGE_ADDED。智能手机桌面应用领域注册了这个广播,当接收到应用领域加装成功之后,就将 apk 的启动 icon 显示在桌面上。

总结

在智能手机上仅仅是点呵呵加装按钮而已,背后却有着这么繁琐的业务流程,相信通过那时的学习大家应该能对控制系统的应用领域加装业务流程有两个完整的认知。回顾呵呵加装的业务流程如下:

  • 点选 APK 加装,会启动 PackageInstallerActivity,再进入 InstallInstalling 这两个 Activity 显示应用领域信息
  • 点选网页上的加装,将 APK 信息存入 PackageInstaller.Session 传到 PMS
  • PMS会做两件事,拷贝加装包和装载代码
  • 在拷贝加装包过程中会开启 Service 来 copyAPK 、检查apk加装路径,包的状态
  • 拷贝完成以 base.apk 形式存在/data/app包名下
  • 装载代码过程中,会继续解析 APK,把清单文件内容存放于 PMS
  • 对 apk 展开签名校验
  • 加装成功后,更新应用领域设置权限,发送广播通知桌面显示APP图标,加装失败则删除加装包和各种缓存文件
  • 执行 dex2oat 优化

「本文源码基于 Android 28」

相关文章

评论列表

发表评论:
验证码

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。