Android-Apk的安装流程分析(一)

Android-Apk的安装流程分析(二)

一、安装方式

1、和用户交互式安装,这个方式可以通过点击一个apk,还有通过代码的方式发起安装,这个方式会弹出安装界面,权限选择以及确定,这个方式也是用户最常用的方式,图形界面安装

2、通过adb install -r xxx 这个命令行的方式发起安装

3、系统核心服务PackageManagerService启动系统的时候,会扫描系统内的app,如果没有安装过 就发起安装

这三种方式最后都会走到PMS来发起安装,今天我们就用最常见的第一种方式来讲解

二、流程分析

代码环境:android7.1.2,其他的版本大同小异

1、发起安装请求的Intent
public void startInstall(Context context, String path) {
        Intent install = new Intent(Intent.ACTION_VIEW);
        install.setDataAndType(Uri.parse("file://" + path), 		                     "application/vnd.android.package-archive");
        install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(install);
    }

一般来将我们通过这个可以发起对一个apk的安装,这个就是发送一个inetnt,

我们来看这个Intent包括一个action 还有Data和对应的Type

action:Intent.ACTION_VIEW

data:Uri.parse(“file://” + path)

type:“application/vnd.android.package-archive”

这个Intent是在PackageInstaller 这个系统内置的app中接收的,我们来看下他的Manifest文件

2、PackageInstaller# AndroidManifest.xml

image

我们从这可以看到第一个filter了,里面有action.VIEW 下面还有对应的mimeType,说明这个activity就是接收那个Intent的,接下来我们看下这个Activity:PackageInstallerActivity

3、PackageInstaller#PackageInstallerActivity
1、onCreate

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

    mPm = getPackageManager();
    mInstaller = mPm.getPackageInstaller();
    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
	
    final Intent intent = getIntent();
    mOriginatingUid = getOriginatingUid(intent);

    final Uri packageUri;

	//这个是需要权限确认的时候要走的
    if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
        final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
        final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
        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 = -1;
        packageUri = intent.getData();
        mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
        mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
    }

    // if there's nothing to do, quietly slip into the ether
    if (packageUri == null) {
        Log.w(TAG, "Unspecified source");
        setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
        finish();
        return;
    }

    if (DeviceUtils.isWear(this)) {
        showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
        return;
    }

    //set view
    setContentView(R.layout.install_start);
    mInstallConfirm = findViewById(R.id.install_confirm_panel);
    mInstallConfirm.setVisibility(View.INVISIBLE);
    mOk = (Button)findViewById(R.id.ok_button);
    mCancel = (Button)findViewById(R.id.cancel_button);
    mOk.setOnClickListener(this);
    mCancel.setOnClickListener(this);

	//这个是从packageUri 中解析出要按照这个apk的packageinfo 等相关性信息
	//是根据scheme来分来的
    boolean wasSetUp = processPackageUri(packageUri);
    if (!wasSetUp) {
        return;
    }
	//这里里面主要是做了安装app前的各项检查,确定这个app是否可以安装
	//下面列举了一些错误可能,要弹出的警告框
	//private static final int DLG_BASE = 0;
    //private static final int DLG_UNKNOWN_SOURCES = DLG_BASE + 1;
    //private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
    //private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
    //private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
    //private static final int DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES = DLG_BASE + 6;
    //private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
    checkIfAllowedAndInitiateInstall(false);
    //上面这个检查完了之后会调用initiateInstall() 方法
}



​ //根据不同的scheme 解析uri
​ //分成三种,file,content package
​ //我们常见的就是file 通过uri传过来 进行安装
​ //content是穿过来个Conprovider的地址 然后通过一个AsyncTask进行读取,保存为file 然后走入file流程
​ //package 这个是就是安装哪些已经卸载了的包 重新安装
​ private boolean processPackageUri(final Uri packageUri) {
​ mPackageURI = packageUri;

        final String scheme = packageUri.getScheme();
        final PackageUtil.AppSnippet as;

        switch (scheme) {
            case SCHEME_PACKAGE: {
                try {
                //packageUri.getSchemeSpecificPart() 获取uri的SchemeSpecificPart 部分,实际是报名
                //这个方法实际获取已经卸载了的包的信息
                    mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
                            PackageManager.GET_PERMISSIONS
                                    | PackageManager.GET_UNINSTALLED_PACKAGES);
                } catch (NameNotFoundException e) {
                }
                if (mPkgInfo == null) {
                    Log.w(TAG, "Requested package " + packageUri.getScheme()
                            + " not available. Discontinuing installation");
                    showDialogInner(DLG_PACKAGE_ERROR);
                    setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                    return false;
                }
                as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
                        mPm.getApplicationIcon(mPkgInfo.applicationInfo));
            } break;

            case SCHEME_FILE: {
                File sourceFile = new File(packageUri.getPath());
                PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);

                // Check for parse errors
                if (parsed == null) {
                    Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
                    showDialogInner(DLG_PACKAGE_ERROR);
                    setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                    return false;
                }
                mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                        PackageManager.GET_PERMISSIONS, 0, 0, null,
                        new PackageUserState());
                as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
            } break;

			//传入ContentPorvider,然后读取保存到一个package.apk的数据,然后走file的流程 安装这个apk
            case SCHEME_CONTENT: {
                mStagingAsynTask = new StagingAsyncTask();
                mStagingAsynTask.execute(packageUri);
                return false;
            }

            default: {
                Log.w(TAG, "Unsupported scheme " + scheme);
                setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
                clearCachedApkIfNeededAndFinish();
                return false;
            }
        }

		//这里就是生成一个要安装的app的图标还有名字 做一个展示
        PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);

        return true;
    }
4、PackageInstallerActivity#initiateInstall
private void initiateInstall() {
    String pkgName = mPkgInfo.packageName;
    // Check if there is already a package on the device with this name
    // but it has been renamed to something else.
    String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
    if (oldName != null && oldName.length > 0 && oldName[0] != null) {
        pkgName = oldName[0];
        mPkgInfo.packageName = pkgName;
        mPkgInfo.applicationInfo.packageName = pkgName;
    }
    // Check if package is already installed. display confirmation dialog if replacing pkg
    try {
        // This is a little convoluted because we want to get all uninstalled
        // apps, but this may include apps with just data, and if it is just
        // data we still want to count it as "installed".
        mAppInfo = mPm.getApplicationInfo(pkgName,
                PackageManager.GET_UNINSTALLED_PACKAGES);
        if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
            mAppInfo = null;
        }
    } catch (NameNotFoundException e) {
        mAppInfo = null;
    }
	//这个方法最后会启动安装的确认View
    startInstallConfirm();
}
5、PackageInstallerActivity#startInstallConfirm
    private void startInstallConfirm() {
        ((TextView) findViewById(R.id.install_confirm_question))
                .setText(R.string.install_confirm_question);
        findViewById(R.id.spacer).setVisibility(View.GONE);
        TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
        tabHost.setup();
        tabHost.setVisibility(View.VISIBLE);
        ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
        TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
        // If the app supports runtime permissions the new permissions will
        // be requested at runtime, hence we do not show them at install.
        boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
                >= Build.VERSION_CODES.M;
        boolean permVisible = false;
        mScrollView = null;
        mOkCanInstall = false;
        int msg = 0;
        //这个是权限展示的View封装,里面会通过mPkgInfo 获取需要展示的权限
    AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
    final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
    if (mAppInfo != null) {
        msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                ? R.string.install_confirm_question_update_system
                : R.string.install_confirm_question_update;
        mScrollView = new CaffeinatedScrollView(this);
        mScrollView.setFillViewport(true);
        boolean newPermissionsFound = false;
        if (!supportsRuntimePermissions) {
            newPermissionsFound =
                    (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
            if (newPermissionsFound) {
                permVisible = true;
                mScrollView.addView(perms.getPermissionsView(
                        AppSecurityPermissions.WHICH_NEW));
            }
        }
        if (!supportsRuntimePermissions && !newPermissionsFound) {
            LayoutInflater inflater = (LayoutInflater)getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE);
            TextView label = (TextView)inflater.inflate(R.layout.label, null);
            label.setText(R.string.no_new_perms);
            mScrollView.addView(label);
        }
        adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
                getText(R.string.newPerms)), mScrollView);
    } else  {
        findViewById(R.id.tabscontainer).setVisibility(View.GONE);
        findViewById(R.id.spacer).setVisibility(View.VISIBLE);
    }
    if (!supportsRuntimePermissions && N > 0) {
        permVisible = true;
        LayoutInflater inflater = (LayoutInflater)getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        View root = inflater.inflate(R.layout.permissions_list, null);
        if (mScrollView == null) {
            mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
        }
        ((ViewGroup)root.findViewById(R.id.permission_list)).addView(
                    perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
        adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
                getText(R.string.allPerms)), root);
    }
    if (!permVisible) {
        if (mAppInfo != null) {
            // This is an update to an application, but there are no
            // permissions at all.
            msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                    ? R.string.install_confirm_question_update_system_no_perms
                    : R.string.install_confirm_question_update_no_perms;

            findViewById(R.id.spacer).setVisibility(View.VISIBLE);
        } else {
            // This is a new application with no permissions.
            msg = R.string.install_confirm_question_no_perms;
        }
        tabHost.setVisibility(View.INVISIBLE);
        mScrollView = null;
    }
    if (msg != 0) {
        ((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
    }
    mInstallConfirm.setVisibility(View.VISIBLE);
    mOk.setEnabled(true);
    if (mScrollView == null) {
        // There is nothing to scroll view, so the ok button is immediately
        // set to install.
        mOk.setText(R.string.install);
        mOkCanInstall = true;
    } else {
        mScrollView.setFullScrollAction(new Runnable() {
            @Override
            public void run() {
                mOk.setText(R.string.install);
                mOkCanInstall = true;
            }
        });
    }
}

上面这个方法就是构建一个确认安装的界面,里面有权限列表,还有确认和取消,这个相信大家应该熟悉这个界面

最终我们需要点击确认按钮 继续走

public void onClick(View v) {
        if (v == mOk) {
            if (mOkCanInstall || mScrollView == null) {
                if (mSessionId != -1) {
                这个是权限同意了之后进来的,
                    mInstaller.setPermissionsResult(mSessionId, true);
                    clearCachedApkIfNeededAndFinish();
                } 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);
            }
            clearCachedApkIfNeededAndFinish();
        }
    }
6、PackageInstallerActivity#startInstall
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);
    //真正的安装apk页面
    newIntent.setClass(this, InstallAppProgress.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();
}

总结PackageInstallerActivity这个页面做的事情:

1、获取传入的uri数据,然后根据scheme类型解析不同的安装类型,就是数据来源不同,不同处理

2、解析这个apk,分析是否有错误,空间够不够,包是不是完整,然后给出提示警告框dialog

3、展示要安装的app信息包括名字 图标,权限,确认和取消键

然后继续往下走

上面这个就是 往Intent添加数据,然后跳转到一个新的页面,这个页面是真正的安装apk的页面

这个页面是InstallAppProgress

7、InstallAppProgress#onCreate

我们直接进入onCreate方法看下

     @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        Intent intent = getIntent();
        mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        mPackageURI = intent.getData();
    final String scheme = mPackageURI.getScheme();
    if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
        throw new IllegalArgumentException("unexpected scheme " + scheme);
    }

    mInstallThread = new HandlerThread("InstallThread");
    mInstallThread.start();
    mInstallHandler = new Handler(mInstallThread.getLooper());

    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BROADCAST_ACTION);
    registerReceiver(
            mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
                initView();
}



上面就是做了初始化通过,实例化了一个HandlerThread,这个是带有Looper的线程,可以让线程队列中依次排队执行

继续看initView()

    void initView() {
        setContentView(R.layout.op_progress);
    final PackageUtil.AppSnippet as;
    final PackageManager pm = getPackageManager();
    final int installFlags = getInstallFlags(mAppInfo.packageName);

    if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
        Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
    }
    if ("package".equals(mPackageURI.getScheme())) {
        as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo),
                pm.getApplicationIcon(mAppInfo));
    } else {
        final File sourceFile = new File(mPackageURI.getPath());
        as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile);
    }
    mLabel = as.label;
    PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
    mStatusTextView = (TextView)findViewById(R.id.center_text);
    mExplanationTextView = (TextView) findViewById(R.id.explanation);
    mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
    mProgressBar.setIndeterminate(true);
    // Hide button till progress is being displayed
    mOkPanel = findViewById(R.id.buttons_panel);
    mDoneButton = (Button)findViewById(R.id.done_button);
    mLaunchButton = (Button)findViewById(R.id.launch_button);
    mOkPanel.setVisibility(View.INVISIBLE);

//这个安装类型少见 我们先不考虑
    if ("package".equals(mPackageURI.getScheme())) {
        try {
            pm.installExistingPackage(mAppInfo.packageName);
            onPackageInstalled(PackageInstaller.STATUS_SUCCESS);
        } catch (PackageManager.NameNotFoundException e) {
            onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID);
        }
    } else {
    //最终进入这里,下面就是构造一个要安装的SessionParams,设置各种参数
        final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        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);

        File file = new File(mPackageURI.getPath());
        try {
            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(TAG, "Cannot parse package " + file + ". Assuming defaults.");
            Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
            params.setSize(file.length());
        } catch (IOException e) {
            Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
            params.setSize(file.length());
        }

        mInstallHandler.post(new Runnable() {
            @Override
            public void run() {
            //进入关键代码 这个是在线程中执行的了
            
                doPackageStage(pm, params);
            }
        });
    }
}
8、InstallAppProgress#doPackageStage
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
		//这个PackageInstaller 其实是这个PackageInstallerService的客户端的代理
		//这个PackageInstaller 是在ApplicationPackageManager中创建的
		//ApplicationPackageManager是PMS的在客户端的实际代理者
        final PackageInstaller packageInstaller = pm.getPackageInstaller();
        //这个Session 其实是远程服务端PackageInstallerSesion的客户端代理类
        //之间通过Binder通信的
        PackageInstaller.Session session = null;
        try {
            final String packageLocation = mPackageURI.getPath();
            final File file = new File(packageLocation);
            //调用远端的PackageInstallerService 创建一个sessionId,这个id是随机产生的
            final int sessionId = packageInstaller.createSession(params);
            final byte[] buffer = new byte[65536];
			调用远端的PackageInstallerService  创建了一个PackageInstallerSession
			//然后返回一个客户端的一个代理,这个代理是通过IPackageInstallerSession 通信的
			//IPackageInstallerSession 其实是通过IPackageInstallerSession.aidl生成的java
			//相关的具体aidl的东西 这里不多说了,自行相关学习
            session = packageInstaller.openSession(sessionId);

            final InputStream in = new FileInputStream(file);
            final long sizeBytes = file.length();
            //这里通过远端构造一个FileDescriptor 然后实例化个FileBridge,文件描述符,最终来对外抛出			一个OutputStream
            //通过FileBridge 桥接多进程文件访问,
            //这个的写入数据,远程端也对应有的相应的文件也在写入数据,远程端写入的数据文件文件名字是“PackageInstaller”,这个文件是记录在session中的
            final OutputStream 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);
                    }
                }
                //刷入
                //这样就把这个apk文件 复制到了session中的某个文件变量中保存起来,
                session.fsync(out);
            } finally {
                IoUtils.closeQuietly(in);
                IoUtils.closeQuietly(out);
            }

            // Create a PendingIntent and use it to generate the IntentSender
            Intent broadcastIntent = new Intent(BROADCAST_ACTION);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    InstallAppProgress.this /*context*/,
                    sessionId,
                    broadcastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            //提交这个安装请求,aidl调用,最终会调用PackageInstallerSession的commit方法
            session.commit(pendingIntent.getIntentSender());
        } catch (IOException e) {
            onPackageInstalled(PackageInstaller.STATUS_FAILURE);
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

上面我们提到了 final int sessionId = packageInstaller.createSession(params); 这个最终是在远程的PackageInstallService 实现的

我们来看下PackageInstallService 是如何创建这个Session的

 @Override
    public int createSession(SessionParams params, String installerPackageName, int userId) {
        try {
            return createSessionInternal(params, installerPackageName, userId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }

    private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
            throws IOException {
            //这个是获取调用者端的uid,这个uid就是PackageInstaller 这个安装器app了
        final int callingUid = Binder.getCallingUid();
        //通过PMS 执行权限申请,如果过程有权限不满足 就抛出异常了
        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
		//检查这个安装app的uid是否具有安装权限,这里说的就是PackageInstaller
        if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
            throw new SecurityException("User restriction prevents installing");
        }
		//如果调用的uid是shell的uid或者Process.ROOT_UID 那么添加adb的flag,就是告诉后面这个是adb执行的,
        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
            params.installFlags |= PackageManager.INSTALL_FROM_ADB;

        } else {
        //不然就是用户正常用户的安装
            mAppOps.checkPackage(callingUid, installerPackageName);
			//去掉INSTALL_FROM_ADB
            params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
            //去掉INSTALL_ALL_USERS
            params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
            params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
        }

        // Only system components can circumvent runtime permissions when installing.
        if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
                && mContext.checkCallingOrSelfPermission(Manifest.permission
                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
            throw new SecurityException("You need the "
                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
        }

        // Defensively resize giant app icons
        if (params.appIcon != null) {
            final ActivityManager am = (ActivityManager) mContext.getSystemService(
                    Context.ACTIVITY_SERVICE);
            final int iconSize = am.getLauncherLargeIconSize();
            if ((params.appIcon.getWidth() > iconSize * 2)
                    || (params.appIcon.getHeight() > iconSize * 2)) {
                params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
                        true);
            }
        }

        switch (params.mode) {
        //一般都是这个模式 全部替换安装
            case SessionParams.MODE_FULL_INSTALL:
            //这个是继承安装,不是全量的
            case SessionParams.MODE_INHERIT_EXISTING:
                break;
            default:
                throw new IllegalArgumentException("Invalid install mode: " + params.mode);
        }

        // If caller requested explicit location, sanity check it, otherwise
        // resolve the best internal or adopted location.
        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
        //检测安装需要的空间够不够
            if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
                throw new IOException("No suitable internal storage available");
            }

        } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
            if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
                throw new IOException("No suitable external storage available");
            }

        } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
            // For now, installs to adopted media are treated as internal from
            // an install flag point-of-view.
            params.setInstallFlagsInternal();

        } else {
            // For now, installs to adopted media are treated as internal from
            // an install flag point-of-view.
            params.setInstallFlagsInternal();

            // Resolve best location for install, based on combination of
            // requested install flags, delta size, and manifest settings.
            final long ident = Binder.clearCallingIdentity();
            try {
                params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
                        params.appPackageName, params.installLocation, params.sizeBytes);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        final int sessionId;
        final PackageInstallerSession session;
        synchronized (mSessions) {
            // Sanity check that installer isn't going crazy
            //这里就是检测要安装的这个app,是否已经在待安装列表里有了 而且数量很大,如果
            //数量超过了MAX_ACTIVE_SESSIONS==1024 就抛出异常
            final int activeCount = getSessionCount(mSessions, callingUid);
            
            if (activeCount >= MAX_ACTIVE_SESSIONS) {
                throw new IllegalStateException(
                        "Too many active sessions for UID " + callingUid);
            }
            //检测历史记录
            final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
            if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
                throw new IllegalStateException(
                        "Too many historical sessions for UID " + callingUid);
            }
			//创建一个sessionid,这个是通过ScureRandom创建的,然后存入本地SparseBooleanArray
            sessionId = allocateSessionIdLocked();
        }

        final long createdMillis = System.currentTimeMillis();
        // We're staging to exactly one location
        File stageDir = null;
        String stageCid = null;
        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
            final boolean isEphemeral =
                    (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
                    //创建要保存的apk file的目录,要按照的file 会被拷贝到这里来,然后保存session
                    //这个是内部的sdcard走的
            stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
        } else {
        //安装到外部sdcard,获取stageCid="smdl" + sessionId + ".tmp";
            stageCid = buildExternalStageCid(sessionId);
        }
		//真正的创建了一个PackageInstallerSession
        session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
                mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
                params, createdMillis, stageDir, stageCid, false, false);

        synchronized (mSessions) {
        //添加到内部的mSessions中
            mSessions.put(sessionId, session);
        }

        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
        //写入到xml中,防止安装终止,还可以从xml里恢复,设计很周全
        writeSessionsAsync();
        return sessionId;
    }

PackageInstallService的作用:

1、维护着安装apk的安装会话PackageInstallSession,比如有一个新的安装请求来了,PackageInstallService不着急安装,先放到里面的SparseArray中存起来,然后等待合适的时候在来安装,对apk的安装起到安装缓冲和调度作用

2、创建PackageInstallSession 创建sessionid 并管理

3、中间还会把安装会话保存到xml本地文件中,比如因为某个原因终端了,等下次启动这个服务了,还能从这个xml中读取会话,然后安装

4、每次安装操作都会去这里查询,然后安装

9、PackageInstallerSession#commit

前面说到了InstallAppProgress 这个页面是最终的用户可见的安装页面了,最后这个调用了session.commit(pendingIntent.getIntentSender());

最终是调用了PackageInstallerSession中的commit方法,过程是aidl,参数传入的是一个IntentSender,是PendingIntent的广播类型的Intent

  /*
  整个方法我们直接看下面的重点
  */
  @Override
    public void commit(IntentSender statusReceiver) {
        Preconditions.checkNotNull(statusReceiver);
   final boolean wasSealed;
    synchronized (mLock) {
        wasSealed = mSealed;
        if (!mSealed) {
            // Verify that all writers are hands-off
            for (FileBridge bridge : mBridges) {
                if (!bridge.isClosed()) {
                    throw new SecurityException("Files still open");
                }
            }
            mSealed = true;
        }

        // Client staging is fully done at this point
        mClientProgress = 1f;
        computeProgressLocked(true);
    }

    if (!wasSealed) {
        // Persist the fact that we've sealed ourselves to prevent
        // mutations of any hard links we create. We do this without holding
        // the session lock, since otherwise it's a lock inversion.
        mCallback.onSessionSealedBlocking(this);
    }

    // This ongoing commit should keep session active, even though client
    // will probably close their end.
    mActiveCount.incrementAndGet();
	//实例化一个观察者,这个观察者主要处理权限不被允许,和安装完成的动作监听
    final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
            statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
            //发送MSG_COMMIT到mHandler中处理
    mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}

继续看mHandler中是如何处理的

mHandler = new Handler(looper, mHandlerCallback);

//继续看mHandlerCallback

    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // Cache package manager data without the lock held
            //mPm 这个是PMS,这个是构造方法中传入的
            //获取PackageInfo
            final PackageInfo pkgInfo = mPm.getPackageInfo(
                    params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
                    //获取ApplicationInfo
            final ApplicationInfo appInfo = mPm.getApplicationInfo(
                    params.appPackageName, 0, userId);

            synchronized (mLock) {
                if (msg.obj != null) {
                //获取一个观察者,这个是一个binder   IPackageInstallObserver2这个是一个aidl文件,mRemoteObserver实际上是IPackageInstallObserver2.Stub 是一个继承Binder的类
                //我个人觉得这里不需要binder 也可以,因为这个Session是和ams是一个进程中的,这里可以直接引用mPm 这个是AMS的直接引用
                    mRemoteObserver = (IPackageInstallObserver2) msg.obj;
                }

                try {
                //我们继续看这个
                    commitLocked(pkgInfo, appInfo);
                } catch (PackageManagerException e) {
                    final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                    Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
                    //销毁内部的各种文件
                    destroyInternal();
                    // 发送回调信息 比如通知PackageInstallService 安装失败
                    dispatchSessionFinished(e.error, completeMsg, null);
                }

                return true;
            }
        }
    };

commitLocked

private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
            throws PackageManagerException {
        if (mDestroyed) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
        }
        if (!mSealed) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
        }

        try {
        //解析和保存apk copy后的目录
            resolveStageDir();
        } catch (IOException e) {
            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
                    "Failed to resolve stage location", e);
        }

        // Verify that stage looks sane with respect to existing application.
        // This currently only ensures packageName, versionCode, and certificate
        // consistency.
        //这个就是安装前的最后检查了,检查各项是否正确,比如版本号,签名,如果是覆盖安装app,前后的信息是否一致等
        validateInstallLocked(pkgInfo, appInfo);

        Preconditions.checkNotNull(mPackageName);
        Preconditions.checkNotNull(mSignatures);
        Preconditions.checkNotNull(mResolvedBaseFile);
		//检查安装器是否有权限安装,没有权限安装就需要回调之前说的那个观察者的onUserActionRequired
		//这个mPermissionsAccepted 这个信息是这个类的构造函数里就初始化的了,
        if (!mPermissionsAccepted) {
            // User needs to accept permissions; give installer an intent they
            // can use to involve user.
            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
            intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
            try {
                mRemoteObserver.onUserActionRequired(intent);
            } catch (RemoteException ignored) {
            }

            // Commit was keeping session marked as active until now; release
            // that extra refcount so session appears idle.
            close();
            return;
        }

        if (stageCid != null) {
            // Figure out the final installed size and resize the container once
            // and for all. Internally the parser handles straddling between two
            // locations when inheriting.
            final long finalSize = calculateInstalledSize();
            //最后在调整安装尺寸
            resizeContainer(stageCid, finalSize);
        }

        // Inherit any packages and native libraries from existing install that
        // haven't been overridden.
        
        //从安装过的app中提取 可用的库文件
        if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
            try {
                final List<File> fromFiles = mResolvedInheritedFiles;
                final File toDir = resolveStageDir();

                if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
                if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
                    throw new IllegalStateException("mInheritedFilesBase == null");
                }

                if (isLinkPossible(fromFiles, toDir)) {
                    if (!mResolvedInstructionSets.isEmpty()) {
                        final File oatDir = new File(toDir, "oat");
                        createOatDirs(mResolvedInstructionSets, oatDir);
                    }
                    linkFiles(fromFiles, toDir, mInheritedFilesBase);
                } else {
                    // TODO: this should delegate to DCS so the system process
                    // avoids holding open FDs into containers.
                    copyFiles(fromFiles, toDir);
                }
            } catch (IOException e) {
                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                        "Failed to inherit existing install", e);
            }
        }

        // TODO: surface more granular state from dexopt
        mInternalProgress = 0.5f;
        computeProgressLocked(true);

        // Unpack native libraries
        //提取native文件  就是那些so
        extractNativeLibraries(mResolvedStageDir, params.abiOverride);

        // Container is ready to go, let's seal it up!
        if (stageCid != null) {
            finalizeAndFixContainer(stageCid);
        }

        // We've reached point of no return; call into PMS to install the stage.
        // Regardless of success or failure we always destroy session.
        //构造一个本地观察者,这个到时候要传入到pms中,pms中 安装完成后,会回调这个
        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, stageCid, localObserver, params,
                installerPackageName, installerUid, user, mCertificates);
    }

总结下这个方法做了什么事情

检查安装前的各项设置是否正常(版本号,包名,签名等),权限是否具备,提取其他已经安装了的apk的库(这个库是以来关系),实例化一个观察者来通知上层(安装完成),提取so文件,设置用用属性,最后调用PMS的

installStage来安装

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值