Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android PackageInstaller那點事兒

android PackageInstaller那點事兒

編輯:關於Android編程

今天簡單講解一下PackageInstaller

文件路徑:

 

下面開始講解:

首先,我們說一下安裝apk的幾種方式,整體上可以分為2類,一類是有界面安裝,一類是無界面安裝。無界面安裝分為內置apk開機安裝和命令安裝,命令安裝又分為兩類,一類電腦安裝也就是adb命令,另一類是手機安裝也就是pm命令。今天我們主要介紹有界面安裝。

當然,我們從這個安裝界面說起,這個界面是那個呢?就是PackageInstallerActivity這個acitvity。它是如何啟動起來的呢?我們去看看它在AndroidManifest是如何定義的

 

 
            
        

很明顯了,我們可以通過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] =  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中定義的

 

 

    
        
        
        
        
        
    

這些變量信息。接下來就是一個大循環了,這裡解析的內容比較多了,我們舉幾個常見的例子,如application也就是

 

 


 

這裡面包含的信息,例如這裡的lable等等,還有以其為父的activity,receiver,service,provider等等,這裡以activity為例,還是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是

 

    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    

這裡有很多變量,在定義一個acitivity的時候有的我們用過,有的沒有用過,Xml文件資源解析器就是從xml中獲取到這先變量的值然後付給這些變量;同樣還有permission權限,也就是

 

 

 

這類,大家經常會見到的;還有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();
        Set permSet = new HashSet();
        if(pkg == null) {
            return;
        }
        // Get requested permissions
        if (pkg.requestedPermissions != null) {
            ArrayList 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 permList) {
        mGroupLabelCache = new HashMap();
        //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();
        // Map containing group names and a list of permissions under that group
        // categorized as normal
        mNormalMap = new HashMap();
        
        // Additional structures needed to ensure that permissions are unique under 
        // each group
        Map> dangerousMap = 
            new HashMap>();
        Map > normalMap = 
            new HashMap>();
        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 > 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 grpPermsList = permInfoMap.get(grpName);
                if(grpPermsList == null) {
                    grpPermsList = new ArrayList();
                    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 permInfoMap = dangerous ? mDangerousMap : mNormalMap;
        LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList;
        permListView.removeAllViews();

        Set 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等工作,具體是什麼不再深究,如果以後有機會再講解。

 

 

還是那句話給大師取樂,給後來者拋磚引玉,不要在背後罵我就謝天謝地了。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved