android PackageInstaller那点事儿

    今天简单讲解一下PackageInstaller

    文件路径:

    packages/apps/PackageInstaller

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

   下面开始讲解:

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

    当然,我们从这个安装界面说起,这个界面是那个呢?就是PackageInstallerActivity这个acitvity。它是如何启动起来的呢?我们去看看它在AndroidManifest是如何定义的
<activity android:name=".PackageInstallerActivity"  
        android:configChanges="orientation|keyboardHidden|screenSize"  
        android:excludeFromRecents="true"  
        android:screenOrientation="unspecified">  
    <intent-filter>  
        <action android:name="android.intent.action.VIEW" />  
        <action android:name="android.intent.action.INSTALL_PACKAGE" />  
        <category android:name="android.intent.category.DEFAULT" />  
        <data android:scheme="content" />  
        <data android:scheme="file" />  
        <data android:mimeType="application/vnd.android.package-archive" />  
    </intent-filter>  
    <intent-filter>  
        <action android:name="android.intent.action.INSTALL_PACKAGE" />  
        <category android:name="android.intent.category.DEFAULT" />  
        <data android:scheme="content" />  
        <data android:scheme="file" />  
    </intent-filter>  
</activity>

很明显了,我们可以通过android.intent.action.INSTALL_PACKAGE这个action启动,也可以通过android.intent.action.VIEW这个action加上"application/vnd.android.package-archive"这个type启动,当然不加这个type也能启动,但是会找到很多这样的activity哦。另外,通过类名或包名启动也未尝不可的。所以,大部分启动是这样的
String apkFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+"/.../packageName.pac";  
File apkFile = new File(apkFileString);  
Intent intent = new Intent(Intent.ACTION_VIEW);  
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");  
mContext.startActivity(intent);

这里我们传进去一个数据就是pakFile的Uri,然后我们去PackageInstallerActivity的onCreate中看看
final Intent intent = getIntent();  
mPackageURI = intent.getData();  
mPm = getPackageManager();  
mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);

获取到,我们刚才传进来的apkFile的Uri给了mPackageURI,接着获取到PackageManager,然后生成一个mPkgInfo也就是PackageParser.Package,这个很重要。我们看看PackageParser.Package是如何生成的,PackageParser.Package里面都包含了什么东西。那我们就要去PackageUtil.getPackageInfo中了
public static  PackageParser.Package getPackageInfo(Uri packageURI) {  
    final String archiveFilePath = packageURI.getPath();  
    PackageParser packageParser = new PackageParser(archiveFilePath);  
    File sourceFile = new File(archiveFilePath);  
    DisplayMetrics metrics = new DisplayMetrics();  
    metrics.setToDefaults();  
    PackageParser.Package pkg =  packageParser.parsePackage(sourceFile,  
            archiveFilePath, metrics, 0);  
    // Nuke the parser reference.  
    packageParser = null;  
    return pkg;  
}

生成一个Package解析器,通过这个解析器来获取到PackageParser.Package中需要的数据,生成一个PackageParser.Package。我们看看PackageParser.parsePackage是如何生成一个PackageParser.Package的,这里传进去四个参数,一个Source File,apk文件,一个apk路径,一个屏幕信息,最后一个0,具体做什么的,进去之后就能明白了
public Package parsePackage(File sourceFile, String destCodePath,  
        DisplayMetrics metrics, int flags) {  
    mParseError = PackageManager.INSTALL_SUCCEEDED;  
  
    mArchiveSourcePath = sourceFile.getPath();  
    if (!sourceFile.isFile()) {  
        Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);  
        mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;  
        return null;  
    }  
    if (!isPackageFilename(sourceFile.getName())  
            && (flags&PARSE_MUST_BE_APK) != 0) {  
        if ((flags&PARSE_IS_SYSTEM) == 0) {  
            // We expect to have non-.apk files in the system dir,  
            // so don't warn about them.  
            Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);  
        }  
        mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;  
        return null;  
    }  
  
    if (DEBUG_JAR)  
        Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);  
  
    XmlResourceParser parser = null;  
    AssetManager assmgr = null;  
    Resources res = null;  
    boolean assetError = true;  
    try {  
        assmgr = new AssetManager();  
        int cookie = assmgr.addAssetPath(mArchiveSourcePath);  
        if (cookie != 0) {  
            res = new Resources(assmgr, metrics, null);  
            assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
                    Build.VERSION.RESOURCES_SDK_INT);  
            parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);  
            assetError = false;  
        } else {  
            Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);  
        }  
    } catch (Exception e) {  
        Slog.w(TAG, "Unable to read AndroidManifest.xml of "  
                + mArchiveSourcePath, e);  
    }  
    if (assetError) {  
        if (assmgr != null) assmgr.close();  
        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;  
        return null;  
    }  
    String[] errorText = new String[1];  
    Package pkg = null;  
    Exception errorException = null;  
    try {  
        // XXXX todo: need to figure out correct configuration.  
        pkg = parsePackage(res, parser, flags, errorText);  
    } catch (Exception e) {  
        errorException = e;  
        mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;  
    }  
  
  
    if (pkg == null) {  
        // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED  
        // just means to skip this app so don't make a fuss about it.  
        if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {  
            if (errorException != null) {  
                Slog.w(TAG, mArchiveSourcePath, errorException);  
            } else {  
                Slog.w(TAG, mArchiveSourcePath + " (at "  
                        + parser.getPositionDescription()  
                        + "): " + errorText[0]);  
            }  
            if (mParseError == PackageManager.INSTALL_SUCCEEDED) {  
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;  
            }  
        }  
        parser.close();  
        assmgr.close();  
        return null;  
    }  
  
    parser.close();  
    assmgr.close();  
  
    // Set code and resource paths  
    pkg.mPath = destCodePath;  
    pkg.mScanPath = mArchiveSourcePath;  
    //pkg.applicationInfo.sourceDir = destCodePath;  
    //pkg.applicationInfo.publicSourceDir = destRes;  
    pkg.mSignatures = null;  
  
    return pkg;  
}

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

这三个是什么呢?这里简单说一下,AssetManager资产管理器,用来管理包中获取到的资源
assmgr = new AssetManager();  
int cookie = assmgr.addAssetPath(mArchiveSourcePath);

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

当然Resources信息也是通过AssetManager获取到的。XmlResourceParser顾名思义就是Xml资源文件解析器了,用来解析我们xml文件的
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

ANDROID_MANIFEST_FILENAME也就是
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";

这样就很明显了,这里生成的xml文件资源解析器是用来解析AndroidManifest文件的了。接下来就是关键了
String[] errorText = new String[1];  
Package pkg = null;  
Exception errorException = null;  
try {  
    // XXXX todo: need to figure out correct configuration.  
    pkg = parsePackage(res, parser, flags, errorText);  
} catch (Exception e) {  
    errorException = e;  
    mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;  
}

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

获取包名。
final Package pkg = new Package(pkgName);  
boolean foundApp = false;  
  
TypedArray sa = res.obtainAttributes(attrs,  
        com.android.internal.R.styleable.AndroidManifest);  
pkg.mVersionCode = sa.getInteger(  
        com.android.internal.R.styleable.AndroidManifest_versionCode, 0);  
pkg.mVersionName = sa.getNonConfigurationString(  
        com.android.internal.R.styleable.AndroidManifest_versionName, 0);  
if (pkg.mVersionName != null) {  
    pkg.mVersionName = pkg.mVersionName.intern();  
}  
String str = sa.getNonConfigurationString(  
        com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);  
if (str != null && str.length() > 0) {  
    String nameError = validateName(str, true);  
    if (nameError != null && !"android".equals(pkgName)) {  
        outError[0] = "<manifest> specifies bad sharedUserId name \""  
            + str + "\": " + nameError;  
        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;  
        return null;  
    }  
    pkg.mSharedUserId = str.intern();  
    pkg.mSharedUserLabel = sa.getResourceId(  
            com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);  
}  
sa.recycle();  
  
pkg.installLocation = sa.getInteger(  
        com.android.internal.R.styleable.AndroidManifest_installLocation,  
        PARSE_DEFAULT_INSTALL_LOCATION);  
pkg.applicationInfo.installLocation = pkg.installLocation;

解析获取,我们AndroidManifest的attrs,也就是frameworks/base/core/res/res/values下的attrs_manifest.xml中定义的
<declare-styleable name="AndroidManifest">  
    <attr name="versionCode" />  
    <attr name="versionName" />  
    <attr name="sharedUserId" />  
    <attr name="sharedUserLabel" />  
    <attr name="installLocation" />  
</declare-styleable>

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

这里面包含的信息,例如这里的lable等等,还有以其为父的"activity","receiver","service","provider"等等,这里以"activity"为例,还是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是
<declare-styleable name="AndroidManifestActivity" parent="AndroidManifestApplication">  
    <!-- Required name of the class implementing the activity, deriving from  
        {@link android.app.Activity}.  This is a fully  
        qualified class name (for example, com.mycompany.myapp.MyActivity); as a  
        short-hand if the first character of the class  
        is a period then it is appended to your package name. -->  
    <attr name="name" />  
    <attr name="theme" />  
    <attr name="label" />  
    <attr name="description" />  
    <attr name="icon" />  
    <attr name="logo" />  
    <attr name="launchMode" />  
    <attr name="screenOrientation" />  
    <attr name="configChanges" />  
    <attr name="permission" />  
    <attr name="multiprocess" />  
    <attr name="process" />  
    <attr name="taskAffinity" />  
    <attr name="allowTaskReparenting" />  
    <attr name="finishOnTaskLaunch" />  
    <attr name="finishOnCloseSystemDialogs" />  
    <attr name="clearTaskOnLaunch" />  
    <attr name="noHistory" />  
    <attr name="alwaysRetainTaskState" />  
    <attr name="stateNotNeeded" />  
    <attr name="excludeFromRecents" />  
    <!-- Specify whether the activity is enabled or not (that is, can be instantiated by the system).  
         It can also be specified for an application as a whole, in which case a value of "false"  
         will override any component specific values (a value of "true" will not override the  
         component specific values). -->  
    <attr name="enabled" />  
    <attr name="exported" />  
    <!-- Specify the default soft-input mode for the main window of  
         this activity.  A value besides "unspecified" here overrides  
         any value in the theme. -->  
    <attr name="windowSoftInputMode" />  
    <attr name="immersive" />  
    <attr name="hardwareAccelerated" />  
    <attr name="uiOptions" />  
</declare-styleable>

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

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

也就是
private void initiateInstall() {  
    String pkgName = mPkgInfo.packageName;  
    // Check if there is already a package on the device with this name  
    // but it has been renamed to something else.  
    String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });  
    if (oldName != null && oldName.length > 0 && oldName[0] != null) {  
        pkgName = oldName[0];  
        mPkgInfo.setPackageName(pkgName);  
    }  
    // Check if package is already installed. display confirmation dialog if replacing pkg  
    try {  
        mAppInfo = mPm.getApplicationInfo(pkgName,  
                PackageManager.GET_UNINSTALLED_PACKAGES);  
    } catch (NameNotFoundException e) {  
        mAppInfo = null;  
    }  
    if (mAppInfo == null || getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_REPLACE, false)) {  
        startInstallConfirm();  
    } else {  
        if(localLOGV) Log.i(TAG, "Replacing existing package:"+  
                mPkgInfo.applicationInfo.packageName);  
        showDialogInner(DLG_REPLACE_APP);  
    }  
}

然后是这里的startInstallConfirm(),也就是
private void startInstallConfirm() {  
    LinearLayout permsSection = (LinearLayout) mInstallConfirm.findViewById(R.id.permissions_section);  
    LinearLayout securityList = (LinearLayout) permsSection.findViewById(  
            R.id.security_settings_list);  
    boolean permVisible = false;  
    if(mPkgInfo != null) {  
        AppSecurityPermissions asp = new AppSecurityPermissions(this, mPkgInfo);  
        if(asp.getPermissionCount() > 0) {  
            permVisible = true;  
            securityList.addView(asp.getPermissionsView());  
        }  
    }  
    if(!permVisible){  
        permsSection.setVisibility(View.INVISIBLE);  
    }  
    mInstallConfirm.setVisibility(View.VISIBLE);  
    mOk = (Button)findViewById(R.id.ok_button);  
    mCancel = (Button)findViewById(R.id.cancel_button);  
    mOk.setOnClickListener(this);  
    mCancel.setOnClickListener(this);  
}

到这里我们的PackageInstallerActivity这个activity才算完成,这里我们看看我们安装界面的权限View是如何生成的,也就是asp.getPermissionsView(),这里的AppSecurityPermissions(this, mPkgInfo)传进去两个参数,一个是Context,一个是我们刚才获取到的Package,我们进去看看,文件在frameworks/base/core/java/android/widget下面
public AppSecurityPermissions(Context context, PackageParser.Package pkg) {  
    mContext = context;  
    mPm = mContext.getPackageManager();  
    mPermsList = new ArrayList<PermissionInfo>();  
    Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();  
    if(pkg == null) {  
        return;  
    }  
    // Get requested permissions  
    if (pkg.requestedPermissions != null) {  
        ArrayList<String> strList = pkg.requestedPermissions;  
        int size = strList.size();  
        if (size > 0) {  
            extractPerms(strList.toArray(new String[size]), permSet);  
        }  
    }  
    // Get permissions related to  shared user if any  
    if(pkg.mSharedUserId != null) {  
        int sharedUid;  
        try {  
            sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId);  
            getAllUsedPermissions(sharedUid, permSet);  
        } catch (NameNotFoundException e) {  
            Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName);  
        }  
    }  
    // Retrieve list of permissions  
    for(PermissionInfo tmpInfo : permSet) {  
        mPermsList.add(tmpInfo);  
    }  
}

就是获取到一个PermissionInfo的List就是mPermsList。然后我们看看asp.getPermissionsView(),也就是
public View getPermissionsView() {  
      
    mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
    mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);  
    mShowMore = mPermsView.findViewById(R.id.show_more);  
    mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon);  
    mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text);  
    mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list);  
    mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list);  
    mNoPermsView = mPermsView.findViewById(R.id.no_permissions);  
  
    // Set up the LinearLayout that acts like a list item.  
    mShowMore.setClickable(true);  
    mShowMore.setOnClickListener(this);  
    mShowMore.setFocusable(true);  
  
    // Pick up from framework resources instead.  
    mDefaultGrpLabel = mContext.getString(R.string.default_permission_group);  
    mPermFormat = mContext.getString(R.string.permissions_format);  
    mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);  
    mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);  
    mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark);  
    mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark);  
      
    // Set permissions view  
    setPermissions(mPermsList);  
    return mPermsView;  
}

这里就是我们的权限View的布局了,也就是frameworks/base/core/res/res/layout下面的app_perms_summary.xml布局了,如果我们想修改权限VIew的话就要从这里开始了。我们去看看是如何生成的,也就是setPermissions(mPermsList)
private void setPermissions(List<PermissionInfo> permList) {  
    mGroupLabelCache = new HashMap<String, CharSequence>();  
    //add the default label so that uncategorized permissions can go here  
    mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel);  
      
    // Map containing group names and a list of permissions under that group  
    // categorized as dangerous  
    mDangerousMap = new HashMap<String, String>();  
    // Map containing group names and a list of permissions under that group  
    // categorized as normal  
    mNormalMap = new HashMap<String, String>();  
      
    // Additional structures needed to ensure that permissions are unique under   
    // each group  
    Map<String, List<PermissionInfo>> dangerousMap =   
        new HashMap<String,  List<PermissionInfo>>();  
    Map<String, List<PermissionInfo> > normalMap =   
        new HashMap<String,  List<PermissionInfo>>();  
    PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);  
      
    if (permList != null) {  
        // First pass to group permissions  
        for (PermissionInfo pInfo : permList) {  
            if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);  
            if(!isDisplayablePermission(pInfo)) {  
                if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");  
                continue;  
            }  
            Map<String, List<PermissionInfo> > permInfoMap =  
                (pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) ?  
                        dangerousMap : normalMap;  
            String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group;  
            if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName);  
            List<PermissionInfo> grpPermsList = permInfoMap.get(grpName);  
            if(grpPermsList == null) {  
                grpPermsList = new ArrayList<PermissionInfo>();  
                permInfoMap.put(grpName, grpPermsList);  
                grpPermsList.add(pInfo);  
            } else {  
                int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator);  
                if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size());  
                if (idx < 0) {  
                    idx = -idx-1;  
                    grpPermsList.add(idx, pInfo);  
                }  
            }  
        }  
        // Second pass to actually form the descriptions  
        // Look at dangerous permissions first  
        aggregateGroupDescs(dangerousMap, mDangerousMap);  
        aggregateGroupDescs(normalMap, mNormalMap);  
    }  
  
    mCurrentState = State.NO_PERMS;  
    if(mDangerousMap.size() > 0) {  
        mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY;  
    } else if(mNormalMap.size() > 0) {  
        mCurrentState = State.NORMAL_ONLY;  
    }  
    if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState);  
    showPermissions();  
}

这里区分一下是dangerousMap,也就是PermissionInfo.PROTECTION_DANGEROUS类权限还是normalMap一般权限,然后就去showPermissions()
private void showPermissions() {  
  
    switch(mCurrentState) {  
    case NO_PERMS:  
        displayNoPermissions();  
        break;  
  
    case DANGEROUS_ONLY:  
        displayPermissions(true);  
        break;  
  
    case NORMAL_ONLY:  
        displayPermissions(false);  
        break;  
  
    case BOTH:  
        displayPermissions(true);  
        if (mExpanded) {  
            displayPermissions(false);  
            mShowMoreIcon.setImageDrawable(mShowMaxIcon);  
            mShowMoreText.setText(R.string.perms_hide);  
            mNonDangerousList.setVisibility(View.VISIBLE);  
        } else {  
            mShowMoreIcon.setImageDrawable(mShowMinIcon);  
            mShowMoreText.setText(R.string.perms_show_all);  
            mNonDangerousList.setVisibility(View.GONE);  
        }  
        mShowMore.setVisibility(View.VISIBLE);  
        break;  
    }  
}

给我们的布局赋显示的内容了,这里不一一解释,我们去看看displayPermissions
private void displayPermissions(boolean dangerous) {  
    Map<String, String> permInfoMap = dangerous ? mDangerousMap : mNormalMap;  
    LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList;  
    permListView.removeAllViews();  
  
    Set<String> permInfoStrSet = permInfoMap.keySet();  
    for (String loopPermGrpInfoStr : permInfoStrSet) {  
        CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr);  
        //guaranteed that grpLabel wont be null since permissions without groups  
        //will belong to the default group  
        if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:"  
                + permInfoMap.get(loopPermGrpInfoStr));  
        permListView.addView(getPermissionItemView(grpLabel,  
                permInfoMap.get(loopPermGrpInfoStr), dangerous));  
    }  
}

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

接下来,我们说说当点击“安装”之后做了什么事情。
public void onClick(View v) {  
    if(v == mOk) {  
        // Start subactivity to actually install the application  
        Intent newIntent = new Intent();  
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,  
                mPkgInfo.applicationInfo);  
        newIntent.setData(mPackageURI);  
        newIntent.setClass(this, InstallAppProgress.class);  
        String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);  
        if (installerPackageName != null) {  
            newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);  
        }  
        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {  
            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);  
            newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);  
        }  
        if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);  
        startActivity(newIntent);  
        finish();  
    } else if(v == mCancel) {  
        // Cancel and finish  
        setResult(RESULT_CANCELED);  
        finish();  
    }  
}

去启动了另外一个acitvity也就是InstallAppProgress,并过去几个数据,主要是mPkgInfo.applicationInfo也就是ApplicationInfo,还有mPackageURI也就是apkFile的Uri,还有一些其他的数据。然后我们就去InstallAppProgress的onCreate中
@Override  
public void onCreate(Bundle icicle) {  
    super.onCreate(icicle);  
    Intent intent = getIntent();  
    mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);  
    mPackageURI = intent.getData();  
    initView();  
}

获取到传过来的两个数据,然后就initView(),initView()里面是一些布局的初始化,不再赘述,只截取重要的,也就是
String installerPackageName = getIntent().getStringExtra(  
        Intent.EXTRA_INSTALLER_PACKAGE_NAME);  
PackageInstallObserver observer = new PackageInstallObserver();  
pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);

pm是PackageManager,这样我们就去PackageManager.installPackage中,在PackageManager的installPackage是abstract函数,具体实现在PackageManagerService中,这里传进去的参数第一个我们已经知道了,第二个是package安装的观察者
class PackageInstallObserver extends IPackageInstallObserver.Stub {  
    public void packageInstalled(String packageName, int returnCode) {  
        Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);  
        Log.d("packageInstalled", "returnCode = "+returnCode);  
        msg.arg1 = returnCode;  
        mHandler.sendMessage(msg);  
    }  
}

当安装完成就会走到packageInstalled个函数中,第三个参数是flag主要标识是第一次安装,还是已经安装更新,第三个参数很明显是安装的包名了。然后我们去看看
/* Called when a downloaded package installation has been confirmed by the user */  
public void installPackage(  
        final Uri packageURI, final IPackageInstallObserver observer, final int flags) {  
    installPackage(packageURI, observer, flags, null);  
}  
  
/* Called when a downloaded package installation has been confirmed by the user */  
public void installPackage(  
        final Uri packageURI, final IPackageInstallObserver observer, final int flags,  
        final String installerPackageName) {  
    installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,  
            null);  
}

也就是installPackageWithVerification
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,  
        int flags, String installerPackageName, Uri verificationURI,  
        ManifestDigest manifestDigest) {  
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);  
  
    final int uid = Binder.getCallingUid();  
  
    final int filteredFlags;  
  
    if (uid == Process.SHELL_UID || uid == 0) {  
        if (DEBUG_INSTALL) {  
            Slog.v(TAG, "Install from ADB");  
        }  
        filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;  
    } else {  
        filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;  
    }  
  
    final Message msg = mHandler.obtainMessage(INIT_COPY);  
    msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,  
            verificationURI, manifestDigest);  
    mHandler.sendMessage(msg);  
}

就去发了一个消息INIT_COPY,并携带了我们传进来的参数组成的一个类InstallParams,InstallParams继承于HandlerParams,我们去看看这个消息执行了什么
case INIT_COPY: {  
    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy");  
    HandlerParams params = (HandlerParams) msg.obj;  
    int idx = mPendingInstalls.size();  
    if (DEBUG_INSTALL) Slog.i(TAG, "idx=" + idx);  
    // If a bind was already initiated we dont really  
    // need to do anything. The pending install  
    // will be processed later on.  
    if (!mBound) {  
        // If this is the only one pending we might  
        // have to bind to the service again.  
        if (!connectToService()) {  
            Slog.e(TAG, "Failed to bind to media container service");  
            params.serviceError();  
            return;  
        } else {  
            // Once we bind to the service, the first  
            // pending request will be processed.  
            mPendingInstalls.add(idx, params);  
        }  
    } else {  
        mPendingInstalls.add(idx, params);  
        // Already bound to the service. Just make  
        // sure we trigger off processing the first request.  
        if (idx == 0) {  
            mHandler.sendEmptyMessage(MCS_BOUND);  
        }  
    }  
    break;  
}

这里先 mPendingInstalls.add(idx, params)把我们要安装的信息放到HandlerParams的一个List中mPendingInstalls,然后去发了一个消息MCS_BOUND,也就是
case MCS_BOUND: {  
    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");  
    if (msg.obj != null) {  
        mContainerService = (IMediaContainerService) msg.obj;  
    }  
    if (mContainerService == null) {  
        // Something seriously wrong. Bail out  
        Slog.e(TAG, "Cannot bind to media container service");  
        for (HandlerParams params : mPendingInstalls) {  
            mPendingInstalls.remove(0);  
            // Indicate service bind error  
            params.serviceError();  
        }  
        mPendingInstalls.clear();  
    } else if (mPendingInstalls.size() > 0) {  
        HandlerParams params = mPendingInstalls.get(0);  
        if (params != null) {  
            if (params.startCopy()) {  
                // We are done...  look for more work or to  
                // go idle.  
                if (DEBUG_SD_INSTALL) Log.i(TAG,  
                        "Checking for more work or unbind...");  
                // Delete pending install  
                if (mPendingInstalls.size() > 0) {  
                    mPendingInstalls.remove(0);  
                }  
                if (mPendingInstalls.size() == 0) {  
                    if (mBound) {  
                        if (DEBUG_SD_INSTALL) Log.i(TAG,  
                                "Posting delayed MCS_UNBIND");  
                        removeMessages(MCS_UNBIND);  
                        Message ubmsg = obtainMessage(MCS_UNBIND);  
                        // Unbind after a little delay, to avoid  
                        // continual thrashing.  
                        sendMessageDelayed(ubmsg, 10000);  
                    }  
                } else {  
                    // There are more pending requests in queue.  
                    // Just post MCS_BOUND message to trigger processing  
                    // of next pending install.  
                    if (DEBUG_SD_INSTALL) Log.i(TAG,  
                            "Posting MCS_BOUND for next woek");  
                    mHandler.sendEmptyMessage(MCS_BOUND);  
                }  
            }  
        }  
    } else {  
        // Should never happen ideally.  
        Slog.w(TAG, "Empty queue");  
    }  
    break;  
}

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

这个了,进去看看
final boolean startCopy() {  
    boolean res;  
    try {  
        if (DEBUG_INSTALL) Slog.i(TAG, "startCopy");  
  
        if (++mRetries > MAX_RETRIES) {  
            Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");  
            mHandler.sendEmptyMessage(MCS_GIVE_UP);  
            handleServiceError();  
            return false;  
        } else {  
            handleStartCopy();  
            res = true;  
        }  
    } catch (RemoteException e) {  
        if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");  
        mHandler.sendEmptyMessage(MCS_RECONNECT);  
        res = false;  
    }  
    handleReturnCode();  
    return res;  
}

这里的handleStartCopy()和handleServiceError()和handleReturnCode()都是abstract函数,希望大家还记得刚才我们发消息的时候携带的是InstallParams个类,InstallParams继承于HandlerParams,所以我们就会知道这三个abstract是在哪里实现的了,我们先说说handleStartCopy(),主要给两个变量完成了赋值工作也个是mArgs也就是InstallArgs,一个是ret标识是否安装成功的。handleServiceError()这个不讲解,然后看handleReturnCode()
@Override  
void handleReturnCode() {  
    // If mArgs is null, then MCS couldn't be reached. When it  
    // reconnects, it will try again to install. At that point, this  
    // will succeed.  
    if (mArgs != null) {  
        processPendingInstall(mArgs, mRet);  
    }  
}

也就是processPendingInstall(mArgs, mRet)
private void processPendingInstall(final InstallArgs args, final int currentStatus) {  
    // Queue up an async operation since the package installation may take a little while.  
    mHandler.post(new Runnable() {  
        public void run() {  
            mHandler.removeCallbacks(this);  
             // Result object to be returned  
            PackageInstalledInfo res = new PackageInstalledInfo();  
            res.returnCode = currentStatus;  
            res.uid = -1;  
            res.pkg = null;  
            res.removedInfo = new PackageRemovedInfo();  
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {  
                args.doPreInstall(res.returnCode);  
                synchronized (mInstallLock) {  
                    installPackageLI(args, true, res);  
                }  
                args.doPostInstall(res.returnCode);  
            }  
  
            // A restore should be performed at this point if (a) the install  
            // succeeded, (b) the operation is not an update, and (c) the new  
            // package has a backupAgent defined.  
            final boolean update = res.removedInfo.removedPackage != null;  
            boolean doRestore = (!update  
                    && res.pkg != null  
                    && res.pkg.applicationInfo.backupAgentName != null);  
  
            // Set up the post-install work request bookkeeping.  This will be used  
            // and cleaned up by the post-install event handling regardless of whether  
            // there's a restore pass performed.  Token values are >= 1.  
            int token;  
            if (mNextInstallToken < 0) mNextInstallToken = 1;  
            token = mNextInstallToken++;  
  
            PostInstallData data = new PostInstallData(args, res);  
            mRunningInstalls.put(token, data);  
            if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);  
  
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {  
                // Pass responsibility to the Backup Manager.  It will perform a  
                // restore if appropriate, then pass responsibility back to the  
                // Package Manager to run the post-install observer callbacks  
                // and broadcasts.  
                IBackupManager bm = IBackupManager.Stub.asInterface(  
                        ServiceManager.getService(Context.BACKUP_SERVICE));  
                if (bm != null) {  
                    if (DEBUG_INSTALL) Log.v(TAG, "token " + token  
                            + " to BM for possible restore");  
                    try {  
                        bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);  
                    } catch (RemoteException e) {  
                        // can't happen; the backup manager is local  
                    } catch (Exception e) {  
                        Slog.e(TAG, "Exception trying to enqueue restore", e);  
                        doRestore = false;  
                    }  
                } else {  
                    Slog.e(TAG, "Backup Manager not found!");  
                    doRestore = false;  
                }  
            }  
  
            if (!doRestore) {  
                // No restore possible, or the Backup Manager was mysteriously not  
                // available -- just fire the post-install work request directly.  
                if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);  
                Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);  
                mHandler.sendMessage(msg);  
            }  
        }  
    });  
}

在这里启动了一个线程进行安装,也就是
PackageInstalledInfo res = new PackageInstalledInfo();  
res.returnCode = currentStatus;  
res.uid = -1;  
res.pkg = null;  
res.removedInfo = new PackageRemovedInfo();  
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {  
    args.doPreInstall(res.returnCode);  
    synchronized (mInstallLock) {  
        installPackageLI(args, true, res);  
    }  
    args.doPostInstall(res.returnCode);  
}

也就是installPackageLI(args, true, res),这里代码较多,不再全部列出
if (replace) {  
    replacePackageLI(pkg, parseFlags, scanMode,  
            installerPackageName, res);  
} else {  
    installNewPackageLI(pkg, parseFlags, scanMode,  
            installerPackageName,res);  
}

很明显,如果是第一次安装走installNewPackageLI,如果是更新走replacePackageLI,我们去installNewPackageLI
/* 
 * Install a non-existing package. 
 */  
private void installNewPackageLI(PackageParser.Package pkg,  
        int parseFlags,  
        int scanMode,  
        String installerPackageName, PackageInstalledInfo res) {  
    // Remember this for later, in case we need to rollback this install  
    String pkgName = pkg.packageName;  
  
    boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();  
    res.name = pkgName;  
    synchronized(mPackages) {  
        if (mSettings.mRenamedPackages.containsKey(pkgName)) {  
            // A package with the same name is already installed, though  
            // it has been renamed to an older name.  The package we  
            // are trying to install should be installed as an update to  
            // the existing one, but that has not been requested, so bail.  
            Slog.w(TAG, "Attempt to re-install " + pkgName  
                    + " without first uninstalling package running as "  
                    + mSettings.mRenamedPackages.get(pkgName));  
            res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;  
            return;  
        }  
        if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {  
            // Don't allow installation over an existing package with the same name.  
            Slog.w(TAG, "Attempt to re-install " + pkgName  
                    + " without first uninstalling.");  
            res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;  
            return;  
        }  
    }  
    mLastScanError = PackageManager.INSTALL_SUCCEEDED;  
    PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,  
            System.currentTimeMillis());  
    if (newPackage == null) {  
        Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);  
        if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {  
            res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;  
        }  
    } else {  
        updateSettingsLI(newPackage,  
                installerPackageName,  
                res);  
        // delete the partially installed application. the data directory will have to be  
        // restored if it was already existing  
        if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {  
            // remove package from internal structures.  Note that we want deletePackageX to  
            // delete the package data and cache directories that it created in  
            // scanPackageLocked, unless those directories existed before we even tried to  
            // install.  
            deletePackageLI(  
                    pkgName, false,  
                    dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,  
                            res.removedInfo, true);  
        }  
    }  
}

也就是
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,  
        System.currentTimeMillis());

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

mInstaller也就是Installer,所以去看看
public int install(String name, int uid, int gid) {  
    StringBuilder builder = new StringBuilder("install");  
    builder.append(' ');  
    builder.append(name);  
    builder.append(' ');  
    builder.append(uid);  
    builder.append(' ');  
    builder.append(gid);  
    return execute(builder.toString());  
}

execute也就是
private int execute(String cmd) {  
    String res = transaction(cmd);  
    try {  
        return Integer.parseInt(res);  
    } catch (NumberFormatException ex) {  
        return -1;  
    }  
}

transaction也就是
private synchronized String transaction(String cmd) {  
    if (!connect()) {  
        Slog.e(TAG, "connection failed");  
        return "-1";  
    }  
  
    if (!writeCommand(cmd)) {  
        /* 
         * If installd died and restarted in the background (unlikely but 
         * possible) we'll fail on the next write (this one). Try to 
         * reconnect and write the command one more time before giving up. 
         */  
        Slog.e(TAG, "write command failed? reconnect!");  
        if (!connect() || !writeCommand(cmd)) {  
            return "-1";  
        }  
    }  
    if (LOCAL_DEBUG) {  
        Slog.i(TAG, "send: '" + cmd + "'");  
    }  
    if (readReply()) {  
        String s = new String(buf, 0, buflen);  
        if (LOCAL_DEBUG) {  
            Slog.i(TAG, "recv: '" + s + "'");  
        }  
        return s;  
    } else {  
        if (LOCAL_DEBUG) {  
            Slog.i(TAG, "fail");  
        }  
        return "-1";  
    }  
}

writeCommand也就是
private boolean writeCommand(String _cmd) {  
    byte[] cmd = _cmd.getBytes();  
    int len = cmd.length;  
    if ((len < 1) || (len > 1024))  
        return false;  
    buf[0] = (byte) (len & 0xff);  
    buf[1] = (byte) ((len >> 8) & 0xff);  
    try {  
        mOut.write(buf, 0, 2);  
        mOut.write(cmd, 0, len);  
    } catch (IOException ex) {  
        Slog.e(TAG, "write error");  
        disconnect();  
        return false;  
    }  
    return true;  
}

mOut是什么呢?
private boolean connect() {  
    if (mSocket != null) {  
        return true;  
    }  
    Slog.i(TAG, "connecting...");  
    try {  
        mSocket = new LocalSocket();  
  
        LocalSocketAddress address = new LocalSocketAddress("installd",  
                LocalSocketAddress.Namespace.RESERVED);  
  
        mSocket.connect(address);  
  
        mIn = mSocket.getInputStream();  
        mOut = mSocket.getOutputStream();  
    } catch (IOException ex) {  
        disconnect();  
        return false;  
    }  
    return true;  
}

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

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

猜你喜欢

转载自iaiai.iteye.com/blog/2184079