应用管理
本文主要是分析的是“设置”--> "应用"中的应用管理中的应用的运行时权限和默认应用配置等。
分析了应用权限管理的整个流程 和 默认应用程序的设置
一、应用运行时权限管理
Settings --> 应用 --> 配置应用(设置中)--> 应用所需权限
界面:
Activity:
com.android.packageinstaller/.permission.ui.ManagePermissionsActivity
Fragment:
com.android.packageinstaller.permission.ui.handheld.ManagePermissionsFragment
ManagePermissionsFragment有两个,其中一个是针对电视设备的,这里调用的是handheld目录下的文件
界面很简单,主要利用listView空间来实现。这里重点关注下这里的数据
//com.android.packageinstaller.permission.ui.handheld.ManagePermissionsFragment.java
private PermissionGroups mPermissions;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
****
//mPermissions的由来
mPermissions = new PermissionGroups(getActivity(), getLoaderManager(), this);
}
private void updatePermissionsUi() {
Context context = getActivity();
if (context == null) {
return;
}
//主要是这个 groups变量,为listView提供数据
List groups = mPermissions.getGroups();
PreferenceScreen screen = getPreferenceScreen();
if (screen == null) {
screen = getPreferenceManager().createPreferenceScreen(getActivity());
setPreferenceScreen(screen);
}
listView的数据主要是由,PermissionGroups对象的getGroups方法获得List
, 处理后然后给listView的litem赋值。
下面看下PermissionGroups怎么构造的数据。
mPermissions
//PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionGroups.java
public final class PermissionGroups implements LoaderCallbacks> {
*****
//构造器
public PermissionGroups(Context context, LoaderManager loaderManager,
PermissionsGroupsChangeCallback callback) {
mContext = context;
mLoaderManager = loaderManager;
mCallback = callback;
}
@Override
public void onLoadFinished(Loader> loader,
List groups) {
if (mGroups.equals(groups)) {
return;
}
mGroups.clear();
mGroups.addAll(groups);
mCallback.onPermissionGroupsChanged();
}
private static final class PermissionsLoader extends AsyncTaskLoader> {
public PermissionsLoader(Context context) {
super(context);
}
@Override
public List loadInBackground() {
****
PackageManager packageManager = getContext().getPackageManager();
List groupInfos = packageManager.getAllPermissionGroups(0);
这里使用了Android提供的处理异步任务的方法,AsyncTaskLoader这里简单介绍下
loadInBackground()方法执行的耗时操作,通过packageManagerService来获取List
,
获取完成后会回调 LoaderCallbacks接口中的 onLoadFinished()方法。
所以权限组的产生还在PackageManagerService里面,下面去PackageManagerService.java文件去看下:
public class PackageManagerService extends IPackageManager.Stub {
***
@Override
public @NonNull ParceledListSlice getAllPermissionGroups(int flags) {
// reader
synchronized (mPackages) {
// 看来权限组的数据源,要去追踪下mPermissionGroups变量的构造
final int N = mPermissionGroups.size();
ArrayList out
= new ArrayList(N);
for (PackageParser.PermissionGroup pg : mPermissionGroups.values()) {
out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
}
return new ParceledListSlice<>(out);
}
}
***
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
****
for (i=0; i
系统启动PackageManagerService的时候,扫描安装应用的时候,扫描到framework-res.apk文件的时候,解析里面的AndroidManifest.xml文件
2. xml对应的数据结构mPermissionGroups
点击权限组弹出的界面:
PermissionAppsFragments.java文件
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setLoading(true /* loading */, false /* animate */);
****
String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
mPermissionApps = new PermissionApps(getActivity(), groupName, this);
mPermissionApps.refresh(true);
}
PermissionApps根据传入的groupName,来返回所有的申请该权限组内权限的应用
@Override
public void onPermissionsLoaded(PermissionApps permissionApps) {
for (PermissionApp app : permissionApps.getApps()) {
......
}
遍历过滤app,然后显示出来
关闭or授予权限执行的操作
//PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionAppsFragment.java
public final class PermissionAppsFragment extends PermissionsFrameFragment implements Callback,
Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(final Preference preference, Object newValue) {
String pkg = preference.getKey();
final PermissionApp app = mPermissionApps.getApp(pkg);
if (app == null) {
return false;
}
addToggledGroup(app.getPackageName(), app.getPermissionGroup());
if (LocationUtils.isLocationGroupAndProvider(mPermissionApps.getGroupName(),
app.getPackageName())) {
LocationUtils.showLocationDialog(getContext(), app.getLabel());
return false;
}
if (newValue == Boolean.TRUE) {
app.grantRuntimePermissions();
} else {
final boolean grantedByDefault = app.hasGrantedByDefaultPermissions();
if (grantedByDefault || (!app.hasRuntimePermissions() && !mHasConfirmedRevoke)) {
new AlertDialog.Builder(getContext())
.setMessage(grantedByDefault ? R.string.system_warning
: R.string.old_sdk_deny_warning)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.grant_dialog_button_deny_anyway,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
((SwitchPreference) preference).setChecked(false);
app.revokeRuntimePermissions();
if (!grantedByDefault) {
mHasConfirmedRevoke = true;
}
}
})
.show();
return false;
} else {
app.revokeRuntimePermissions();
}
}
return true;
}
其中app.grantRuntimePermissions
和 app.revokeRuntimePermissions();
是真正授予或关闭权限的操作
app.grantRuntimePermissions 和 app.revokeRuntimePermissions()
file: PermissionsState.java
/**
* Grant a runtime permission for a given device user.
*
* @param permission The permission to grant.
* @param userId The device user id.
* @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
* or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
* #PERMISSION_OPERATION_FAILURE}.
*/
public int grantRuntimePermission(BasePermission permission, int userId) {
enforceValidUserId(userId);
if (userId == UserHandle.USER_ALL) {
return PERMISSION_OPERATION_FAILURE;
}
//转而去调用 grantPermission()方法
return grantPermission(permission, userId);
}
private int grantPermission(BasePermission permission, int userId) {
if (hasPermission(permission.name, userId)) {
return PERMISSION_OPERATION_FAILURE;
}
final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
//将permission对象封装成 PermissionData(PermissionData是个内部类)
PermissionData permissionData = ensurePermissionData(permission);
//构建PermissionState(首次),将PermissionState的mGranted属性置true
if (!permissionData.grant(userId)) {
return PERMISSION_OPERATION_FAILURE;
}
if (hasGids) {
final int[] newGids = computeGids(userId);
if (oldGids.length != newGids.length) {
return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
}
}
return PERMISSION_OPERATION_SUCCESS;
}
//file: PermissionsState.java
private static final class PermissionData {
....
// grant方法,将该PermissionState对象的mGranted属性置true
public boolean grant(int userId) {
if (!isCompatibleUserId(userId)) {
return false;
}
if (isGranted(userId)) {
return false;
}
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
userState = new PermissionState(mPerm.name);
mUserStates.put(userId, userState);
}
userState.mGranted = true;
return true;
}
动态权限写的文件
file: /data/system/users/0/runtime-permissions.xml
shared-user标签对应packageManagerService.java构造器中的
file: PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
......
mSettings = new Settings(mPackages);
//为系统中一些重要的应用提前授予运行时权限
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
......
}
file: /frameworks/base/services/core/java/com/android/server/pm/Settings.java
private void writePermissionsSync(int userId) {
AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId));
ArrayMap> permissionsForPackage = new ArrayMap<>();
ArrayMap> permissionsForSharedUser = new ArrayMap<>();
synchronized (mLock) {
mWriteScheduled.delete(userId);
final int packageCount = mPackages.size();
for (int i = 0; i < packageCount; i++) {
String packageName = mPackages.keyAt(i);
PackageSetting packageSetting = mPackages.valueAt(i);
if (packageSetting.sharedUser == null) {
PermissionsState permissionsState = packageSetting.getPermissionsState();
List permissionsStates = permissionsState
.getRuntimePermissionStates(userId);
if (!permissionsStates.isEmpty()) {
permissionsForPackage.put(packageName, permissionsStates);
}
}
}
final int sharedUserCount = mSharedUsers.size();
for (int i = 0; i < sharedUserCount; i++) {
String sharedUserName = mSharedUsers.keyAt(i);
SharedUserSetting sharedUser = mSharedUsers.valueAt(i);
PermissionsState permissionsState = sharedUser.getPermissionsState();
List permissionsStates = permissionsState
.getRuntimePermissionStates(userId);
if (!permissionsStates.isEmpty()) {
permissionsForSharedUser.put(sharedUserName, permissionsStates);
}
}
}
FileOutputStream out = null;
try {
out = destination.startWrite();
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(out, StandardCharsets.UTF_8.name());
serializer.setFeature(
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startDocument(null, true);
serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);
String fingerprint = mFingerprints.get(userId);
if (fingerprint != null) {
serializer.attribute(null, ATTR_FINGERPRINT, fingerprint);
}
final int packageCount = permissionsForPackage.size();
for (int i = 0; i < packageCount; i++) {
String packageName = permissionsForPackage.keyAt(i);
List permissionStates = permissionsForPackage.valueAt(i);
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, packageName);
writePermissions(serializer, permissionStates);
serializer.endTag(null, TAG_PACKAGE);
}
final int sharedUserCount = permissionsForSharedUser.size();
for (int i = 0; i < sharedUserCount; i++) {
String packageName = permissionsForSharedUser.keyAt(i);
List permissionStates = permissionsForSharedUser.valueAt(i);
serializer.startTag(null, TAG_SHARED_USER);
serializer.attribute(null, ATTR_NAME, packageName);
writePermissions(serializer, permissionStates);
serializer.endTag(null, TAG_SHARED_USER);
}
serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
// Now any restored permission grants that are waiting for the apps
// in question to be installed. These are stored as per-package
// TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some
// number of individual permission grant entities.
if (mRestoredUserGrants.get(userId) != null) {
ArrayMap> restoredGrants =
mRestoredUserGrants.get(userId);
if (restoredGrants != null) {
final int pkgCount = restoredGrants.size();
for (int i = 0; i < pkgCount; i++) {
final ArraySet pkgGrants =
restoredGrants.valueAt(i);
if (pkgGrants != null && pkgGrants.size() > 0) {
final String pkgName = restoredGrants.keyAt(i);
serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName);
final int N = pkgGrants.size();
for (int z = 0; z < N; z++) {
RestoredPermissionGrant g = pkgGrants.valueAt(z);
serializer.startTag(null, TAG_PERMISSION_ENTRY);
serializer.attribute(null, ATTR_NAME, g.permissionName);
if (g.granted) {
serializer.attribute(null, ATTR_GRANTED, "true");
}
if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
serializer.attribute(null, ATTR_USER_SET, "true");
}
if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
serializer.attribute(null, ATTR_USER_FIXED, "true");
}
if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
}
serializer.endTag(null, TAG_PERMISSION_ENTRY);
}
serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
}
}
}
}
serializer.endDocument();
destination.finishWrite(out);
if (Build.FINGERPRINT.equals(fingerprint)) {
mDefaultPermissionsGranted.put(userId, true);
}
// Any error while writing is fatal.
} catch (Throwable t) {
Slog.wtf(PackageManagerService.TAG,
"Failed to write settings, restoring backup", t);
destination.failWrite(out);
} finally {
IoUtils.closeQuietly(out);
}
}
二、默认应用
默认应用的一些配置信息都写在了package-restrictions.xml 文件
默认浏览器配置
系统记录默认浏览器的位置在:
手机上的文件位置:data/system/users/0/package-restrictions.xml
源码配置默认浏览器的地方:
frameworks/base/core/res/res/values/config.xml
//file: frameworks/base/core/res/res/values/config.xml
恢复默认浏览器的代码:
//file: frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void applyFactoryDefaultBrowserLPw(int userId) {
// The default browser app's package name is stored in a string resource,
// with a product-specific overlay used for vendor customization.
String browserPkg = mContext.getResources().getString(
com.android.internal.R.string.default_browser);
if (!TextUtils.isEmpty(browserPkg)) {
// non-empty string => required to be a known package
PackageSetting ps = mSettings.mPackages.get(browserPkg);
if (ps == null) {
Slog.e(TAG, "Product default browser app does not exist: " + browserPkg);
browserPkg = null;
} else {
mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
}
}
// Nothing valid explicitly set? Make the factory-installed browser the explicit
// default. If there's more than one, just leave everything alone.
if (browserPkg == null) {
calculateDefaultBrowserLPw(userId);
}
}
private void calculateDefaultBrowserLPw(int userId) {
List allBrowsers = resolveAllBrowserApps(userId);
final String browserPkg = (allBrowsers.size() == 1) ? allBrowsers.get(0) : null;
mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
}
从代码可以看出,系统会去读取frameworks/base/core/res/res/values/config.xml
文件中的default_browser参数,来获取默认浏览器的包名,然后查询Settings中有没有该包的信息(没有的话输出错误log),然后调用Settings.setDefaultBrowserPackageNameLPw()
方法,最终写入package-restrictions.xml
文件的
//file: framework/base/services/core/java/com/android/server/pm/Settings.java
boolean setDefaultBrowserPackageNameLPw(String packageName, int userId) {
if (userId == UserHandle.USER_ALL) {
return false;
}
mDefaultBrowserApp.put(userId, packageName);
writePackageRestrictionsLPr(userId);
return true;
}
其中 writePackageRestrictionsLPr(userId)
就是写package-restrictions.xml
文件的操作,其中就 包含默认浏览器的标签
//file: framework/base/services/core/java/com/android/server/pm/Settings.java
void writePackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Writing package restrictions for user=" + userId);
}
// Keep the old stopped packages around until we know the new ones have
// been successfully written.
File userPackagesStateFile = getUserPackagesStateFile(userId);
File backupFile = getUserPackagesStateBackupFile(userId);
new File(userPackagesStateFile.getParent()).mkdirs();
if (userPackagesStateFile.exists()) {
// Presence of backup settings file indicates that we failed
// to persist packages earlier. So preserve the older
// backup for future reference since the current packages
// might have been corrupted.
if (!backupFile.exists()) {
if (!userPackagesStateFile.renameTo(backupFile)) {
Slog.wtf(PackageManagerService.TAG,
"Unable to backup user packages state file, "
+ "current changes will be lost at reboot");
return;
}
} else {
userPackagesStateFile.delete();
Slog.w(PackageManagerService.TAG, "Preserving older stopped packages backup");
}
}
try {
final FileOutputStream fstr = new FileOutputStream(userPackagesStateFile);
final BufferedOutputStream str = new BufferedOutputStream(fstr);
final XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(str, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);
for (final PackageSetting pkg : mPackages.values()) {
final PackageUserState ustate = pkg.readUserState(userId);
if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled);
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, pkg.name);
if (ustate.ceDataInode != 0) {
XmlUtils.writeLongAttribute(serializer, ATTR_CE_DATA_INODE, ustate.ceDataInode);
}
if (!ustate.installed) {
serializer.attribute(null, ATTR_INSTALLED, "false");
}
if (ustate.stopped) {
serializer.attribute(null, ATTR_STOPPED, "true");
}
if (ustate.notLaunched) {
serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
}
if (ustate.hidden) {
serializer.attribute(null, ATTR_HIDDEN, "true");
}
if (ustate.suspended) {
serializer.attribute(null, ATTR_SUSPENDED, "true");
}
if (ustate.blockUninstall) {
serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true");
}
if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
serializer.attribute(null, ATTR_ENABLED,
Integer.toString(ustate.enabled));
if (ustate.lastDisableAppCaller != null) {
serializer.attribute(null, ATTR_ENABLED_CALLER,
ustate.lastDisableAppCaller);
}
}
if (ustate.domainVerificationStatus !=
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
XmlUtils.writeIntAttribute(serializer, ATTR_DOMAIN_VERIFICATON_STATE,
ustate.domainVerificationStatus);
}
if (ustate.appLinkGeneration != 0) {
XmlUtils.writeIntAttribute(serializer, ATTR_APP_LINK_GENERATION,
ustate.appLinkGeneration);
}
if (!ArrayUtils.isEmpty(ustate.enabledComponents)) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
for (final String name : ustate.enabledComponents) {
serializer.startTag(null, TAG_ITEM);
serializer.attribute(null, ATTR_NAME, name);
serializer.endTag(null, TAG_ITEM);
}
serializer.endTag(null, TAG_ENABLED_COMPONENTS);
}
if (!ArrayUtils.isEmpty(ustate.disabledComponents)) {
serializer.startTag(null, TAG_DISABLED_COMPONENTS);
for (final String name : ustate.disabledComponents) {
serializer.startTag(null, TAG_ITEM);
serializer.attribute(null, ATTR_NAME, name);
serializer.endTag(null, TAG_ITEM);
}
serializer.endTag(null, TAG_DISABLED_COMPONENTS);
}
serializer.endTag(null, TAG_PACKAGE);
}
writePreferredActivitiesLPr(serializer, userId, true);
writePersistentPreferredActivitiesLPr(serializer, userId);
writeCrossProfileIntentFiltersLPr(serializer, userId);
//写默认浏览器操作
writeDefaultAppsLPr(serializer, userId);
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
serializer.endDocument();
str.flush();
FileUtils.sync(fstr);
str.close();
// New settings successfully written, old ones are no longer
// needed.
backupFile.delete();
FileUtils.setPermissions(userPackagesStateFile.toString(),
FileUtils.S_IRUSR|FileUtils.S_IWUSR
|FileUtils.S_IRGRP|FileUtils.S_IWGRP,
-1, -1);
// Done, all is good!
return;
} catch(java.io.IOException e) {
Slog.wtf(PackageManagerService.TAG,
"Unable to write package manager user packages state, "
+ " current changes will be lost at reboot", e);
}
// Clean up partially written files
if (userPackagesStateFile.exists()) {
if (!userPackagesStateFile.delete()) {
Log.i(PackageManagerService.TAG, "Failed to clean up mangled file: "
+ mStoppedPackagesFilename);
}
}
}
如下便是写默认浏览器和默认拨号应用的操作,各自对应的标签是
和
//file: framework/base/services/core/java/com/android/server/pm/Settings.java
void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, TAG_DEFAULT_APPS);
String defaultBrowser = mDefaultBrowserApp.get(userId);
if (!TextUtils.isEmpty(defaultBrowser)) {
//其中 TAG_DEFAULT_BROWSER = "default-browser"
serializer.startTag(null, TAG_DEFAULT_BROWSER);
serializer.attribute(null, ATTR_PACKAGE_NAME, defaultBrowser);
serializer.endTag(null, TAG_DEFAULT_BROWSER);
}
String defaultDialer = mDefaultDialerApp.get(userId);
if (!TextUtils.isEmpty(defaultDialer)) {
//其中TAG_DEFAULT_DIALER = "default-dialer"
serializer.startTag(null, TAG_DEFAULT_DIALER);
serializer.attribute(null, ATTR_PACKAGE_NAME, defaultDialer);
serializer.endTag(null, TAG_DEFAULT_DIALER);
}
serializer.endTag(null, TAG_DEFAULT_APPS);
}