首页 > 代码库 > android PackageInstaller那点事儿
android PackageInstaller那点事儿
今天简单讲解一下PackageInstaller
文件路径:
packages/apps/PackageInstaller
frameworks/base/core/java/android/content/pm&res
下面开始讲解:
首先,我们说一下安装apk的几种方式,整体上可以分为2类,一类是有界面安装,一类是无界面安装。无界面安装分为内置apk开机安装和命令安装,命令安装又分为两类,一类电脑安装也就是adb命令,另一类是手机安装也就是pm命令。今天我们主要介绍有界面安装。
当然,我们从这个安装界面说起,这个界面是那个呢?就是PackageInstallerActivity这个acitvity。它是如何启动起来的呢?我们去看看它在AndroidManifest是如何定义的
- <activity android:name=".PackageInstallerActivity"
- android:configChanges="orientation|keyboardHidden|screenSize"
- android:excludeFromRecents="true"
- android:screenOrientation="unspecified">
- <intent-filter>
- <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="content" />
- <data android:scheme="file" />
- <data android:mimeType="application/vnd.android.package-archive" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.INSTALL_PACKAGE" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="content" />
- <data android:scheme="file" />
- </intent-filter>
- </activity>
很明显了,我们可以通过android.intent.action.INSTALL_PACKAGE这个action启动,也可以通过android.intent.action.VIEW这个action加上"application/vnd.android.package-archive"这个type启动,当然不加这个type也能启动,但是会找到很多这样的activity哦。另外,通过类名或包名启动也未尝不可的。所以,大部分启动是这样的
- String apkFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+"/.../packageName.pac";
- File apkFile = new File(apkFileString);
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
- mContext.startActivity(intent);
这里我们传进去一个数据就是pakFile的Uri,然后我们去PackageInstallerActivity的onCreate中看看
- final Intent intent = getIntent();
- mPackageURI = intent.getData();
- mPm = getPackageManager();
- mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);
获取到,我们刚才传进来的apkFile的Uri给了mPackageURI,接着获取到PackageManager,然后生成一个mPkgInfo也就是PackageParser.Package,这个很重要。我们看看PackageParser.Package是如何生成的,PackageParser.Package里面都包含了什么东西。那我们就要去PackageUtil.getPackageInfo中了
- public static PackageParser.Package getPackageInfo(Uri packageURI) {
- final String archiveFilePath = packageURI.getPath();
- PackageParser packageParser = new PackageParser(archiveFilePath);
- File sourceFile = new File(archiveFilePath);
- DisplayMetrics metrics = new DisplayMetrics();
- metrics.setToDefaults();
- PackageParser.Package pkg = packageParser.parsePackage(sourceFile,
- archiveFilePath, metrics, 0);
- // Nuke the parser reference.
- packageParser = null;
- return pkg;
- }
生成一个Package解析器,通过这个解析器来获取到PackageParser.Package中需要的数据,生成一个PackageParser.Package。我们看看PackageParser.parsePackage是如何生成一个PackageParser.Package的,这里传进去四个参数,一个Source File,apk文件,一个apk路径,一个屏幕信息,最后一个0,具体做什么的,进去之后就能明白了
- public Package parsePackage(File sourceFile, String destCodePath,
- DisplayMetrics metrics, int flags) {
- mParseError = PackageManager.INSTALL_SUCCEEDED;
- mArchiveSourcePath = sourceFile.getPath();
- if (!sourceFile.isFile()) {
- Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
- mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
- return null;
- }
- if (!isPackageFilename(sourceFile.getName())
- && (flags&PARSE_MUST_BE_APK) != 0) {
- if ((flags&PARSE_IS_SYSTEM) == 0) {
- // We expect to have non-.apk files in the system dir,
- // so don‘t warn about them.
- Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
- }
- mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
- return null;
- }
- if (DEBUG_JAR)
- Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);
- XmlResourceParser parser = null;
- AssetManager assmgr = null;
- Resources res = null;
- boolean assetError = true;
- try {
- assmgr = new AssetManager();
- int cookie = assmgr.addAssetPath(mArchiveSourcePath);
- if (cookie != 0) {
- res = new Resources(assmgr, metrics, null);
- assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
- parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
- assetError = false;
- } else {
- Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
- }
- } catch (Exception e) {
- Slog.w(TAG, "Unable to read AndroidManifest.xml of "
- + mArchiveSourcePath, e);
- }
- if (assetError) {
- if (assmgr != null) assmgr.close();
- mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
- return null;
- }
- String[] errorText = new String[1];
- Package pkg = null;
- Exception errorException = null;
- try {
- // XXXX todo: need to figure out correct configuration.
- pkg = parsePackage(res, parser, flags, errorText);
- } catch (Exception e) {
- errorException = e;
- mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
- }
- if (pkg == null) {
- // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
- // just means to skip this app so don‘t make a fuss about it.
- if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
- if (errorException != null) {
- Slog.w(TAG, mArchiveSourcePath, errorException);
- } else {
- Slog.w(TAG, mArchiveSourcePath + " (at "
- + parser.getPositionDescription()
- + "): " + errorText[0]);
- }
- if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
- mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- }
- }
- parser.close();
- assmgr.close();
- return null;
- }
- parser.close();
- assmgr.close();
- // Set code and resource paths
- pkg.mPath = destCodePath;
- pkg.mScanPath = mArchiveSourcePath;
- //pkg.applicationInfo.sourceDir = destCodePath;
- //pkg.applicationInfo.publicSourceDir = destRes;
- pkg.mSignatures = null;
- return pkg;
- }
首先sourceFile.isFile()判断一下是不是文件,如果不是,返回;接着isPackageFilename(sourceFile.getName())判断是不是apk文件,如果不是,返回;接着去获取三个关键变量,也就是
- XmlResourceParser parser = null;
- AssetManager assmgr = null;
- Resources res = null;
这三个是什么呢?这里简单说一下,AssetManager资产管理器,用来管理包中获取到的资源
- assmgr = new AssetManager();
- int cookie = assmgr.addAssetPath(mArchiveSourcePath);
通过addAssetPath可以获取到唯一标识该apk包资产的关键字cookie,也就是通过cookie可以找到该包的资源信息。Resources就是资源了,包括图片,color,xml等资源
- res = new Resources(assmgr, metrics, null);
当然Resources信息也是通过AssetManager获取到的。XmlResourceParser顾名思义就是Xml资源文件解析器了,用来解析我们xml文件的
- parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
ANDROID_MANIFEST_FILENAME也就是
- private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
这样就很明显了,这里生成的xml文件资源解析器是用来解析AndroidManifest文件的了。接下来就是关键了
- String[] errorText = new String[1];
- Package pkg = null;
- Exception errorException = null;
- try {
- // XXXX todo: need to figure out correct configuration.
- pkg = parsePackage(res, parser, flags, errorText);
- } catch (Exception e) {
- errorException = e;
- mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
- }
这里才是我们Package真正生成的地方了,也就是pkg = parsePackage(res, parser, flags, errorText)了。parsePackage是同构函数,一个是以File为首个参数,就是我们现在分析的这个,一个是以Resources为首个参数,就是我们接下来要讲的了,由于这个函数比较大,所以不再全部列出,只选取主要的
- String pkgName = parsePackageName(parser, attrs, flags, outError);
获取包名。
- final Package pkg = new Package(pkgName);
- boolean foundApp = false;
- TypedArray sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AndroidManifest);
- pkg.mVersionCode = sa.getInteger(
- com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
- pkg.mVersionName = sa.getNonConfigurationString(
- com.android.internal.R.styleable.AndroidManifest_versionName, 0);
- if (pkg.mVersionName != null) {
- pkg.mVersionName = pkg.mVersionName.intern();
- }
- String str = sa.getNonConfigurationString(
- com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
- if (str != null && str.length() > 0) {
- String nameError = validateName(str, true);
- if (nameError != null && !"android".equals(pkgName)) {
- outError[0] = "<manifest> specifies bad sharedUserId name \""
- + str + "\": " + nameError;
- mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
- return null;
- }
- pkg.mSharedUserId = str.intern();
- pkg.mSharedUserLabel = sa.getResourceId(
- com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
- }
- sa.recycle();
- pkg.installLocation = sa.getInteger(
- com.android.internal.R.styleable.AndroidManifest_installLocation,
- PARSE_DEFAULT_INSTALL_LOCATION);
- pkg.applicationInfo.installLocation = pkg.installLocation;
解析获取,我们AndroidManifest的attrs,也就是frameworks/base/core/res/res/values下的attrs_manifest.xml中定义的
- <declare-styleable name="AndroidManifest">
- <attr name="versionCode" />
- <attr name="versionName" />
- <attr name="sharedUserId" />
- <attr name="sharedUserLabel" />
- <attr name="installLocation" />
- </declare-styleable>
这些变量信息。接下来就是一个大循环了,这里解析的内容比较多了,我们举几个常见的例子,如"application"也就是
- <application android:label="@string/app_name">
- </application>
这里面包含的信息,例如这里的lable等等,还有以其为父的"activity","receiver","service","provider"等等,这里以"activity"为例,还是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是
- <declare-styleable name="AndroidManifestActivity" parent="AndroidManifestApplication">
- <!-- Required name of the class implementing the activity, deriving from
- {@link android.app.Activity}. This is a fully
- qualified class name (for example, com.mycompany.myapp.MyActivity); as a
- short-hand if the first character of the class
- is a period then it is appended to your package name. -->
- <attr name="name" />
- <attr name="theme" />
- <attr name="label" />
- <attr name="description" />
- <attr name="icon" />
- <attr name="logo" />
- <attr name="launchMode" />
- <attr name="screenOrientation" />
- <attr name="configChanges" />
- <attr name="permission" />
- <attr name="multiprocess" />
- <attr name="process" />
- <attr name="taskAffinity" />
- <attr name="allowTaskReparenting" />
- <attr name="finishOnTaskLaunch" />
- <attr name="finishOnCloseSystemDialogs" />
- <attr name="clearTaskOnLaunch" />
- <attr name="noHistory" />
- <attr name="alwaysRetainTaskState" />
- <attr name="stateNotNeeded" />
- <attr name="excludeFromRecents" />
- <!-- Specify whether the activity is enabled or not (that is, can be instantiated by the system).
- It can also be specified for an application as a whole, in which case a value of "false"
- will override any component specific values (a value of "true" will not override the
- component specific values). -->
- <attr name="enabled" />
- <attr name="exported" />
- <!-- Specify the default soft-input mode for the main window of
- this activity. A value besides "unspecified" here overrides
- any value in the theme. -->
- <attr name="windowSoftInputMode" />
- <attr name="immersive" />
- <attr name="hardwareAccelerated" />
- <attr name="uiOptions" />
- </declare-styleable>
这里有很多变量,在定义一个acitivity的时候有的我们用过,有的没有用过,Xml文件资源解析器就是从xml中获取到这先变量的值然后付给这些变量;同样还有"permission"权限,也就是
- <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
这类,大家经常会见到的;还有"permission-group","uses-sdk"等等吧,有兴趣的可以一一研究。最终这些信息都会囊括到我们的Package中。这里我们明白Package是什么了吧?就是包含包中所有信息的的玩意。到此位置我们的Package已经生成,然后我们还回到PackageInstallerActivity的onCreate中,接着往下看,不重要的就跳过了
- 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.setPackageName(pkgName);
- }
- // Check if package is already installed. display confirmation dialog if replacing pkg
- try {
- mAppInfo = mPm.getApplicationInfo(pkgName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (NameNotFoundException e) {
- mAppInfo = null;
- }
- if (mAppInfo == null || getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_REPLACE, false)) {
- startInstallConfirm();
- } else {
- if(localLOGV) Log.i(TAG, "Replacing existing package:"+
- mPkgInfo.applicationInfo.packageName);
- showDialogInner(DLG_REPLACE_APP);
- }
- }
然后是这里的startInstallConfirm(),也就是
- private void startInstallConfirm() {
- LinearLayout permsSection = (LinearLayout) mInstallConfirm.findViewById(R.id.permissions_section);
- LinearLayout securityList = (LinearLayout) permsSection.findViewById(
- R.id.security_settings_list);
- boolean permVisible = false;
- if(mPkgInfo != null) {
- AppSecurityPermissions asp = new AppSecurityPermissions(this, mPkgInfo);
- if(asp.getPermissionCount() > 0) {
- permVisible = true;
- securityList.addView(asp.getPermissionsView());
- }
- }
- if(!permVisible){
- permsSection.setVisibility(View.INVISIBLE);
- }
- mInstallConfirm.setVisibility(View.VISIBLE);
- mOk = (Button)findViewById(R.id.ok_button);
- mCancel = (Button)findViewById(R.id.cancel_button);
- mOk.setOnClickListener(this);
- mCancel.setOnClickListener(this);
- }
到这里我们的PackageInstallerActivity这个activity才算完成,这里我们看看我们安装界面的权限View是如何生成的,也就是asp.getPermissionsView(),这里的AppSecurityPermissions(this, mPkgInfo)传进去两个参数,一个是Context,一个是我们刚才获取到的Package,我们进去看看,文件在frameworks/base/core/java/android/widget下面
- public AppSecurityPermissions(Context context, PackageParser.Package pkg) {
- mContext = context;
- mPm = mContext.getPackageManager();
- mPermsList = new ArrayList<PermissionInfo>();
- Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();
- if(pkg == null) {
- return;
- }
- // Get requested permissions
- if (pkg.requestedPermissions != null) {
- ArrayList<String> strList = pkg.requestedPermissions;
- int size = strList.size();
- if (size > 0) {
- extractPerms(strList.toArray(new String[size]), permSet);
- }
- }
- // Get permissions related to shared user if any
- if(pkg.mSharedUserId != null) {
- int sharedUid;
- try {
- sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId);
- getAllUsedPermissions(sharedUid, permSet);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Could‘nt retrieve shared user id for:"+pkg.packageName);
- }
- }
- // Retrieve list of permissions
- for(PermissionInfo tmpInfo : permSet) {
- mPermsList.add(tmpInfo);
- }
- }
就是获取到一个PermissionInfo的List就是mPermsList。然后我们看看asp.getPermissionsView(),也就是
- public View getPermissionsView() {
- mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
- mShowMore = mPermsView.findViewById(R.id.show_more);
- mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon);
- mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text);
- mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list);
- mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list);
- mNoPermsView = mPermsView.findViewById(R.id.no_permissions);
- // Set up the LinearLayout that acts like a list item.
- mShowMore.setClickable(true);
- mShowMore.setOnClickListener(this);
- mShowMore.setFocusable(true);
- // Pick up from framework resources instead.
- mDefaultGrpLabel = mContext.getString(R.string.default_permission_group);
- mPermFormat = mContext.getString(R.string.permissions_format);
- mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);
- mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);
- mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark);
- mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark);
- // Set permissions view
- setPermissions(mPermsList);
- return mPermsView;
- }
这里就是我们的权限View的布局了,也就是frameworks/base/core/res/res/layout下面的app_perms_summary.xml布局了,如果我们想修改权限VIew的话就要从这里开始了。我们去看看是如何生成的,也就是setPermissions(mPermsList)
- private void setPermissions(List<PermissionInfo> permList) {
- mGroupLabelCache = new HashMap<String, CharSequence>();
- //add the default label so that uncategorized permissions can go here
- mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel);
- // Map containing group names and a list of permissions under that group
- // categorized as dangerous
- mDangerousMap = new HashMap<String, String>();
- // Map containing group names and a list of permissions under that group
- // categorized as normal
- mNormalMap = new HashMap<String, String>();
- // Additional structures needed to ensure that permissions are unique under
- // each group
- Map<String, List<PermissionInfo>> dangerousMap =
- new HashMap<String, List<PermissionInfo>>();
- Map<String, List<PermissionInfo> > normalMap =
- new HashMap<String, List<PermissionInfo>>();
- PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);
- if (permList != null) {
- // First pass to group permissions
- for (PermissionInfo pInfo : permList) {
- if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);
- if(!isDisplayablePermission(pInfo)) {
- if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");
- continue;
- }
- Map<String, List<PermissionInfo> > permInfoMap =
- (pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) ?
- dangerousMap : normalMap;
- String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group;
- if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName);
- List<PermissionInfo> grpPermsList = permInfoMap.get(grpName);
- if(grpPermsList == null) {
- grpPermsList = new ArrayList<PermissionInfo>();
- permInfoMap.put(grpName, grpPermsList);
- grpPermsList.add(pInfo);
- } else {
- int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator);
- if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size());
- if (idx < 0) {
- idx = -idx-1;
- grpPermsList.add(idx, pInfo);
- }
- }
- }
- // Second pass to actually form the descriptions
- // Look at dangerous permissions first
- aggregateGroupDescs(dangerousMap, mDangerousMap);
- aggregateGroupDescs(normalMap, mNormalMap);
- }
- mCurrentState = State.NO_PERMS;
- if(mDangerousMap.size() > 0) {
- mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY;
- } else if(mNormalMap.size() > 0) {
- mCurrentState = State.NORMAL_ONLY;
- }
- if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState);
- showPermissions();
- }
这里区分一下是dangerousMap,也就是PermissionInfo.PROTECTION_DANGEROUS类权限还是normalMap一般权限,然后就去showPermissions()
- private void showPermissions() {
- switch(mCurrentState) {
- case NO_PERMS:
- displayNoPermissions();
- break;
- case DANGEROUS_ONLY:
- displayPermissions(true);
- break;
- case NORMAL_ONLY:
- displayPermissions(false);
- break;
- case BOTH:
- displayPermissions(true);
- if (mExpanded) {
- displayPermissions(false);
- mShowMoreIcon.setImageDrawable(mShowMaxIcon);
- mShowMoreText.setText(R.string.perms_hide);
- mNonDangerousList.setVisibility(View.VISIBLE);
- } else {
- mShowMoreIcon.setImageDrawable(mShowMinIcon);
- mShowMoreText.setText(R.string.perms_show_all);
- mNonDangerousList.setVisibility(View.GONE);
- }
- mShowMore.setVisibility(View.VISIBLE);
- break;
- }
- }
给我们的布局赋显示的内容了,这里不一一解释,我们去看看displayPermissions
- private void displayPermissions(boolean dangerous) {
- Map<String, String> permInfoMap = dangerous ? mDangerousMap : mNormalMap;
- LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList;
- permListView.removeAllViews();
- Set<String> permInfoStrSet = permInfoMap.keySet();
- for (String loopPermGrpInfoStr : permInfoStrSet) {
- CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr);
- //guaranteed that grpLabel wont be null since permissions without groups
- //will belong to the default group
- if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:"
- + permInfoMap.get(loopPermGrpInfoStr));
- permListView.addView(getPermissionItemView(grpLabel,
- permInfoMap.get(loopPermGrpInfoStr), dangerous));
- }
- }
看到这里就很明白了,我们的权限View是怎么生成的了。不再多做解释了。至此我们PackageInstallerActivity这个activity已经完全形成了,截个图吧
接下来,我们说说当点击“安装”之后做了什么事情。
- public void onClick(View v) {
- if(v == mOk) {
- // 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, InstallAppProgress.class);
- String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
- 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();
- } else if(v == mCancel) {
- // Cancel and finish
- setResult(RESULT_CANCELED);
- finish();
- }
- }
去启动了另外一个acitvity也就是InstallAppProgress,并过去几个数据,主要是mPkgInfo.applicationInfo也就是ApplicationInfo,还有mPackageURI也就是apkFile的Uri,还有一些其他的数据。然后我们就去InstallAppProgress的onCreate中
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- Intent intent = getIntent();
- mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
- mPackageURI = intent.getData();
- initView();
- }
获取到传过来的两个数据,然后就initView(),initView()里面是一些布局的初始化,不再赘述,只截取重要的,也就是
- String installerPackageName = getIntent().getStringExtra(
- Intent.EXTRA_INSTALLER_PACKAGE_NAME);
- PackageInstallObserver observer = new PackageInstallObserver();
- pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);
pm是PackageManager,这样我们就去PackageManager.installPackage中,在PackageManager的installPackage是abstract函数,具体实现在PackageManagerService中,这里传进去的参数第一个我们已经知道了,第二个是package安装的观察者
- class PackageInstallObserver extends IPackageInstallObserver.Stub {
- public void packageInstalled(String packageName, int returnCode) {
- Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);
- Log.d("packageInstalled", "returnCode = "+returnCode);
- msg.arg1 = returnCode;
- mHandler.sendMessage(msg);
- }
- }
当安装完成就会走到packageInstalled个函数中,第三个参数是flag主要标识是第一次安装,还是已经安装更新,第三个参数很明显是安装的包名了。然后我们去看看
- /* Called when a downloaded package installation has been confirmed by the user */
- public void installPackage(
- final Uri packageURI, final IPackageInstallObserver observer, final int flags) {
- installPackage(packageURI, observer, flags, null);
- }
- /* Called when a downloaded package installation has been confirmed by the user */
- public void installPackage(
- final Uri packageURI, final IPackageInstallObserver observer, final int flags,
- final String installerPackageName) {
- installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,
- null);
- }
也就是installPackageWithVerification
- public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
- int flags, String installerPackageName, Uri verificationURI,
- ManifestDigest manifestDigest) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
- final int uid = Binder.getCallingUid();
- final int filteredFlags;
- if (uid == Process.SHELL_UID || uid == 0) {
- if (DEBUG_INSTALL) {
- Slog.v(TAG, "Install from ADB");
- }
- filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;
- } else {
- filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;
- }
- final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
- verificationURI, manifestDigest);
- mHandler.sendMessage(msg);
- }
就去发了一个消息INIT_COPY,并携带了我们传进来的参数组成的一个类InstallParams,InstallParams继承于HandlerParams,我们去看看这个消息执行了什么
- case INIT_COPY: {
- if (DEBUG_INSTALL) Slog.i(TAG, "init_copy");
- HandlerParams params = (HandlerParams) msg.obj;
- int idx = mPendingInstalls.size();
- if (DEBUG_INSTALL) Slog.i(TAG, "idx=" + idx);
- // If a bind was already initiated we dont really
- // need to do anything. The pending install
- // will be processed later on.
- if (!mBound) {
- // If this is the only one pending we might
- // have to bind to the service again.
- if (!connectToService()) {
- Slog.e(TAG, "Failed to bind to media container service");
- params.serviceError();
- return;
- } else {
- // Once we bind to the service, the first
- // pending request will be processed.
- mPendingInstalls.add(idx, params);
- }
- } else {
- mPendingInstalls.add(idx, params);
- // Already bound to the service. Just make
- // sure we trigger off processing the first request.
- if (idx == 0) {
- mHandler.sendEmptyMessage(MCS_BOUND);
- }
- }
- break;
- }
这里先 mPendingInstalls.add(idx, params)把我们要安装的信息放到HandlerParams的一个List中mPendingInstalls,然后去发了一个消息MCS_BOUND,也就是
- case MCS_BOUND: {
- if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
- if (msg.obj != null) {
- mContainerService = (IMediaContainerService) msg.obj;
- }
- if (mContainerService == null) {
- // Something seriously wrong. Bail out
- Slog.e(TAG, "Cannot bind to media container service");
- for (HandlerParams params : mPendingInstalls) {
- mPendingInstalls.remove(0);
- // Indicate service bind error
- params.serviceError();
- }
- mPendingInstalls.clear();
- } else if (mPendingInstalls.size() > 0) {
- HandlerParams params = mPendingInstalls.get(0);
- if (params != null) {
- if (params.startCopy()) {
- // We are done... look for more work or to
- // go idle.
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Checking for more work or unbind...");
- // Delete pending install
- if (mPendingInstalls.size() > 0) {
- mPendingInstalls.remove(0);
- }
- if (mPendingInstalls.size() == 0) {
- if (mBound) {
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Posting delayed MCS_UNBIND");
- removeMessages(MCS_UNBIND);
- Message ubmsg = obtainMessage(MCS_UNBIND);
- // Unbind after a little delay, to avoid
- // continual thrashing.
- sendMessageDelayed(ubmsg, 10000);
- }
- } else {
- // There are more pending requests in queue.
- // Just post MCS_BOUND message to trigger processing
- // of next pending install.
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Posting MCS_BOUND for next woek");
- mHandler.sendEmptyMessage(MCS_BOUND);
- }
- }
- }
- } else {
- // Should never happen ideally.
- Slog.w(TAG, "Empty queue");
- }
- break;
- }
HandlerParams params = mPendingInstalls.get(0)读取出我们要安装的包信息,然后清楚该包信息,如果还有其他包就继续发MCS_BOUND这个消息,循环,直到都安装完了。然后安装在哪里呢?也就是
- params.startCopy()
这个了,进去看看
- final boolean startCopy() {
- boolean res;
- try {
- if (DEBUG_INSTALL) Slog.i(TAG, "startCopy");
- if (++mRetries > MAX_RETRIES) {
- Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
- mHandler.sendEmptyMessage(MCS_GIVE_UP);
- handleServiceError();
- return false;
- } else {
- handleStartCopy();
- res = true;
- }
- } catch (RemoteException e) {
- if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
- mHandler.sendEmptyMessage(MCS_RECONNECT);
- res = false;
- }
- handleReturnCode();
- return res;
- }
这里的handleStartCopy()和handleServiceError()和handleReturnCode()都是abstract函数,希望大家还记得刚才我们发消息的时候携带的是InstallParams个类,InstallParams继承于HandlerParams,所以我们就会知道这三个abstract是在哪里实现的了,我们先说说handleStartCopy(),主要给两个变量完成了赋值工作也个是mArgs也就是InstallArgs,一个是ret标识是否安装成功的。handleServiceError()这个不讲解,然后看handleReturnCode()
- @Override
- void handleReturnCode() {
- // If mArgs is null, then MCS couldn‘t be reached. When it
- // reconnects, it will try again to install. At that point, this
- // will succeed.
- if (mArgs != null) {
- processPendingInstall(mArgs, mRet);
- }
- }
也就是processPendingInstall(mArgs, mRet)
- private void processPendingInstall(final InstallArgs args, final int currentStatus) {
- // Queue up an async operation since the package installation may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- // Result object to be returned
- PackageInstalledInfo res = new PackageInstalledInfo();
- res.returnCode = currentStatus;
- res.uid = -1;
- res.pkg = null;
- res.removedInfo = new PackageRemovedInfo();
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- args.doPreInstall(res.returnCode);
- synchronized (mInstallLock) {
- installPackageLI(args, true, res);
- }
- args.doPostInstall(res.returnCode);
- }
- // A restore should be performed at this point if (a) the install
- // succeeded, (b) the operation is not an update, and (c) the new
- // package has a backupAgent defined.
- final boolean update = res.removedInfo.removedPackage != null;
- boolean doRestore = (!update
- && res.pkg != null
- && res.pkg.applicationInfo.backupAgentName != null);
- // Set up the post-install work request bookkeeping. This will be used
- // and cleaned up by the post-install event handling regardless of whether
- // there‘s a restore pass performed. Token values are >= 1.
- int token;
- if (mNextInstallToken < 0) mNextInstallToken = 1;
- token = mNextInstallToken++;
- PostInstallData data = new PostInstallData(args, res);
- mRunningInstalls.put(token, data);
- if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
- // Pass responsibility to the Backup Manager. It will perform a
- // restore if appropriate, then pass responsibility back to the
- // Package Manager to run the post-install observer callbacks
- // and broadcasts.
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (bm != null) {
- if (DEBUG_INSTALL) Log.v(TAG, "token " + token
- + " to BM for possible restore");
- try {
- bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
- } catch (RemoteException e) {
- // can‘t happen; the backup manager is local
- } catch (Exception e) {
- Slog.e(TAG, "Exception trying to enqueue restore", e);
- doRestore = false;
- }
- } else {
- Slog.e(TAG, "Backup Manager not found!");
- doRestore = false;
- }
- }
- if (!doRestore) {
- // No restore possible, or the Backup Manager was mysteriously not
- // available -- just fire the post-install work request directly.
- if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
- Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
- mHandler.sendMessage(msg);
- }
- }
- });
- }
在这里启动了一个线程进行安装,也就是
- PackageInstalledInfo res = new PackageInstalledInfo();
- res.returnCode = currentStatus;
- res.uid = -1;
- res.pkg = null;
- res.removedInfo = new PackageRemovedInfo();
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- args.doPreInstall(res.returnCode);
- synchronized (mInstallLock) {
- installPackageLI(args, true, res);
- }
- args.doPostInstall(res.returnCode);
- }
也就是installPackageLI(args, true, res),这里代码较多,不再全部列出
- if (replace) {
- replacePackageLI(pkg, parseFlags, scanMode,
- installerPackageName, res);
- } else {
- installNewPackageLI(pkg, parseFlags, scanMode,
- installerPackageName,res);
- }
很明显,如果是第一次安装走installNewPackageLI,如果是更新走replacePackageLI,我们去installNewPackageLI
- /*
- * Install a non-existing package.
- */
- private void installNewPackageLI(PackageParser.Package pkg,
- int parseFlags,
- int scanMode,
- String installerPackageName, PackageInstalledInfo res) {
- // Remember this for later, in case we need to rollback this install
- String pkgName = pkg.packageName;
- boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
- res.name = pkgName;
- synchronized(mPackages) {
- if (mSettings.mRenamedPackages.containsKey(pkgName)) {
- // A package with the same name is already installed, though
- // it has been renamed to an older name. The package we
- // are trying to install should be installed as an update to
- // the existing one, but that has not been requested, so bail.
- Slog.w(TAG, "Attempt to re-install " + pkgName
- + " without first uninstalling package running as "
- + mSettings.mRenamedPackages.get(pkgName));
- res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
- return;
- }
- if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {
- // Don‘t allow installation over an existing package with the same name.
- Slog.w(TAG, "Attempt to re-install " + pkgName
- + " without first uninstalling.");
- res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
- return;
- }
- }
- mLastScanError = PackageManager.INSTALL_SUCCEEDED;
- PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
- System.currentTimeMillis());
- if (newPackage == null) {
- Slog.w(TAG, "Package couldn‘t be installed in " + pkg.mPath);
- if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
- res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
- }
- } else {
- updateSettingsLI(newPackage,
- installerPackageName,
- res);
- // delete the partially installed application. the data directory will have to be
- // restored if it was already existing
- if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- // remove package from internal structures. Note that we want deletePackageX to
- // delete the package data and cache directories that it created in
- // scanPackageLocked, unless those directories existed before we even tried to
- // install.
- deletePackageLI(
- pkgName, false,
- dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,
- res.removedInfo, true);
- }
- }
- }
也就是
- PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
- System.currentTimeMillis());
其他都是判断一下到目前位置是否符合安装条件,也就是PackageManager.INSTALL_SUCCEEDED是否成功,如果成功就继续安装,不成功就重新安装或者返回了。scanPackageLI是一个重构函数,一个首参数是PackageParser.Package,一个首参数是File,我们看第一种,由于scanPackageLI是我们安装包的主要过程,有八百多行,做了很多安装需要的工作,具体在安装时做了什么工作,有兴趣的可以研究一下,这里就不再一一列出。我们只看
- int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.uid);
mInstaller也就是Installer,所以去看看
- public int install(String name, int uid, int gid) {
- StringBuilder builder = new StringBuilder("install");
- builder.append(‘ ‘);
- builder.append(name);
- builder.append(‘ ‘);
- builder.append(uid);
- builder.append(‘ ‘);
- builder.append(gid);
- return execute(builder.toString());
- }
execute也就是
- private int execute(String cmd) {
- String res = transaction(cmd);
- try {
- return Integer.parseInt(res);
- } catch (NumberFormatException ex) {
- return -1;
- }
- }
transaction也就是
- private synchronized String transaction(String cmd) {
- if (!connect()) {
- Slog.e(TAG, "connection failed");
- return "-1";
- }
- if (!writeCommand(cmd)) {
- /*
- * If installd died and restarted in the background (unlikely but
- * possible) we‘ll fail on the next write (this one). Try to
- * reconnect and write the command one more time before giving up.
- */
- Slog.e(TAG, "write command failed? reconnect!");
- if (!connect() || !writeCommand(cmd)) {
- return "-1";
- }
- }
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "send: ‘" + cmd + "‘");
- }
- if (readReply()) {
- String s = new String(buf, 0, buflen);
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "recv: ‘" + s + "‘");
- }
- return s;
- } else {
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "fail");
- }
- return "-1";
- }
- }
writeCommand也就是
- private boolean writeCommand(String _cmd) {
- byte[] cmd = _cmd.getBytes();
- int len = cmd.length;
- if ((len < 1) || (len > 1024))
- return false;
- buf[0] = (byte) (len & 0xff);
- buf[1] = (byte) ((len >> 8) & 0xff);
- try {
- mOut.write(buf, 0, 2);
- mOut.write(cmd, 0, len);
- } catch (IOException ex) {
- Slog.e(TAG, "write error");
- disconnect();
- return false;
- }
- return true;
- }
mOut是什么呢?
- private boolean connect() {
- if (mSocket != null) {
- return true;
- }
- Slog.i(TAG, "connecting...");
- try {
- mSocket = new LocalSocket();
- LocalSocketAddress address = new LocalSocketAddress("installd",
- LocalSocketAddress.Namespace.RESERVED);
- mSocket.connect(address);
- mIn = mSocket.getInputStream();
- mOut = mSocket.getOutputStream();
- } catch (IOException ex) {
- disconnect();
- return false;
- }
- return true;
- }
真实面目,原来这里在用Socket进行通信,把我们要安装的包信息告诉服务器,让服务器来完成余下的工作,这个服务器在底层,完成了一些copy等工作,具体是什么不再深究,如果以后有机会再讲解。
还是那句话给大师取乐,给后来者抛砖引玉,不要在背后骂我就谢天谢地了。
原文:http://blog.csdn.net/zhiyuan263287/article/details/21033309
android PackageInstaller那点事儿