QQ: 591098085
http://blog.csdn.net/superkris
包管理服务调用ContextImpl类的getPakcageManager()函数返回PackageManager对象
系统权限的目录有两个地方:
一个是/system/etc/permissions/*
比如这下面的platform.xml文件,该文件为某些uid和gid分配特定的权限,
比如
<assign-permission name="android.permission.DELETE_PACKAGES" uid="shell" />
为shell这个uid分配删除安装包的权限。
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_rw" />
</permission>
这个是为sdcard_rw的gid分配访问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.java和Installer.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 id或shared 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,使用linux的inotify来实现的,但是监控/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