标签:android packagemanager 应用安装 应用卸载 package
Android通过PackageManagerService(后面简称Pms)进行包管理,其主要功能包括:用户ID分配、包解析、包的安装卸载等。本文不对Pms进行分析,主要目的是探讨一下包安装。在本文中主要探讨包安装的相关操作,卸载作为安装的逆过程,实现类似,不再赘述。
在Android中APK的安装有三种方式:
@/frameworks/base/services/java/com/android/server/SystemServer.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 | publicvoidinitAndLoop() {    ......        IPackageManager pm = null;        ......        try{        ......                pm = PackageManagerService.main(context, installer,                factoryTest != SystemServer.FACTORY_TEST_OFF,                onlyCore);        ......    } catch(RuntimeException e) {        Slog.e("System", "******************************************");        Slog.e("System", "************ Failure starting core service", e);    }    ......} | 
@/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
| 
1 
2 
3 
4 
5 
6 
7 | publicstaticfinalIPackageManager main(Context context, Installer installer,        booleanfactoryTest, booleanonlyCore) {    PackageManagerService m = newPackageManagerService(context, installer,            factoryTest, onlyCore);    ServiceManager.addService("package", m);    returnm;} | 
下面是Pms构造函数的实现:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 | publicPackageManagerService(Context context, Installer installer,        boolean factoryTest, boolean onlyCore) {    ......        synchronized (mInstallLock) {    // writer    synchronized (mPackages) {        ......                File dataDir = Environment.getDataDirectory();        mAppDataDir = newFile(dataDir, "data");        mAppInstallDir = newFile(dataDir, "app");        mAppLibInstallDir = newFile(dataDir, "app-lib");        mAsecInternalPath = newFile(dataDir, "app-asec").getPath();        mUserAppDataDir = newFile(dataDir, "user");        mDrmAppPrivateInstallDir = newFile(dataDir, "app-private");        ......                // Find base frameworks (resource packages without code).        mFrameworkInstallObserver = newAppDirObserver(            frameworkDir.getPath(), OBSERVER_EVENTS, true, false);        mFrameworkInstallObserver.startWatching();        scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM                | PackageParser.PARSE_IS_SYSTEM_DIR,                scanMode | SCAN_NO_DEX, 0);        // Collected privileged system packages.        File privilegedAppDir = newFile(Environment.getRootDirectory(), "priv-app");        mPrivilegedInstallObserver = newAppDirObserver(                privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true);        mPrivilegedInstallObserver.startWatching();            scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM                    | PackageParser.PARSE_IS_SYSTEM_DIR                    | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);        // Collect ordinary system packages.        File systemAppDir = newFile(Environment.getRootDirectory(), "app");        mSystemInstallObserver = newAppDirObserver(            systemAppDir.getPath(), OBSERVER_EVENTS, true, false);        mSystemInstallObserver.startWatching();        scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM                | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);        // Collect all vendor packages.        File vendorAppDir = newFile("/vendor/app");        mVendorInstallObserver = newAppDirObserver(            vendorAppDir.getPath(), OBSERVER_EVENTS, true, false);        mVendorInstallObserver.startWatching();        scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM                | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);        ......        if(!mOnlyCore) {            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,                    SystemClock.uptimeMillis());            mAppInstallObserver = newAppDirObserver(                mAppInstallDir.getPath(), OBSERVER_EVENTS, false, false);            mAppInstallObserver.startWatching();            scanDirLI(mAppInstallDir, 0, scanMode, 0);            mDrmAppInstallObserver = newAppDirObserver(                mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false, false);            mDrmAppInstallObserver.startWatching();            scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,                    scanMode, 0);            ......    } // synchronized (mPackages)    } // synchronized (mInstallLock)} | 
通过Pms的构造函数可以看出,Pms在初始化时会扫描/system/app、vender/app、/data/app、/data/app-private四个应用安装目录,然后调用sanDirLI方法进行安装。Pms通过AppDirObserver对这四个应用安装目录进行监控,一旦发现APK格式的文件则会调用scanPackageLI进行安装。
Android提供了一个默认的包安装器,位于/package/app/PackageInstaller目录。通过其Manifest文件可以看出,PackageInstaller会对我们安装应用发出的Intent进行处理,这里PackageInstaller提供了两种处理方式,分别是:file方式和package方式。
@/package/app/PackageInstaller/AndroidManifest.xml
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 | <activityandroid:name=".PackageInstallerActivity"        android:configChanges="orientation|keyboardHidden|screenSize"        android:excludeFromRecents="true">    <intent-filter>        <actionandroid:name="android.intent.action.VIEW"/>        <actionandroid:name="android.intent.action.INSTALL_PACKAGE"/>        <categoryandroid:name="android.intent.category.DEFAULT"/>        <dataandroid:scheme="file"/>        <dataandroid:mimeType="application/vnd.android.package-archive"/>    </intent-filter>    <intent-filter>        <actionandroid:name="android.intent.action.INSTALL_PACKAGE"/>        <categoryandroid:name="android.intent.category.DEFAULT"/>        <dataandroid:scheme="file"/>        <dataandroid:scheme="package"/>    </intent-filter></activity> | 
@/package/app/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
| 
1 
2 
3 
4 
5 
6 
7 
8 | @OverrideprotectedvoidonCreate(Bundle icicle) {    super.onCreate(icicle);    ......        initiateInstall();} | 
| 
1 
2 
3 
4 
5 | privatevoidinitiateInstall() {    ......    startInstallConfirm();} | 
在startInstallConfirm方法中点击“确认”后,会发出一个Intent,接收者为InstallAppProgress。
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 | publicvoidonClick(View v) {    if(v == mOk) {        if(mOkCanInstall || mScrollView == null) {            // Start subactivity to actually install the application            mInstallFlowAnalytics.setInstallButtonClicked();            Intent newIntent = newIntent();            newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,                    mPkgInfo.applicationInfo);            newIntent.setData(mPackageURI);            newIntent.setClass(this, InstallAppProgress.class);            ......                        startActivity(newIntent);            finish();        } else{            mScrollView.pageScroll(View.FOCUS_DOWN);        }    } elseif(v == mCancel) {        ......    }} | 
@/package/app/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 | publicvoidinitView() {    setContentView(R.layout.op_progress);    intinstallFlags = 0;    PackageManager pm = getPackageManager();        ......        String installerPackageName = getIntent().getStringExtra(            Intent.EXTRA_INSTALLER_PACKAGE_NAME);    Uri originatingURI = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);    Uri referrer = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);    intoriginatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,            VerificationParams.NO_UID);    ManifestDigest manifestDigest = getIntent().getParcelableExtra(EXTRA_MANIFEST_DIGEST);    VerificationParams verificationParams = newVerificationParams(null, originatingURI,            referrer, originatingUid, manifestDigest);    PackageInstallObserver observer = newPackageInstallObserver();    if("package".equals(mPackageURI.getScheme())) {        try{            pm.installExistingPackage(mAppInfo.packageName);            observer.packageInstalled(mAppInfo.packageName,                    PackageManager.INSTALL_SUCCEEDED);        } catch(PackageManager.NameNotFoundException e) {            observer.packageInstalled(mAppInfo.packageName,                    PackageManager.INSTALL_FAILED_INVALID_APK);        }    } else{        pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,                installerPackageName, verificationParams, null);    }} | 
InstallAppProgress即应用安装过程中的进度条界面。通过上面的代码可以看到在initView方法的最后会调用Pms的installPackageWithVerificationAndEncryption方法进行安装。
adb命令pm是Pms的Shell客户端,通过pm可以进行包相关的一些操作,包括安装和卸载。pm命令的用法如下:


pm的代码实现在Pm.java中,如下:
@/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 | publicstaticvoidmain(String[] args) {    newPm().run(args);}publicvoidrun(String[] args) {    ......        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));        ......    if("install".equals(op)) {        runInstall();        return;    }    if("uninstall".equals(op)) {        runUninstall();        return;    }    ......} | 
在run方法中初始化了一个Pms的客户端代理对象mPm,后续的相关操作将有mPm完成。下面看一下Pm中负责安装的方法runInstall的代码实现:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 | privatevoidrunInstall() {    intinstallFlags = PackageManager.INSTALL_ALL_USERS;        ......        while((opt=nextOption()) != null) {        if(opt.equals("-l")) {            installFlags |= PackageManager.INSTALL_FORWARD_LOCK;        } elseif(opt.equals("-r")) {            installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;        } elseif(opt.equals("-i")) {            installerPackageName = nextOptionData();            if(installerPackageName == null) {                System.err.println("Error: no value specified for -i");                return;            }        } elseif(opt.equals("-t")) {            installFlags |= PackageManager.INSTALL_ALLOW_TEST;        } elseif(opt.equals("-s")) {            // Override if -s option is specified.            installFlags |= PackageManager.INSTALL_EXTERNAL;        } elseif(opt.equals("-f")) {            // Override if -s option is specified.            installFlags |= PackageManager.INSTALL_INTERNAL;        } elseif(opt.equals("-d")) {            installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;        ......                   PackageInstallObserver obs = newPackageInstallObserver();    try{        VerificationParams verificationParams = newVerificationParams(verificationURI,                originatingURI, referrerURI, VerificationParams.NO_UID, null);        mPm.installPackageWithVerificationAndEncryption(apkURI, obs, installFlags,                installerPackageName, verificationParams, encryptionParams);        synchronized(obs) {            while(!obs.finished) {                try{                    obs.wait();                } catch(InterruptedException e) {                }            }            if(obs.result == PackageManager.INSTALL_SUCCEEDED) {                System.out.println("Success");            } else{                System.err.println("Failure ["                        + installFailureToString(obs.result)                        + "]");            }        }    } catch(RemoteException e) {        System.err.println(e.toString());        System.err.println(PM_NOT_RUNNING_ERR);    }} | 
可以看出runInstall最终会调用Pms的installPackageWithVerificationAndEncryption方法进行安装。通过pm安装时,安装成功的返回信息为“Success”,安装失败的返回信息为”Failure[失败信息]"。
在了解了Android中包安装的方式后,接下来探讨一些如何实现”静默安装“。所谓静默安装即跳过安装界面和进度条,在不被用户察觉的情况下载后台安装。下面针对上面的三种安装方式分别来分析如何实现静默安装。
在Pms初始化时安装包的流程中,我们知道Pms会监控/system/app、vender/app、/data/app、/data/app-private这四个应用安装目录。因此如果能够将APK文件push进应用安装目录不就可以触发AppDirObserver中的包安装逻辑了了吗?所以这种思路理论上是行得通的,但有两个局限:
局限一:如下图所示,/system/app的访问权限为root,这就要求在push到/system/app目录时必须具有root权限。
而/data/app的访问权限为system。要获得system权限就要求使用这种方式的应用程序必须签名为platform并且sharedUserId制定为“android.uid.system”。
局限二:系统应用(/system/app)与普通应用(/data/app)的安装方式是不同的,对于系统应用,所有资源都包含在apk这个zip包中,而且其在/system/app不必以包名命名(理论上可以随便起名)。
而对于普通应用安装后,它的dex、lib、资源文件(安装包)分别存放在不同的目录,并且安装后以packagename-x.apk的形式保存在/data/app目录下。?

那这种安装方式是不是就没有用了呢?非也。
网上有些电子市场或管家类软件实现的”秒装“功能应该就是安装这个思路实现的,当然这里只是猜测,需要进一步研究。
2、调用Pm隐藏API
Android实现了一个应用安装器的APK负责包的安装工作,在上面的分析中我们知道,PackageInstaller的工作实际上只是安装界面、权限确认、进度显示等,真正的安装工作依然是调用Pms实现的。到这里我们就有了第二种思路,能不能绕过安装界面,直接调用Pms里面的相应方法呢?当然可以,PackageManager类中就提供了这样的方法:
@/frameworks/base/core/java/android/content/pm/PackageManager.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 | /** * @hide * * Install a package. Since this may take a little while, the result will * be posted back to the given observer.  An installation will fail if the calling context * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the * package named in the package file‘s manifest is already installed, or if there‘s no space * available on the device. * * @param packageURI The location of the package file to install.  This can be a ‘file:‘ or a * ‘content:‘ URI. * @param observer An observer callback to get notified when the package installation is * complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be * called when that happens.  observer may be null to indicate that no callback is desired. * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. * @param installerPackageName Optional package name of the application that is performing the * installation. This identifies which market the package came from. */publicabstractvoidinstallPackage(        Uri packageURI, IPackageInstallObserver observer, intflags,        String installerPackageName); | 
可以看出,这个方法是hide的,因此在应用开发时如果要使用,必须通过反射。
这里的IPackageInstallObserver是installPackage方法的一个回调接口通知,其实现在IPackageInstallObserver.aidl中,如下:
@/frameworks/base/core/java/com/android/content/pm/IPackageInstallObserver.aidl
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 | packageandroid.content.pm;/** * API for installation callbacks from the Package Manager. * @hide */oneway interfaceIPackageInstallObserver {    voidpackageInstalled(in String packageName, intreturnCode);} | 
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 | classMyPakcageInstallObserver extendsIPackageInstallObserver.Stub
 {        Context
 cxt;        String
 appName;        String
 filename;        String
 pkname;        publicMyPakcageInstallObserver(Context
 c, String appName,                 String
 filename,String packagename) {            this.cxt
 = c;            this.appName
 = appName;            this.filename
 = filename;            this.pkname
 = packagename;        }        @Override        publicvoidpackageInstalled(String
 packageName, intreturnCode)
 {            Log.i(TAG, "returnCode
 = "+
 returnCode);//
 返回1代表安装成功                        if(returnCode
 == 1)
 {                            //TODO                        }            Intent
 it = newIntent();            it.setAction(CustomAction.INSTALL_ACTION);            it.putExtra("install_returnCode",
 returnCode);            it.putExtra("install_packageName",
 packageName);            it.putExtra("install_appName",
 appName); cxt.sendBroadcast(it);        }    } | 
调用PackageManager.java隐藏方法,代码如下:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 | /**     *
 静默安装     *
 */    publicstaticvoidautoInstallApk(Context
 context, String fileName,            String
 packageName, String APPName) {        Log.d(TAG, "jing
 mo an zhuang:"+
 packageName + ",fileName:"+
 fileName);        File
 file = newFile(fileName);        intinstallFlags
 = 0;        if(!file.exists())            return;        installFlags
 |= PackageManager.INSTALL_REPLACE_EXISTING;        if(hasSdcard())
 {            installFlags
 |= PackageManager.INSTALL_EXTERNAL;        }        PackageManager
 pm = context.getPackageManager();        try{            IPackageInstallObserver
 observer = newMyPakcageInstallObserver(                    context,
 APPName, appId, fileName,packageName,type_name);            Log.i(TAG, "########installFlags:"+
 installFlags+"packagename:"+packageName);            pm.installPackage(Uri.fromFile(file),
 observer, installFlags,                    packageName);        } catch(Exception
 e) {                    }    } | 
这种方法也有一定的限制:
其次,应用需要system权限。
在adb窗口通过pm install安装包本来就是没有安装界面的,这不正是我们想要的吗?通过pm的安装方式需要取得root或system权限。
pm的安装方式有两种,一种需要root权限,示例代码如下:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 | newThread()
 {    publicvoidrun()
 {    Process
 process = null;    OutputStream
 out = null;    InputStream
 in = null;    try{    //
 请求root    process
 = Runtime.getRuntime().exec("su");    out
 = process.getOutputStream();    //
 调用安装    out.write(("pm
 install -r "+
 currentTempFilePath + "\n").getBytes());    in
 = process.getInputStream();    intlen
 = 0;    byte[]
 bs = newbyte[256];    while(-1!=
 (len = in.read(bs))) {    String
 state = newString(bs, 0,
 len);    if(state.equals("Success\n"))
 {       //安装成功后的操作         }       }    } catch(IOException
 e) {        e.printStackTrace();    } catch(Exception
 e) {        e.printStackTrace();    } finally{        try{            if(out
 != null)
 {                out.flush();                out.close();            }            if(in
 != null)
 {                in.close();            }        } catch(IOException
 e) {            e.printStackTrace();        }    }  }}.start(); | 
另一钟需要system权限,示例如下:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 | newThread() {    publicvoidrun() {    Process process = null;    InputStream in = null;    try{    // 请求root    process = Runtime.getRuntime().exec("pm install -r "+ currentTempFilePath + "\n");     in = process.getInputStream();    intlen = 0;    byte[] bs = newbyte[256];    while(-1!= (len = in.read(bs))) {    String state = newString(bs, 0, len);    if(state.equals("Success\n")) {       //安装成功后的操作         }       }    } catch(IOException e) {        e.printStackTrace();    } catch(Exception e) {        e.printStackTrace();    } finally{        try{            if(in != null) {                in.close();            }        } catch(IOException e) {            e.printStackTrace();        }    }  }}.start(); | 
关于system权限的获取在介绍push方式的安装时已做介绍。上面的代码只给出了比较核心的部分,在实际实现中,对返回结果的处理同样重要。
标签:android packagemanager 应用安装 应用卸载 package
原文地址:http://blog.csdn.net/zhgxhuaa/article/details/25841585