Merge "Add resource based mechanism to grant default permissions" into nyc-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
9a6008a334
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user