summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSvetoslav <svetoslavganov@google.com>2015-02-26 14:44:43 -0800
committerSvetoslav <svetoslavganov@google.com>2015-03-23 18:50:35 -0700
commitc6d1c345f41cf817bf2c07c97b97107d94296064 (patch)
tree4bb0bac510ae49524f25a71e3f9e73f5f78cf36f
parent3910eb551c7736015708c627dcabaa75c66d9ec3 (diff)
downloadframeworks_base-c6d1c345f41cf817bf2c07c97b97107d94296064.zip
frameworks_base-c6d1c345f41cf817bf2c07c97b97107d94296064.tar.gz
frameworks_base-c6d1c345f41cf817bf2c07c97b97107d94296064.tar.bz2
Runtime permissions: per user permission tracking.
Before all permissions were granted at install time at once, so the user was persented with an all or nothing choice. In the new runtime permissions model all dangarous permissions (nomal are always granted and signature one are granted if signatures match) are not granted at install time and the app can request them as necessary at runtime. Before, all granted permission to an app were identical for all users as granting is performed at install time. However, the new runtime model allows the same app running under two different users to have different runtime permission grants. This change refactors the permissions book keeping in the package manager to enable per user permission tracking. The change also adds the app facing APIs for requesting runtime permissions. Change-Id: Icbf2fc2ced15c42ca206c335996206bd1a4a4be5
-rw-r--r--api/current.txt8
-rw-r--r--api/removed.txt8
-rw-r--r--api/system-current.txt13
-rw-r--r--api/system-removed.txt8
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java23
-rw-r--r--core/java/android/app/Activity.java123
-rw-r--r--core/java/android/app/ActivityManager.java3
-rw-r--r--core/java/android/app/ApplicationPackageManager.java15
-rw-r--r--core/java/android/app/ContextImpl.java9
-rw-r--r--core/java/android/app/Fragment.java101
-rw-r--r--core/java/android/content/Context.java14
-rw-r--r--core/java/android/content/ContextWrapper.java5
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl44
-rw-r--r--core/java/android/content/pm/PackageInfo.java5
-rw-r--r--core/java/android/content/pm/PackageManager.java129
-rw-r--r--core/java/android/content/pm/PackageParser.java34
-rw-r--r--core/java/android/widget/AppSecurityPermissions.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java7
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/pm/BasePermission.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java500
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java10
-rw-r--r--services/core/java/com/android/server/pm/PermissionsState.java523
-rw-r--r--services/core/java/com/android/server/pm/SettingBase.java (renamed from services/core/java/com/android/server/pm/GrantedPermissions.java)21
-rw-r--r--services/core/java/com/android/server/pm/Settings.java696
-rw-r--r--services/core/java/com/android/server/pm/SharedUserSetting.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java36
-rw-r--r--test-runner/src/android/test/mock/MockContext.java5
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java4
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java6
31 files changed, 1881 insertions, 492 deletions
diff --git a/api/current.txt b/api/current.txt
index 0329f93..d38d80f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3431,6 +3431,7 @@ package android.app {
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
method public void onProvideAssistContent(android.app.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
method protected void onRestoreInstanceState(android.os.Bundle);
method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
@@ -3461,6 +3462,7 @@ package android.app {
method public boolean releaseInstance();
method public final deprecated void removeDialog(int);
method public void reportFullyDrawn();
+ method public final void requestPermissions(java.lang.String[], int);
method public boolean requestVisibleBehind(boolean);
method public final boolean requestWindowFeature(int);
method public final void runOnUiThread(java.lang.Runnable);
@@ -4315,6 +4317,7 @@ package android.app {
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPause();
method public void onPrepareOptionsMenu(android.view.Menu);
+ method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method public void onResume();
method public void onSaveInstanceState(android.os.Bundle);
method public void onStart();
@@ -4323,6 +4326,7 @@ package android.app {
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
method public void registerForContextMenu(android.view.View);
+ method public final void requestPermissions(java.lang.String[], int);
method public void setAllowEnterTransitionOverlap(boolean);
method public void setAllowReturnTransitionOverlap(boolean);
method public void setArguments(android.os.Bundle);
@@ -7409,6 +7413,7 @@ package android.content {
method public abstract int checkCallingPermission(java.lang.String);
method public abstract int checkCallingUriPermission(android.net.Uri, int);
method public abstract int checkPermission(java.lang.String, int, int);
+ method public abstract int checkSelfPermission(java.lang.String);
method public abstract int checkUriPermission(android.net.Uri, int, int, int);
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
@@ -7586,6 +7591,7 @@ package android.content {
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
+ method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public deprecated void clearWallpaper() throws java.io.IOException;
@@ -8838,7 +8844,6 @@ package android.content.pm {
field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
- field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
field public int baseRevisionCode;
@@ -30192,6 +30197,7 @@ package android.test.mock {
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
+ method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
diff --git a/api/removed.txt b/api/removed.txt
index 1b209a9..c2b9d3e 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,3 +1,11 @@
+package android.content.pm {
+
+ public class PackageInfo implements android.os.Parcelable {
+ field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
+ }
+
+}
+
package android.media {
public class AudioFormat {
diff --git a/api/system-current.txt b/api/system-current.txt
index aeff38b..0fc3a90 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3514,6 +3514,7 @@ package android.app {
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
method public void onProvideAssistContent(android.app.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
method protected void onRestoreInstanceState(android.os.Bundle);
method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
@@ -3544,6 +3545,7 @@ package android.app {
method public boolean releaseInstance();
method public final deprecated void removeDialog(int);
method public void reportFullyDrawn();
+ method public final void requestPermissions(java.lang.String[], int);
method public boolean requestVisibleBehind(boolean);
method public final boolean requestWindowFeature(int);
method public final void runOnUiThread(java.lang.Runnable);
@@ -4405,6 +4407,7 @@ package android.app {
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPause();
method public void onPrepareOptionsMenu(android.view.Menu);
+ method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method public void onResume();
method public void onSaveInstanceState(android.os.Bundle);
method public void onStart();
@@ -4413,6 +4416,7 @@ package android.app {
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
method public void registerForContextMenu(android.view.View);
+ method public final void requestPermissions(java.lang.String[], int);
method public void setAllowEnterTransitionOverlap(boolean);
method public void setAllowReturnTransitionOverlap(boolean);
method public void setArguments(android.os.Bundle);
@@ -7615,6 +7619,7 @@ package android.content {
method public abstract int checkCallingPermission(java.lang.String);
method public abstract int checkCallingUriPermission(android.net.Uri, int);
method public abstract int checkPermission(java.lang.String, int, int);
+ method public abstract int checkSelfPermission(java.lang.String);
method public abstract int checkUriPermission(android.net.Uri, int, int, int);
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
@@ -7798,6 +7803,7 @@ package android.content {
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
+ method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public deprecated void clearWallpaper() throws java.io.IOException;
@@ -9078,7 +9084,6 @@ package android.content.pm {
field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
- field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
field public int baseRevisionCode;
@@ -9278,6 +9283,7 @@ package android.content.pm {
method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public abstract void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean hasSystemFeature(java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9293,16 +9299,20 @@ package android.content.pm {
method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
+ method public abstract void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void verifyPendingInstall(int, int);
+ field public static final java.lang.String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
field public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4; // 0x4
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
+ field public static final java.lang.String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
+ field public static final java.lang.String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
field public static final java.lang.String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
field public static final java.lang.String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
@@ -32551,6 +32561,7 @@ package android.test.mock {
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
+ method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 1b209a9..c2b9d3e 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,3 +1,11 @@
+package android.content.pm {
+
+ public class PackageInfo implements android.os.Parcelable {
+ field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
+ }
+
+}
+
package android.media {
public class AudioFormat {
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c48a618..f38b9e7 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1517,6 +1517,15 @@ public final class Pm {
}
private int runGrantRevokePermission(boolean grant) {
+ int userId = UserHandle.USER_CURRENT;
+
+ String opt = null;
+ while ((opt = nextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = Integer.parseInt(nextArg());
+ }
+ }
+
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
@@ -1529,11 +1538,12 @@ public final class Pm {
showUsage();
return 1;
}
+
try {
if (grant) {
- mPm.grantPermission(pkg, perm);
+ mPm.grantPermission(pkg, perm, userId);
} else {
- mPm.revokePermission(pkg, perm);
+ mPm.revokePermission(pkg, perm, userId);
}
return 0;
} catch (RemoteException e) {
@@ -1815,8 +1825,8 @@ public final class Pm {
System.err.println(" pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm hide [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
- System.err.println(" pm grant PACKAGE PERMISSION");
- System.err.println(" pm revoke PACKAGE PERMISSION");
+ System.err.println(" pm grant [--user USER_ID] PACKAGE PERMISSION");
+ System.err.println(" pm revoke [--user USER_ID] PACKAGE PERMISSION");
System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]");
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
@@ -1889,8 +1899,9 @@ public final class Pm {
System.err.println(" as \"package/class\").");
System.err.println("");
System.err.println("pm grant, revoke: these commands either grant or revoke permissions");
- System.err.println(" to applications. Only optional permissions the application has");
- System.err.println(" declared can be granted or revoked.");
+ System.err.println(" to apps. The permissions must be declared as used in the app's");
+ System.err.println(" manifest, be runtime permissions (protection level dangerous),");
+ System.err.println(" and the app targeting SDK greater than Lollipop MR1.");
System.err.println("");
System.err.println("pm get-install-location: returns the current install location.");
System.err.println(" 0 [auto]: Let system decide the best location");
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7fcbe35..b5817df 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3726,6 +3726,95 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Requests permissions to be granted to this application. These permissions
+ * must be requested in your manifest, they should not be granted to your app,
+ * and they should have protection level {@link android.content.pm.PermissionInfo
+ * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+ * the platform or a third-party app.
+ * <p>
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+ * are granted at install time if requested in the manifest. Signature permissions
+ * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+ * install time if requested in the manifest and the signature of your app matches
+ * the signature of the app declaring the permissions.
+ * </p>
+ * <p>
+ * If your app does not have the requested permissions the user will be presented
+ * with UI for accepting them. After the user has accepted or rejected the
+ * requested permissions you will receive a callback on {@link
+ * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+ * permissions were granted or not.
+ * </p>
+ * <p>
+ * Note that requesting a permission does not guarantee it will be granted and
+ * your app should be able to run without having this permission.
+ * </p>
+ * <p>
+ * This method may start an activity allowing the user to choose which permissions
+ * to grant and which to reject. Hence, you should be prepared that your activity
+ * may be paused and resumed. Further, granting some permissions may require
+ * a restart of you application. In such a case, the system will recreate the
+ * activity stack before delivering the result to {@link
+ * #onRequestPermissionsResult(int, String[], int[])}.
+ * </p>
+ * <p>
+ * When checking whether you have a permission you should use {@link
+ * #checkSelfPermission(String)}.
+ * </p>
+ * <p>
+ * A sample permissions request looks like this:
+ * </p>
+ * <code><pre><p>
+ * private void showContacts() {
+ * if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
+ * != PackageManager.PERMISSION_GRANTED) {
+ * requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+ * PERMISSIONS_REQUEST_READ_CONTACTS);
+ * } else {
+ * doShowContacts();
+ * }
+ * }
+ *
+ * {@literal @}Override
+ * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ * int[] grantResults) {
+ * if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+ * && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ * showContacts();
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param permissions The requested permissions.
+ * @param requestCode Application specific request code to match with a result
+ * reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ * @see #checkSelfPermission(String)
+ */
+ public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+ Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+ startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(String[], int)}.
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ /* callback - no nothing */
+ }
+
+ /**
* Same as calling {@link #startActivityForResult(Intent, int, Bundle)}
* with no options.
*
@@ -6269,11 +6358,19 @@ public class Activity extends ContextThemeWrapper
+ ", resCode=" + resultCode + ", data=" + data);
mFragments.noteStateNotSaved();
if (who == null) {
- onActivityResult(requestCode, resultCode, data);
+ if (isRequestPermissionResult(data)) {
+ dispatchRequestPermissionsResult(requestCode, data);
+ } else {
+ onActivityResult(requestCode, resultCode, data);
+ }
} else {
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
- frag.onActivityResult(requestCode, resultCode, data);
+ if (isRequestPermissionResult(data)) {
+ dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
+ } else {
+ frag.onActivityResult(requestCode, resultCode, data);
+ }
}
}
}
@@ -6343,4 +6440,26 @@ public class Activity extends ContextThemeWrapper
*/
public void onTranslucentConversionComplete(boolean drawComplete);
}
+
+ private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
+ String[] permissions = data.getStringArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ final int[] grantResults = data.getIntArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS);
+ onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ private void dispatchRequestPermissionsResultToFragment(int requestCode, Intent data,
+ Fragment fragement) {
+ String[] permissions = data.getStringArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ final int[] grantResults = data.getIntArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS);
+ fragement.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ private static boolean isRequestPermissionResult(Intent intent) {
+ return intent != null
+ && PackageManager.ACTION_REQUEST_PERMISSIONS.equals(intent.getAction());
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 29b024ac..d143f8b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2493,7 +2493,8 @@ public class ActivityManager {
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
- if (uid == 0 || uid == Process.SYSTEM_UID) {
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9f81670..6d74905 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -81,7 +81,6 @@ import java.util.List;
/*package*/
final class ApplicationPackageManager extends PackageManager {
private static final String TAG = "ApplicationPackageManager";
- private final static boolean DEBUG = false;
private final static boolean DEBUG_ICONS = false;
// Default flags to use with PackageManager when no flags are given.
@@ -186,8 +185,8 @@ final class ApplicationPackageManager extends PackageManager {
public int[] getPackageGids(String packageName)
throws NameNotFoundException {
try {
- int[] gids = mPM.getPackageGids(packageName);
- if (gids == null || gids.length > 0) {
+ int[] gids = mPM.getPackageGids(packageName, mContext.getUserId());
+ if (gids != null) {
return gids;
}
} catch (RemoteException e) {
@@ -398,7 +397,7 @@ final class ApplicationPackageManager extends PackageManager {
@Override
public int checkPermission(String permName, String pkgName) {
try {
- return mPM.checkPermission(permName, pkgName);
+ return mPM.checkPermission(permName, pkgName, mContext.getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -432,18 +431,18 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
- public void grantPermission(String packageName, String permissionName) {
+ public void grantPermission(String packageName, String permissionName, UserHandle user) {
try {
- mPM.grantPermission(packageName, permissionName);
+ mPM.grantPermission(packageName, permissionName, user.getIdentifier());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
}
@Override
- public void revokePermission(String packageName, String permissionName) {
+ public void revokePermission(String packageName, String permissionName, UserHandle user) {
try {
- mPM.revokePermission(packageName, permissionName);
+ mPM.revokePermission(packageName, permissionName, user.getIdentifier());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index eb27830..4ccd69f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1323,6 +1323,15 @@ class ContextImpl extends Context {
Binder.getCallingUid());
}
+ @Override
+ public int checkSelfPermission(String permission) {
+ if (permission == null) {
+ throw new IllegalArgumentException("permission is null");
+ }
+
+ return checkPermission(permission, Process.myPid(), Process.myUid());
+ }
+
private void enforce(
String permission, int resultOfCheck,
boolean selfToo, int uid, String message) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index bdcc312..4fdae7f 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -17,6 +17,7 @@
package android.app;
import android.animation.Animator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.ComponentCallbacks2;
@@ -1092,13 +1093,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
if (mActivity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- if (options != null) {
- mActivity.startActivityFromFragment(this, intent, requestCode, options);
- } else {
- // Note we want to go through this call for compatibility with
- // applications that may have overridden the method.
- mActivity.startActivityFromFragment(this, intent, requestCode, options);
- }
+ mActivity.startActivityFromFragment(this, intent, requestCode, options);
}
/**
@@ -1119,6 +1114,98 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
}
/**
+ * Requests permissions to be granted to this application. These permissions
+ * must be requested in your manifest, they should not be granted to your app,
+ * and they should have protection level {@link android.content.pm.PermissionInfo
+ * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+ * the platform or a third-party app.
+ * <p>
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+ * are granted at install time if requested in the manifest. Signature permissions
+ * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+ * install time if requested in the manifest and the signature of your app matches
+ * the signature of the app declaring the permissions.
+ * </p>
+ * <p>
+ * If your app does not have the requested permissions the user will be presented
+ * with UI for accepting them. After the user has accepted or rejected the
+ * requested permissions you will receive a callback on {@link
+ * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+ * permissions were granted or not.
+ * </p>
+ * <p>
+ * Note that requesting a permission does not guarantee it will be granted and
+ * your app should be able to run without having this permission.
+ * </p>
+ * <p>
+ * This method may start an activity allowing the user to choose which permissions
+ * to grant and which to reject. Hence, you should be prepared that your activity
+ * may be paused and resumed. Further, granting some permissions may require
+ * a restart of you application. In such a case, the system will recreate the
+ * activity stack before delivering the result to {@link
+ * #onRequestPermissionsResult(int, String[], int[])}.
+ * </p>
+ * <p>
+ * When checking whether you have a permission you should use {@link
+ * android.content.Context#checkSelfPermission(String)}.
+ * </p>
+ * <p>
+ * A sample permissions request looks like this:
+ * </p>
+ * <code><pre><p>
+ * private void showContacts() {
+ * if (getActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS)
+ * != PackageManager.PERMISSION_GRANTED) {
+ * requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+ * PERMISSIONS_REQUEST_READ_CONTACTS);
+ * } else {
+ * doShowContacts();
+ * }
+ * }
+ *
+ * {@literal @}Override
+ * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ * int[] grantResults) {
+ * if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+ * && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ * doShowContacts();
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param permissions The requested permissions.
+ * @param requestCode Application specific request code to match with a result
+ * reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ * @see android.content.Context#checkSelfPermission(String)
+ */
+ public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+ if (mActivity == null) {
+ throw new IllegalStateException("Fragment " + this + " not attached to Activity");
+ }
+ Intent intent = mActivity.getPackageManager().buildRequestPermissionsIntent(permissions);
+ mActivity.startActivityFromFragment(this, intent, requestCode, null);
+ }
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(String[], int)}.
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ /* callback - do nothing */
+ }
+
+ /**
* @hide Hack so that DialogFragment can make its Dialog before creating
* its views, and the view construction can use the dialog's context for
* inflation. Maybe this should become a public API. Note sure.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 80b5e0b..39a70be 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3109,6 +3109,20 @@ public abstract class Context {
public abstract int checkCallingOrSelfPermission(@NonNull String permission);
/**
+ * Determine whether <em>you</em> have been granted a particular permission.
+ *
+ * @param permission The name of the permission being checked.
+ *
+ * @return {@link PackageManager#PERMISSION_GRANTED} if you have the
+ * permission, or {@link PackageManager#PERMISSION_DENIED} if not.
+ *
+ * @see PackageManager#checkPermission(String, String)
+ * @see #checkCallingPermission(String)
+ */
+ @PackageManager.PermissionResult
+ public abstract int checkSelfPermission(@NonNull String permission);
+
+ /**
* If the given permission is not allowed for a particular process
* and user ID running in the system, throw a {@link SecurityException}.
*
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 6e8b7c1..8c5a87c 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -602,6 +602,11 @@ public class ContextWrapper extends Context {
}
@Override
+ public int checkSelfPermission(String permission) {
+ return mBase.checkSelfPermission(permission);
+ }
+
+ @Override
public void enforcePermission(
String permission, int pid, int uid, String message) {
mBase.enforcePermission(permission, pid, uid, message);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 3e5d362..c6d97f1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -46,32 +46,34 @@ import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.IntentSender;
+import com.android.internal.os.IResultReceiver;
/**
* See {@link PackageManager} for documentation on most of the APIs
* here.
- *
+ *
* {@hide}
*/
interface IPackageManager {
boolean isPackageAvailable(String packageName, int userId);
PackageInfo getPackageInfo(String packageName, int flags, int userId);
int getPackageUid(String packageName, int userId);
- int[] getPackageGids(String packageName);
-
+ int[] getPackageGids(String packageName, int userId);
+
String[] currentToCanonicalPackageNames(in String[] names);
String[] canonicalToCurrentPackageNames(in String[] names);
PermissionInfo getPermissionInfo(String name, int flags);
-
+
List<PermissionInfo> queryPermissionsByGroup(String group, int flags);
-
+
PermissionGroupInfo getPermissionGroupInfo(String name, int flags);
-
+
List<PermissionGroupInfo> getAllPermissionGroups(int flags);
-
+
ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
@@ -85,28 +87,28 @@ interface IPackageManager {
ProviderInfo getProviderInfo(in ComponentName className, int flags, int userId);
- int checkPermission(String permName, String pkgName);
-
+ int checkPermission(String permName, String pkgName, int userId);
+
int checkUidPermission(String permName, int uid);
-
+
boolean addPermission(in PermissionInfo info);
-
+
void removePermission(String name);
- void grantPermission(String packageName, String permissionName);
+ boolean grantPermission(String packageName, String permissionName, int userId);
- void revokePermission(String packageName, String permissionName);
+ boolean revokePermission(String packageName, String permissionName, int userId);
boolean isProtectedBroadcast(String actionName);
-
+
int checkSignatures(String pkg1, String pkg2);
-
+
int checkUidSignatures(int uid1, int uid2);
-
+
String[] getPackagesForUid(int uid);
-
+
String getNameForUid(int uid);
-
+
int getUidForSharedUser(String sharedUserName);
int getFlagsForUid(int uid);
@@ -121,7 +123,7 @@ interface IPackageManager {
boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
- List<ResolveInfo> queryIntentActivities(in Intent intent,
+ List<ResolveInfo> queryIntentActivities(in Intent intent,
String resolvedType, int flags, int userId);
List<ResolveInfo> queryIntentActivityOptions(
@@ -168,7 +170,7 @@ interface IPackageManager {
/**
* Retrieve all applications that are marked as persistent.
- *
+ *
* @return A List&lt;applicationInfo> containing one entry for each persistent
* application.
*/
@@ -178,7 +180,7 @@ interface IPackageManager {
/**
* Retrieve sync information for all content providers.
- *
+ *
* @param outNames Filled in with a list of the root names of the content
* providers that can sync.
* @param outInfo Filled in with a list of the ProviderInfo for each
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9223269..9e6c6b5 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -167,8 +167,7 @@ public class PackageInfo implements Parcelable {
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_PERMISSIONS} was set. Each value matches
* the corresponding entry in {@link #requestedPermissions}, and will have
- * the flags {@link #REQUESTED_PERMISSION_REQUIRED} and
- * {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+ * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
*/
public int[] requestedPermissionsFlags;
@@ -176,6 +175,8 @@ public class PackageInfo implements Parcelable {
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
+ *
+ * @removed We do not support required permissions.
*/
public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3da57cb..314b0b4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -44,6 +44,7 @@ import android.os.Environment;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AndroidException;
+import com.android.internal.util.ArrayUtils;
import java.io.File;
import java.lang.annotation.Retention;
@@ -1663,21 +1664,46 @@ public abstract class PackageManager {
= "android.content.pm.extra.VERIFICATION_VERSION_CODE";
/**
- * The action used to request that the user approve a permission request
- * from the application.
+ * The action used to request that the user approve a grant permissions
+ * request from the application.
*
* @hide
*/
- public static final String ACTION_REQUEST_PERMISSION
- = "android.content.pm.action.REQUEST_PERMISSION";
+ @SystemApi
+ public static final String ACTION_REQUEST_PERMISSIONS =
+ "android.content.pm.action.REQUEST_PERMISSIONS";
/**
- * Extra field name for the list of permissions, which the user must approve.
+ * The component name handling runtime permission grants.
*
* @hide
*/
- public static final String EXTRA_REQUEST_PERMISSION_PERMISSION_LIST
- = "android.content.pm.extra.PERMISSION_LIST";
+ public static final String GRANT_PERMISSIONS_PACKAGE_NAME =
+ "com.android.packageinstaller";
+
+ /**
+ * The names of the requested permissions.
+ * <p>
+ * <strong>Type:</strong> String[]
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
+ "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
+
+ /**
+ * The results from the permissions request.
+ * <p>
+ * <strong>Type:</strong> int[] of #PermissionResult
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS
+ = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
/**
* String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of
@@ -2184,51 +2210,70 @@ public abstract class PackageManager {
public abstract void removePermission(String name);
/**
- * Returns an {@link Intent} suitable for passing to {@code startActivityForResult}
- * which prompts the user to grant {@code permissions} to this application.
- * @hide
+ * Grant a runtime permission to an application which the application does not
+ * already have. The permission must have been requested by the application.
+ * If the application is not allowed to hold the permission, a {@link
+ * java.lang.SecurityException} is thrown.
+ * <p>
+ * <strong>Note: </strong>Using this API requires holding
+ * android.permission.GRANT_REVOKE_PERMISSIONS and if the user id is
+ * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+ * </p>
+ *
+ * @param packageName The package to which to grant the permission.
+ * @param permissionName The permission name to grant.
+ * @param user The user for which to grant the permission.
*
- * @throws NullPointerException if {@code permissions} is {@code null}.
- * @throws IllegalArgumentException if {@code permissions} contains {@code null}.
+ * @see #revokePermission(String, String, android.os.UserHandle)
+ *
+ * @hide
*/
- public Intent buildPermissionRequestIntent(String... permissions) {
- if (permissions == null) {
- throw new NullPointerException("permissions cannot be null");
- }
- for (String permission : permissions) {
- if (permission == null) {
- throw new IllegalArgumentException("permissions cannot contain null");
- }
- }
-
- Intent i = new Intent(ACTION_REQUEST_PERMISSION);
- i.putExtra(EXTRA_REQUEST_PERMISSION_PERMISSION_LIST, permissions);
- i.setPackage("com.android.packageinstaller");
- return i;
- }
+ @SystemApi
+ public abstract void grantPermission(@NonNull String packageName,
+ @NonNull String permissionName, @NonNull UserHandle user);
/**
- * Grant a permission to an application which the application does not
- * already have. The permission must have been requested by the application,
- * but as an optional permission. If the application is not allowed to
- * hold the permission, a SecurityException is thrown.
- * @hide
+ * Revoke a runtime permission that was previously granted by {@link
+ * #grantPermission(String, String, android.os.UserHandle)}. The permission
+ * must have been requested by and granted to the application. If the
+ * application is not allowed to hold the permission, a {@link
+ * java.lang.SecurityException} is thrown.
+ * <p>
+ * <strong>Note: </strong>Using this API requires holding
+ * android.permission.GRANT_REVOKE_PERMISSIONS and if the user id is
+ * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+ * </p>
+ *
+ * @param packageName The package from which to revoke the permission.
+ * @param permissionName The permission name to revoke.
+ * @param user The user for which to revoke the permission.
+ *
+ * @see #grantPermission(String, String, android.os.UserHandle)
*
- * @param packageName The name of the package that the permission will be
- * granted to.
- * @param permissionName The name of the permission.
+ * @hide
*/
- public abstract void grantPermission(String packageName, String permissionName);
+ @SystemApi
+ public abstract void revokePermission(@NonNull String packageName,
+ @NonNull String permissionName, @NonNull UserHandle user);
/**
- * Revoke a permission that was previously granted by {@link #grantPermission}.
- * @hide
+ * Returns an {@link android.content.Intent} suitable for passing to
+ * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
+ * which prompts the user to grant permissions to this application.
+ *
+ * @throws NullPointerException if {@code permissions} is {@code null} or empty.
*
- * @param packageName The name of the package that the permission will be
- * granted to.
- * @param permissionName The name of the permission.
+ * @hide
*/
- public abstract void revokePermission(String packageName, String permissionName);
+ public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
+ if (ArrayUtils.isEmpty(permissions)) {
+ throw new NullPointerException("permission cannot be null or empty");
+ }
+ Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
+ intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
+ intent.setPackage(GRANT_PERMISSIONS_PACKAGE_NAME);
+ return intent;
+ }
/**
* Compare the signatures of two packages to determine if the same
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index eaad9ac..1ba74d5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -410,7 +410,7 @@ public class PackageParser {
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- ArraySet<String> grantedPermissions, PackageUserState state, int userId) {
+ Set<String> grantedPermissions, PackageUserState state, int userId) {
if (!checkUseInstalledOrHidden(flags, state)) {
return null;
@@ -569,9 +569,8 @@ public class PackageParser {
for (int i=0; i<N; i++) {
final String perm = p.requestedPermissions.get(i);
pi.requestedPermissions[i] = perm;
- if (p.requestedPermissionsRequired.get(i)) {
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
- }
+ // The notion of requried permissions is deprecated but for compatibility.
+ pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
@@ -1812,7 +1811,6 @@ public class PackageParser {
}
implicitPerms.append(npi.name);
pkg.requestedPermissions.add(npi.name);
- pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
}
if (implicitPerms != null) {
@@ -1831,7 +1829,6 @@ public class PackageParser {
final String perm = spi.newPerms[in];
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
- pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
}
}
@@ -1865,17 +1862,6 @@ public class PackageParser {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
- /*
- * b/8528162: Ignore the <uses-permission android:required> attribute if
- * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild
- * which are improperly using this attribute, even though it never worked.
- */
- if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) {
- for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) {
- pkg.requestedPermissionsRequired.set(i, Boolean.TRUE);
- }
- }
-
return pkg;
}
@@ -1911,11 +1897,6 @@ public class PackageParser {
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
-/*
- boolean required = sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
-*/
- boolean required = true; // Optional <uses-permission> not supported
int maxSdkVersion = 0;
TypedValue val = sa.peekValue(
@@ -1933,13 +1914,9 @@ public class PackageParser {
int index = pkg.requestedPermissions.indexOf(name);
if (index == -1) {
pkg.requestedPermissions.add(name.intern());
- pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
} else {
- if (pkg.requestedPermissionsRequired.get(index) != required) {
- outError[0] = "conflicting <uses-permission> entries";
- mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return false;
- }
+ Slog.w(TAG, "Ignoring duplicate uses-permission: " + name + " in package: "
+ + pkg.packageName + " at: " + parser.getPositionDescription());
}
}
}
@@ -4217,7 +4194,6 @@ public class PackageParser {
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
- public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();
public ArrayList<String> protectedBroadcasts;
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 5c05b5a..6feb94b 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -16,6 +16,7 @@
*/
package android.widget;
+import android.os.UserHandle;
import com.android.internal.R;
import android.app.AlertDialog;
@@ -243,7 +244,8 @@ public class AppSecurityPermissions {
@Override
public void onClick(DialogInterface dialog, int which) {
PackageManager pm = getContext().getPackageManager();
- pm.revokePermission(mPackageName, mPerm.name);
+ pm.revokePermission(mPackageName, mPerm.name,
+ new UserHandle(mContext.getUserId()));
PermissionItemView.this.setVisibility(View.GONE);
}
};
@@ -298,7 +300,7 @@ public class AppSecurityPermissions {
}
extractPerms(info, permSet, installedPkgInfo);
}
- // Get permissions related to shared user if any
+ // Get permissions related to shared user if any
if (info.sharedUserId != null) {
int sharedUid;
try {
@@ -358,7 +360,7 @@ public class AppSecurityPermissions {
String permName = strList[i];
// If we are only looking at an existing app, then we only
// care about permissions that have actually been granted to it.
- if (installedPkgInfo != null && info == installedPkgInfo) {
+ if (installedPkgInfo != null && info != installedPkgInfo) {
if ((flagsList[i]&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
continue;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11c3ea6..fa1a1f1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -76,6 +76,7 @@ import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.MemInfoReader;
@@ -3066,7 +3067,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* Add shared application and profile GIDs so applications can share some
* resources like shared libraries and access user-wide resources
*/
- if (permGids == null) {
+ if (ArrayUtils.isEmpty(permGids)) {
gids = new int[2];
} else {
gids = new int[permGids.length + 2];
@@ -6530,7 +6531,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+ return checkComponentPermission(permission, pid, uid, -1, true);
}
@Override
@@ -6550,7 +6551,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pid = tlsIdentity.pid;
}
- return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+ return checkComponentPermission(permission, pid, uid, -1, true);
}
/**
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8fe1238..34c1c53 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -765,7 +765,7 @@ public final class BroadcastQueue {
try {
perm = AppGlobals.getPackageManager().
checkPermission(r.requiredPermission,
- info.activityInfo.applicationInfo.packageName);
+ info.activityInfo.applicationInfo.packageName, r.userId);
} catch (RemoteException e) {
perm = PackageManager.PERMISSION_DENIED;
}
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 4f27408..138a146 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -56,4 +56,9 @@ final class BasePermission {
return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+ "}";
}
+
+ public boolean isRuntime() {
+ return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 52411bf..5d20528 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -54,7 +54,6 @@ import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.ArrayUtils.appendInt;
-import static com.android.internal.util.ArrayUtils.removeInt;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -249,6 +248,9 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
+ private static final boolean RUNTIME_PERMISSIONS_ENABLED =
+ SystemProperties.getInt("ro.runtime.premissions.enabled", 0) == 1;
+
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
@@ -321,10 +323,28 @@ public class PackageManagerService extends IPackageManager.Stub {
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
+ private static final String KILL_APP_REASON_GIDS_CHANGED =
+ "permission grant or revoke changed gids";
+
+ private static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
+ "permissions revoked";
+
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
+ /** Permission grant: not grant the permission. */
+ private static final int GRANT_DENIED = 1;
+
+ /** Permission grant: grant the permission as an install permission. */
+ private static final int GRANT_INSTALL = 2;
+
+ /** Permission grant: grant the permission as a runtime permission. */
+ private static final int GRANT_RUNTIME = 3;
+
+ /** Permission grant: grant as runtime a permission that was granted as an install time one. */
+ private static final int GRANT_UPGRADE = 4;
+
final ServiceThread mHandlerThread;
final PackageHandler mHandler;
@@ -1243,7 +1263,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- public static final PackageManagerService main(Context context, Installer installer,
+ public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
@@ -1293,7 +1313,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mOnlyCore = onlyCore;
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
- mSettings = new Settings(context);
+ mSettings = new Settings(mContext, mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -1832,14 +1852,8 @@ public class PackageManagerService extends IPackageManager.Stub {
final String packageName = info.activityInfo.packageName;
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- continue;
- }
-
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- if (!gp.grantedPermissions
- .contains(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)) {
+ if (checkPermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
continue;
}
@@ -1895,26 +1909,21 @@ public class PackageManagerService extends IPackageManager.Stub {
return cur;
}
- static int[] removeInts(int[] cur, int[] rem) {
- if (rem == null) return cur;
- if (cur == null) return cur;
- final int N = rem.length;
- for (int i=0; i<N; i++) {
- cur = removeInt(cur, rem[i]);
- }
- return cur;
- }
-
PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final PackageSetting ps = (PackageSetting) p.mExtras;
if (ps == null) {
return null;
}
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+
+ PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int[] gids = permissionsState.computeGids(userId);
+ Set<String> permissions = permissionsState.getPermissions(userId);
+
final PackageUserState state = ps.readUserState(userId);
- return PackageParser.generatePackageInfo(p, gp.gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
+ return PackageParser.generatePackageInfo(p, gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, permissions,
state, userId);
}
@@ -1986,6 +1995,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public int getPackageUid(String packageName, int userId) {
if (!sUserManager.exists(userId)) return -1;
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid");
+
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -2002,22 +2012,30 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
- public int[] getPackageGids(String packageName) {
+ public int[] getPackageGids(String packageName, int userId) throws RemoteException {
+ if (!sUserManager.exists(userId)) {
+ return null;
+ }
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
+ "getPackageGids");
+
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
- if (DEBUG_PACKAGE_INFO)
+ if (DEBUG_PACKAGE_INFO) {
Log.v(TAG, "getPackageGids" + packageName + ": " + p);
+ }
if (p != null) {
- final PackageSetting ps = (PackageSetting)p.mExtras;
- return ps.getGids();
+ PackageSetting ps = (PackageSetting) p.mExtras;
+ return ps.getPermissionsState().computeGids(userId);
}
}
- // stupid thing to indicate an error.
- return new int[0];
+
+ return null;
}
- static final PermissionInfo generatePermissionInfo(
+ static PermissionInfo generatePermissionInfo(
BasePermission bp, int flags) {
if (bp.perm != null) {
return PackageParser.generatePermissionInfo(bp.perm, flags);
@@ -2381,30 +2399,37 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
- public int checkPermission(String permName, String pkgName) {
+ public int checkPermission(String permName, String pkgName, int userId) {
+ if (!sUserManager.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
synchronized (mPackages) {
- PackageParser.Package p = mPackages.get(pkgName);
+ final PackageParser.Package p = mPackages.get(pkgName);
if (p != null && p.mExtras != null) {
- PackageSetting ps = (PackageSetting)p.mExtras;
- if (ps.sharedUser != null) {
- if (ps.sharedUser.grantedPermissions.contains(permName)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else if (ps.grantedPermissions.contains(permName)) {
+ final PackageSetting ps = (PackageSetting) p.mExtras;
+ if (ps.getPermissionsState().hasPermission(permName, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
+
return PackageManager.PERMISSION_DENIED;
}
@Override
public int checkUidPermission(String permName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+
+ if (!sUserManager.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj != null) {
- GrantedPermissions gp = (GrantedPermissions)obj;
- if (gp.grantedPermissions.contains(permName)) {
+ final SettingBase ps = (SettingBase) obj;
+ if (ps.getPermissionsState().hasPermission(permName, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
@@ -2414,6 +2439,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
return PackageManager.PERMISSION_DENIED;
}
@@ -2620,120 +2646,114 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private static void checkGrantRevokePermissions(PackageParser.Package pkg, BasePermission bp) {
+ private static void enforceDeclaredAsUsedAndRuntimePermission(PackageParser.Package pkg,
+ BasePermission bp) {
int index = pkg.requestedPermissions.indexOf(bp.name);
if (index == -1) {
throw new SecurityException("Package " + pkg.packageName
+ " has not requested permission " + bp.name);
}
- boolean isNormal =
- ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_NORMAL);
- boolean isDangerous =
- ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_DANGEROUS);
- boolean isDevelopment =
- ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
-
- if (!isNormal && !isDangerous && !isDevelopment) {
+ if (!bp.isRuntime()) {
throw new SecurityException("Permission " + bp.name
+ " is not a changeable permission type");
}
-
- if (isNormal || isDangerous) {
- if (pkg.requestedPermissionsRequired.get(index)) {
- throw new SecurityException("Can't change " + bp.name
- + ". It is required by the application");
- }
- }
}
@Override
- public void grantPermission(String packageName, String permissionName) {
+ public boolean grantPermission(String packageName, String name, int userId) {
+ if (!sUserManager.exists(userId)) {
+ return false;
+ }
+
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
+ android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+ "grantPermission");
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "grantPermission");
+
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);
+
+ final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + permissionName);
+ throw new IllegalArgumentException("Unknown permission: " + name);
}
- checkGrantRevokePermissions(pkg, bp);
+ enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- return;
+ final SettingBase sb = (SettingBase) pkg.mExtras;
+ if (sb == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
- if (gp.grantedPermissions.add(permissionName)) {
- if (ps.haveGids) {
- gp.gids = appendInts(gp.gids, bp.gids);
+
+ final PermissionsState permissionsState = sb.getPermissionsState();
+
+ final int result = permissionsState.grantRuntimePermission(bp, userId);
+ switch (result) {
+ case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+ return false;
}
- mSettings.writeLPr();
+
+ case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+ killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED);
+ } break;
}
+
+ // Not critical if that is lost - app has to request again.
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+
+ return true;
}
}
@Override
- public void revokePermission(String packageName, String permissionName) {
- int changedAppId = -1;
+ public boolean revokePermission(String packageName, String name, int userId) {
+ if (!sUserManager.exists(userId)) {
+ return false;
+ }
+
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+ "revokePermission");
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "revokePermission");
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- if (pkg.applicationInfo.uid != Binder.getCallingUid()) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
- }
- final BasePermission bp = mSettings.mPermissions.get(permissionName);
+
+ final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + permissionName);
+ throw new IllegalArgumentException("Unknown permission: " + name);
}
- checkGrantRevokePermissions(pkg, bp);
+ enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- return;
- }
- final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
- if (gp.grantedPermissions.remove(permissionName)) {
- gp.grantedPermissions.remove(permissionName);
- if (ps.haveGids) {
- gp.gids = removeInts(gp.gids, bp.gids);
- }
- mSettings.writeLPr();
- changedAppId = ps.appId;
+ final SettingBase sb = (SettingBase) pkg.mExtras;
+ if (sb == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
- }
- if (changedAppId >= 0) {
- // We changed the perm on someone, kill its processes.
- IActivityManager am = ActivityManagerNative.getDefault();
- if (am != null) {
- final int callingUserId = UserHandle.getCallingUserId();
- final long ident = Binder.clearCallingIdentity();
- try {
- //XXX we should only revoke for the calling user's app permissions,
- // but for now we impact all users.
- //am.killUid(UserHandle.getUid(callingUserId, changedAppId),
- // "revoke " + permissionName);
- int[] users = sUserManager.getUserIds();
- for (int user : users) {
- am.killUid(UserHandle.getUid(user, changedAppId),
- "revoke " + permissionName);
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ final PermissionsState permissionsState = sb.getPermissionsState();
+
+ if (permissionsState.revokeRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ return false;
}
+
+ killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+
+ // Critical, after this call all should never have the permission.
+ mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+
+ return true;
}
}
@@ -2794,6 +2814,46 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ private void killSettingPackagesForUser(SettingBase sb, int userId, String reason) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (sb instanceof SharedUserSetting) {
+ SharedUserSetting sus = (SharedUserSetting) sb;
+ final int packageCount = sus.packages.size();
+ for (int i = 0; i < packageCount; i++) {
+ PackageSetting susPs = sus.packages.valueAt(i);
+ if (userId == UserHandle.USER_ALL) {
+ killApplication(susPs.pkg.packageName, susPs.appId, reason);
+ } else {
+ final int uid = UserHandle.getUid(userId, susPs.appId);
+ killUid(uid, reason);
+ }
+ }
+ } else if (sb instanceof PackageSetting) {
+ PackageSetting ps = (PackageSetting) sb;
+ if (userId == UserHandle.USER_ALL) {
+ killApplication(ps.pkg.packageName, ps.appId, reason);
+ } else {
+ final int uid = UserHandle.getUid(userId, ps.appId);
+ killUid(uid, reason);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private static void killUid(int uid, String reason) {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ if (am != null) {
+ try {
+ am.killUid(uid, reason);
+ } catch (RemoteException e) {
+ /* ignore - same process */
+ }
+ }
+ }
+
/**
* Compares two sets of signatures. Returns:
* <br />
@@ -3875,9 +3935,10 @@ public class PackageManagerService extends IPackageManager.Stub {
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
String[] permissions, boolean[] tmp, int flags, int userId) {
int numMatch = 0;
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+ final PermissionsState permissionsState = ps.getPermissionsState();
for (int i=0; i<permissions.length; i++) {
- if (gp.grantedPermissions.contains(permissions[i])) {
+ final String permission = permissions[i];
+ if (permissionsState.hasPermission(permission, userId)) {
tmp[i] = true;
numMatch++;
} else {
@@ -6853,36 +6914,42 @@ public class PackageManagerService extends IPackageManager.Stub {
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
String packageOfInterest) {
+ // IMPORTANT: There are two types of permissions: install and runtime.
+ // Install time permissions are granted when the app is installed to
+ // all device users and users added in the future. Runtime permissions
+ // are granted at runtime explicitly to specific users. Normal and signature
+ // protected permissions are install time permissions. Dangerous permissions
+ // are install permissions if the app's target SDK is Lollipop MR1 or older,
+ // otherwise they are runtime permissions. This function does not manage
+ // runtime permissions except for the case an app targeting Lollipop MR1
+ // being upgraded to target a newer SDK, in which case dangerous permissions
+ // are transformed from install time to runtime ones.
+
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
return;
}
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- ArraySet<String> origPermissions = gp.grantedPermissions;
+
+ PermissionsState permissionsState = ps.getPermissionsState();
+ PermissionsState origPermissions = permissionsState;
+
boolean changedPermission = false;
if (replace) {
ps.permissionsFixed = false;
- if (gp == ps) {
- origPermissions = new ArraySet<String>(gp.grantedPermissions);
- gp.grantedPermissions.clear();
- gp.gids = mGlobalGids;
- }
+ origPermissions = new PermissionsState(permissionsState);
+ permissionsState.reset();
}
- if (gp.gids == null) {
- gp.gids = mGlobalGids;
- }
+ permissionsState.setGlobalGids(mGlobalGids);
final int N = pkg.requestedPermissions.size();
for (int i=0; i<N; i++) {
final String name = pkg.requestedPermissions.get(i);
- final boolean required = pkg.requestedPermissionsRequired.get(i);
final BasePermission bp = mSettings.mPermissions.get(name);
+
if (DEBUG_INSTALL) {
- if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
- }
+ Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
}
if (bp == null || bp.packageSetting == null) {
@@ -6894,10 +6961,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final String perm = bp.name;
- boolean allowed;
boolean allowedSig = false;
- if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- // Keep track of app op permissions.
+ int grant = GRANT_DENIED;
+
+ // Keep track of app op permissions.
+ if ((bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
if (pkgs == null) {
pkgs = new ArraySet<>();
@@ -6905,65 +6973,108 @@ public class PackageManagerService extends IPackageManager.Stub {
}
pkgs.add(pkg.packageName);
}
+
final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- if (level == PermissionInfo.PROTECTION_NORMAL
- || level == PermissionInfo.PROTECTION_DANGEROUS) {
- // We grant a normal or dangerous permission if any of the following
- // are true:
- // 1) The permission is required
- // 2) The permission is optional, but was granted in the past
- // 3) The permission is optional, but was requested by an
- // app in /system (not /data)
- //
- // Otherwise, reject the permission.
- allowed = (required || origPermissions.contains(perm)
- || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
- } else if (bp.packageSetting == null) {
- // This permission is invalid; skip it.
- allowed = false;
- } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
- allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);
- if (allowed) {
- allowedSig = true;
- }
- } else {
- allowed = false;
+ switch (level) {
+ case PermissionInfo.PROTECTION_NORMAL: {
+ // For all apps normal permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } break;
+
+ case PermissionInfo.PROTECTION_DANGEROUS: {
+ if (!RUNTIME_PERMISSIONS_ENABLED
+ || pkg.applicationInfo.targetSdkVersion
+ <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ // For legacy apps dangerous permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // For modern system apps dangerous permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else {
+ if (origPermissions.hasInstallPermission(bp.name)) {
+ // For legacy apps that became modern, install becomes runtime.
+ grant = GRANT_UPGRADE;
+ } else if (replace) {
+ // For upgraded modern apps keep runtime permissions unchanged.
+ grant = GRANT_RUNTIME;
+ }
+ }
+ } break;
+
+ case PermissionInfo.PROTECTION_SIGNATURE: {
+ // For all apps signature permissions are install time ones.
+ allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
+ if (allowedSig) {
+ grant = GRANT_INSTALL;
+ }
+ } break;
}
+
if (DEBUG_INSTALL) {
- if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
- }
+ Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
}
- if (allowed) {
+
+ if (grant != GRANT_DENIED) {
if (!isSystemApp(ps) && ps.permissionsFixed) {
// If this is an existing, non-system package, then
// we can't add any new permissions to it.
- if (!allowedSig && !gp.grantedPermissions.contains(perm)) {
+ if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
- allowed = isNewPlatformPermissionForPackage(perm, pkg);
+ if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ grant = GRANT_DENIED;
+ }
}
}
- if (allowed) {
- if (!gp.grantedPermissions.contains(perm)) {
- changedPermission = true;
- gp.grantedPermissions.add(perm);
- gp.gids = appendInts(gp.gids, bp.gids);
- } else if (!ps.haveGids) {
- gp.gids = appendInts(gp.gids, bp.gids);
- }
- } else {
- if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " because it was previously installed without");
- }
+
+ switch (grant) {
+ case GRANT_INSTALL: {
+ // Grant an install permission.
+ if (permissionsState.grantInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ changedPermission = true;
+ }
+ } break;
+
+ case GRANT_RUNTIME: {
+ // Grant previously granted runtime permissions.
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ // Make sure runtime permissions are loaded.
+ if (origPermissions.hasRuntimePermission(bp.name, userId)) {
+ if (permissionsState.grantRuntimePermission(bp, userId) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ changedPermission = true;
+ }
+ }
+ }
+ } break;
+
+ case GRANT_UPGRADE: {
+ // Grant runtime permissions for a previously held install permission.
+ permissionsState.revokeInstallPermission(bp);
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ // Make sure runtime permissions are loaded.
+ if (permissionsState.grantRuntimePermission(bp, userId) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ changedPermission = true;
+ }
+ }
+ } break;
+
+ default: {
+ if (packageOfInterest == null
+ || packageOfInterest.equals(pkg.packageName)) {
+ Slog.w(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " because it was previously installed without");
+ }
+ } break;
}
} else {
- if (gp.grantedPermissions.remove(perm)) {
+ if (permissionsState.revokeInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
changedPermission = true;
- gp.gids = removeInts(gp.gids, bp.gids);
Slog.i(TAG, "Un-granting permission " + perm
+ " from package " + pkg.packageName
+ " (protectionLevel=" + bp.protectionLevel
@@ -6990,7 +7101,6 @@ public class PackageManagerService extends IPackageManager.Stub {
// changed.
ps.permissionsFixed = true;
}
- ps.haveGids = true;
}
private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
@@ -7011,7 +7121,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
- BasePermission bp, ArraySet<String> origPermissions) {
+ BasePermission bp, PermissionsState origPermissions) {
boolean allowed;
allowed = (compareSignatures(
bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
@@ -7026,10 +7136,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isUpdatedSystemApp(pkg)) {
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
- final GrantedPermissions origGp = sysPs.sharedUser != null
- ? sysPs.sharedUser : sysPs;
-
- if (origGp.grantedPermissions.contains(perm)) {
+ if (sysPs.getPermissionsState().hasInstallPermission(perm)) {
// If the original was granted this permission, we take
// that grant decision as read and propagate it to the
// update.
@@ -7063,7 +7170,7 @@ public class PackageManagerService extends IPackageManager.Stub {
& PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
// For development permissions, a development permission
// is granted only if it was already granted.
- allowed = origPermissions.contains(perm);
+ allowed = origPermissions.hasInstallPermission(perm);
}
return allowed;
}
@@ -10821,11 +10928,26 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
outInfo.removedAppId = mSettings.removePackageLPw(packageName);
}
- if (deletedPs != null) {
- updatePermissionsLPw(deletedPs.name, null, 0);
- if (deletedPs.sharedUser != null) {
- // remove permissions associated with package
- mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
+ updatePermissionsLPw(deletedPs.name, null, 0);
+ if (deletedPs.sharedUser != null) {
+ // Remove permissions associated with package. Since runtime
+ // permissions are per user we have to kill the removed package
+ // or packages running under the shared user of the removed
+ // package if revoking the permissions requested only by the removed
+ // package is successful and this causes a change in gids.
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
+ userId);
+ if (userIdToKill == userId) {
+ // If gids changed for this user, kill all affected packages.
+ killSettingPackagesForUser(deletedPs, userIdToKill,
+ KILL_APP_REASON_GIDS_CHANGED);
+ } else if (userIdToKill == UserHandle.USER_ALL) {
+ // If gids changed for all users, kill them all - done.
+ killSettingPackagesForUser(deletedPs, userIdToKill,
+ KILL_APP_REASON_GIDS_CHANGED);
+ break;
+ }
}
}
clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 06d842a..889164c 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -57,8 +57,10 @@ final class PackageSetting extends PackageSettingBase {
+ " " + name + "/" + appId + "}";
}
- public int[] getGids() {
- return sharedUser != null ? sharedUser.gids : gids;
+ public PermissionsState getPermissionsState() {
+ return (sharedUser != null)
+ ? sharedUser.getPermissionsState()
+ : super.getPermissionsState();
}
public boolean isPrivileged() {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 4b8ca42..9e8b3df 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -29,7 +29,7 @@ import java.io.File;
/**
* Settings base class for pending and resolved classes.
*/
-class PackageSettingBase extends GrantedPermissions {
+abstract class PackageSettingBase extends SettingBase {
/**
* Indicates the state of installation. Used by PackageManager to figure out
* incomplete installations. Say a package is being installed (the state is
@@ -93,7 +93,6 @@ class PackageSettingBase extends GrantedPermissions {
PackageSignatures signatures = new PackageSignatures();
boolean permissionsFixed;
- boolean haveGids;
PackageKeySetData keySetData = new PackageKeySetData();
@@ -147,7 +146,6 @@ class PackageSettingBase extends GrantedPermissions {
signatures = new PackageSignatures(base.signatures);
permissionsFixed = base.permissionsFixed;
- haveGids = base.haveGids;
userState.clear();
for (int i=0; i<base.userState.size(); i++) {
userState.put(base.userState.keyAt(i),
@@ -160,7 +158,6 @@ class PackageSettingBase extends GrantedPermissions {
installerPackageName = base.installerPackageName;
keySetData = new PackageKeySetData(base.keySetData);
-
}
void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
@@ -201,9 +198,7 @@ class PackageSettingBase extends GrantedPermissions {
* Make a shallow copy of this package settings.
*/
public void copyFrom(PackageSettingBase base) {
- grantedPermissions = base.grantedPermissions;
- gids = base.gids;
-
+ getPermissionsState().copyFrom(base.getPermissionsState());
primaryCpuAbiString = base.primaryCpuAbiString;
secondaryCpuAbiString = base.secondaryCpuAbiString;
cpuAbiOverrideString = base.cpuAbiOverrideString;
@@ -212,7 +207,6 @@ class PackageSettingBase extends GrantedPermissions {
lastUpdateTime = base.lastUpdateTime;
signatures = base.signatures;
permissionsFixed = base.permissionsFixed;
- haveGids = base.haveGids;
userState.clear();
for (int i=0; i<base.userState.size(); i++) {
userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
new file mode 100644
index 0000000..f6417ce
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * This class encapsulates the permissions for a package or a shared user.
+ * <p>
+ * There are two types of permissions: install (granted at installation)
+ * and runtime (granted at runtime). Install permissions are granted to
+ * all device users while runtime permissions are granted explicitly to
+ * specific users.
+ * </p>
+ * <p>
+ * The permissions are kept on a per device user basis. For example, an
+ * application may have some runtime permissions granted under the device
+ * owner but not granted under the secondary user.
+ * <p>
+ * This class is also responsible for keeping track of the Linux gids per
+ * user for a package or a shared user. The gids are computed as a set of
+ * the gids for all granted permissions' gids on a per user basis.
+ * </p>
+ */
+public final class PermissionsState {
+
+ /** The permission operation succeeded and no gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS = 1;
+
+ /** The permission operation succeeded and gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 2;
+
+ /** The permission operation failed. */
+ public static final int PERMISSION_OPERATION_FAILURE = 3;
+
+ private static final int[] USERS_ALL = {UserHandle.USER_ALL};
+
+ private static final int[] USERS_NONE = {};
+
+ private static final int[] NO_GIDS = {};
+
+ private ArrayMap<String, PermissionData> mPermissions;
+
+ private int[] mGlobalGids = NO_GIDS;
+
+ public PermissionsState() {
+ /* do nothing */
+ }
+
+ public PermissionsState(PermissionsState prototype) {
+ copyFrom(prototype);
+ }
+
+ /**
+ * Sets the global gids, applicable to all users.
+ *
+ * @param globalGids The global gids.
+ */
+ public void setGlobalGids(int[] globalGids) {
+ if (!ArrayUtils.isEmpty(globalGids)) {
+ mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+ }
+ }
+
+ /**
+ * Initialized this instance from another one.
+ *
+ * @param other The other instance.
+ */
+ public void copyFrom(PermissionsState other) {
+ if (mPermissions != null) {
+ if (other.mPermissions == null) {
+ mPermissions = null;
+ } else {
+ mPermissions.clear();
+ }
+ }
+ if (other.mPermissions != null) {
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+ final int permissionCount = other.mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String name = other.mPermissions.keyAt(i);
+ PermissionData permissionData = other.mPermissions.valueAt(i);
+ mPermissions.put(name, new PermissionData(permissionData));
+ }
+ }
+
+ mGlobalGids = NO_GIDS;
+ if (other.mGlobalGids != NO_GIDS) {
+ mGlobalGids = Arrays.copyOf(other.mGlobalGids,
+ other.mGlobalGids.length);
+ }
+ }
+
+ /**
+ * Grant an install permission.
+ *
+ * @param permission The permission to grant.
+ * @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 grantInstallPermission(BasePermission permission) {
+ return grantPermission(permission, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Revoke an install permission.
+ *
+ * @param permission The permission to revoke.
+ * @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 revokeInstallPermission(BasePermission permission) {
+ return revokePermission(permission, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Grant a runtime permission.
+ *
+ * @param permission The permission to grant.
+ * @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) {
+ return grantPermission(permission, userId);
+ }
+
+ /**
+ * Revoke a runtime permission for a given device user.
+ *
+ * @param permission The permission to revoke.
+ * @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 revokeRuntimePermission(BasePermission permission, int userId) {
+ return revokePermission(permission, userId);
+ }
+
+ /**
+ * Gets whether this state has a given permission, regardless if
+ * it is install time or runtime one.
+ *
+ * @param name The permission name.
+ * @return Whether this state has the permission.
+ */
+ public boolean hasPermission(String name) {
+ return mPermissions != null && mPermissions.get(name) != null;
+ }
+
+ /**
+ * Gets whether this state has a given runtime permission for a
+ * given device user id.
+ *
+ * @param name The permission name.
+ * @param userId The device user id.
+ * @return Whether this state has the permission.
+ */
+ public boolean hasRuntimePermission(String name, int userId) {
+ return !hasInstallPermission(name) && hasPermission(name, userId);
+ }
+
+ /**
+ * Gets whether this state has a given install permission.
+ *
+ * @param name The permission name.
+ * @return Whether this state has the permission.
+ */
+ public boolean hasInstallPermission(String name) {
+ return hasPermission(name, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Revokes a permission for all users regardless if it is an install or
+ * a runtime permission.
+ *
+ * @param permission The permission to revoke.
+ * @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 revokePermission(BasePermission permission) {
+ if (!hasPermission(permission.name)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ int result = PERMISSION_OPERATION_SUCCESS;
+
+ PermissionData permissionData = mPermissions.get(permission.name);
+ if (permissionData.getGids() != NO_GIDS) {
+ for (int userId : permissionData.getUserIds()) {
+ if (revokePermission(permission, userId)
+ == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+ break;
+ }
+ }
+ }
+
+ mPermissions.remove(permission.name);
+
+ return result;
+ }
+
+ /**
+ * Gets whether the state has a given permission for the specified
+ * user, regardless if this is an install or a runtime permission.
+ *
+ * @param name The permission name.
+ * @param userId The device user id.
+ * @return Whether the user has the permission.
+ */
+ public boolean hasPermission(String name, int userId) {
+ enforceValidUserId(userId);
+
+ if (mPermissions == null) {
+ return false;
+ }
+
+ PermissionData permissionData = mPermissions.get(name);
+ return permissionData != null && permissionData.hasUserId(userId);
+ }
+
+ /**
+ * Gets all permissions regardless if they are install or runtime.
+ *
+ * @return The permissions or an empty set.
+ */
+ public Set<String> getPermissions() {
+ if (mPermissions != null) {
+ return mPermissions.keySet();
+ }
+
+ return Collections.emptySet();
+ }
+
+ /**
+ * Gets all permissions for a given device user id regardless if they
+ * are install time or runtime permissions.
+ *
+ * @param userId The device user id.
+ * @return The permissions or an empty set.
+ */
+ public Set<String> getPermissions(int userId) {
+ enforceValidUserId(userId);
+
+ if (mPermissions == null) {
+ return Collections.emptySet();
+ }
+
+ Set<String> permissions = new ArraySet<>();
+
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String permission = mPermissions.keyAt(i);
+ if (userId == UserHandle.USER_ALL) {
+ if (hasInstallPermission(permission)) {
+ permissions.add(permission);
+ }
+ } else {
+ if (hasRuntimePermission(permission, userId)) {
+ permissions.add(permission);
+ }
+ }
+ }
+
+ return permissions;
+ }
+
+ /**
+ * Gets all runtime permissions.
+ *
+ * @return The permissions or an empty set.
+ */
+ public Set<String> getRuntimePermissions(int userId) {
+ return getPermissions(userId);
+ }
+
+ /**
+ * Gets all install permissions.
+ *
+ * @return The permissions or an empty set.
+ */
+ public Set<String> getInstallPermissions() {
+ return getPermissions(UserHandle.USER_ALL);
+ }
+
+ /**
+ * Compute the Linux gids for a given device user from the permissions
+ * granted to this user. Note that these are computed to avoid additional
+ * state as they are rarely accessed.
+ *
+ * @param userId The device user id.
+ * @return The gids for the device user.
+ */
+ public int[] computeGids(int userId) {
+ enforceValidUserId(userId);
+
+ int[] gids = mGlobalGids;
+
+ if (mPermissions != null) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String permission = mPermissions.keyAt(i);
+ if (!hasPermission(permission, userId)) {
+ continue;
+ }
+ PermissionData permissionData = mPermissions.valueAt(i);
+ final int[] permGids = permissionData.getGids();
+ if (permGids != NO_GIDS) {
+ gids = appendInts(gids, permGids);
+ }
+ }
+ }
+
+ return gids;
+ }
+
+ /**
+ * Compute the Linux gids for all device users from the permissions
+ * granted to these users.
+ *
+ * @return The gids for all device users.
+ */
+ public int[] computeGids() {
+ int[] gids = mGlobalGids;
+
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final int[] userGids = computeGids(userId);
+ gids = appendInts(gids, userGids);
+ }
+
+ return gids;
+ }
+
+ /**
+ * Resets the internal state of this object.
+ */
+ public void reset() {
+ mGlobalGids = NO_GIDS;
+ mPermissions = null;
+ }
+
+ private int grantPermission(BasePermission permission, int userId) {
+ if (hasPermission(permission.name, userId)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ final boolean hasGids = permission.gids != NO_GIDS;
+ final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
+
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+
+ PermissionData permissionData = mPermissions.get(permission.name);
+ if (permissionData == null) {
+ permissionData = new PermissionData(permission.gids);
+ mPermissions.put(permission.name, permissionData);
+ }
+
+ if (!permissionData.addUserId(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;
+ }
+
+ private int revokePermission(BasePermission permission, int userId) {
+ if (!hasPermission(permission.name, userId)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ final boolean hasGids = permission.gids != NO_GIDS;
+ final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
+
+ PermissionData permissionData = mPermissions.get(permission.name);
+
+ if (!permissionData.removeUserId(userId)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ if (permissionData.getUserIds() == USERS_NONE) {
+ mPermissions.remove(permission.name);
+ }
+
+ if (mPermissions.isEmpty()) {
+ mPermissions = null;
+ }
+
+ if (hasGids) {
+ final int[] newGids = computeGids(userId);
+ if (oldGids.length != newGids.length) {
+ return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+ }
+ }
+
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ private static int[] appendInts(int[] current, int[] added) {
+ if (current != null && added != null) {
+ for (int guid : added) {
+ current = ArrayUtils.appendInt(current, guid);
+ }
+ }
+ return current;
+ }
+
+ private static void enforceValidUserId(int userId) {
+ if (userId != UserHandle.USER_ALL && userId < 0) {
+ throw new IllegalArgumentException("Invalid userId:" + userId);
+ }
+ }
+
+ private static final class PermissionData {
+ private final int[] mGids;
+ private int[] mUserIds = USERS_NONE;
+
+ public PermissionData(int[] gids) {
+ mGids = !ArrayUtils.isEmpty(gids)
+ ? Arrays.copyOf(gids, gids.length)
+ : NO_GIDS;
+ }
+
+ public PermissionData(PermissionData other) {
+ this(other.mGids);
+
+ if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) {
+ mUserIds = other.mUserIds;
+ } else {
+ mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length);
+ }
+ }
+
+ public int[] getGids() {
+ return mGids;
+ }
+
+ public int[] getUserIds() {
+ return mUserIds;
+ }
+
+ public boolean hasUserId(int userId) {
+ if (mUserIds == USERS_ALL) {
+ return true;
+ }
+
+ if (userId != UserHandle.USER_ALL) {
+ return ArrayUtils.contains(mUserIds, userId);
+ }
+
+ return false;
+ }
+
+ public boolean addUserId(int userId) {
+ if (hasUserId(userId)) {
+ return false;
+ }
+
+ if (userId == UserHandle.USER_ALL) {
+ mUserIds = USERS_ALL;
+ return true;
+ }
+
+ mUserIds = ArrayUtils.appendInt(mUserIds, userId);
+
+ return true;
+ }
+
+ public boolean removeUserId(int userId) {
+ if (!hasUserId(userId)) {
+ return false;
+ }
+
+ if (mUserIds == USERS_ALL) {
+ mUserIds = UserManagerService.getInstance().getUserIds();
+ }
+
+ mUserIds = ArrayUtils.removeInt(mUserIds, userId);
+
+ if (mUserIds.length == 0) {
+ mUserIds = USERS_NONE;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/GrantedPermissions.java b/services/core/java/com/android/server/pm/SettingBase.java
index e87546c..d350c09 100644
--- a/services/core/java/com/android/server/pm/GrantedPermissions.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -19,27 +19,26 @@ package com.android.server.pm;
import android.content.pm.ApplicationInfo;
import android.util.ArraySet;
-class GrantedPermissions {
+abstract class SettingBase {
int pkgFlags;
int pkgPrivateFlags;
- ArraySet<String> grantedPermissions = new ArraySet<String>();
+ private final PermissionsState mPermissionsState;
- int[] gids;
-
- GrantedPermissions(int pkgFlags, int pkgPrivateFlags) {
+ SettingBase(int pkgFlags, int pkgPrivateFlags) {
setFlags(pkgFlags);
setPrivateFlags(pkgPrivateFlags);
+ mPermissionsState = new PermissionsState();
}
- @SuppressWarnings("unchecked")
- GrantedPermissions(GrantedPermissions base) {
+ SettingBase(SettingBase base) {
pkgFlags = base.pkgFlags;
- grantedPermissions = new ArraySet<>(base.grantedPermissions);
+ pkgPrivateFlags = base.pkgPrivateFlags;
+ mPermissionsState = new PermissionsState(base.mPermissionsState);
+ }
- if (base.gids != null) {
- gids = base.gids.clone();
- }
+ public PermissionsState getPermissionsState() {
+ return mPermissionsState;
}
void setFlags(int pkgFlags) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b820d7e..1e0a49e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.PACKAGE_INFO_GID;
+import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
@@ -33,17 +34,27 @@ import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Message;
import android.os.PatternMatcher;
import android.os.Process;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.AtomicFile;
import android.util.LogPrinter;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.PackageManagerService.DumpState;
+import java.io.FileNotFoundException;
import java.util.Collection;
import org.xmlpull.v1.XmlPullParser;
@@ -51,7 +62,6 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
@@ -134,6 +144,8 @@ final class Settings {
private static final boolean DEBUG_STOPPED = false;
private static final boolean DEBUG_MU = false;
+ private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml";
+
private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage";
private static final String ATTR_ENFORCEMENT = "enforcement";
@@ -142,6 +154,9 @@ final class Settings {
private static final String TAG_ENABLED_COMPONENTS = "enabled-components";
private static final String TAG_PACKAGE_RESTRICTIONS = "package-restrictions";
private static final String TAG_PACKAGE = "pkg";
+ private static final String TAG_SHARED_USER = "shared-user";
+ private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
+ private static final String TAG_PERMISSIONS = "perms";
private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
"persistent-preferred-activities";
static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
@@ -161,6 +176,11 @@ final class Settings {
private static final String ATTR_INSTALLED = "inst";
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
+ private final Object mLock;
+ private final Context mContext;
+
+ private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
+
private final File mSettingsFilename;
private final File mBackupSettingsFilename;
private final File mPackageListFilename;
@@ -257,11 +277,16 @@ final class Settings {
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
- Settings(Context context) {
- this(context, Environment.getDataDirectory());
+ Settings(Context context, Object lock) {
+ this(context, Environment.getDataDirectory(), lock);
}
- Settings(Context context, File dataDir) {
+ Settings(Context context, File dataDir, Object lock) {
+ mContext = context;
+ mLock = lock;
+
+ mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
+
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
@@ -468,9 +493,9 @@ final class Settings {
private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString,
- int vc, int pkgFlags, int pkgPrivateFlags, UserHandle installUser, boolean add,
- boolean allowInstall) {
+ String legacyNativeLibraryPathString, String primaryCpuAbiString,
+ String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags,
+ UserHandle installUser, boolean add, boolean allowInstall) {
PackageSetting p = mPackages.get(name);
UserManagerService userManager = UserManagerService.getInstance();
if (p != null) {
@@ -589,7 +614,7 @@ final class Settings {
}
p.appId = dis.appId;
// Clone permissions
- p.grantedPermissions = new ArraySet<String>(dis.grantedPermissions);
+ p.getPermissionsState().copyFrom(dis.getPermissionsState());
// Clone component info
List<UserInfo> users = getAllUsers();
if (users != null) {
@@ -732,45 +757,60 @@ final class Settings {
* not in use by other permissions of packages in the
* shared user setting.
*/
- void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) {
+ int updateSharedUserPermsLPw(PackageSetting deletedPs, int userId) {
if ((deletedPs == null) || (deletedPs.pkg == null)) {
Slog.i(PackageManagerService.TAG,
"Trying to update info for null package. Just ignoring");
- return;
+ return UserHandle.USER_NULL;
}
+
// No sharedUserId
if (deletedPs.sharedUser == null) {
- return;
+ return UserHandle.USER_NULL;
}
+
SharedUserSetting sus = deletedPs.sharedUser;
+
// Update permissions
for (String eachPerm : deletedPs.pkg.requestedPermissions) {
- boolean used = false;
- if (!sus.grantedPermissions.contains(eachPerm)) {
+ BasePermission bp = mPermissions.get(eachPerm);
+ if (bp == null) {
continue;
}
- for (PackageSetting pkg:sus.packages) {
- if (pkg.pkg != null &&
- !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
- pkg.pkg.requestedPermissions.contains(eachPerm)) {
+
+ // If no user has the permission, nothing to remove.
+ if (!sus.getPermissionsState().hasPermission(bp.name, userId)) {
+ continue;
+ }
+
+ boolean used = false;
+
+ // Check if another package in the shared user needs the permission.
+ for (PackageSetting pkg : sus.packages) {
+ if (pkg.pkg != null
+ && !pkg.pkg.packageName.equals(deletedPs.pkg.packageName)
+ && pkg.pkg.requestedPermissions.contains(eachPerm)) {
used = true;
break;
}
}
+
if (!used) {
- // can safely delete this permission from list
- sus.grantedPermissions.remove(eachPerm);
- }
- }
- // Update gids
- int newGids[] = globalGids;
- for (String eachPerm : sus.grantedPermissions) {
- BasePermission bp = mPermissions.get(eachPerm);
- if (bp != null) {
- newGids = PackageManagerService.appendInts(newGids, bp.gids);
+ // Try to revoke as an install permission which is for all users.
+ if (sus.getPermissionsState().revokeInstallPermission(bp) ==
+ PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ return UserHandle.USER_ALL;
+ }
+
+ // Try to revoke as an install permission which is per user.
+ if (sus.getPermissionsState().revokeRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ return userId;
+ }
}
}
- sus.gids = newGids;
+
+ return UserHandle.USER_NULL;
}
int removePackageLPw(String name) {
@@ -895,7 +935,17 @@ final class Settings {
}
private File getUserPackagesStateFile(int userId) {
- return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml");
+ // TODO: Implement a cleaner solution when adding tests.
+ // This instead of Environment.getUserSystemDirectory(userId) to support testing.
+ File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
+ return new File(userDir, "package-restrictions.xml");
+ }
+
+ private File getUserRuntimePermissionsFile(int userId) {
+ // TODO: Implement a cleaner solution when adding tests.
+ // This instead of Environment.getUserSystemDirectory(userId) to support testing.
+ File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
+ return new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME);
}
private File getUserPackagesStateBackupFile(int userId) {
@@ -912,15 +962,9 @@ final class Settings {
}
}
- void readAllUsersPackageRestrictionsLPr() {
- List<UserInfo> users = getAllUsers();
- if (users == null) {
- readPackageRestrictionsLPr(0);
- return;
- }
-
- for (UserInfo user : users) {
- readPackageRestrictionsLPr(user.id);
+ void writeAllRuntimePermissionsLPr() {
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
}
}
@@ -1360,6 +1404,7 @@ final class Settings {
}
serializer.endTag(null, TAG_DISABLED_COMPONENTS);
}
+
serializer.endTag(null, TAG_PACKAGE);
}
}
@@ -1403,6 +1448,58 @@ final class Settings {
}
}
+ void readInstallPermissionsLPr(XmlPullParser parser,
+ PermissionsState permissionsState) throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_ITEM)) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+
+ BasePermission bp = mPermissions.get(name);
+ if (bp == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ if (permissionsState.grantInstallPermission(bp) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ } else {
+ Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ void writePermissionsLPr(XmlSerializer serializer, Set<String> permissions)
+ throws IOException {
+ if (permissions.isEmpty()) {
+ return;
+ }
+
+ serializer.startTag(null, TAG_PERMISSIONS);
+
+ for (String permission : permissions) {
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, permission);
+ serializer.endTag(null, TAG_ITEM);
+ }
+
+ serializer.endTag(null, TAG_PERMISSIONS);
+ }
+
// Note: assumed "stopped" field is already cleared in all packages.
// Legacy reader, used to read in the old file format after an upgrade. Not used after that.
void readStoppedLPw() {
@@ -1594,13 +1691,7 @@ final class Settings {
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");
+ writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissions());
serializer.endTag(null, "shared-user");
}
@@ -1614,7 +1705,7 @@ final class Settings {
serializer.endTag(null, "cleaning-package");
}
}
-
+
if (mRenamedPackages.size() > 0) {
for (Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
serializer.startTag(null, "renamed-package");
@@ -1623,7 +1714,7 @@ final class Settings {
serializer.endTag(null, "renamed-package");
}
}
-
+
mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
serializer.endTag(null, "packages");
@@ -1662,7 +1753,7 @@ final class Settings {
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();
+ final int[] gids = pkg.getPermissionsState().computeGids();
// Avoid any application that has a space in its path.
if (dataPath.indexOf(" ") >= 0)
@@ -1718,6 +1809,8 @@ final class Settings {
}
writeAllUsersPackageRestrictionsLPr();
+
+ writeAllRuntimePermissionsLPr();
return;
} catch(XmlPullParserException e) {
@@ -1770,26 +1863,12 @@ final class Settings {
} else {
serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
}
- serializer.startTag(null, "perms");
+
+ // If this is a shared user, the permissions will be written there.
if (pkg.sharedUser == null) {
- // If this is a shared user, the permissions will
- // be written there. We still need to write an
- // empty permissions list so permissionsFixed will
- // be set.
- for (final String name : pkg.grantedPermissions) {
- BasePermission bp = mPermissions.get(name);
- if (bp != null) {
- // We only need to write signature or system permissions but
- // this wont
- // match the semantics of grantedPermissions. So write all
- // permissions.
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, name);
- serializer.endTag(null, TAG_ITEM);
- }
- }
+ writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
}
- serializer.endTag(null, "perms");
+
serializer.endTag(null, "updated-package");
}
@@ -1840,19 +1919,7 @@ final class Settings {
}
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- serializer.startTag(null, "perms");
- if (pkg.sharedUser == null) {
- // If this is a shared user, the permissions will
- // be written there. We still need to write an
- // empty permissions list so permissionsFixed will
- // be set.
- for (final String name : pkg.grantedPermissions) {
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, name);
- serializer.endTag(null, TAG_ITEM);
- }
- }
- serializer.endTag(null, "perms");
+ writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
}
writeSigningKeySetsLPr(serializer, pkg.keySetData);
@@ -2161,9 +2228,11 @@ final class Settings {
} else {
if (users == null) {
readPackageRestrictionsLPr(0);
+ mRuntimePermissionsPersistence.readStateForUserSyncLPr(UserHandle.USER_OWNER);
} else {
for (UserInfo user : users) {
readPackageRestrictionsLPr(user.id);
+ mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
}
}
}
@@ -2537,7 +2606,7 @@ final class Settings {
final String ptype = parser.getAttributeValue(null, "type");
if (name != null && sourcePackage != null) {
final boolean dynamic = "dynamic".equals(ptype);
- final BasePermission bp = new BasePermission(name, sourcePackage,
+ final BasePermission bp = new BasePermission(name.intern(), sourcePackage,
dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
@@ -2643,6 +2712,7 @@ final class Settings {
String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
ps.appId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
}
+
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2651,9 +2721,8 @@ final class Settings {
continue;
}
- String tagName = parser.getName();
- if (tagName.equals("perms")) {
- readGrantedPermissionsLPw(parser, ps.grantedPermissions);
+ if (parser.getName().equals(TAG_PERMISSIONS)) {
+ readInstallPermissionsLPr(parser, ps.getPermissionsState());
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <updated-package>: " + parser.getName());
@@ -2711,7 +2780,7 @@ final class Settings {
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
primaryCpuAbiString = legacyCpuAbiString;
}
-;
+
version = parser.getAttributeValue(null, "version");
if (version != null) {
try {
@@ -2902,7 +2971,6 @@ final class Settings {
packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;
}
}
-
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2919,8 +2987,9 @@ final class Settings {
readEnabledComponentsLPw(packageSetting, parser, 0);
} else if (tagName.equals("sigs")) {
packageSetting.signatures.readXml(parser, mPastSignatures);
- } else if (tagName.equals("perms")) {
- readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
+ } else if (tagName.equals(TAG_PERMISSIONS)) {
+ readInstallPermissionsLPr(parser,
+ packageSetting.getPermissionsState());
packageSetting.permissionsFixed = true;
} else if (tagName.equals("proper-signing-keyset")) {
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
@@ -3039,7 +3108,6 @@ final class Settings {
"Error in package manager settings: package " + name + " has bad userId "
+ idStr + " at " + parser.getPositionDescription());
}
- ;
if (su != null) {
int outerDepth = parser.getDepth();
@@ -3054,47 +3122,18 @@ final class Settings {
if (tagName.equals("sigs")) {
su.signatures.readXml(parser, mPastSignatures);
} else if (tagName.equals("perms")) {
- readGrantedPermissionsLPw(parser, su.grantedPermissions);
+ readInstallPermissionsLPr(parser, su.getPermissionsState());
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <shared-user>: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
-
} else {
XmlUtils.skipCurrentTag(parser);
}
}
- private void readGrantedPermissionsLPw(XmlPullParser parser, ArraySet<String> outPerms)
- throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals(TAG_ITEM)) {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- if (name != null) {
- outPerms.add(name.intern());
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <perms> has" + " no name at "
- + parser.getPositionDescription());
- }
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Unknown element under <perms>: " + parser.getName());
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
void createNewUserLILPw(PackageManagerService service, Installer installer,
int userHandle, File path) {
path.mkdir();
@@ -3126,6 +3165,8 @@ final class Settings {
file = getUserPackagesStateBackupFile(userId);
file.delete();
removeCrossProfileIntentFiltersLPw(userId);
+
+ mRuntimePermissionsPersistence.onUserRemoved(userId);
}
void removeCrossProfileIntentFiltersLPw(int userId) {
@@ -3317,7 +3358,7 @@ final class Settings {
return null;
}
- static final void printFlags(PrintWriter pw, int val, Object[] spec) {
+ static void printFlags(PrintWriter pw, int val, Object[] spec) {
pw.print("[ ");
for (int i=0; i<spec.length; i+=2) {
int mask = (Integer)spec[i];
@@ -3414,8 +3455,8 @@ final class Settings {
pw.println(ps.name);
}
- pw.print(prefix); pw.print(" userId="); pw.print(ps.appId);
- pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
+ pw.print(prefix); pw.print(" userId="); pw.println(ps.appId);
+
if (ps.sharedUser != null) {
pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.sharedUser);
}
@@ -3525,10 +3566,15 @@ final class Settings {
}
pw.print(prefix); pw.print(" signatures="); pw.println(ps.signatures);
pw.print(prefix); pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
- pw.print(" haveGids="); pw.print(ps.haveGids);
pw.print(" installStatus="); pw.println(ps.installStatus);
pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
pw.println();
+
+ if (ps.sharedUser == null) {
+ PermissionsState permissionsState = ps.getPermissionsState();
+ dumpInstallPermissionsLPr(pw, prefix + " ", permissionsState);
+ }
+
for (UserInfo user : users) {
pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": ");
pw.print(" installed=");
@@ -3546,6 +3592,14 @@ final class Settings {
pw.print(prefix); pw.print(" lastDisabledCaller: ");
pw.println(lastDisabledAppCaller);
}
+
+ if (ps.sharedUser == null) {
+ PermissionsState permissionsState = ps.getPermissionsState();
+ dumpGidsLPr(pw, prefix + " ", permissionsState.computeGids(user.id));
+ dumpRuntimePermissionsLPr(pw, prefix + " ", permissionsState
+ .getRuntimePermissions(user.id));
+ }
+
ArraySet<String> cmp = ps.getDisabledComponents(user.id);
if (cmp != null && cmp.size() > 0) {
pw.print(prefix); pw.println(" disabledComponents:");
@@ -3561,12 +3615,6 @@ final class Settings {
}
}
}
- if (ps.grantedPermissions.size() > 0) {
- pw.print(prefix); pw.println(" grantedPermissions:");
- for (String s : ps.grantedPermissions) {
- pw.print(prefix); pw.print(" "); pw.println(s);
- }
- }
}
void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState, boolean checkin) {
@@ -3688,14 +3736,21 @@ final class Settings {
pw.print("] (");
pw.print(Integer.toHexString(System.identityHashCode(su)));
pw.println("):");
- pw.print(" userId=");
- pw.print(su.userId);
- pw.print(" gids=");
- pw.println(PackageManagerService.arrayToString(su.gids));
- pw.println(" grantedPermissions:");
- for (String s : su.grantedPermissions) {
- pw.print(" ");
- pw.println(s);
+
+ String prefix = " ";
+ pw.print(prefix); pw.print("userId="); pw.println(su.userId);
+
+ PermissionsState permissionsState = su.getPermissionsState();
+ dumpInstallPermissionsLPr(pw, prefix, permissionsState);
+
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final int[] gids = permissionsState.computeGids(userId);
+ Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+ if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
+ pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
+ dumpGidsLPr(pw, prefix + " ", gids);
+ dumpRuntimePermissionsLPr(pw, prefix + " ", permissions);
+ }
}
} else {
pw.print("suid,"); pw.print(su.userId); pw.print(","); pw.println(su.name);
@@ -3730,4 +3785,373 @@ final class Settings {
pw.print("]");
}
}
+
+ void dumpGidsLPr(PrintWriter pw, String prefix, int[] gids) {
+ if (!ArrayUtils.isEmpty(gids)) {
+ pw.print(prefix); pw.print("gids="); pw.println(
+ PackageManagerService.arrayToString(gids));
+ }
+ }
+
+ void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, Set<String> permissions) {
+ if (!permissions.isEmpty()) {
+ pw.print(prefix); pw.println("runtime permissions:");
+ for (String permission : permissions) {
+ pw.print(prefix); pw.print(" "); pw.println(permission);
+ }
+ }
+ }
+
+ void dumpInstallPermissionsLPr(PrintWriter pw, String prefix,
+ PermissionsState permissionsState) {
+ Set<String> permissions = permissionsState.getInstallPermissions();
+ if (!permissions.isEmpty()) {
+ pw.print(prefix); pw.println("install permissions:");
+ for (String permission : permissions) {
+ pw.print(prefix); pw.print(" "); pw.println(permission);
+ }
+ }
+ }
+
+ public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
+ if (sync) {
+ mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
+ } else {
+ mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
+ }
+ }
+
+ private final class RuntimePermissionPersistence {
+ private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200;
+
+ private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000;
+
+ private final Handler mHandler = new MyHandler();
+
+ private final Object mLock;
+
+ @GuardedBy("mLock")
+ private SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
+
+ @GuardedBy("mLock")
+ private SparseLongArray mLastNotWrittenMutationTimesMillis = new SparseLongArray();
+
+ public RuntimePermissionPersistence(Object lock) {
+ mLock = lock;
+ }
+
+ public void writePermissionsForUserSyncLPr(int userId) {
+ mHandler.removeMessages(userId);
+ writePermissionsSync(userId);
+ }
+
+ public void writePermissionsForUserAsyncLPr(int userId) {
+ final long currentTimeMillis = SystemClock.uptimeMillis();
+
+ if (mWriteScheduled.get(userId)) {
+ mHandler.removeMessages(userId);
+
+ // If enough time passed, write without holding off anymore.
+ final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
+ .get(userId);
+ final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
+ - lastNotWrittenMutationTimeMillis;
+ if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
+ mHandler.obtainMessage(userId).sendToTarget();
+ return;
+ }
+
+ // Hold off a bit more as settings are frequently changing.
+ final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
+ + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
+ final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
+ maxDelayMillis);
+
+ Message message = mHandler.obtainMessage(userId);
+ mHandler.sendMessageDelayed(message, writeDelayMillis);
+ } else {
+ mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
+ Message message = mHandler.obtainMessage(userId);
+ mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
+ mWriteScheduled.put(userId, true);
+ }
+ }
+
+ private void writePermissionsSync(int userId) {
+ AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId));
+
+ ArrayMap<String, Set<String>> permissionsForPackage = new ArrayMap<>();
+ ArrayMap<String, Set<String>> 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();
+ Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+ if (!permissions.isEmpty()) {
+ permissionsForPackage.put(packageName, permissions);
+ }
+ }
+ }
+
+ 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();
+ Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+ if (!permissions.isEmpty()) {
+ permissionsForSharedUser.put(sharedUserName, permissions);
+ }
+ }
+ }
+
+ FileOutputStream out = null;
+ try {
+ out = destination.startWrite();
+
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);
+
+ final int packageCount = permissionsForPackage.size();
+ for (int i = 0; i < packageCount; i++) {
+ String packageName = permissionsForPackage.keyAt(i);
+ Set<String> permissions = permissionsForPackage.valueAt(i);
+ serializer.startTag(null, TAG_PACKAGE);
+ serializer.attribute(null, ATTR_NAME, packageName);
+ writePermissions(serializer, permissions);
+ serializer.endTag(null, TAG_PACKAGE);
+ }
+
+ final int sharedUserCount = permissionsForSharedUser.size();
+ for (int i = 0; i < sharedUserCount; i++) {
+ String packageName = permissionsForSharedUser.keyAt(i);
+ Set<String> permissions = permissionsForSharedUser.valueAt(i);
+ serializer.startTag(null, TAG_SHARED_USER);
+ serializer.attribute(null, ATTR_NAME, packageName);
+ writePermissions(serializer, permissions);
+ serializer.endTag(null, TAG_SHARED_USER);
+ }
+
+ serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
+ serializer.endDocument();
+ destination.finishWrite(out);
+ } catch (IOException e) {
+ Slog.wtf(PackageManagerService.TAG,
+ "Failed to write settings, restoring backup", e);
+ destination.failWrite(out);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ // Make sure we do not
+ mHandler.removeMessages(userId);
+
+ for (SettingBase sb : mPackages.values()) {
+ revokeRuntimePermissions(sb, userId);
+ }
+
+ for (SettingBase sb : mSharedUsers.values()) {
+ revokeRuntimePermissions(sb, userId);
+ }
+ }
+
+ private void revokeRuntimePermissions(SettingBase sb, int userId) {
+ PermissionsState permissionsState = sb.getPermissionsState();
+ for (String permission : permissionsState.getRuntimePermissions(userId)) {
+ BasePermission bp = mPermissions.get(permission);
+ if (bp != null) {
+ permissionsState.revokeRuntimePermission(bp, userId);
+ }
+ }
+ }
+
+ public void readStateForUserSyncLPr(int userId) {
+ File permissionsFile = getUserRuntimePermissionsFile(userId);
+ if (!permissionsFile.exists()) {
+ return;
+ }
+
+ FileInputStream in;
+ try {
+ in = new FileInputStream(permissionsFile);
+ } catch (FileNotFoundException fnfe) {
+ Slog.i(PackageManagerService.TAG, "No permissions state");
+ return;
+ }
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseRuntimePermissionsLPr(parser, userId);
+ } catch (XmlPullParserException | IOException ise) {
+ throw new IllegalStateException("Failed parsing permissions file: "
+ + permissionsFile , ise);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ parser.next();
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_RUNTIME_PERMISSIONS)) {
+ return;
+ }
+
+ parser.next();
+
+ while (parsePackageLPr(parser, userId)
+ || parseSharedUserLPr(parser, userId)) {
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_RUNTIME_PERMISSIONS);
+ }
+
+ private boolean parsePackageLPr(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_PACKAGE)) {
+ return false;
+ }
+
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+
+ parser.next();
+
+ PackageSetting ps = mPackages.get(name);
+ if (ps != null) {
+ while (parsePermissionLPr(parser, ps.getPermissionsState(), userId)) {
+ parser.next();
+ }
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PACKAGE);
+
+ return true;
+ }
+
+ private boolean parseSharedUserLPr(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_SHARED_USER)) {
+ return false;
+ }
+
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+
+ parser.next();
+
+ SharedUserSetting sus = mSharedUsers.get(name);
+ if (sus != null) {
+ while (parsePermissionLPr(parser, sus.getPermissionsState(), userId)) {
+ parser.next();
+ }
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_SHARED_USER);
+
+ return true;
+ }
+
+ private boolean parsePermissionLPr(XmlPullParser parser, PermissionsState permissionsState,
+ int userId) throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_ITEM)) {
+ return false;
+ }
+
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+
+ parser.next();
+
+ BasePermission bp = mPermissions.get(name);
+ if (bp != null) {
+ if (permissionsState.grantRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+ }
+ } else {
+ Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ITEM);
+
+ return true;
+ }
+
+ private void expect(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (!accept(parser, type, tag)) {
+ throw new XmlPullParserException("Expected event: " + type
+ + " and tag: " + tag + " but got event: " + parser.getEventType()
+ + " and tag:" + parser.getName());
+ }
+ }
+
+ private void skipEmptyTextTags(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ while (accept(parser, XmlPullParser.TEXT, null)
+ && parser.isWhitespace()) {
+ parser.next();
+ }
+ }
+
+ private boolean accept(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (parser.getEventType() != type) {
+ return false;
+ }
+ if (tag != null) {
+ if (!tag.equals(parser.getName())) {
+ return false;
+ }
+ } else if (parser.getName() != null) {
+ return false;
+ }
+ return true;
+ }
+
+ private void writePermissions(XmlSerializer serializer, Set<String> permissions)
+ throws IOException {
+ for (String permission : permissions) {
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, permission);
+ serializer.endTag(null, TAG_ITEM);
+ }
+ }
+
+ private final class MyHandler extends Handler {
+ public MyHandler() {
+ super(BackgroundThread.getHandler().getLooper());
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ final int userId = message.what;
+ Runnable callback = (Runnable) message.obj;
+ writePermissionsSync(userId);
+ if (callback != null) {
+ callback.run();
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index d95739c..06e020a 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -21,7 +21,7 @@ import android.util.ArraySet;
/**
* Settings data for a particular shared user ID we know about.
*/
-final class SharedUserSetting extends GrantedPermissions {
+final class SharedUserSetting extends SettingBase {
final String name;
int userId;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index b631331..4dc1131 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -32,7 +32,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
public class PackageManagerSettingsTests extends AndroidTestCase {
-
private static final String PACKAGE_NAME_2 = "com.google.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
private static final String PACKAGE_NAME_1 = "com.google.app1";
@@ -56,7 +55,7 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
writeFile(new File(getContext().getFilesDir(), "system/packages.xml"),
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<packages>"
- + "<last-platform-version internal=\"15\" external=\"0\" />"
+ + "<last-platform-version internal=\"15\" external=\"0\" fingerprint=\"foo\" />"
+ "<permission-trees>"
+ "<item name=\"com.google.android.permtree\" package=\"com.google.android.permpackage\" />"
+ "</permission-trees>"
@@ -110,28 +109,32 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
.getBytes());
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ private void deleteSystemFolder() {
+ File systemFolder = new File(getContext().getFilesDir(), "system");
+ deleteFolder(systemFolder);
+ }
+
+ private static void deleteFolder(File folder) {
+ File[] files = folder.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ deleteFolder(file);
+ }
+ }
+ folder.delete();
}
private void writeOldFiles() {
+ deleteSystemFolder();
writePackagesXml();
writeStoppedPackagesXml();
writePackagesList();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
public void testSettingsReadOld() {
- // Debug.waitForDebugger();
-
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
@@ -149,11 +152,12 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
public void testNewPackageRestrictionsFile() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
+ settings.writeLPr();
// Create Settings again to make it read from the new files
- settings = new Settings(getContext(), getContext().getFilesDir());
+ settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
@@ -164,7 +168,7 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
// Enable/Disable a package
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index cfbebba..b265d47 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -506,6 +506,11 @@ public class MockContext extends Context {
}
@Override
+ public int checkSelfPermission(String permission) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void enforcePermission(
String permission, int pid, int uid, String message) {
throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 7531d7b..67a8c2b 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -191,13 +191,13 @@ public class MockPackageManager extends PackageManager {
/** @hide */
@Override
- public void grantPermission(String packageName, String permissionName) {
+ public void grantPermission(String packageName, String permissionName, UserHandle user) {
throw new UnsupportedOperationException();
}
/** @hide */
@Override
- public void revokePermission(String packageName, String permissionName) {
+ public void revokePermission(String packageName, String permissionName, UserHandle user) {
throw new UnsupportedOperationException();
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e1c58fd..8e74ce1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1010,6 +1010,12 @@ public final class BridgeContext extends Context {
}
@Override
+ public int checkSelfPermission(String arg0) {
+ // pass
+ return 0;
+ }
+
+ @Override
public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) {
// pass
return 0;