Apk安装之——点击安装确认页面的确认按钮,到将安装过程交给PMS的过程分析

上篇文章Apk安装之——安装确认页面的初始化,讲解了apk包安装的一个小的阶段,展示安装确认页面的过程。这篇文章,继续分析,点击这个页面的确定按钮,开始安装apk的过程。下面是触发点击事件的具体代码:

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

public void onClick(View v) {
    if (v == mOk) {
        if (mOk.isEnabled()) {
            if (mOkCanInstall || mScrollView == null) {
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, true);
                    finish();
                } else {
                    startInstall();
                }
            } else {
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        }
    } else if (v == mCancel) {
        // Cancel and finish
        setResult(RESULT_CANCELED);
        if (mSessionId != -1) {
            mInstaller.setPermissionsResult(mSessionId, false);
        }
        finish();
    }
}

点击确认按钮,只要mSessionId的值不是-1,就会执行,startInstall()方法进行apk的安装。

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

private void startInstall() {
     // Start subactivity 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);
     ...
     newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
     if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
     startActivity(newIntent);
     finish();
}

这个方法内部,会跳转到InstallInstalling这个Activity中,并关闭当前的PackageInstallerActivity页面。下面看看InstallInstalling这个类的onCreate方法:

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.install_installing);

    ApplicationInfo appInfo = getIntent()
            .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
    mPackageURI = getIntent().getData();

    if ("package".equals(mPackageURI.getScheme())) {
        try {
            getPackageManager().installExistingPackage(appInfo.packageName);
            launchSuccess();
        } catch (PackageManager.NameNotFoundException e) {
            launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
        }
    } else {
        //关键代码1
        final File sourceFile = new File(mPackageURI.getPath());
        PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
                sourceFile), R.id.app_snippet);
	//关键代码2
        if (savedInstanceState != null) {
            mSessionId = savedInstanceState.getInt(SESSION_ID);
            mInstallId = savedInstanceState.getInt(INSTALL_ID);

            // Reregister for result; might instantly call back if result was delivered while
            // activity was destroyed
            try {
	        //关键代码3
                InstallEventReceiver.addObserver(this, mInstallId,
                        this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                // Does not happen
            }
        } else {
	    //关键代码4
            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 {
	        //关键代码5
                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 {
		//关键代码6
                mInstallId = InstallEventReceiver
                        .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }

            try {	
		//关键代码7
                mSessionId = getPackageManager().getPackageInstaller().createSession(params);
            } catch (IOException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        }

        mCancelButton = (Button) findViewById(R.id.cancel_button);

        mCancelButton.setOnClickListener(view -> {
            if (mInstallingTask != null) {
                mInstallingTask.cancel(true);
            }

            if (mSessionId > 0) {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                mSessionId = 0;
            }

            setResult(RESULT_CANCELED);
            finish();
        });

        mSessionCallback = new InstallSessionCallback();
    }
}

这个方法内部,首先根据是content协议还是package协议,来进行不同的处理,本案例是已content协议启动apk的安装流程的,所以会执行关键代码1处的代码,这里会判断savedInstanceState如果不为null,则会取出保存的sessionId和installId,sessionId表示此次安装的会话,installId表示等待安装事件id,在关键代码3处,并给InstallEventReceiver添加一个观察者,回调时,观察者的launchFinishBasedOnResult方法会执行。如果savedInstanceState为null,在关键代码4处,会创建PackageInstaller.SessionParams对象,并给这个对象的各个字段赋值。在关键代码5处,调用PackageParser.parsePackageLite方法,解析apk文件,这里是对apk进行轻量解析,主要是获取apk的包名,apk的位置,以及apk的大小等信息,保存到PackageParser.PackageLite类型的对象pkg中。关键代码6处,也是给InstallEventReceiver添加一个观察者,并返回一个installId,关键代码7处,会创建一个sessionId。以上便是InstallInstalling的onCreate方法的分析。下面接着看这个类的onResume方法:

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

@Override
protected void onResume() {
    super.onResume();

    // This is the first 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 will receive a broadcast when the install is finished
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        }
    }
}

这个方法中,如果sessionInfo不会null,并且sessionInfo是非活动的,则会创建一个InstallingAsyncTask(),并执行这个asynTask。下面看看这个InstallingAsyncTask()具体做了什么:

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

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();
            }
        }
    }

    @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);
            }
        }
    }
}

这个InstallingAsyncTask的doInBackground方法内部,将apk文件写入到了PackageInstaller.Session类型的对象session中,在onPostExecute方法中,执行PackageInstaller.Session的commit方法。

http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageInstaller.java

public void commit(@NonNull IntentSender statusReceiver) {
   try {
       mSession.commit(statusReceiver, false);
   } catch (RemoteException e) {
       throw e.rethrowFromSystemServer();
   }
}

这个方法内部会调用mSession的commit方法,mSession是IPackageInstallerSession类型的,IPackageInstallerSession其实是system_servier进程的PackageInstallerSession在客户端的一个代理对象。mSession。commit方法,其实就是发起了进程间的通信,system_servier进程的PackageInstallerSession的commit方法会执行这个请求。下面看看这个方法的具体实现:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    Preconditions.checkNotNull(statusReceiver);

    final boolean wasSealed;
    synchronized (mLock) {
        ...
        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    }

    ...
}


private final Handler.Callback mHandlerCallback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            ...
            case MSG_COMMIT:
                synchronized (mLock) {
                    try {
			//关键代码
                        commitLocked();
                    } catch (PackageManagerException e) {
                        final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                        Slog.e(TAG,
                                "Commit of session " + sessionId + " failed: " + completeMsg);
                        destroyInternal();
                        dispatchSessionFinished(e.error, completeMsg, null);
                    }
                }

                break;
            case MSG_ON_PACKAGE_INSTALLED:
                ...

                break;
        }

        return true;
    }
}

这个方法内部,会通过handler发送一个MSG_COMMIT消息,使得handler中的commitLocked()方法得到执行。下面看看这个方法:

@GuardedBy("mLock")
private void commitLocked()throws PackageManagerException {
    ...

    // We've reached point of no return; call into PMS to install the stage.
    // Regardless of success or failure we always destroy session.
    final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
        @Override
        public void onUserActionRequired(Intent intent) {
            throw new IllegalStateException();
        }

        @Override
        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                Bundle extras) {
            destroyInternal();
            dispatchSessionFinished(returnCode, msg, extras);
        }
    };

    final UserHandle user;
    if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
        user = UserHandle.ALL;
    } else {
        user = new UserHandle(userId);
    }

    mRelinquished = true;
    //关键代码
    mPm.installStage(mPackageName, stageDir, localObserver, params,
            mInstallerPackageName, mInstallerUid, user, mSigningDetails);
}

这个方法内部,主要是执行mPm的installStage方法,前面的很多代码都是为这个方法的创建很多的参数。这个mPm是PackageManagerService类型的,其实这里的安装,交给了PackageManagerService来完成。

总结:点击安装确认页面的 确定按钮后,会创建安装会话id,和准备安装的事件id,接着会将apk文件读取到PackageInstaller.Session类型的对象中,在调用这PackageInstaller.Session类型的对象的commit方法,将具体的安装交给PMS。

参考:Android包管理机制(二)PackageInstaller安装APK

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值