今天简单讲解一下PackageInstaller
文件路径:
下面开始讲解:
首先,我们说一下安装apk的几种方式,整体上可以分为2类,一类是有界面安装,一类是无界面安装。无界面安装分为内置apk开机安装和命令安装,命令安装又分为两类,一类电脑安装也就是adb命令,另一类是手机安装也就是pm命令。今天我们主要介绍有界面安装。
当然,我们从这个安装界面说起,这个界面是那个呢?就是PackageInstallerActivity这个acitvity。它是如何启动起来的呢?我们去看看它在AndroidManifest是如何定义的
1
2
3
|
<intent-filter><category android:name=
"android.intent.category.DEFAULT"
><data android:scheme=
"content"
><data android:scheme=
"file"
>
</data></data></category></action></intent-filter>
</activity>
|
很明显了,我们可以通过android.intent.action.INSTALL_PACKAGE这个action启动,也可以通过android.intent.action.VIEW这个action加上application/vnd.android.package-archive这个type启动,当然不加这个type也能启动,但是会找到很多这样的activity哦。另外,通过类名或包名启动也未尝不可的。所以,大部分启动是这样的
1
2
3
4
5
6
|
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中看看
1
2
3
4
|
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中了
1
2
3
4
5
6
7
8
9
10
11
12
|
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,具体做什么的,进去之后就能明白了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
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文件,如果不是,返回;接着去获取三个关键变量,也就是
1
2
3
|
XmlResourceParser parser =
null
;
AssetManager assmgr =
null
;
Resources res =
null
;
|
这三个是什么呢?这里简单说一下,AssetManager资产管理器,用来管理包中获取到的资源
1
2
|
assmgr =
new
AssetManager();
int
cookie = assmgr.addAssetPath(mArchiveSourcePath);
|
通过addAssetPath可以获取到唯一标识该apk包资产的关键字cookie,也就是通过cookie可以找到该包的资源信息。Resources就是资源了,包括图片,color,xml等资源
1
|
res =
new
Resources(assmgr, metrics,
null
);
|
1
|
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
|
ANDROID_MANIFEST_FILENAME也就是
1
|
private
static
final
String ANDROID_MANIFEST_FILENAME = AndroidManifest.xml;
|
这样就很明显了,这里生成的xml文件资源解析器是用来解析AndroidManifest文件的了。接下来就是关键了
1
2
3
4
5
6
7
8
9
10
|
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为首个参数,就是我们接下来要讲的了,由于这个函数比较大,所以不再全部列出,只选取主要的
1
|
String pkgName = parsePackageName(parser, attrs, flags, outError);
|
获取包名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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;</manifest>
|
解析获取,我们AndroidManifest的attrs,也就是frameworks/base/core/res/res/values下的attrs_manifest.xml中定义的
1
2
3
4
5
6
7
|
<declare-styleable name=
"AndroidManifest"
>
</attr></attr></attr></attr></attr></declare-styleable>
|
这些变量信息。接下来就是一个大循环了,这里解析的内容比较多了,我们举几个常见的例子,如application也就是
1
2
|
</application>
|
这里面包含的信息,例如这里的lable等等,还有以其为父的activity,receiver,service,provider等等,这里以activity为例,还是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<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. -->
<!-- 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"
>
</attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></declare-styleable>
|
这里有很多变量,在定义一个acitivity的时候有的我们用过,有的没有用过,Xml文件资源解析器就是从xml中获取到这先变量的值然后付给这些变量;同样还有permission权限,也就是
1
|
<uses-permission android:name=
"android.permission.INSTALL_PACKAGES"
></uses-permission>
|
这类,大家经常会见到的;还有permission-group,uses-sdk等等吧,有兴趣的可以一一研究。最终这些信息都会囊括到我们的Package中。这里我们明白Package是什么了吧?就是包含包中所有信息的的玩意。到此位置我们的Package已经生成,然后我们还回到PackageInstallerActivity的onCreate中,接着往下看,不重要的就跳过了
1
|
initiateInstall()
|
也就是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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(),也就是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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下面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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);
}
}</string></permissioninfo></permissioninfo></permissioninfo>
|
就是获取到一个PermissionInfo的List就是mPermsList。然后我们看看asp.getPermissionsView(),也就是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
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, permissioninfo=
""
>> dangerousMap =
new
HashMap<string, permissioninfo=
""
>>();
Map<string, permissioninfo=
""
> > normalMap =
new
HashMap<string, 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, 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();
}</permissioninfo></permissioninfo></string,></string,></string,></string,></string,></string,></string,></string,></permissioninfo>
|
这里区分一下是dangerousMap,也就是PermissionInfo.PROTECTION_DANGEROUS类权限还是normalMap一般权限,然后就去showPermissions()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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));
}
}</string></string,>
|
看到这里就很明白了,我们的权限View是怎么生成的了。不再多做解释了。至此我们PackageInstallerActivity这个activity已经完全形成了,截个图吧
接下来,我们说说当点击“安装”之后做了什么事情。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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中
1
2
3
4
5
6
7
8
|
@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()里面是一些布局的初始化,不再赘述,只截取重要的,也就是
1
2
3
4
|
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安装的观察者
1
2
3
4
5
6
7
8
|
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主要标识是第一次安装,还是已经安装更新,第三个参数很明显是安装的包名了。然后我们去看看
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/* 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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,我们去看看这个消息执行了什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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,也就是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
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这个消息,循环,直到都安装完了。然后安装在哪里呢?也就是
1
|
params.startCopy()
|
这个了,进去看看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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;
}
|
还是那句话给大师取乐,给后来者抛砖引玉,不要在背后骂我就谢天谢地了。