最近在学习Android8.0的Launcher源码遇到APP安装过程中监听APK安装过程,顺便就看了一遍APK源码安装的过程。相比较之前的版本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 = -1; packageUri = intent.getData(); //获取安装APK安装包的路径 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, "Unspe