android PackageInstaller那点事儿

今天简单讲解一下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 );
当然Resources信息也是通过AssetManager获取到的。XmlResourceParser顾名思义就是Xml资源文件解析器了,用来解析我们xml文件的

 

 

?
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;
}


 

还是那句话给大师取乐,给后来者抛砖引玉,不要在背后骂我就谢天谢地了。

 转载:http://www.2cto.com/kf/201403/285212.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值