searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Android包安装器简析

2024-06-26 09:44:46
9
0

1. 运行过程分析

包安装器的运行过程分为安装和卸载过程。

1.1 发起应用安装

第三方发起INSTALL_PACKAGE广播,并拉起包安装器;

  • 分别经过InstallStart、InstallStaging、PackageInstallerActivity,停留在安装确认界面;
  • 点击确认后进入InstallInstalling界面,和PKMS通信执行应用安装;
  • 安装完成后回调DeleteStagedFileOnResult删除Staged临时文件;
  • 显示安装结果界面,成功:InstallSuccess,失败:InstallFailed;

Staged文件

/data/user_de/0/com.android.packageinstaller/no_backup

1.2 发起应用卸载

  • 第三方发起DELETE或者UNINSTALL_PACKAGE广播,拉起包安装器;
  • UninstallerActivity界面用户确认卸载后进入UninstallUninstalling界面,和PKMS通信执行应用卸载;
  • 卸载任务完成后进入UninstallFinish界面;

2. 框架接口

主要实现类

  • PackageInstallerService
  • PackageInstaller.SessionParams
  • PackageInstallerSession
  • PackageManagerService.installStage()

下面基于Android9源码进行分析:

2.1 通过PackageInstaller服务生成SessionId

 
 
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.installFlags = PackageManager.INSTALL_FULL_APP;
                params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
                params.originatingUri = getIntent()
                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
                params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                        UID_UNKNOWN);
                params.installerPackageName =
                        getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);

                File file = new File(mPackageURI.getPath());
                try {
                    PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
                    params.setAppPackageName(pkg.packageName);
                    params.setInstallLocation(pkg.installLocation);
                    params.setSize(
                            PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
                } catch (PackageParser.PackageParserException e) {
                    Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                } catch (IOException e) {
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                }

                try {
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

                try {
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

2.2 通过SessionId获取Session,把APK以流形式读取到Session中

 
 


    /**
     * Send the package to the package installer and then register a event result observer that
     * will call {@link #launchFinishBasedOnResult(int, int, String)}
     */
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
        volatile boolean isDone;

        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
                return null;
            }

            session.setStagingProgress(0);

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

                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    try (OutputStream out = session
                            .openWrite("PackageInstaller", 0, sizeBytes)) {
                        byte[] buffer = new byte[1024 * 1024];
                        while (true) {
                            int numRead = in.read(buffer);

                            if (numRead == -1) {
                                session.fsync(out);
                                break;
                            }

                            if (isCancelled()) {
                                session.close();
                                break;
                            }

                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                float fraction = ((float) numRead / (float) sizeBytes);
                                session.addProgress(fraction);
                            }
                        }
                    }
                }

                return session;
            } catch (IOException | SecurityException e) {
                Log.e(LOG_TAG, "Could not write package", e);

                session.close();

                return null;
            } finally {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
            }
        }


2.3 最终把Session安装事务提交到PackageInstaller服务处理

 
 

        @Override
        protected void onPostExecute(PackageInstaller.Session session) {
            if (session != null) {
                Intent broadcastIntent = new Intent(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);

                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);

                if (!isCancelled()) {
                    launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
                }
            }
        }

发起应用安装流程

  1. create Sessions, 获取sessionID
  2. open session将文件写入暂存区
  3. commit Session执行
0条评论
0 / 1000
hi_long
15文章数
0粉丝数
hi_long
15 文章 | 0 粉丝
hi_long
15文章数
0粉丝数
hi_long
15 文章 | 0 粉丝
原创

Android包安装器简析

2024-06-26 09:44:46
9
0

1. 运行过程分析

包安装器的运行过程分为安装和卸载过程。

1.1 发起应用安装

第三方发起INSTALL_PACKAGE广播,并拉起包安装器;

  • 分别经过InstallStart、InstallStaging、PackageInstallerActivity,停留在安装确认界面;
  • 点击确认后进入InstallInstalling界面,和PKMS通信执行应用安装;
  • 安装完成后回调DeleteStagedFileOnResult删除Staged临时文件;
  • 显示安装结果界面,成功:InstallSuccess,失败:InstallFailed;

Staged文件

/data/user_de/0/com.android.packageinstaller/no_backup

1.2 发起应用卸载

  • 第三方发起DELETE或者UNINSTALL_PACKAGE广播,拉起包安装器;
  • UninstallerActivity界面用户确认卸载后进入UninstallUninstalling界面,和PKMS通信执行应用卸载;
  • 卸载任务完成后进入UninstallFinish界面;

2. 框架接口

主要实现类

  • PackageInstallerService
  • PackageInstaller.SessionParams
  • PackageInstallerSession
  • PackageManagerService.installStage()

下面基于Android9源码进行分析:

2.1 通过PackageInstaller服务生成SessionId

 
 
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.installFlags = PackageManager.INSTALL_FULL_APP;
                params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
                params.originatingUri = getIntent()
                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
                params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                        UID_UNKNOWN);
                params.installerPackageName =
                        getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);

                File file = new File(mPackageURI.getPath());
                try {
                    PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
                    params.setAppPackageName(pkg.packageName);
                    params.setInstallLocation(pkg.installLocation);
                    params.setSize(
                            PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
                } catch (PackageParser.PackageParserException e) {
                    Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                } catch (IOException e) {
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                }

                try {
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

                try {
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

2.2 通过SessionId获取Session,把APK以流形式读取到Session中

 
 


    /**
     * Send the package to the package installer and then register a event result observer that
     * will call {@link #launchFinishBasedOnResult(int, int, String)}
     */
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
        volatile boolean isDone;

        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
                return null;
            }

            session.setStagingProgress(0);

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

                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    try (OutputStream out = session
                            .openWrite("PackageInstaller", 0, sizeBytes)) {
                        byte[] buffer = new byte[1024 * 1024];
                        while (true) {
                            int numRead = in.read(buffer);

                            if (numRead == -1) {
                                session.fsync(out);
                                break;
                            }

                            if (isCancelled()) {
                                session.close();
                                break;
                            }

                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                float fraction = ((float) numRead / (float) sizeBytes);
                                session.addProgress(fraction);
                            }
                        }
                    }
                }

                return session;
            } catch (IOException | SecurityException e) {
                Log.e(LOG_TAG, "Could not write package", e);

                session.close();

                return null;
            } finally {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
            }
        }


2.3 最终把Session安装事务提交到PackageInstaller服务处理

 
 

        @Override
        protected void onPostExecute(PackageInstaller.Session session) {
            if (session != null) {
                Intent broadcastIntent = new Intent(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);

                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);

                if (!isCancelled()) {
                    launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
                }
            }
        }

发起应用安装流程

  1. create Sessions, 获取sessionID
  2. open session将文件写入暂存区
  3. commit Session执行
文章来自个人专栏
Android
15 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0