【Android】APK应用安装过程源码解析

Android系统在启动的过程中,会启动一个应用程序管理服务PackageManagerService,这个服务负责扫描系统中特定的目录,找到里面的应用程序文件,即以Apk为后缀的文件,然后对这些文件进解析,得到应用程序的相关信息,完成应用程序的安装过程,本文将详细分析这个过程。

一、启动安装

1、InstallStart

在开发过程中有时候需要进行APP更新,或者下载其他APP到本地让后使用以下代码进行安装,我这里单单讲解在执行startActivity之后的运行过程。

        Intent intent = new Intent(Intent.ACTION_VIEW);
//设置intent的数据类型是应用程序application  
        intent.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
//为这个新apk开启一个新的activity栈  
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//开始安装  
        startActivity(intent);

在执行完这几行代码后会启动系统的一个APP进行APK的安装,我们可以通过源码查看在系统中有一个packageinstaller的系统应用。然后首先查看看清单文件

<activity android:name=".InstallStart"
   android:exported="true"
   android:excludeFromRecents="true">
           <intent-filter android:priority="1">
               <action android:name="android.intent.action.VIEW" />
               <action android:name="android.intent.action.INSTALL_PACKAGE" />
               <category android:name="android.intent.category.DEFAULT" />
               <data android:scheme="file" />
               <data android:scheme="content" />
               <data android:mimeType="application/vnd.android.package-archive" />
           </intent-filter>
           <intent-filter android:priority="1">
               <action android:name="android.intent.action.INSTALL_PACKAGE" />
               <category android:name="android.intent.category.DEFAULT" />
               <data android:scheme="file" />
               <data android:scheme="package" />
               <data android:scheme="content" />
           </intent-filter>
           <intent-filter android:priority="1">
               <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
       </activity>

从中可以发现首先启动的是名为InstallStart的Activity,由于整个源码有点多,因此这里只是贴出部分重要方法。在这里只需要看onCreate的方法

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;

public class InstallStart extends Activity {
   
    private static final String LOG_TAG = InstallStart.class.getSimpleName();

    private static final String SCHEME_CONTENT = "content";
    private static final String DOWNLOADS_AUTHORITY = "downloads";
    private IActivityManager mIActivityManager;
    private IPackageManager mIPackageManager;
    private boolean mAbortInstall = false;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
   
        super.onCreate(savedInstanceState);
        mIPackageManager = AppGlobals.getPackageManager();
        Intent intent = getIntent();
        String callingPackage = getCallingPackage();

        // If the activity was started via a PackageInstaller session, we retrieve the calling
        // package from that session
        int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
        if (callingPackage == null && sessionId != -1) {
         //我们第三发安装sessionId为-1
            PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
            PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
            callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
        }

        final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
        final int originatingUid = getOriginatingUid(sourceInfo);
        boolean isTrustedSource = false;
        if (sourceInfo != null
                && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
   
            isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
        }

        if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
   
            final int targetSdkVersion = getMaxTargetSdkVersionForUid(originatingUid);
            if (targetSdkVersion < 0) {
   
                Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
                // Invalid originating uid supplied. Abort install.
                mAbortInstall = true;
            } else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission(
                    originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
   
                //在现版本第三方安装APK需要申明REQUEST_INSTALL_PACKAGES权限,declaresAppOpPermission检测
                //是否申请改权限
                Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
                        + Manifest.permission.REQUEST_INSTALL_PACKAGES);
                mAbortInstall = true;
            }
        }
        if (mAbortInstall) {
    //如果经过上述一系列操作没问题,当然是false
            setResult(RESULT_CANCELED);
            finish();
            return;
        }

        Intent nextActivity = new Intent(intent);
        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);

        // The the installation source as the nextActivity thinks this activity is the source, hence
        // set the originating UID and sourceInfo explicitly
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);

        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
   
            //在这里我们第三方app是不会跳转到这里的
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
   
            Uri packageUri = intent.getData();

            if (packageUri == null) {
   
                // if there's nothing to do, quietly slip into the ether
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            } else {
   
                if (packageUri.getScheme().equals(SCHEME_CONTENT)) {
   
                    nextActivity.setClass(this, InstallStaging.class);
                } else {
   
                    nextActivity.setClass(this, PackageInstallerActivity.class);
                }
            }
        }

        if (nextActivity != null) {
   
            //跳转到下一个Activity
            startActivity(nextActivity);
        }
        finish();
    }

   ......
}

经过一系列的解析操作之后会跳转到新的界面

2、PackageInstallerActivity

public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener {
   
    private static final String TAG = "PackageInstaller";
    .....
    @Override
    protected void onCreate(Bundle icicle) {
   
        super.onCreate(icicle);

        if (icicle != null) {
   
            mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
        }

        mPm = getPackageManager(); //获取PackageManager
        mIpm = AppGlobals.getPackageManager();
        mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
        mInstaller = mPm.getPackageInstaller();//获取PackageInstaller
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);

        final Intent intent = getIntent();

        mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
        mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
        mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                PackageInstaller.SessionParams.UID_UNKNOWN);
        mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
                ? getPackageNameForUid(mOriginatingUid) : null;


        final Uri packageUri;

        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
   //非Android的GooglePaly上的app可以直接忽略了
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId); //在PackageInstaller中创建一个Session
            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
   
                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
                finish();
                return;
            }

            mSessionId = sessionId;//
            packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
            mOriginatingURI = null;
            mReferrerURI = null;
        } else {
   
            mSessionId = 
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值