diff options
| author | 2016-08-19 13:17:57 +0000 | |
|---|---|---|
| committer | 2016-08-19 13:17:57 +0000 | |
| commit | 34d85c6087bc88654f7bf51f51bef1c1f9984d6b (patch) | |
| tree | 7ae70c8f0b8c475b4025a6c1ee305220c93d7012 | |
| parent | d8f1599a6a067eeacff2af842b50a1ce77a28958 (diff) | |
| parent | 59020bffb7dde02a2cd4cb797cadfe56bb36db44 (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.java | 212 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 14 |
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) { |