android程序保管理器

android程序包管理器

 li_ji_qun@163.com

QQ: 591098085

http://blog.csdn.net/superkris

 

包管理服务调用ContextImpl类的getPakcageManager()函数返回PackageManager对象

系统权限的目录有两个地方:

一个是/system/etc/permissions/*

比如这下面的platform.xml文件,该文件为某些uidgid分配特定的权限,

比如

<assign-permission name="android.permission.DELETE_PACKAGES" uid="shell" />

shell这个uid分配删除安装包的权限。

 

<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >

   <group gid="sdcard_rw" />

</permission>

这个是为sdcard_rwgid分配访问SD卡的权限。

 

而在应用程序中要获得相关权限,需要在AndroidManifest.xml中添加,比如获取wifi权限

<use-feature android:name=”android.hardware.wifi” android:required=”true”>

其中required=”true”表示没有wifi这个feature的话,程序不能被运行。

 

/data/system/packages.xml文件中保存了所有安装程序基本信息:包名、安装路径、程序需要的权限、本地库、userid等,比如

<package name="com.android.email" codePath="/system/app/Email.apk" nativeLibraryPath="/data/data/com.android.email/lib" flags="1" ft="1397c473c98" it="1397c473c98" ut="1397c473c98" version="234000" userId="10026">

 

ft fix time

it install time

ut update time

  

 

包管理服务在启动的时候,会解析相关的xml文件,建立包信息。包管理服务有两个辅助的服务。DefaultContainerService.javaInstaller.java

安装位置:

    系统程序都保存在/system/app下。用户安装的程序在/data/app下面,以包名”+”-安装次数来给程序命名.通过dalvik源代码可以知道,/data/dalvik-cache目录是程序的可执行代码,即dex文件,通过dexopt生成的,看看里面的文件就知道命名规则了。

 

重要的成员变量Settings

 

包管理的代码packageManagerService.java

   public static final IPackageManager main(Context context, boolean factoryTest) {

       PackageManagerService m = new PackageManagerService(context, factoryTest);

       ServiceManager.addService("package", m);

       return m;

   }

packageManagerService.java里有内部类Settings的构造函数

       Settings() {

           mSettingsFilename = new File(systemDir, "packages.xml");

           mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");

           mPackageListFilename = new File(systemDir, "packages.list");

}

可以看到包管理的文件是packages.xml,备份文件是packages-backup.xml,安装包的信息是packages.list

shell看看文件的属性,可以知道这几个文件所有用户都是可以读取的。

-rw-rw-r-- system  system      3644 2011-01-03 15:23 packages.list

-rw-rw-r-- system  system     60526 2011-01-03 15:23 packages.xml

打开packages.list,里面的每一行代表一个应用

com.android.launcher 10029 0 /data/data/com.android.launcher

第一项是包名,第二项是user idshared user id,第三项表示是否可以被debug,最后是程序数据文件的目录

final HashMap<String, PackageParser.Package> mPackages是扫描程序目录下APK文件生成的

private final HashMap<String, PackageSetting> mPackages是读取packages.xml生成的。

final HashMap<String, PackageSetting> mDisabledSysPackages是没通过标准卸载方法删除的程序列表

标准卸载会清楚packages.xml对应的项,如果用adb或直接操作文件删除apk,则packages.xml里不会删除,每次开机包管理服务会检查packages.xml里的程序在不在,不在则加到mDisabledSysPackages

 

已经删除的包外部,数据还没有清除的保存在下面这个变量中。

// Packages that have been uninstalled and still need their external

// storage data deleted.

final ArrayList<String> mPackagesToBeCleaned

 

下面几个变量是监听对应的目录是否有访问、创建、修改、删除、移动、关闭等操作等动作。具体可以参考FileObserver,使用linuxinotify来实现的,但是监控/data/data目录需要system权限才可以。

 

// This is the object monitoring the framework dir.

 final FileObserver mFrameworkInstallObserver;

// This is the object monitoring the system app dir.

 final FileObserver mSystemInstallObserver;

// This is the object monitoring the system app dir.

 final FileObserver mVendorInstallObserver;

// This is the object monitoring mAppInstallDir.

 final FileObserver mAppInstallObserver;

// This is the object monitoring mDrmAppPrivateInstallDir.

 final FileObserver mDrmAppInstallObserver;

 

应用程序的安装和卸载过程

 

应用程序的安装是调用PackageManager的installPackage(), 是异步安装的,通过向PackageHandler类发送INIT_COPY消息,最后在PackageHandler类的handleMessage来处理INIT_COPY消息。

期间会判断是否要绑定Media Container Service,需要的话要去启动MCS服务,并在MCS的回调函数onServiceConnected里去发送MCS_BOUND这个绑定消息去绑定。最后调用下面的代码去复制应用程序

case MCS_BOUND: {

       ……

       HandlerParams params = mPendingInstalls.get(0);

                        if (params != null) {

                            params.startCopy();

                        }

       ……

}

Copy过程中调handleStartCopy()里面会对安装目录做调整

 

mAppInstallObserver会监视/data/app目录,在onEvent()里会处理ADD_EVENT消息。

  private final class AppDirObserver extends FileObserver {

        public AppDirObserver(String path, int mask, boolean isrom) {

            super(path, mask);

            mRootDir = path;

            mIsRom = isrom;

        }

 

        public void onEvent(int event, String path) {

            String removedPackage = null;

            int removedUid = -1;

            String addedPackage = null;

            int addedUid = -1;

            ……

                if ((event&ADD_EVENTS) != 0) {

                    if (p == null) {

                        p = scanPackageLI(fullPath,

                                (mIsRom ? PackageParser.PARSE_IS_SYSTEM

                                        | PackageParser.PARSE_IS_SYSTEM_DIR: 0) |

                                PackageParser.PARSE_CHATTY |

                                PackageParser.PARSE_MUST_BE_APK,

                                SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,

                                System.currentTimeMillis());

                        if (p != null) {

                            synchronized (mPackages) {

                                updatePermissionsLP(p.packageName, p,

                                        p.permissions.size() > 0, false, false);

                            }

                            addedPackage = p.applicationInfo.packageName;

                            addedUid = p.applicationInfo.uid;

                        }

                    }

                }

                …….

        }

在ADD_EVENT()里先调用scanPackageLI从APK中提取包管理信息,然后调updatePermissionsLP来提取权限信息。

对于系统程序,可以直接拷贝到/system/app就行了,其它程序不能就直接放到/data/app下,必须安装才可以。

 

 

应用的程序的卸载和安装的过程基本是是做相反的动作,但是卸载不是谁都可以卸载的,就好比你的windows电脑上有些软件是需要管理员权限才可以卸载的。

卸载的函数是PackageManager中的deletePackage(),它也是开了一个线程异步卸载的,调的是deletePackageX()

    public void deletePackage(final String packageName,

                              final IPackageDeleteObserver observer,

                              final int flags) {

        mContext.enforceCallingOrSelfPermission(

                android.Manifest.permission.DELETE_PACKAGES, null);

        // Queue up an async operation since the package deletion may take a little while.

        mHandler.post(new Runnable() {

            public void run() {

                mHandler.removeCallbacks(this);

                final boolean succeded = deletePackageX(packageName, true, true, flags);

                if (observer != null) {

                    try {

                        observer.packageDeleted(succeded);

                    } catch (RemoteException e) {

                        Log.i(TAG, "Observer no longer exists.");

                    } //end catch

                } //end if

            } //end run

        });

    }

    看看deletePackageX函数前的注释: 它会发消息通知

    /**

     *  This method is an internal method that could be get invoked either

     *  to delete an installed package or to clean up a failed installation.

     *  After deleting an installed package, a broadcast is sent to notify any

     *  listeners that the package has been installed. For cleaning up a failed

     *  installation, the broadcast is not necessary since the package's

     *  installation wouldn't have sent the initial broadcast either

     *  The key steps in deleting a package are

     *  deleting the package information in internal structures like mPackages,

     *  deleting the packages base directories through installd

     *  updating mSettings to reflect current status

     *  persisting settings for later use

     *  sending a broadcast if necessary

     */

 

    private boolean deletePackageX(String packageName, boolean sendBroadCast,

                                   boolean deleteCodeAndResources, int flags) {

        PackageRemovedInfo info = new PackageRemovedInfo();

        boolean res;

 

        IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(

                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));

        try {

            if (dpm != null && dpm.packageHasActiveAdmins(packageName)) {

                Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");

                return false;

            }

        } catch (RemoteException e) {

        }

      

再看deletePackageX里的代码可以看到卸载系统和安装的应用走的是不同的路径。卸载程序要调IDevicePolicyManager服务里(在DevicePolicyManagerService.java里实现)的packageHasActiveAdmins()函数检查是否具备admin权限,如果没有admin权限,则直接返回不卸载程序,有了admin才去卸载程序,删除程序数据,发送广播消息(Intent.ACTION_PACKAGE_REMOVE或Intent.ACTION_UID_REMOVE),修改packages.xml文件等。

在APK中的xml文件中要添加android.permission.DELETE_PACKAGES才可以获得卸载程序的权限

在APK中的xml文件中添加android.permission.DELETE_CACHE_FILE可以删除其它APK的缓存,在程序包里可以通过这个给某个特殊的应用强制分配这些权限提高,比如垃圾处理程序,软件管家等程序。

 

Android上实在自己的安装、卸载和更新功能

安装程序的方法:

1、  用Intent机制调出系统安装应用,重新安装应用的话,会保留原应用的数据。

String fileName = Environment.getExternalStorageDirectory() + apkName;

Uri uri = Uri.fromFile(new File(fileName));

Intent intent = new Intent(Intent.ACTION_VIEW);

intent.setDataAndType(Uri, application/vnd.android.package-archive");

startActivity(intent);

 

2、  *面安装安装接口。

 

Uri mPackageURI = Uri.fromFile(new File(Environment.getExternalStorageDirectory() + apkName));

int installFlags = 0;

PackageManager pm = getPackageManager();

try

{

    PackageInfo pi = pm.getPackageInfo(packageName,

    PackageManager.GET_UNINSTALLED_PACKAGES);

    if(pi != null)

    {

        installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;

    }

}

catch (NameNotFoundException e)

{}

PackageInstallObserver observer = new PackageInstallObserver();

pm.installPackage(mPackageURI, observer, installFlags); //这个SDK没有导出来,需要用java发射机制才能调得到

 

权限需求:android.permission.INSTALL_PACKAGES  system权限

 

3、  执行install命令。

 

install –r 更新安装,默认新安装;如果不附上-r参数,会清除原来的数据,版本一致则不安装。

(1)am start …

(2)Runtime.exec(String[] args)

(3)Class<?> execClass = Class.forName("android.os.Exec");

 

4、  执行cp 或 adb push命令。

  由系统检测到应用程序有更新,自动完成重新安装。

5、  通过第三方软件实现。

 

Market,eTrackDog均采用第一种方法实现更新。

 

实例:Market查找安装程序

Intent intent =

new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:your.app.id"));

startActivity(intent);

 

卸载程序的方法:

 

1、  通过Intent机制,调出系统卸载应用

Uri packageURI = Uri.parse("package: your.app.id");

Intent intent = new Intent(Intent.ACTION_DELETE);

startActivity(intent);

 

2、  直接调用卸载接口。

 

PackageInstallObserver observer = new PackageInstallObserver();

pm.deletePackage (mPackageURI, observer, installFlags);

 

卸载应用权限:android.permission.DELETE_PACKAGES

 

3、  运行rm apk安装文件,由系统检测后调用卸载应用。

 

备注说明:

Android系统的应用安装,在系统设置里面有一项,是否安装未知源,所在在软件更新的时候,需要检测这个选项,如果打钩,则只允许安装Market源提供的安装程序,如果没有打钩的话,系统安装应用时会提示用户设置,如果选择设置,设置好后,无法返回安装界面;如果选择取消,则推出安装程序。所以,如果是更新的话,一定要在下载之前就检测许可安装源的设置,或者在下载前检测是否已经下载过新的安装程序,避免重复下载安装程序。

 

相关的代码如下:

1.          int result = Settings.Secure.getInt(getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 0);      

2.          if (result == 0) {      

3.          // show some dialog here      

4.          // ...      

5.          // and may be show application settings dialog manually      

6.          Intent intent = new Intent();      

7.          intent.setAction(Settings.ACTION_APPLICATION_SETTINGS);      

8.          startActivity(intent);     

9.          }

 

public static final class Settings.Secure extends Settings.NameValueTable

public static final String INSTALL_NON_MARKET_APPS

Since: API Level 3

Whether the package installer should allow installation of apps downloaded from sources other than the Android Market (vending machine). 1 = allow installing from other sources 0 = only allow installing from the Android Market。

 

以上可以在PmPermissionsTests.java里看到例子。

 

 Intent匹配

Package Manager Service在初始化的时候,会读所有程序的AndroidManifest.xml文件,提取intent-filter并保存,以后应用程序可以通过PackageManager提供的

queryIntentActivities

queryIntentActivityOptions

queryIntentServices

queryContentProviders

queryInstrumentation

queryIntentReceivers

如果intent-filter匹配,就启动相关的APK