Android8.0 PackageManagerService相关 -- APK安装和install 的变更和源码浅析

 

 

1.1           APK安装和install

 

installd服务是用来执行程序包的安装与卸载的

 

1.1.1          参考资料

//各版本支持的命令

 

http://blog.csdn.net/lusing/article/details/52166533

 

 

 

//installer overview

http://blog.csdn.net/lusing/article/details/52223903

 

//Android中installd进程存在的意义

http://blog.csdn.net/liqingxu2005/article/details/43447941

 

 

//install源码分析4.4

 

http://blog.csdn.net/yangwen123/article/details/11104397

 

 

////install 源码分析7.0

http://blog.csdn.net/gaugamela/article/details/52769139

 

//8.0 install源码有很大变化,概述见本文,详细的需要自行分析代码

 

 

 

//apk安装过程PMS的源码分析

http://blog.csdn.net/wocao1226/article/details/51161990

 

//apk安装过程overview

http://blog.csdn.net/u012481172/article/details/49981673

 

 

//4种安装方法的代码分析,代码过时,可参考分析问题的方法

http://blog.csdn.net/wh_19910525/article/details/7909686

 

1.1.2          初始过程的变化

 

在android 7.0,installd的启动设置不再放在init.rc里面了,放在frameworks\native\cmds\installd\installd.rc,并frameworks\native\cmds\installd\Android.mk 里设置这个rc文件进行编译,

LOCAL_STATIC_LIBRARIES := libdiskusage

LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk

LOCAL_INIT_RC :=installd.rc

LOCAL_CLANG := true

include $(BUILD_EXECUTABLE)

 

在android8.0,又发生了一些变化,install.rc里创建了很多目录,并将rc文件放在frameworks/native/cmds/installd/Android.bp进行编译,

 cc_binary {

    name:”installd”,

    defaults:[“installd_defaults”],

    srcs:[“installd.cpp”],

 

    static_libs:[“libdiskusage”],

 

    init_rc: [“installd.rc“],

}

 

 

 

1.1.3         IPC变化

 

在android7.0及之前,IPC通过socket完成;

在android8.0,IPC通过binder实现。

 

在下面文件可看到变化

frameworks\base\core\java\com\android\internal\os\Installer.java

frameworks\native\cmds\installd\

 《Android8.0 PackageManagerService相关 -- APK安装和install 的变更和源码浅析》

 

 

1.1.4         Apk安装

Apk安装有几种常见的场景:系统内置apk的安装,adb install安装,应用市场下载后点击安装或本地文件点击安装,静默安装等。

 

 

//开机PKMS扫描、解析  4.4

http://blog.csdn.net/luoshengyang/article/details/6766010

 

 

 

//安装源码分析,开机安装的java层 6.0

http://blog.csdn.net/qq_23547831/article/details/51203482

 

在7.0开始,安装apk的函数createDataDirsLI()就没有了,使用了新的逻辑,可从PackageManagerService.java的scanPackageDirtyLI继续向下分析代码。

 

顺便提下,6.0的scanPackageDirtyLI向下调用到installd的接口,操作文件时使用了selinux_android_setfilecon,这里和selinux的文件访问策略有关,有兴趣的可以分析下。

 

 

 

//安装源码分析,点击安装的java层 6.0

http://blog.csdn.net/qq_23547831/article/details/51210682

 

在应用层,8.0的代码发生了一些变化,下面这个控制安装界面的文件没有了,

 

/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java

 

,用InstallInstalling取代了它,

 

 

主要的文件差异如下,针对不同设备进行了相应处理,处理逻辑的变化就暂不分析了,

《Android8.0 PackageManagerService相关 -- APK安装和install 的变更和源码浅析》

 

 

1.1.5          8.0 apk安装

安装应用的时候会发一个intent,

 

PMS解析intent,调用PackageInstallerActivity来启动安装界面,

 

会调用到PackageInstallerActivityonCreate—startInstallConfirm,在之后的onClick,执行startInstall,启动android O新增加的安装activity InstallInstalling

     private void startInstall() {

        // Startsubactivity to actually install the application

        Intent newIntent =new Intent();

       newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,

               mPkgInfo.applicationInfo);

       newIntent.setData(mPackageURI);

       newIntent.setClass(this, InstallInstalling.class);

        String installerPackageName =getIntent().getStringExtra(

               Intent.EXTRA_INSTALLER_PACKAGE_NAME);

        if(mOriginatingURI != null) {

           newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);

        }

        if (mReferrerURI!= null) {

           newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);

        }

        if(mOriginatingUid != VerificationParams.NO_UID) {

           newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);

        }

        if (installerPackageName != null) {

           newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,

                   installerPackageName);

        }

        if(getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {

            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT,true);

           newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);

        }

        if(localLOGV)Log.i(TAG, “downloaded app uri=”+mPackageURI);

       startActivity(newIntent);

        finish();

    }

 

在InstallInstalling的onCreate方法里,有两个主要步骤,一个是PackageParser.parsePackageLite,他解析apk里面的部分信息出来;另一个是getPackageInstaller().createSession,它负责创建一个会话,用于后续的安装,会话是通过binder调用在PackageInstallerServices里面创建的。

                     PackageParser.PackageLitepkg = PackageParser.parsePackageLite(file, 0);

 

                   mSessionId =getPackageManager().getPackageInstaller().createSession(params);

 

 

在InstallInstalling.java的onResume启动一个异步task InstallingAsyncTask,

     protected void onResume() {

        super.onResume();

 

        // This is thefirst onResume in a single life of the activity

        if(mInstallingTask == null) {

           PackageInstaller installer = getPackageManager().getPackageInstaller();

           PackageInstaller.SessionInfo sessionInfo =installer.getSessionInfo(mSessionId);

 

            if(sessionInfo != null && !sessionInfo.isActive()) {

               mInstallingTask = new InstallingAsyncTask();

               mInstallingTask.execute();

            } else {

                // we willreceive a broadcast when the install is finished

               mCancelButton.setEnabled(false);

               setFinishOnTouchOutside(false);

            }

        }

    }

 

InstallingAsyncTask主要是操作session,session也通过binder方式来操作,主要完成的工作是通过之前建立的session,创建文件夹,将文件拷贝到安装目录下面,

         protected PackageInstaller.SessiondoInBackground(Void… params) {

           PackageInstaller.Session session;

            try {

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

            } catch (IOException e) {

                returnnull;

            }

 

           session.setStagingProgress(0);

 

            try {

                File file= new File(mPackageURI.getPath());

 

                try(InputStream in = new FileInputStream(file)) {

                    longsizeBytes = file.length();

                    try(OutputStream out = session

                           .openWrite(“PackageInstaller”, 0, sizeBytes)) {

                       byte[] buffer = new byte[4096];

                       while (true) {

                           int numRead = in.read(buffer);

 

                            if (numRead == -1) {

                               session.fsync(out);

                               break;

                           }

 

                           if (isCancelled()) {

                               session.close();

                                break;

                           }

 

                           out.write(buffer, 0, numRead);

 

 

 

 

     public IPackageInstallerSessionopenSession(int sessionId) {

        try {

            returnopenSessionInternal(sessionId);

        } catch(IOException e) {

            throwExceptionUtils.wrap(e);

        }

    }

 

 

通过prepareStageDir创建目录,

     static void prepareStageDir(File stageDir)throws IOException {

        if(stageDir.exists()) {

            throw newIOException(“Session dir already exists: ” + stageDir);

        }

 

        try {

           Os.mkdir(stageDir.getAbsolutePath(), 0755);

           Os.chmod(stageDir.getAbsolutePath(), 0755);

        } catch(ErrnoException e) {

            // Thispurposefully throws if directory already exists

            throw newIOException(“Failed to prepare session dir: ” + stageDir, e);

        }

 

        if(!SELinux.restorecon(stageDir)) {

            throw newIOException(“Failed to restorecon session dir: ” + stageDir);

        }

    }

 

 

目前为止apk的安装都只是在PackageInstaller apk和framework的PackageInstaller框架之间交换,还没有涉及到PMS,这一点是怎么做到的呢,如果没有PMS参与,就没有参考文档里面我们了解的那些对manifest的解析、权限处理、文件拷贝和dex优化等相关流程,这里就产生了割裂感。我看到的很多文档对这点都没讲透,所以这里多写一点。

PMS在上层的接口类是APM,通过PM类在PackageInstaller和PackageInstallerSession的引用就能找到相关的关联关系,然后我们就会发现一个很重要的方法,就是session的commit方法,详细说来,

 

在7.0系统,InstallAppProgress的doPackageStage直接调用了session.commit(前面还有session的创建和open等),

     private void doPackageStage(PackageManagerpm, PackageInstaller.SessionParams params) {

        finalPackageInstaller packageInstaller = pm.getPackageInstaller();

       PackageInstaller.Session session = null;

        try {

            final StringpackageLocation = mPackageURI.getPath();

            final File file = newFile(packageLocation);

            final intsessionId = packageInstaller.createSession(params);

            final byte[]buffer = new byte[65536];

 

            session =packageInstaller.openSession(sessionId);

 

            finalInputStream in = new FileInputStream(file);

            final longsizeBytes = file.length();

            finalOutputStream out = session.openWrite(“PackageInstaller”, 0,sizeBytes);

            try {

                int c;

                while ((c = in.read(buffer)) != -1) {

                   out.write(buffer, 0, c);

                    if(sizeBytes > 0) {

                       final float fraction = ((float) c / (float) sizeBytes);

                       session.addProgress(fraction);

                    }

                }

               session.fsync(out);

            } finally {

               IoUtils.closeQuietly(in);

               IoUtils.closeQuietly(out);

            }

 

            // Create aPendingIntent and use it to generate the IntentSender

            IntentbroadcastIntent = new Intent(BROADCAST_ACTION);

            PendingIntentpendingIntent = PendingIntent.getBroadcast(

                   InstallAppProgress.this /*context*/,

                   sessionId,

                    broadcastIntent,

                   PendingIntent.FLAG_UPDATE_CURRENT);

            session.commit(pendingIntent.getIntentSender());

        } catch(IOException e) {

           onPackageInstalled(PackageInstaller.STATUS_FAILURE);

        } finally {

           IoUtils.closeQuietly(session);

        }

    }

 

然后,通过binder,在PackageInstallerSession.java里,调用commit,给handler发一个消息MSG_COMMIT,

         final PackageInstallObserverAdapteradapter = new PackageInstallObserverAdapter(mContext,

               statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);

       mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();

 

然后Handler.CallbackmHandlerCallback再调用commitLocked(),这里就开始调用到PMS的安装接口了,然后就是PMS的流程,相关参考文档都有很详尽的说明,我就不赘述了。

     private void commitLocked() throwsPackageManagerException {

        // Unpack nativelibraries

       extractNativeLibraries(mResolvedStageDir, params.abiOverride);

。。。

 

        mRelinquished =true;

        mPm.installStage(mPackageName,stageDir, stageCid, localObserver, params,

               installerPackageName, installerUid, user, mCertificates);

    }

 

 

在8.0上呢,没有了InstallAppProgress文件,改用InstallInstalling,但思路差不多,在异步taskInstallingAsyncTask的方法中调用commit,之后的逻辑差不多。

 

         protected voidonPostExecute(PackageInstaller.Session session) {

            if (session !=null) {

                IntentbroadcastIntent = new Intent(BROADCAST_ACTION);

                broadcastIntent.setPackage(

                       getPackageManager().getPermissionControllerPackageName());

               broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

 

               PendingIntent pendingIntent = PendingIntent.getBroadcast(

                       InstallInstalling.this,

                       mInstallId,

                       broadcastIntent,

                       PendingIntent.FLAG_UPDATE_CURRENT);

 

                session.commit(pendingIntent.getIntentSender());

               mCancelButton.setEnabled(false);

               setFinishOnTouchOutside(false);

            } else {

               getPackageManager().getPackageInstaller().abandonSession(mSessionId);

 

                if(!isCancelled()) {

                   launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);

                }

            }

        }

 

    原文作者:维民所止1226
    原文地址: https://blog.csdn.net/xiashaohua/article/details/75129690
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞