zoukankan      html  css  js  c++  java
  • android PackageInstaller那点事儿

    今天简单讲解一下PackageInstaller

        文件路径:

        packages/apps/PackageInstaller

        frameworks/base/core/java/android/content/pm&res

       下面开始讲解:

        首先,我们说一下安装apk的几种方式,整体上可以分为2类,一类是有界面安装,一类是无界面安装。无界面安装分为内置apk开机安装和命令安装,命令安装又分为两类,一类电脑安装也就是adb命令,另一类是手机安装也就是pm命令。今天我们主要介绍有界面安装。

        当然,我们从这个安装界面说起,这个界面是那个呢?就是PackageInstallerActivity这个acitvity。它是如何启动起来的呢?我们去看看它在AndroidManifest是如何定义的

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <activity android:name=".PackageInstallerActivity"  
    2.         android:configChanges="orientation|keyboardHidden|screenSize"  
    3.         android:excludeFromRecents="true"  
    4.         android:screenOrientation="unspecified">  
    5.     <intent-filter>  
    6.         <action android:name="android.intent.action.VIEW" />  
    7.         <action android:name="android.intent.action.INSTALL_PACKAGE" />  
    8.         <category android:name="android.intent.category.DEFAULT" />  
    9.         <data android:scheme="content" />  
    10.         <data android:scheme="file" />  
    11.         <data android:mimeType="application/vnd.android.package-archive" />  
    12.     </intent-filter>  
    13.     <intent-filter>  
    14.         <action android:name="android.intent.action.INSTALL_PACKAGE" />  
    15.         <category android:name="android.intent.category.DEFAULT" />  
    16.         <data android:scheme="content" />  
    17.         <data android:scheme="file" />  
    18.     </intent-filter>  
    19. </activity>  


    很明显了,我们可以通过android.intent.action.INSTALL_PACKAGE这个action启动,也可以通过android.intent.action.VIEW这个action加上"application/vnd.android.package-archive"这个type启动,当然不加这个type也能启动,但是会找到很多这样的activity哦。另外,通过类名或包名启动也未尝不可的。所以,大部分启动是这样的

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. String apkFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+"/.../packageName.pac";  
    2. File apkFile = new File(apkFileString);  
    3. Intent intent = new Intent(Intent.ACTION_VIEW);  
    4. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
    5. intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");  
    6. mContext.startActivity(intent);  


    这里我们传进去一个数据就是pakFile的Uri,然后我们去PackageInstallerActivity的onCreate中看看

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. final Intent intent = getIntent();  
    2. mPackageURI = intent.getData();  
    3. mPm = getPackageManager();  
    4. mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);  

    获取到,我们刚才传进来的apkFile的Uri给了mPackageURI,接着获取到PackageManager,然后生成一个mPkgInfo也就是PackageParser.Package,这个很重要。我们看看PackageParser.Package是如何生成的,PackageParser.Package里面都包含了什么东西。那我们就要去PackageUtil.getPackageInfo中了

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public static  PackageParser.Package getPackageInfo(Uri packageURI) {  
    2.     final String archiveFilePath = packageURI.getPath();  
    3.     PackageParser packageParser = new PackageParser(archiveFilePath);  
    4.     File sourceFile = new File(archiveFilePath);  
    5.     DisplayMetrics metrics = new DisplayMetrics();  
    6.     metrics.setToDefaults();  
    7.     PackageParser.Package pkg =  packageParser.parsePackage(sourceFile,  
    8.             archiveFilePath, metrics, 0);  
    9.     // Nuke the parser reference.  
    10.     packageParser = null;  
    11.     return pkg;  
    12. }  


    生成一个Package解析器,通过这个解析器来获取到PackageParser.Package中需要的数据,生成一个PackageParser.Package。我们看看PackageParser.parsePackage是如何生成一个PackageParser.Package的,这里传进去四个参数,一个Source File,apk文件,一个apk路径,一个屏幕信息,最后一个0,具体做什么的,进去之后就能明白了

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public Package parsePackage(File sourceFile, String destCodePath,  
    2.         DisplayMetrics metrics, int flags) {  
    3.     mParseError = PackageManager.INSTALL_SUCCEEDED;  
    4.   
    5.     mArchiveSourcePath = sourceFile.getPath();  
    6.     if (!sourceFile.isFile()) {  
    7.         Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);  
    8.         mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;  
    9.         return null;  
    10.     }  
    11.     if (!isPackageFilename(sourceFile.getName())  
    12.             && (flags&PARSE_MUST_BE_APK) != 0) {  
    13.         if ((flags&PARSE_IS_SYSTEM) == 0) {  
    14.             // We expect to have non-.apk files in the system dir,  
    15.             // so don't warn about them.  
    16.             Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);  
    17.         }  
    18.         mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;  
    19.         return null;  
    20.     }  
    21.   
    22.     if (DEBUG_JAR)  
    23.         Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);  
    24.   
    25.     XmlResourceParser parser = null;  
    26.     AssetManager assmgr = null;  
    27.     Resources res = null;  
    28.     boolean assetError = true;  
    29.     try {  
    30.         assmgr = new AssetManager();  
    31.         int cookie = assmgr.addAssetPath(mArchiveSourcePath);  
    32.         if (cookie != 0) {  
    33.             res = new Resources(assmgr, metrics, null);  
    34.             assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
    35.                     Build.VERSION.RESOURCES_SDK_INT);  
    36.             parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);  
    37.             assetError = false;  
    38.         } else {  
    39.             Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);  
    40.         }  
    41.     } catch (Exception e) {  
    42.         Slog.w(TAG, "Unable to read AndroidManifest.xml of "  
    43.                 + mArchiveSourcePath, e);  
    44.     }  
    45.     if (assetError) {  
    46.         if (assmgr != null) assmgr.close();  
    47.         mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;  
    48.         return null;  
    49.     }  
    50.     String[] errorText = new String[1];  
    51.     Package pkg = null;  
    52.     Exception errorException = null;  
    53.     try {  
    54.         // XXXX todo: need to figure out correct configuration.  
    55.         pkg = parsePackage(res, parser, flags, errorText);  
    56.     } catch (Exception e) {  
    57.         errorException = e;  
    58.         mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;  
    59.     }  
    60.   
    61.   
    62.     if (pkg == null) {  
    63.         // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED  
    64.         // just means to skip this app so don't make a fuss about it.  
    65.         if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {  
    66.             if (errorException != null) {  
    67.                 Slog.w(TAG, mArchiveSourcePath, errorException);  
    68.             } else {  
    69.                 Slog.w(TAG, mArchiveSourcePath + " (at "  
    70.                         + parser.getPositionDescription()  
    71.                         + "): " + errorText[0]);  
    72.             }  
    73.             if (mParseError == PackageManager.INSTALL_SUCCEEDED) {  
    74.                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;  
    75.             }  
    76.         }  
    77.         parser.close();  
    78.         assmgr.close();  
    79.         return null;  
    80.     }  
    81.   
    82.     parser.close();  
    83.     assmgr.close();  
    84.   
    85.     // Set code and resource paths  
    86.     pkg.mPath = destCodePath;  
    87.     pkg.mScanPath = mArchiveSourcePath;  
    88.     //pkg.applicationInfo.sourceDir = destCodePath;  
    89.     //pkg.applicationInfo.publicSourceDir = destRes;  
    90.     pkg.mSignatures = null;  
    91.   
    92.     return pkg;  
    93. }  


    首先sourceFile.isFile()判断一下是不是文件,如果不是,返回;接着isPackageFilename(sourceFile.getName())判断是不是apk文件,如果不是,返回;接着去获取三个关键变量,也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. XmlResourceParser parser = null;  
    2. AssetManager assmgr = null;  
    3. Resources res = null;  


    这三个是什么呢?这里简单说一下,AssetManager资产管理器,用来管理包中获取到的资源

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. assmgr = new AssetManager();  
    2. int cookie = assmgr.addAssetPath(mArchiveSourcePath);  


    通过addAssetPath可以获取到唯一标识该apk包资产的关键字cookie,也就是通过cookie可以找到该包的资源信息。Resources就是资源了,包括图片,color,xml等资源

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. res = new Resources(assmgr, metrics, null);  

    当然Resources信息也是通过AssetManager获取到的。XmlResourceParser顾名思义就是Xml资源文件解析器了,用来解析我们xml文件的

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);  


    ANDROID_MANIFEST_FILENAME也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";  


    这样就很明显了,这里生成的xml文件资源解析器是用来解析AndroidManifest文件的了。接下来就是关键了

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. String[] errorText = new String[1];  
    2. Package pkg = null;  
    3. Exception errorException = null;  
    4. try {  
    5.     // XXXX todo: need to figure out correct configuration.  
    6.     pkg = parsePackage(res, parser, flags, errorText);  
    7. catch (Exception e) {  
    8.     errorException = e;  
    9.     mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;  
    10. }  


    这里才是我们Package真正生成的地方了,也就是pkg = parsePackage(res, parser, flags, errorText)了。parsePackage是同构函数,一个是以File为首个参数,就是我们现在分析的这个,一个是以Resources为首个参数,就是我们接下来要讲的了,由于这个函数比较大,所以不再全部列出,只选取主要的

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. String pkgName = parsePackageName(parser, attrs, flags, outError);  


    获取包名。

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. final Package pkg = new Package(pkgName);  
    2. boolean foundApp = false;  
    3.   
    4. TypedArray sa = res.obtainAttributes(attrs,  
    5.         com.android.internal.R.styleable.AndroidManifest);  
    6. pkg.mVersionCode = sa.getInteger(  
    7.         com.android.internal.R.styleable.AndroidManifest_versionCode, 0);  
    8. pkg.mVersionName = sa.getNonConfigurationString(  
    9.         com.android.internal.R.styleable.AndroidManifest_versionName, 0);  
    10. if (pkg.mVersionName != null) {  
    11.     pkg.mVersionName = pkg.mVersionName.intern();  
    12. }  
    13. String str = sa.getNonConfigurationString(  
    14.         com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);  
    15. if (str != null && str.length() > 0) {  
    16.     String nameError = validateName(str, true);  
    17.     if (nameError != null && !"android".equals(pkgName)) {  
    18.         outError[0] = "<manifest> specifies bad sharedUserId name ""  
    19.             + str + "": " + nameError;  
    20.         mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;  
    21.         return null;  
    22.     }  
    23.     pkg.mSharedUserId = str.intern();  
    24.     pkg.mSharedUserLabel = sa.getResourceId(  
    25.             com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);  
    26. }  
    27. sa.recycle();  
    28.   
    29. pkg.installLocation = sa.getInteger(  
    30.         com.android.internal.R.styleable.AndroidManifest_installLocation,  
    31.         PARSE_DEFAULT_INSTALL_LOCATION);  
    32. pkg.applicationInfo.installLocation = pkg.installLocation;  


    解析获取,我们AndroidManifest的attrs,也就是frameworks/base/core/res/res/values下的attrs_manifest.xml中定义的

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <declare-styleable name="AndroidManifest">  
    2.     <attr name="versionCode" />  
    3.     <attr name="versionName" />  
    4.     <attr name="sharedUserId" />  
    5.     <attr name="sharedUserLabel" />  
    6.     <attr name="installLocation" />  
    7. </declare-styleable>  


    这些变量信息。接下来就是一个大循环了,这里解析的内容比较多了,我们举几个常见的例子,如"application"也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <application android:label="@string/app_name">  
    2. </application>  

    这里面包含的信息,例如这里的lable等等,还有以其为父的"activity","receiver","service","provider"等等,这里以"activity"为例,还是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <declare-styleable name="AndroidManifestActivity" parent="AndroidManifestApplication">  
    2.     <!-- Required name of the class implementing the activity, deriving from  
    3.         {@link android.app.Activity}.  This is a fully  
    4.         qualified class name (for example, com.mycompany.myapp.MyActivity); as a  
    5.         short-hand if the first character of the class  
    6.         is a period then it is appended to your package name. -->  
    7.     <attr name="name" />  
    8.     <attr name="theme" />  
    9.     <attr name="label" />  
    10.     <attr name="description" />  
    11.     <attr name="icon" />  
    12.     <attr name="logo" />  
    13.     <attr name="launchMode" />  
    14.     <attr name="screenOrientation" />  
    15.     <attr name="configChanges" />  
    16.     <attr name="permission" />  
    17.     <attr name="multiprocess" />  
    18.     <attr name="process" />  
    19.     <attr name="taskAffinity" />  
    20.     <attr name="allowTaskReparenting" />  
    21.     <attr name="finishOnTaskLaunch" />  
    22.     <attr name="finishOnCloseSystemDialogs" />  
    23.     <attr name="clearTaskOnLaunch" />  
    24.     <attr name="noHistory" />  
    25.     <attr name="alwaysRetainTaskState" />  
    26.     <attr name="stateNotNeeded" />  
    27.     <attr name="excludeFromRecents" />  
    28.     <!-- Specify whether the activity is enabled or not (that is, can be instantiated by the system).  
    29.          It can also be specified for an application as a whole, in which case a value of "false"  
    30.          will override any component specific values (a value of "true" will not override the  
    31.          component specific values). -->  
    32.     <attr name="enabled" />  
    33.     <attr name="exported" />  
    34.     <!-- Specify the default soft-input mode for the main window of  
    35.          this activity.  A value besides "unspecified" here overrides  
    36.          any value in the theme. -->  
    37.     <attr name="windowSoftInputMode" />  
    38.     <attr name="immersive" />  
    39.     <attr name="hardwareAccelerated" />  
    40.     <attr name="uiOptions" />  
    41. </declare-styleable>  


    这里有很多变量,在定义一个acitivity的时候有的我们用过,有的没有用过,Xml文件资源解析器就是从xml中获取到这先变量的值然后付给这些变量;同样还有"permission"权限,也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <uses-permission android:name="android.permission.INSTALL_PACKAGES" />  

    这类,大家经常会见到的;还有"permission-group","uses-sdk"等等吧,有兴趣的可以一一研究。最终这些信息都会囊括到我们的Package中。这里我们明白Package是什么了吧?就是包含包中所有信息的的玩意。到此位置我们的Package已经生成,然后我们还回到PackageInstallerActivity的onCreate中,接着往下看,不重要的就跳过了

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. initiateInstall()  

    也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private void initiateInstall() {  
    2.     String pkgName = mPkgInfo.packageName;  
    3.     // Check if there is already a package on the device with this name  
    4.     // but it has been renamed to something else.  
    5.     String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });  
    6.     if (oldName != null && oldName.length > 0 && oldName[0] != null) {  
    7.         pkgName = oldName[0];  
    8.         mPkgInfo.setPackageName(pkgName);  
    9.     }  
    10.     // Check if package is already installed. display confirmation dialog if replacing pkg  
    11.     try {  
    12.         mAppInfo = mPm.getApplicationInfo(pkgName,  
    13.                 PackageManager.GET_UNINSTALLED_PACKAGES);  
    14.     } catch (NameNotFoundException e) {  
    15.         mAppInfo = null;  
    16.     }  
    17.     if (mAppInfo == null || getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_REPLACE, false)) {  
    18.         startInstallConfirm();  
    19.     } else {  
    20.         if(localLOGV) Log.i(TAG, "Replacing existing package:"+  
    21.                 mPkgInfo.applicationInfo.packageName);  
    22.         showDialogInner(DLG_REPLACE_APP);  
    23.     }  
    24. }  


    然后是这里的startInstallConfirm(),也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private void startInstallConfirm() {  
    2.     LinearLayout permsSection = (LinearLayout) mInstallConfirm.findViewById(R.id.permissions_section);  
    3.     LinearLayout securityList = (LinearLayout) permsSection.findViewById(  
    4.             R.id.security_settings_list);  
    5.     boolean permVisible = false;  
    6.     if(mPkgInfo != null) {  
    7.         AppSecurityPermissions asp = new AppSecurityPermissions(this, mPkgInfo);  
    8.         if(asp.getPermissionCount() > 0) {  
    9.             permVisible = true;  
    10.             securityList.addView(asp.getPermissionsView());  
    11.         }  
    12.     }  
    13.     if(!permVisible){  
    14.         permsSection.setVisibility(View.INVISIBLE);  
    15.     }  
    16.     mInstallConfirm.setVisibility(View.VISIBLE);  
    17.     mOk = (Button)findViewById(R.id.ok_button);  
    18.     mCancel = (Button)findViewById(R.id.cancel_button);  
    19.     mOk.setOnClickListener(this);  
    20.     mCancel.setOnClickListener(this);  
    21. }  


    到这里我们的PackageInstallerActivity这个activity才算完成,这里我们看看我们安装界面的权限View是如何生成的,也就是asp.getPermissionsView(),这里的AppSecurityPermissions(this, mPkgInfo)传进去两个参数,一个是Context,一个是我们刚才获取到的Package,我们进去看看,文件在frameworks/base/core/java/android/widget下面

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public AppSecurityPermissions(Context context, PackageParser.Package pkg) {  
    2.     mContext = context;  
    3.     mPm = mContext.getPackageManager();  
    4.     mPermsList = new ArrayList<PermissionInfo>();  
    5.     Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();  
    6.     if(pkg == null) {  
    7.         return;  
    8.     }  
    9.     // Get requested permissions  
    10.     if (pkg.requestedPermissions != null) {  
    11.         ArrayList<String> strList = pkg.requestedPermissions;  
    12.         int size = strList.size();  
    13.         if (size > 0) {  
    14.             extractPerms(strList.toArray(new String[size]), permSet);  
    15.         }  
    16.     }  
    17.     // Get permissions related to  shared user if any  
    18.     if(pkg.mSharedUserId != null) {  
    19.         int sharedUid;  
    20.         try {  
    21.             sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId);  
    22.             getAllUsedPermissions(sharedUid, permSet);  
    23.         } catch (NameNotFoundException e) {  
    24.             Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName);  
    25.         }  
    26.     }  
    27.     // Retrieve list of permissions  
    28.     for(PermissionInfo tmpInfo : permSet) {  
    29.         mPermsList.add(tmpInfo);  
    30.     }  
    31. }  


    就是获取到一个PermissionInfo的List就是mPermsList。然后我们看看asp.getPermissionsView(),也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public View getPermissionsView() {  
    2.       
    3.     mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
    4.     mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);  
    5.     mShowMore = mPermsView.findViewById(R.id.show_more);  
    6.     mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon);  
    7.     mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text);  
    8.     mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list);  
    9.     mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list);  
    10.     mNoPermsView = mPermsView.findViewById(R.id.no_permissions);  
    11.   
    12.     // Set up the LinearLayout that acts like a list item.  
    13.     mShowMore.setClickable(true);  
    14.     mShowMore.setOnClickListener(this);  
    15.     mShowMore.setFocusable(true);  
    16.   
    17.     // Pick up from framework resources instead.  
    18.     mDefaultGrpLabel = mContext.getString(R.string.default_permission_group);  
    19.     mPermFormat = mContext.getString(R.string.permissions_format);  
    20.     mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);  
    21.     mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);  
    22.     mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark);  
    23.     mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark);  
    24.       
    25.     // Set permissions view  
    26.     setPermissions(mPermsList);  
    27.     return mPermsView;  
    28. }  


    这里就是我们的权限View的布局了,也就是frameworks/base/core/res/res/layout下面的app_perms_summary.xml布局了,如果我们想修改权限VIew的话就要从这里开始了。我们去看看是如何生成的,也就是setPermissions(mPermsList)

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private void setPermissions(List<PermissionInfo> permList) {  
    2.     mGroupLabelCache = new HashMap<String, CharSequence>();  
    3.     //add the default label so that uncategorized permissions can go here  
    4.     mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel);  
    5.       
    6.     // Map containing group names and a list of permissions under that group  
    7.     // categorized as dangerous  
    8.     mDangerousMap = new HashMap<String, String>();  
    9.     // Map containing group names and a list of permissions under that group  
    10.     // categorized as normal  
    11.     mNormalMap = new HashMap<String, String>();  
    12.       
    13.     // Additional structures needed to ensure that permissions are unique under   
    14.     // each group  
    15.     Map<String, List<PermissionInfo>> dangerousMap =   
    16.         new HashMap<String,  List<PermissionInfo>>();  
    17.     Map<String, List<PermissionInfo> > normalMap =   
    18.         new HashMap<String,  List<PermissionInfo>>();  
    19.     PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);  
    20.       
    21.     if (permList != null) {  
    22.         // First pass to group permissions  
    23.         for (PermissionInfo pInfo : permList) {  
    24.             if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);  
    25.             if(!isDisplayablePermission(pInfo)) {  
    26.                 if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");  
    27.                 continue;  
    28.             }  
    29.             Map<String, List<PermissionInfo> > permInfoMap =  
    30.                 (pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) ?  
    31.                         dangerousMap : normalMap;  
    32.             String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group;  
    33.             if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName);  
    34.             List<PermissionInfo> grpPermsList = permInfoMap.get(grpName);  
    35.             if(grpPermsList == null) {  
    36.                 grpPermsList = new ArrayList<PermissionInfo>();  
    37.                 permInfoMap.put(grpName, grpPermsList);  
    38.                 grpPermsList.add(pInfo);  
    39.             } else {  
    40.                 int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator);  
    41.                 if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size());  
    42.                 if (idx < 0) {  
    43.                     idx = -idx-1;  
    44.                     grpPermsList.add(idx, pInfo);  
    45.                 }  
    46.             }  
    47.         }  
    48.         // Second pass to actually form the descriptions  
    49.         // Look at dangerous permissions first  
    50.         aggregateGroupDescs(dangerousMap, mDangerousMap);  
    51.         aggregateGroupDescs(normalMap, mNormalMap);  
    52.     }  
    53.   
    54.     mCurrentState = State.NO_PERMS;  
    55.     if(mDangerousMap.size() > 0) {  
    56.         mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY;  
    57.     } else if(mNormalMap.size() > 0) {  
    58.         mCurrentState = State.NORMAL_ONLY;  
    59.     }  
    60.     if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState);  
    61.     showPermissions();  
    62. }  


    这里区分一下是dangerousMap,也就是PermissionInfo.PROTECTION_DANGEROUS类权限还是normalMap一般权限,然后就去showPermissions()

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private void showPermissions() {  
    2.   
    3.     switch(mCurrentState) {  
    4.     case NO_PERMS:  
    5.         displayNoPermissions();  
    6.         break;  
    7.   
    8.     case DANGEROUS_ONLY:  
    9.         displayPermissions(true);  
    10.         break;  
    11.   
    12.     case NORMAL_ONLY:  
    13.         displayPermissions(false);  
    14.         break;  
    15.   
    16.     case BOTH:  
    17.         displayPermissions(true);  
    18.         if (mExpanded) {  
    19.             displayPermissions(false);  
    20.             mShowMoreIcon.setImageDrawable(mShowMaxIcon);  
    21.             mShowMoreText.setText(R.string.perms_hide);  
    22.             mNonDangerousList.setVisibility(View.VISIBLE);  
    23.         } else {  
    24.             mShowMoreIcon.setImageDrawable(mShowMinIcon);  
    25.             mShowMoreText.setText(R.string.perms_show_all);  
    26.             mNonDangerousList.setVisibility(View.GONE);  
    27.         }  
    28.         mShowMore.setVisibility(View.VISIBLE);  
    29.         break;  
    30.     }  
    31. }  


    给我们的布局赋显示的内容了,这里不一一解释,我们去看看displayPermissions

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private void displayPermissions(boolean dangerous) {  
    2.     Map<String, String> permInfoMap = dangerous ? mDangerousMap : mNormalMap;  
    3.     LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList;  
    4.     permListView.removeAllViews();  
    5.   
    6.     Set<String> permInfoStrSet = permInfoMap.keySet();  
    7.     for (String loopPermGrpInfoStr : permInfoStrSet) {  
    8.         CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr);  
    9.         //guaranteed that grpLabel wont be null since permissions without groups  
    10.         //will belong to the default group  
    11.         if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:"  
    12.                 + permInfoMap.get(loopPermGrpInfoStr));  
    13.         permListView.addView(getPermissionItemView(grpLabel,  
    14.                 permInfoMap.get(loopPermGrpInfoStr), dangerous));  
    15.     }  
    16. }  


    看到这里就很明白了,我们的权限View是怎么生成的了。不再多做解释了。至此我们PackageInstallerActivity这个activity已经完全形成了,截个图吧

    接下来,我们说说当点击“安装”之后做了什么事情。

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public void onClick(View v) {  
    2.     if(v == mOk) {  
    3.         // Start subactivity to actually install the application  
    4.         Intent newIntent = new Intent();  
    5.         newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,  
    6.                 mPkgInfo.applicationInfo);  
    7.         newIntent.setData(mPackageURI);  
    8.         newIntent.setClass(this, InstallAppProgress.class);  
    9.         String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);  
    10.         if (installerPackageName != null) {  
    11.             newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);  
    12.         }  
    13.         if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {  
    14.             newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);  
    15.             newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);  
    16.         }  
    17.         if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);  
    18.         startActivity(newIntent);  
    19.         finish();  
    20.     } else if(v == mCancel) {  
    21.         // Cancel and finish  
    22.         setResult(RESULT_CANCELED);  
    23.         finish();  
    24.     }  
    25. }  


    去启动了另外一个acitvity也就是InstallAppProgress,并过去几个数据,主要是mPkgInfo.applicationInfo也就是ApplicationInfo,还有mPackageURI也就是apkFile的Uri,还有一些其他的数据。然后我们就去InstallAppProgress的onCreate中

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. @Override  
    2. public void onCreate(Bundle icicle) {  
    3.     super.onCreate(icicle);  
    4.     Intent intent = getIntent();  
    5.     mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);  
    6.     mPackageURI = intent.getData();  
    7.     initView();  
    8. }  


    获取到传过来的两个数据,然后就initView(),initView()里面是一些布局的初始化,不再赘述,只截取重要的,也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. String installerPackageName = getIntent().getStringExtra(  
    2.         Intent.EXTRA_INSTALLER_PACKAGE_NAME);  
    3. PackageInstallObserver observer = new PackageInstallObserver();  
    4. pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);  


    pm是PackageManager,这样我们就去PackageManager.installPackage中,在PackageManager的installPackage是abstract函数,具体实现在PackageManagerService中,这里传进去的参数第一个我们已经知道了,第二个是package安装的观察者

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. class PackageInstallObserver extends IPackageInstallObserver.Stub {  
    2.     public void packageInstalled(String packageName, int returnCode) {  
    3.         Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);  
    4.         Log.d("packageInstalled", "returnCode = "+returnCode);  
    5.         msg.arg1 = returnCode;  
    6.         mHandler.sendMessage(msg);  
    7.     }  
    8. }  


    当安装完成就会走到packageInstalled个函数中,第三个参数是flag主要标识是第一次安装,还是已经安装更新,第三个参数很明显是安装的包名了。然后我们去看看

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. /* Called when a downloaded package installation has been confirmed by the user */  
    2. public void installPackage(  
    3.         final Uri packageURI, final IPackageInstallObserver observer, final int flags) {  
    4.     installPackage(packageURI, observer, flags, null);  
    5. }  
    6.   
    7. /* Called when a downloaded package installation has been confirmed by the user */  
    8. public void installPackage(  
    9.         final Uri packageURI, final IPackageInstallObserver observer, final int flags,  
    10.         final String installerPackageName) {  
    11.     installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,  
    12.             null);  
    13. }  


    也就是installPackageWithVerification

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,  
    2.         int flags, String installerPackageName, Uri verificationURI,  
    3.         ManifestDigest manifestDigest) {  
    4.     mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);  
    5.   
    6.     final int uid = Binder.getCallingUid();  
    7.   
    8.     final int filteredFlags;  
    9.   
    10.     if (uid == Process.SHELL_UID || uid == 0) {  
    11.         if (DEBUG_INSTALL) {  
    12.             Slog.v(TAG, "Install from ADB");  
    13.         }  
    14.         filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;  
    15.     } else {  
    16.         filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;  
    17.     }  
    18.   
    19.     final Message msg = mHandler.obtainMessage(INIT_COPY);  
    20.     msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,  
    21.             verificationURI, manifestDigest);  
    22.     mHandler.sendMessage(msg);  
    23. }  


    就去发了一个消息INIT_COPY,并携带了我们传进来的参数组成的一个类InstallParams,InstallParams继承于HandlerParams,我们去看看这个消息执行了什么

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. case INIT_COPY: {  
    2.     if (DEBUG_INSTALL) Slog.i(TAG, "init_copy");  
    3.     HandlerParams params = (HandlerParams) msg.obj;  
    4.     int idx = mPendingInstalls.size();  
    5.     if (DEBUG_INSTALL) Slog.i(TAG, "idx=" + idx);  
    6.     // If a bind was already initiated we dont really  
    7.     // need to do anything. The pending install  
    8.     // will be processed later on.  
    9.     if (!mBound) {  
    10.         // If this is the only one pending we might  
    11.         // have to bind to the service again.  
    12.         if (!connectToService()) {  
    13.             Slog.e(TAG, "Failed to bind to media container service");  
    14.             params.serviceError();  
    15.             return;  
    16.         } else {  
    17.             // Once we bind to the service, the first  
    18.             // pending request will be processed.  
    19.             mPendingInstalls.add(idx, params);  
    20.         }  
    21.     } else {  
    22.         mPendingInstalls.add(idx, params);  
    23.         // Already bound to the service. Just make  
    24.         // sure we trigger off processing the first request.  
    25.         if (idx == 0) {  
    26.             mHandler.sendEmptyMessage(MCS_BOUND);  
    27.         }  
    28.     }  
    29.     break;  
    30. }  


    这里先 mPendingInstalls.add(idx, params)把我们要安装的信息放到HandlerParams的一个List中mPendingInstalls,然后去发了一个消息MCS_BOUND,也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. case MCS_BOUND: {  
    2.     if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");  
    3.     if (msg.obj != null) {  
    4.         mContainerService = (IMediaContainerService) msg.obj;  
    5.     }  
    6.     if (mContainerService == null) {  
    7.         // Something seriously wrong. Bail out  
    8.         Slog.e(TAG, "Cannot bind to media container service");  
    9.         for (HandlerParams params : mPendingInstalls) {  
    10.             mPendingInstalls.remove(0);  
    11.             // Indicate service bind error  
    12.             params.serviceError();  
    13.         }  
    14.         mPendingInstalls.clear();  
    15.     } else if (mPendingInstalls.size() > 0) {  
    16.         HandlerParams params = mPendingInstalls.get(0);  
    17.         if (params != null) {  
    18.             if (params.startCopy()) {  
    19.                 // We are done...  look for more work or to  
    20.                 // go idle.  
    21.                 if (DEBUG_SD_INSTALL) Log.i(TAG,  
    22.                         "Checking for more work or unbind...");  
    23.                 // Delete pending install  
    24.                 if (mPendingInstalls.size() > 0) {  
    25.                     mPendingInstalls.remove(0);  
    26.                 }  
    27.                 if (mPendingInstalls.size() == 0) {  
    28.                     if (mBound) {  
    29.                         if (DEBUG_SD_INSTALL) Log.i(TAG,  
    30.                                 "Posting delayed MCS_UNBIND");  
    31.                         removeMessages(MCS_UNBIND);  
    32.                         Message ubmsg = obtainMessage(MCS_UNBIND);  
    33.                         // Unbind after a little delay, to avoid  
    34.                         // continual thrashing.  
    35.                         sendMessageDelayed(ubmsg, 10000);  
    36.                     }  
    37.                 } else {  
    38.                     // There are more pending requests in queue.  
    39.                     // Just post MCS_BOUND message to trigger processing  
    40.                     // of next pending install.  
    41.                     if (DEBUG_SD_INSTALL) Log.i(TAG,  
    42.                             "Posting MCS_BOUND for next woek");  
    43.                     mHandler.sendEmptyMessage(MCS_BOUND);  
    44.                 }  
    45.             }  
    46.         }  
    47.     } else {  
    48.         // Should never happen ideally.  
    49.         Slog.w(TAG, "Empty queue");  
    50.     }  
    51.     break;  
    52. }  


    HandlerParams params = mPendingInstalls.get(0)读取出我们要安装的包信息,然后清楚该包信息,如果还有其他包就继续发MCS_BOUND这个消息,循环,直到都安装完了。然后安装在哪里呢?也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. params.startCopy()  

    这个了,进去看看

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. final boolean startCopy() {  
    2.     boolean res;  
    3.     try {  
    4.         if (DEBUG_INSTALL) Slog.i(TAG, "startCopy");  
    5.   
    6.         if (++mRetries > MAX_RETRIES) {  
    7.             Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");  
    8.             mHandler.sendEmptyMessage(MCS_GIVE_UP);  
    9.             handleServiceError();  
    10.             return false;  
    11.         } else {  
    12.             handleStartCopy();  
    13.             res = true;  
    14.         }  
    15.     } catch (RemoteException e) {  
    16.         if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");  
    17.         mHandler.sendEmptyMessage(MCS_RECONNECT);  
    18.         res = false;  
    19.     }  
    20.     handleReturnCode();  
    21.     return res;  
    22. }  

    这里的handleStartCopy()和handleServiceError()和handleReturnCode()都是abstract函数,希望大家还记得刚才我们发消息的时候携带的是InstallParams个类,InstallParams继承于HandlerParams,所以我们就会知道这三个abstract是在哪里实现的了,我们先说说handleStartCopy(),主要给两个变量完成了赋值工作也个是mArgs也就是InstallArgs,一个是ret标识是否安装成功的。handleServiceError()这个不讲解,然后看handleReturnCode()

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. @Override  
    2. void handleReturnCode() {  
    3.     // If mArgs is null, then MCS couldn't be reached. When it  
    4.     // reconnects, it will try again to install. At that point, this  
    5.     // will succeed.  
    6.     if (mArgs != null) {  
    7.         processPendingInstall(mArgs, mRet);  
    8.     }  
    9. }  


    也就是processPendingInstall(mArgs, mRet)

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private void processPendingInstall(final InstallArgs args, final int currentStatus) {  
    2.     // Queue up an async operation since the package installation may take a little while.  
    3.     mHandler.post(new Runnable() {  
    4.         public void run() {  
    5.             mHandler.removeCallbacks(this);  
    6.              // Result object to be returned  
    7.             PackageInstalledInfo res = new PackageInstalledInfo();  
    8.             res.returnCode = currentStatus;  
    9.             res.uid = -1;  
    10.             res.pkg = null;  
    11.             res.removedInfo = new PackageRemovedInfo();  
    12.             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {  
    13.                 args.doPreInstall(res.returnCode);  
    14.                 synchronized (mInstallLock) {  
    15.                     installPackageLI(args, true, res);  
    16.                 }  
    17.                 args.doPostInstall(res.returnCode);  
    18.             }  
    19.   
    20.             // A restore should be performed at this point if (a) the install  
    21.             // succeeded, (b) the operation is not an update, and (c) the new  
    22.             // package has a backupAgent defined.  
    23.             final boolean update = res.removedInfo.removedPackage != null;  
    24.             boolean doRestore = (!update  
    25.                     && res.pkg != null  
    26.                     && res.pkg.applicationInfo.backupAgentName != null);  
    27.   
    28.             // Set up the post-install work request bookkeeping.  This will be used  
    29.             // and cleaned up by the post-install event handling regardless of whether  
    30.             // there's a restore pass performed.  Token values are >= 1.  
    31.             int token;  
    32.             if (mNextInstallToken < 0) mNextInstallToken = 1;  
    33.             token = mNextInstallToken++;  
    34.   
    35.             PostInstallData data = new PostInstallData(args, res);  
    36.             mRunningInstalls.put(token, data);  
    37.             if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);  
    38.   
    39.             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {  
    40.                 // Pass responsibility to the Backup Manager.  It will perform a  
    41.                 // restore if appropriate, then pass responsibility back to the  
    42.                 // Package Manager to run the post-install observer callbacks  
    43.                 // and broadcasts.  
    44.                 IBackupManager bm = IBackupManager.Stub.asInterface(  
    45.                         ServiceManager.getService(Context.BACKUP_SERVICE));  
    46.                 if (bm != null) {  
    47.                     if (DEBUG_INSTALL) Log.v(TAG, "token " + token  
    48.                             + " to BM for possible restore");  
    49.                     try {  
    50.                         bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);  
    51.                     } catch (RemoteException e) {  
    52.                         // can't happen; the backup manager is local  
    53.                     } catch (Exception e) {  
    54.                         Slog.e(TAG, "Exception trying to enqueue restore", e);  
    55.                         doRestore = false;  
    56.                     }  
    57.                 } else {  
    58.                     Slog.e(TAG, "Backup Manager not found!");  
    59.                     doRestore = false;  
    60.                 }  
    61.             }  
    62.   
    63.             if (!doRestore) {  
    64.                 // No restore possible, or the Backup Manager was mysteriously not  
    65.                 // available -- just fire the post-install work request directly.  
    66.                 if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);  
    67.                 Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);  
    68.                 mHandler.sendMessage(msg);  
    69.             }  
    70.         }  
    71.     });  
    72. }  


    在这里启动了一个线程进行安装,也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. PackageInstalledInfo res = new PackageInstalledInfo();  
    2. res.returnCode = currentStatus;  
    3. res.uid = -1;  
    4. res.pkg = null;  
    5. res.removedInfo = new PackageRemovedInfo();  
    6. if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {  
    7.     args.doPreInstall(res.returnCode);  
    8.     synchronized (mInstallLock) {  
    9.         installPackageLI(args, true, res);  
    10.     }  
    11.     args.doPostInstall(res.returnCode);  
    12. }  


    也就是installPackageLI(args, true, res),这里代码较多,不再全部列出

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. if (replace) {  
    2.     replacePackageLI(pkg, parseFlags, scanMode,  
    3.             installerPackageName, res);  
    4. else {  
    5.     installNewPackageLI(pkg, parseFlags, scanMode,  
    6.             installerPackageName,res);  
    7. }  


    很明显,如果是第一次安装走installNewPackageLI,如果是更新走replacePackageLI,我们去installNewPackageLI

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. /* 
    2.  * Install a non-existing package. 
    3.  */  
    4. private void installNewPackageLI(PackageParser.Package pkg,  
    5.         int parseFlags,  
    6.         int scanMode,  
    7.         String installerPackageName, PackageInstalledInfo res) {  
    8.     // Remember this for later, in case we need to rollback this install  
    9.     String pkgName = pkg.packageName;  
    10.   
    11.     boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();  
    12.     res.name = pkgName;  
    13.     synchronized(mPackages) {  
    14.         if (mSettings.mRenamedPackages.containsKey(pkgName)) {  
    15.             // A package with the same name is already installed, though  
    16.             // it has been renamed to an older name.  The package we  
    17.             // are trying to install should be installed as an update to  
    18.             // the existing one, but that has not been requested, so bail.  
    19.             Slog.w(TAG, "Attempt to re-install " + pkgName  
    20.                     + " without first uninstalling package running as "  
    21.                     + mSettings.mRenamedPackages.get(pkgName));  
    22.             res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;  
    23.             return;  
    24.         }  
    25.         if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {  
    26.             // Don't allow installation over an existing package with the same name.  
    27.             Slog.w(TAG, "Attempt to re-install " + pkgName  
    28.                     + " without first uninstalling.");  
    29.             res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;  
    30.             return;  
    31.         }  
    32.     }  
    33.     mLastScanError = PackageManager.INSTALL_SUCCEEDED;  
    34.     PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,  
    35.             System.currentTimeMillis());  
    36.     if (newPackage == null) {  
    37.         Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);  
    38.         if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {  
    39.             res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;  
    40.         }  
    41.     } else {  
    42.         updateSettingsLI(newPackage,  
    43.                 installerPackageName,  
    44.                 res);  
    45.         // delete the partially installed application. the data directory will have to be  
    46.         // restored if it was already existing  
    47.         if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {  
    48.             // remove package from internal structures.  Note that we want deletePackageX to  
    49.             // delete the package data and cache directories that it created in  
    50.             // scanPackageLocked, unless those directories existed before we even tried to  
    51.             // install.  
    52.             deletePackageLI(  
    53.                     pkgName, false,  
    54.                     dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,  
    55.                             res.removedInfo, true);  
    56.         }  
    57.     }  
    58. }  


    也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,  
    2.         System.currentTimeMillis());  


    其他都是判断一下到目前位置是否符合安装条件,也就是PackageManager.INSTALL_SUCCEEDED是否成功,如果成功就继续安装,不成功就重新安装或者返回了。scanPackageLI是一个重构函数,一个首参数是PackageParser.Package,一个首参数是File,我们看第一种,由于scanPackageLI是我们安装包的主要过程,有八百多行,做了很多安装需要的工作,具体在安装时做了什么工作,有兴趣的可以研究一下,这里就不再一一列出。我们只看

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,  
    2.         pkg.applicationInfo.uid);  


    mInstaller也就是Installer,所以去看看

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public int install(String name, int uid, int gid) {  
    2.     StringBuilder builder = new StringBuilder("install");  
    3.     builder.append(' ');  
    4.     builder.append(name);  
    5.     builder.append(' ');  
    6.     builder.append(uid);  
    7.     builder.append(' ');  
    8.     builder.append(gid);  
    9.     return execute(builder.toString());  
    10. }  


    execute也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private int execute(String cmd) {  
    2.     String res = transaction(cmd);  
    3.     try {  
    4.         return Integer.parseInt(res);  
    5.     } catch (NumberFormatException ex) {  
    6.         return -1;  
    7.     }  
    8. }  


    transaction也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private synchronized String transaction(String cmd) {  
    2.     if (!connect()) {  
    3.         Slog.e(TAG, "connection failed");  
    4.         return "-1";  
    5.     }  
    6.   
    7.     if (!writeCommand(cmd)) {  
    8.         /* 
    9.          * If installd died and restarted in the background (unlikely but 
    10.          * possible) we'll fail on the next write (this one). Try to 
    11.          * reconnect and write the command one more time before giving up. 
    12.          */  
    13.         Slog.e(TAG, "write command failed? reconnect!");  
    14.         if (!connect() || !writeCommand(cmd)) {  
    15.             return "-1";  
    16.         }  
    17.     }  
    18.     if (LOCAL_DEBUG) {  
    19.         Slog.i(TAG, "send: '" + cmd + "'");  
    20.     }  
    21.     if (readReply()) {  
    22.         String s = new String(buf, 0, buflen);  
    23.         if (LOCAL_DEBUG) {  
    24.             Slog.i(TAG, "recv: '" + s + "'");  
    25.         }  
    26.         return s;  
    27.     } else {  
    28.         if (LOCAL_DEBUG) {  
    29.             Slog.i(TAG, "fail");  
    30.         }  
    31.         return "-1";  
    32.     }  
    33. }  


    writeCommand也就是

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private boolean writeCommand(String _cmd) {  
    2.     byte[] cmd = _cmd.getBytes();  
    3.     int len = cmd.length;  
    4.     if ((len < 1) || (len > 1024))  
    5.         return false;  
    6.     buf[0] = (byte) (len & 0xff);  
    7.     buf[1] = (byte) ((len >> 8) & 0xff);  
    8.     try {  
    9.         mOut.write(buf, 0, 2);  
    10.         mOut.write(cmd, 0, len);  
    11.     } catch (IOException ex) {  
    12.         Slog.e(TAG, "write error");  
    13.         disconnect();  
    14.         return false;  
    15.     }  
    16.     return true;  
    17. }  


    mOut是什么呢?

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private boolean connect() {  
    2.     if (mSocket != null) {  
    3.         return true;  
    4.     }  
    5.     Slog.i(TAG, "connecting...");  
    6.     try {  
    7.         mSocket = new LocalSocket();  
    8.   
    9.         LocalSocketAddress address = new LocalSocketAddress("installd",  
    10.                 LocalSocketAddress.Namespace.RESERVED);  
    11.   
    12.         mSocket.connect(address);  
    13.   
    14.         mIn = mSocket.getInputStream();  
    15.         mOut = mSocket.getOutputStream();  
    16.     } catch (IOException ex) {  
    17.         disconnect();  
    18.         return false;  
    19.     }  
    20.     return true;  
    21. }  


    真实面目,原来这里在用Socket进行通信,把我们要安装的包信息告诉服务器,让服务器来完成余下的工作,这个服务器在底层,完成了一些copy等工作,具体是什么不再深究,如果以后有机会再讲解。

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

    原文:http://blog.csdn.net/zhiyuan263287/article/details/21033309

  • 相关阅读:
    函数式编程理解
    Java8 lambda表达式10个示例
    MD5进行文件完整性校验的操作方法
    加密算法和MD5等散列算法的区别(转)
    随笔
    瑕疵(bug)严重性定义
    无需Cygwin,如果没有在命令行,Eclipse编NDK
    BZOJ 1878 SDOI 2009 HH项链 树状数组 + 脱机处理
    Teamcity+SVN+VisualStudio在持续集成简明教程
    UVALive3713-Astronauts(2-SAT)
  • 原文地址:https://www.cnblogs.com/veins/p/4128348.html
Copyright © 2011-2022 走看看