CustomLocale.apk所需要的权限"android.permission.CHANGE_CONFIGURATION"自Android 4.2,4.2.2起系统定义为android:protectionLevel="signature|system|development",这就需要在已root的android设置上运行命令:
adb shell pm grant application_package android.permission.CHANGE_CONFIGURATION
下面具体说说pm grant的调用路径。
Pm.java


View Codepublic void run(String[] args) { boolean validCommand = false; if (args.length < 1) { showUsage(); return; } ... if ("grant".equals(op)) { runGrantRevokePermission(true); return; } ... private void runGrantRevokePermission(boolean grant) { String pkg = nextArg(); if (pkg == null) { System.err.println("Error: no package specified"); showUsage(); return; } String perm = nextArg(); if (perm == null) { System.err.println("Error: no permission specified"); showUsage(); return; } try { if (grant) { mPm.grantPermission(pkg, perm); } else { mPm.revokePermission(pkg, perm); } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); } catch (IllegalArgumentException e) { System.err.println("Bad argument: " + e.toString()); showUsage(); } catch (SecurityException e) { System.err.println("Operation not allowed: " + e.toString()); } }
PackageManagerService.java


View Codepublic class PackageManagerService extends IPackageManager.Stub { ... public void grantPermission(String packageName, String permissionName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null); synchronized (mPackages) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } final BasePermission bp = mSettings.mPermissions.get(permissionName); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permissionName); } checkGrantRevokePermissions(pkg, bp); final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { return; } final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps; if (gp.grantedPermissions.add(permissionName)) { if (ps.haveGids) { gp.gids = appendInts(gp.gids, bp.gids); } mSettings.writeLPr(); } } } ...
Settings.java


View Codefinal class Settings { ... private final File mSettingsFilename; ... Settings(Context context, File dataDir) { mContext = context; mSystemDir = new File(dataDir, "system"); mSystemDir.mkdirs(); FileUtils.setPermissions(mSystemDir.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG |FileUtils.S_IROTH|FileUtils.S_IXOTH, -1, -1); mSettingsFilename = new File(mSystemDir, "packages.xml"); mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml"); mPackageListFilename = new File(mSystemDir, "packages.list"); FileUtils.setPermissions(mPackageListFilename, 0660, SYSTEM_UID, PACKAGE_INFO_GID); // Deprecated: Needed for migration mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); } ... void writeLPr() { //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); // Keep the old settings around until we know the new ones have // been successfully written. if (mSettingsFilename.exists()) { // Presence of backup settings file indicates that we failed // to persist settings earlier. So preserve the older // backup for future reference since the current settings // might have been corrupted. if (!mBackupSettingsFilename.exists()) { if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) { Log.wtf(PackageManagerService.TAG, "Unable to backup package manager settings, " + " current changes will be lost at reboot"); return; } } else { mSettingsFilename.delete(); Slog.w(PackageManagerService.TAG, "Preserving older settings backup"); } } mPastSignatures.clear(); try { FileOutputStream fstr = new FileOutputStream(mSettingsFilename); BufferedOutputStream str = new BufferedOutputStream(fstr); //XmlSerializer serializer = XmlUtils.serializerInstance(); XmlSerializer serializer = new FastXmlSerializer(); serializer.setOutput(str, "utf-8"); serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, "packages"); serializer.startTag(null, "last-platform-version"); serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform)); serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform)); serializer.endTag(null, "last-platform-version"); if (mVerifierDeviceIdentity != null) { serializer.startTag(null, "verifier"); serializer.attribute(null, "device", mVerifierDeviceIdentity.toString()); serializer.endTag(null, "verifier"); } if (mReadExternalStorageEnforced != null) { serializer.startTag(null, TAG_READ_EXTERNAL_STORAGE); serializer.attribute( null, ATTR_ENFORCEMENT, mReadExternalStorageEnforced ? "1" : "0"); serializer.endTag(null, TAG_READ_EXTERNAL_STORAGE); } serializer.startTag(null, "permission-trees"); for (BasePermission bp : mPermissionTrees.values()) { writePermissionLPr(serializer, bp); } serializer.endTag(null, "permission-trees"); serializer.startTag(null, "permissions"); for (BasePermission bp : mPermissions.values()) { writePermissionLPr(serializer, bp); } serializer.endTag(null, "permissions"); for (final PackageSetting pkg : mPackages.values()) { writePackageLPr(serializer, pkg); } for (final PackageSetting pkg : mDisabledSysPackages.values()) { writeDisabledSysPackageLPr(serializer, pkg); } for (final SharedUserSetting usr : mSharedUsers.values()) { serializer.startTag(null, "shared-user"); serializer.attribute(null, ATTR_NAME, usr.name); serializer.attribute(null, "userId", Integer.toString(usr.userId)); usr.signatures.writeXml(serializer, "sigs", mPastSignatures); serializer.startTag(null, "perms"); for (String name : usr.grantedPermissions) { serializer.startTag(null, TAG_ITEM); serializer.attribute(null, ATTR_NAME, name); serializer.endTag(null, TAG_ITEM); } serializer.endTag(null, "perms"); serializer.endTag(null, "shared-user"); } if (mPackagesToBeCleaned.size() > 0) { for (PackageCleanItem item : mPackagesToBeCleaned) { final String userStr = Integer.toString(item.userId); serializer.startTag(null, "cleaning-package"); serializer.attribute(null, ATTR_NAME, item.packageName); serializer.attribute(null, ATTR_CODE, item.andCode ? "true" : "false"); serializer.attribute(null, ATTR_USER, userStr); serializer.endTag(null, "cleaning-package"); } } if (mRenamedPackages.size() > 0) { for (Map.Entrye : mRenamedPackages.entrySet()) { serializer.startTag(null, "renamed-package"); serializer.attribute(null, "new", e.getKey()); serializer.attribute(null, "old", e.getValue()); serializer.endTag(null, "renamed-package"); } } mKeySetManager.writeKeySetManagerLPr(serializer); serializer.endTag(null, "packages"); serializer.endDocument(); str.flush(); FileUtils.sync(fstr); str.close(); // New settings successfully written, old ones are no longer // needed. mBackupSettingsFilename.delete(); FileUtils.setPermissions(mSettingsFilename.toString(), FileUtils.S_IRUSR|FileUtils.S_IWUSR |FileUtils.S_IRGRP|FileUtils.S_IWGRP, -1, -1); // Write package list file now, use a JournaledFile. File tempFile = new File(mPackageListFilename.getAbsolutePath() + ".tmp"); JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile); final File writeTarget = journal.chooseForWrite(); fstr = new FileOutputStream(writeTarget); str = new BufferedOutputStream(fstr); try { FileUtils.setPermissions(fstr.getFD(), 0660, SYSTEM_UID, PACKAGE_INFO_GID); StringBuilder sb = new StringBuilder(); for (final PackageSetting pkg : mPackages.values()) { if (pkg.pkg == null || pkg.pkg.applicationInfo == null) { Slog.w(TAG, "Skipping " + pkg + " due to missing metadata"); continue; } final ApplicationInfo ai = pkg.pkg.applicationInfo; final String dataPath = ai.dataDir; final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; final int[] gids = pkg.getGids(); // Avoid any application that has a space in its path. if (dataPath.indexOf("") >= 0) continue; // we store on each line the following information for now: // // pkgName - package name // userId - application-specific user id // debugFlag - 0 or 1 if the package is debuggable. // dataPath - path to package's data path // seinfo - seinfo label for the app (assigned at install time) // gids - supplementary gids this app launches with // // NOTE: We prefer not to expose all ApplicationInfo flags for now. // // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES: // system/core/run-as/run-as.c // system/core/sdcard/sdcard.c // sb.setLength(0); sb.append(ai.packageName); sb.append(""); sb.append((int)ai.uid); sb.append(isDebug ? " 1 " : " 0 "); sb.append(dataPath); sb.append(""); sb.append(ai.seinfo); sb.append(""); if (gids != null && gids.length > 0) { sb.append(gids[0]); for (int i = 1; i < gids.length; i++) { sb.append(","); sb.append(gids[i]); } } else { sb.append("none"); } sb.append("\n"); str.write(sb.toString().getBytes()); } str.flush(); FileUtils.sync(fstr); str.close(); journal.commit(); } catch (Exception e) { Log.wtf(TAG, "Failed to write packages.list", e); IoUtils.closeQuietly(str); journal.rollback(); } writeAllUsersPackageRestrictionsLPr(); return; } catch(XmlPullParserException e) { Log.wtf(PackageManagerService.TAG, "Unable to write package manager settings, " + "current changes will be lost at reboot", e); } catch(java.io.IOException e) { Log.wtf(PackageManagerService.TAG, "Unable to write package manager settings, " + "current changes will be lost at reboot", e); } // Clean up partially written files if (mSettingsFilename.exists()) { if (!mSettingsFilename.delete()) { Log.wtf(PackageManagerService.TAG, "Failed to clean up mangled file: " + mSettingsFilename); } } //Debug.stopMethodTracing(); } ...