summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Svet Ganov <svetoslavganov@google.com> 2016-08-19 13:17:57 +0000
committer android-build-merger <android-build-merger@google.com> 2016-08-19 13:17:57 +0000
commit34d85c6087bc88654f7bf51f51bef1c1f9984d6b (patch)
tree7ae70c8f0b8c475b4025a6c1ee305220c93d7012
parentd8f1599a6a067eeacff2af842b50a1ce77a28958 (diff)
parent59020bffb7dde02a2cd4cb797cadfe56bb36db44 (diff)
Add resource based mechanism to grant default permissions
am: 59020bffb7 Change-Id: I34d675b7f2ef4fb439bb582c6b36cb00de16acd8
-rw-r--r--services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java212
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java14
2 files changed, 222 insertions, 4 deletions
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 9c9e97e31f29..d1dbdd8b1945 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.Manifest;
+import android.annotation.NonNull;
import android.app.DownloadManager;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
@@ -30,6 +31,9 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.print.PrintManager;
@@ -39,12 +43,23 @@ import android.provider.MediaStore;
import android.provider.Telephony.Sms.Intents;
import android.telephony.TelephonyManager;
import android.security.Credentials;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedInputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import static android.os.Process.FIRST_APPLICATION_UID;
@@ -65,6 +80,13 @@ final class DefaultPermissionGrantPolicy {
private static final String AUDIO_MIME_TYPE = "audio/mpeg";
+ private static final String TAG_EXCEPTIONS = "exceptions";
+ private static final String TAG_EXCEPTION = "exception";
+ private static final String TAG_PERMISSION = "permission";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_FIXED = "fixed";
+
private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
static {
PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
@@ -126,7 +148,10 @@ final class DefaultPermissionGrantPolicy {
STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
+ private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
+
private final PackageManagerService mService;
+ private final Handler mHandler;
private PackagesProvider mLocationPackagesProvider;
private PackagesProvider mVoiceInteractionPackagesProvider;
@@ -135,8 +160,22 @@ final class DefaultPermissionGrantPolicy {
private PackagesProvider mSimCallManagerPackagesProvider;
private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
+ private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
+
public DefaultPermissionGrantPolicy(PackageManagerService service) {
mService = service;
+ mHandler = new Handler(mService.mHandlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
+ synchronized (mService.mPackages) {
+ if (mGrantExceptions == null) {
+ mGrantExceptions = readDefaultPermissionExceptionsLPw();
+ }
+ }
+ }
+ }
+ };
}
public void setLocationPackagesProviderLPw(PackagesProvider provider) {
@@ -166,6 +205,11 @@ final class DefaultPermissionGrantPolicy {
public void grantDefaultPermissions(int userId) {
grantPermissionsToSysComponentsAndPrivApps(userId);
grantDefaultSystemHandlerPermissions(userId);
+ grantDefaultPermissionExceptions(userId);
+ }
+
+ public void scheduleReadDefaultPermissionExceptions() {
+ mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
}
private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
@@ -916,7 +960,175 @@ final class DefaultPermissionGrantPolicy {
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
}
+ private void grantDefaultPermissionExceptions(int userId) {
+ synchronized (mService.mPackages) {
+ mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
+
+ if (mGrantExceptions == null) {
+ mGrantExceptions = readDefaultPermissionExceptionsLPw();
+ }
+
+ // mGrantExceptions is null only before the first read and then
+ // it serves as a cache of the default grants that should be
+ // performed for every user. If there is an entry then the app
+ // is on the system image and supports runtime permissions.
+ Set<String> permissions = null;
+ final int exceptionCount = mGrantExceptions.size();
+ for (int i = 0; i < exceptionCount; i++) {
+ String packageName = mGrantExceptions.keyAt(i);
+ PackageParser.Package pkg = getSystemPackageLPr(packageName);
+ List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
+ final int permissionGrantCount = permissionGrants.size();
+ for (int j = 0; j < permissionGrantCount; j++) {
+ DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
+ if (permissions == null) {
+ permissions = new ArraySet<>();
+ } else {
+ permissions.clear();
+ }
+ permissions.add(permissionGrant.name);
+ grantRuntimePermissionsLPw(pkg, permissions, false,
+ permissionGrant.fixed, userId);
+ }
+ }
+ }
+ }
+
+ private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
+ readDefaultPermissionExceptionsLPw() {
+ File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
+ if (!dir.exists() || !dir.isDirectory() || !dir.canRead()) {
+ return new ArrayMap<>(0);
+ }
+
+ File[] files = dir.listFiles();
+ if (files == null) {
+ return new ArrayMap<>(0);
+ }
+
+ ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>();
+
+ // Iterate over the files in the directory and scan .xml files
+ for (File file : files) {
+ if (!file.getPath().endsWith(".xml")) {
+ Slog.i(TAG, "Non-xml file " + file + " in " + dir + " directory, ignoring");
+ continue;
+ }
+ if (!file.canRead()) {
+ Slog.w(TAG, "Default permissions file " + file + " cannot be read");
+ continue;
+ }
+ try (
+ InputStream str = new BufferedInputStream(new FileInputStream(file))
+ ) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(str, null);
+ parse(parser, grantExceptions);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.w(TAG, "Error reading default permissions file " + file, e);
+ }
+ }
+
+ return grantExceptions;
+ }
+
+ private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+ outGrantExceptions) throws IOException, XmlPullParserException {
+ final 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;
+ }
+ if (TAG_EXCEPTIONS.equals(parser.getName())) {
+ parseExceptions(parser, outGrantExceptions);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName());
+ }
+ }
+ }
+
+ private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+ outGrantExceptions) throws IOException, XmlPullParserException {
+ final 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;
+ }
+ if (TAG_EXCEPTION.equals(parser.getName())) {
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+
+ List<DefaultPermissionGrant> packageExceptions =
+ outGrantExceptions.get(packageName);
+ if (packageExceptions == null) {
+ // The package must be on the system image
+ PackageParser.Package pkg = getSystemPackageLPr(packageName);
+ if (pkg == null) {
+ Log.w(TAG, "Unknown package:" + packageName);
+ XmlUtils.skipCurrentTag(parser);
+ return;
+ }
+
+ // The package must support runtime permissions
+ if (!doesPackageSupportRuntimePermissions(pkg)) {
+ Log.w(TAG, "Skipping non supporting runtime permissions package:"
+ + packageName);
+ XmlUtils.skipCurrentTag(parser);
+ return;
+ }
+ packageExceptions = new ArrayList<>();
+ outGrantExceptions.put(packageName, packageExceptions);
+ }
+
+ parsePermission(parser, packageExceptions);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>");
+ }
+ }
+ }
+
+ private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
+ outPackageExceptions) throws IOException, XmlPullParserException {
+ final 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;
+ }
+
+ if (TAG_PERMISSION.contains(parser.getName())) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ if (name == null) {
+ Log.w(TAG, "Mandatory name attribute missing for permission tag");
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED);
+
+ DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed);
+ outPackageExceptions.add(exception);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>");
+ }
+ }
+ }
+
private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
}
+
+ private static final class DefaultPermissionGrant {
+ final String name;
+ final boolean fixed;
+
+ public DefaultPermissionGrant(String name, boolean fixed) {
+ this.name = name;
+ this.fixed = fixed;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2ed64f18349c..f326555ee00a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -35,7 +35,6 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DEXOPT;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
import static android.content.pm.PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID;
@@ -101,7 +100,6 @@ import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCES
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
@@ -737,8 +735,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<IntentFilterVerificationState>();
- final DefaultPermissionGrantPolicy mDefaultPermissionPolicy =
- new DefaultPermissionGrantPolicy(this);
+ final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
@@ -2115,6 +2112,8 @@ public class PackageManagerService extends IPackageManager.Stub {
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
+ mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
+
File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
@@ -17937,6 +17936,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
+ // If we did not grant default permissions, we preload from this the
+ // default permission exceptions lazily to ensure we don't hit the
+ // disk on a new user creation.
+ if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
+ mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions();
+ }
+
// Kick off any messages waiting for system ready
if (mPostSystemReadyMessages != null) {
for (Message msg : mPostSystemReadyMessages) {