转载来源 http://www.myexception.cn/android/1591173.html
作者讲解的很详细,很喜欢,果断转走,哈哈。
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等工作,具体是什么不再深究,如果以后有机会再讲解。
还是那句话给大师取乐,给后来者抛砖引玉,不要在背后骂我就谢天谢地了。