Merge "Uri permission grants improvements, tests, fixes." into rvc-dev
This commit is contained in:
@@ -2683,7 +2683,6 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
Slog.d("AppOps", "AppOpsService published");
|
||||
LocalServices.addService(ActivityManagerInternal.class, mInternal);
|
||||
mActivityTaskManager.onActivityManagerInternalAdded();
|
||||
mUgmInternal.onActivityManagerInternalAdded();
|
||||
mPendingIntentController.onActivityManagerInternalAdded();
|
||||
// Wait for the synchronized block started in mProcessCpuThread,
|
||||
// so that any other access to mProcessCpuTracker from main thread
|
||||
@@ -6412,7 +6411,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
if (pid == MY_PID) {
|
||||
return PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, false), uid, modeFlags)
|
||||
return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, modeFlags), uid, modeFlags)
|
||||
? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
@@ -6424,7 +6423,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,
|
||||
final int modeFlags, int userId) {
|
||||
enforceNotIsolatedCaller("grantUriPermission");
|
||||
GrantUri grantUri = new GrantUri(userId, uri, false);
|
||||
GrantUri grantUri = new GrantUri(userId, uri, modeFlags);
|
||||
synchronized(this) {
|
||||
final ProcessRecord r = getRecordForAppLocked(caller);
|
||||
if (r == null) {
|
||||
@@ -6482,8 +6481,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
mUgmInternal.revokeUriPermission(targetPackage, r.uid, new GrantUri(userId, uri, false),
|
||||
modeFlags);
|
||||
mUgmInternal.revokeUriPermission(targetPackage, r.uid,
|
||||
new GrantUri(userId, uri, modeFlags), modeFlags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.uri;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
@@ -27,12 +28,12 @@ import com.android.server.am.GrantUriProto;
|
||||
public class GrantUri {
|
||||
public final int sourceUserId;
|
||||
public final Uri uri;
|
||||
public boolean prefix;
|
||||
public final boolean prefix;
|
||||
|
||||
public GrantUri(int sourceUserId, Uri uri, boolean prefix) {
|
||||
public GrantUri(int sourceUserId, Uri uri, int modeFlags) {
|
||||
this.sourceUserId = sourceUserId;
|
||||
this.uri = uri;
|
||||
this.prefix = prefix;
|
||||
this.prefix = (modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,12 +75,12 @@ public class GrantUri {
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
|
||||
public static GrantUri resolve(int defaultSourceUserHandle, Uri uri, int modeFlags) {
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
|
||||
return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
|
||||
ContentProvider.getUriWithoutUserId(uri), false);
|
||||
ContentProvider.getUriWithoutUserId(uri), modeFlags);
|
||||
} else {
|
||||
return new GrantUri(defaultSourceUserHandle, uri, false);
|
||||
return new GrantUri(defaultSourceUserHandle, uri, modeFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,23 @@
|
||||
|
||||
package com.android.server.uri;
|
||||
|
||||
import android.util.ArraySet;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.server.am.NeededUriGrantsProto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** List of {@link GrantUri} a process needs. */
|
||||
public class NeededUriGrants extends ArrayList<GrantUri> {
|
||||
public class NeededUriGrants {
|
||||
final String targetPkg;
|
||||
final int targetUid;
|
||||
final int flags;
|
||||
final ArraySet<GrantUri> uris;
|
||||
|
||||
public NeededUriGrants(String targetPkg, int targetUid, int flags) {
|
||||
this.targetPkg = targetPkg;
|
||||
this.targetUid = targetUid;
|
||||
this.flags = flags;
|
||||
this.uris = new ArraySet<>();
|
||||
}
|
||||
|
||||
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
|
||||
@@ -40,9 +41,9 @@ public class NeededUriGrants extends ArrayList<GrantUri> {
|
||||
proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
|
||||
proto.write(NeededUriGrantsProto.FLAGS, flags);
|
||||
|
||||
final int N = this.size();
|
||||
final int N = uris.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
this.get(i).dumpDebug(proto, NeededUriGrantsProto.GRANTS);
|
||||
uris.valueAt(i).dumpDebug(proto, NeededUriGrantsProto.GRANTS);
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
30
services/core/java/com/android/server/uri/TEST_MAPPING
Normal file
30
services/core/java/com/android/server/uri/TEST_MAPPING
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "FrameworksServicesTests",
|
||||
"options": [
|
||||
{
|
||||
"include-filter": "com.android.server.uri."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"postsubmit": [
|
||||
{
|
||||
"name": "CtsAppSecurityHostTestCases",
|
||||
"options": [
|
||||
{
|
||||
"include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CtsWindowManagerDeviceTestCases",
|
||||
"options": [
|
||||
{
|
||||
"include-filter": "android.server.wm.CrossAppDragAndDropTests"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import java.io.PrintWriter;
|
||||
*/
|
||||
public interface UriGrantsManagerInternal {
|
||||
void onSystemReady();
|
||||
void onActivityManagerInternalAdded();
|
||||
void removeUriPermissionIfNeeded(UriPermission perm);
|
||||
void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
|
||||
final int modeFlags, UriPermissionOwner owner, int targetUserId);
|
||||
|
||||
@@ -21,6 +21,9 @@ import static android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS;
|
||||
import static android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS;
|
||||
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
|
||||
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
|
||||
import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
|
||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||
import static android.content.pm.PackageManager.MATCH_ANY_USER;
|
||||
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
|
||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
|
||||
@@ -53,7 +56,6 @@ import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
@@ -76,6 +78,8 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.IoThread;
|
||||
@@ -83,11 +87,11 @@ import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.SystemServiceManager;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
@@ -114,7 +118,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final Context mContext;
|
||||
private final H mH;
|
||||
ActivityManagerInternal mAmInternal;
|
||||
PackageManagerInternal mPmInternal;
|
||||
@@ -143,27 +146,38 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
private final SparseArray<ArrayMap<GrantUri, UriPermission>>
|
||||
mGrantedUriPermissions = new SparseArray<>();
|
||||
|
||||
private UriGrantsManagerService(Context context) {
|
||||
mContext = context;
|
||||
private UriGrantsManagerService() {
|
||||
this(SystemServiceManager.ensureSystemDir());
|
||||
}
|
||||
|
||||
private UriGrantsManagerService(File systemDir) {
|
||||
mH = new H(IoThread.get().getLooper());
|
||||
final File systemDir = SystemServiceManager.ensureSystemDir();
|
||||
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static UriGrantsManagerService createForTest(File systemDir) {
|
||||
final UriGrantsManagerService service = new UriGrantsManagerService(systemDir);
|
||||
service.mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
|
||||
service.mPmInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
return service;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
UriGrantsManagerInternal getLocalService() {
|
||||
return new LocalService();
|
||||
}
|
||||
|
||||
private void start() {
|
||||
LocalServices.addService(UriGrantsManagerInternal.class, new LocalService());
|
||||
}
|
||||
|
||||
void onActivityManagerInternalAdded() {
|
||||
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
|
||||
}
|
||||
|
||||
public static final class Lifecycle extends SystemService {
|
||||
private final UriGrantsManagerService mService;
|
||||
|
||||
public Lifecycle(Context context) {
|
||||
super(context);
|
||||
mService = new UriGrantsManagerService(context);
|
||||
mService = new UriGrantsManagerService();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,11 +186,27 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
mService.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootPhase(int phase) {
|
||||
if (phase == PHASE_SYSTEM_SERVICES_READY) {
|
||||
mService.mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
|
||||
mService.mPmInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
}
|
||||
}
|
||||
|
||||
public UriGrantsManagerService getService() {
|
||||
return mService;
|
||||
}
|
||||
}
|
||||
|
||||
private int checkUidPermission(String permission, int uid) {
|
||||
try {
|
||||
return AppGlobals.getPackageManager().checkUidPermission(permission, uid);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri This uri must NOT contain an embedded userId.
|
||||
* @param sourceUserId The userId in which the uri is to be resolved.
|
||||
@@ -207,7 +237,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
throw new IllegalArgumentException("null uri");
|
||||
}
|
||||
|
||||
grantUriPermission(fromUid, targetPkg, new GrantUri(sourceUserId, uri, false),
|
||||
grantUriPermission(fromUid, targetPkg, new GrantUri(sourceUserId, uri, modeFlags),
|
||||
modeFlags, owner, targetUserId);
|
||||
}
|
||||
}
|
||||
@@ -220,16 +250,12 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingUserId = UserHandle.getUserId(callingUid);
|
||||
final IPackageManager pm = AppGlobals.getPackageManager();
|
||||
try {
|
||||
final int packageUid = pm.getPackageUid(packageName,
|
||||
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUserId);
|
||||
if (packageUid != callingUid) {
|
||||
throw new SecurityException(
|
||||
"Package " + packageName + " does not belong to calling UID " + callingUid);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new SecurityException("Failed to verify package name ownership");
|
||||
final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
|
||||
final int packageUid = pm.getPackageUidInternal(packageName,
|
||||
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUserId);
|
||||
if (packageUid != callingUid) {
|
||||
throw new SecurityException(
|
||||
"Package " + packageName + " does not belong to calling UID " + callingUid);
|
||||
}
|
||||
|
||||
final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
|
||||
@@ -303,7 +329,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
if (toPackage != null) {
|
||||
mAmInternal.enforceCallingPermission(FORCE_PERSISTABLE_URI_PERMISSIONS,
|
||||
"takePersistableUriPermission");
|
||||
uid = getPmInternal().getPackageUid(toPackage, 0, userId);
|
||||
uid = mPmInternal.getPackageUidInternal(toPackage, 0, userId);
|
||||
} else {
|
||||
enforceNotIsolatedCaller("takePersistableUriPermission");
|
||||
uid = Binder.getCallingUid();
|
||||
@@ -314,11 +340,11 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
|
||||
synchronized (mLock) {
|
||||
boolean persistChanged = false;
|
||||
GrantUri grantUri = new GrantUri(userId, uri, false);
|
||||
|
||||
UriPermission exactPerm = findUriPermissionLocked(uid, grantUri);
|
||||
UriPermission exactPerm = findUriPermissionLocked(uid,
|
||||
new GrantUri(userId, uri, 0));
|
||||
UriPermission prefixPerm = findUriPermissionLocked(uid,
|
||||
new GrantUri(userId, uri, true));
|
||||
new GrantUri(userId, uri, FLAG_GRANT_PREFIX_URI_PERMISSION));
|
||||
|
||||
final boolean exactValid = (exactPerm != null)
|
||||
&& ((modeFlags & exactPerm.persistableModeFlags) == modeFlags);
|
||||
@@ -327,7 +353,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
|
||||
if (!(exactValid || prefixValid)) {
|
||||
throw new SecurityException("No persistable permission grants found for UID "
|
||||
+ uid + " and Uri " + grantUri.toSafeString());
|
||||
+ uid + " and Uri " + uri.toSafeString());
|
||||
}
|
||||
|
||||
if (exactValid) {
|
||||
@@ -368,7 +394,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
if (toPackage != null) {
|
||||
mAmInternal.enforceCallingPermission(FORCE_PERSISTABLE_URI_PERMISSIONS,
|
||||
"releasePersistableUriPermission");
|
||||
uid = getPmInternal().getPackageUid(toPackage, 0, userId);
|
||||
uid = mPmInternal.getPackageUidInternal(toPackage, 0, userId);
|
||||
} else {
|
||||
enforceNotIsolatedCaller("releasePersistableUriPermission");
|
||||
uid = Binder.getCallingUid();
|
||||
@@ -381,9 +407,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
boolean persistChanged = false;
|
||||
|
||||
UriPermission exactPerm = findUriPermissionLocked(uid,
|
||||
new GrantUri(userId, uri, false));
|
||||
new GrantUri(userId, uri, 0));
|
||||
UriPermission prefixPerm = findUriPermissionLocked(uid,
|
||||
new GrantUri(userId, uri, true));
|
||||
new GrantUri(userId, uri, FLAG_GRANT_PREFIX_URI_PERMISSION));
|
||||
if (exactPerm == null && prefixPerm == null && toPackage == null) {
|
||||
throw new SecurityException("No permission grants found for UID " + uid
|
||||
+ " and Uri " + uri.toSafeString());
|
||||
@@ -559,16 +585,12 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
if (contentUserHint == UserHandle.USER_CURRENT) {
|
||||
contentUserHint = UserHandle.getUserId(callingUid);
|
||||
}
|
||||
final IPackageManager pm = AppGlobals.getPackageManager();
|
||||
int targetUid;
|
||||
if (needed != null) {
|
||||
targetUid = needed.targetUid;
|
||||
} else {
|
||||
try {
|
||||
targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
|
||||
} catch (RemoteException ex) {
|
||||
return null;
|
||||
}
|
||||
targetUid = mPmInternal.getPackageUidInternal(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
|
||||
targetUserId);
|
||||
if (targetUid < 0) {
|
||||
if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg
|
||||
+ " on user " + targetUserId);
|
||||
@@ -576,27 +598,27 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
}
|
||||
}
|
||||
if (data != null) {
|
||||
GrantUri grantUri = GrantUri.resolve(contentUserHint, data);
|
||||
GrantUri grantUri = GrantUri.resolve(contentUserHint, data, mode);
|
||||
targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, mode, targetUid);
|
||||
if (targetUid > 0) {
|
||||
if (needed == null) {
|
||||
needed = new NeededUriGrants(targetPkg, targetUid, mode);
|
||||
}
|
||||
needed.add(grantUri);
|
||||
needed.uris.add(grantUri);
|
||||
}
|
||||
}
|
||||
if (clip != null) {
|
||||
for (int i=0; i<clip.getItemCount(); i++) {
|
||||
Uri uri = clip.getItemAt(i).getUri();
|
||||
if (uri != null) {
|
||||
GrantUri grantUri = GrantUri.resolve(contentUserHint, uri);
|
||||
GrantUri grantUri = GrantUri.resolve(contentUserHint, uri, mode);
|
||||
targetUid = checkGrantUriPermission(callingUid, targetPkg,
|
||||
grantUri, mode, targetUid);
|
||||
if (targetUid > 0) {
|
||||
if (needed == null) {
|
||||
needed = new NeededUriGrants(targetPkg, targetUid, mode);
|
||||
}
|
||||
needed.add(grantUri);
|
||||
needed.uris.add(grantUri);
|
||||
}
|
||||
} else {
|
||||
Intent clipIntent = clip.getItemAt(i).getIntent();
|
||||
@@ -666,16 +688,13 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
final ProviderInfo pi = getProviderInfo(uri.getAuthority(), sourceUserId,
|
||||
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
|
||||
if (pi != null && sourcePkg.equals(pi.packageName)) {
|
||||
int targetUid = -1;
|
||||
try {
|
||||
targetUid = AppGlobals.getPackageManager().getPackageUid(
|
||||
int targetUid = mPmInternal.getPackageUidInternal(
|
||||
targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
if (targetUid != -1) {
|
||||
final GrantUri grantUri = new GrantUri(sourceUserId, uri,
|
||||
prefix ? Intent.FLAG_GRANT_PREFIX_URI_PERMISSION : 0);
|
||||
final UriPermission perm = findOrCreateUriPermission(
|
||||
sourcePkg, targetPkg, targetUid,
|
||||
new GrantUri(sourceUserId, uri, prefix));
|
||||
sourcePkg, targetPkg, targetUid, grantUri);
|
||||
perm.initPersistedModes(modeFlags, createdTime);
|
||||
}
|
||||
} else {
|
||||
@@ -733,13 +752,10 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) {
|
||||
grantUri.prefix = true;
|
||||
}
|
||||
final UriPermission perm = findOrCreateUriPermission(
|
||||
pi.packageName, targetPkg, targetUid, grantUri);
|
||||
perm.grantModes(modeFlags, owner);
|
||||
getPmInternal().grantImplicitAccess(UserHandle.getUserId(targetUid), null,
|
||||
mPmInternal.grantImplicitAccess(UserHandle.getUserId(targetUid), null,
|
||||
UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/);
|
||||
}
|
||||
|
||||
@@ -748,10 +764,10 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
if (needed == null) {
|
||||
return;
|
||||
}
|
||||
for (int i=0; i<needed.size(); i++) {
|
||||
GrantUri grantUri = needed.get(i);
|
||||
final int N = needed.uris.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
grantUriPermissionUnchecked(needed.targetUid, needed.targetPkg,
|
||||
grantUri, needed.flags, owner);
|
||||
needed.uris.valueAt(i), needed.flags, owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,13 +776,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
if (targetPkg == null) {
|
||||
throw new NullPointerException("targetPkg");
|
||||
}
|
||||
int targetUid;
|
||||
final IPackageManager pm = AppGlobals.getPackageManager();
|
||||
try {
|
||||
targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
|
||||
} catch (RemoteException ex) {
|
||||
return;
|
||||
}
|
||||
int targetUid = mPmInternal.getPackageUidInternal(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
|
||||
targetUserId);
|
||||
|
||||
targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, modeFlags, targetUid);
|
||||
if (targetUid < 0) {
|
||||
@@ -780,7 +791,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
final int modeFlags) {
|
||||
if (DEBUG) Slog.v(TAG, "Revoking all granted permissions to " + grantUri);
|
||||
|
||||
final IPackageManager pm = AppGlobals.getPackageManager();
|
||||
final String authority = grantUri.uri.getAuthority();
|
||||
final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
|
||||
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
|
||||
@@ -791,7 +801,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
}
|
||||
|
||||
// Does the caller have this permission on the URI?
|
||||
if (!checkHoldingPermissions(pm, pi, grantUri, callingUid, modeFlags)) {
|
||||
if (!checkHoldingPermissions(pi, grantUri, callingUid, modeFlags)) {
|
||||
// If they don't have direct access to the URI, then revoke any
|
||||
// ownerless URI permissions that have been granted to them.
|
||||
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
|
||||
@@ -864,7 +874,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
* in {@link ContentProvider}.
|
||||
*/
|
||||
private boolean checkHoldingPermissions(
|
||||
IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
|
||||
ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
|
||||
if (DEBUG) Slog.v(TAG, "checkHoldingPermissions: uri=" + grantUri + " uid=" + uid);
|
||||
if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
|
||||
if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
|
||||
@@ -872,10 +882,10 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return checkHoldingPermissionsInternal(pm, pi, grantUri, uid, modeFlags, true);
|
||||
return checkHoldingPermissionsInternal(pi, grantUri, uid, modeFlags, true);
|
||||
}
|
||||
|
||||
private boolean checkHoldingPermissionsInternal(IPackageManager pm, ProviderInfo pi,
|
||||
private boolean checkHoldingPermissionsInternal(ProviderInfo pi,
|
||||
GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
|
||||
if (pi.applicationInfo.uid == uid) {
|
||||
return true;
|
||||
@@ -885,74 +895,70 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
|
||||
boolean readMet = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
|
||||
boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
|
||||
try {
|
||||
// check if target holds top-level <provider> permissions
|
||||
if (!readMet && pi.readPermission != null && considerUidPermissions
|
||||
&& (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
|
||||
readMet = true;
|
||||
}
|
||||
if (!writeMet && pi.writePermission != null && considerUidPermissions
|
||||
&& (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
|
||||
writeMet = true;
|
||||
}
|
||||
|
||||
// track if unprotected read/write is allowed; any denied
|
||||
// <path-permission> below removes this ability
|
||||
boolean allowDefaultRead = pi.readPermission == null;
|
||||
boolean allowDefaultWrite = pi.writePermission == null;
|
||||
// check if target holds top-level <provider> permissions
|
||||
if (!readMet && pi.readPermission != null && considerUidPermissions
|
||||
&& (checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
|
||||
readMet = true;
|
||||
}
|
||||
if (!writeMet && pi.writePermission != null && considerUidPermissions
|
||||
&& (checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
|
||||
writeMet = true;
|
||||
}
|
||||
|
||||
// check if target holds any <path-permission> that match uri
|
||||
final PathPermission[] pps = pi.pathPermissions;
|
||||
if (pps != null) {
|
||||
final String path = grantUri.uri.getPath();
|
||||
int i = pps.length;
|
||||
while (i > 0 && (!readMet || !writeMet)) {
|
||||
i--;
|
||||
PathPermission pp = pps[i];
|
||||
if (pp.match(path)) {
|
||||
if (!readMet) {
|
||||
final String pprperm = pp.getReadPermission();
|
||||
if (DEBUG) Slog.v(TAG,
|
||||
"Checking read perm for " + pprperm + " for " + pp.getPath()
|
||||
+ ": match=" + pp.match(path)
|
||||
+ " check=" + pm.checkUidPermission(pprperm, uid));
|
||||
if (pprperm != null) {
|
||||
if (considerUidPermissions && pm.checkUidPermission(pprperm, uid)
|
||||
== PERMISSION_GRANTED) {
|
||||
readMet = true;
|
||||
} else {
|
||||
allowDefaultRead = false;
|
||||
}
|
||||
// track if unprotected read/write is allowed; any denied
|
||||
// <path-permission> below removes this ability
|
||||
boolean allowDefaultRead = pi.readPermission == null;
|
||||
boolean allowDefaultWrite = pi.writePermission == null;
|
||||
|
||||
// check if target holds any <path-permission> that match uri
|
||||
final PathPermission[] pps = pi.pathPermissions;
|
||||
if (pps != null) {
|
||||
final String path = grantUri.uri.getPath();
|
||||
int i = pps.length;
|
||||
while (i > 0 && (!readMet || !writeMet)) {
|
||||
i--;
|
||||
PathPermission pp = pps[i];
|
||||
if (pp.match(path)) {
|
||||
if (!readMet) {
|
||||
final String pprperm = pp.getReadPermission();
|
||||
if (DEBUG) Slog.v(TAG,
|
||||
"Checking read perm for " + pprperm + " for " + pp.getPath()
|
||||
+ ": match=" + pp.match(path)
|
||||
+ " check=" + checkUidPermission(pprperm, uid));
|
||||
if (pprperm != null) {
|
||||
if (considerUidPermissions && checkUidPermission(pprperm, uid)
|
||||
== PERMISSION_GRANTED) {
|
||||
readMet = true;
|
||||
} else {
|
||||
allowDefaultRead = false;
|
||||
}
|
||||
}
|
||||
if (!writeMet) {
|
||||
final String ppwperm = pp.getWritePermission();
|
||||
if (DEBUG) Slog.v(TAG,
|
||||
"Checking write perm " + ppwperm + " for " + pp.getPath()
|
||||
+ ": match=" + pp.match(path)
|
||||
+ " check=" + pm.checkUidPermission(ppwperm, uid));
|
||||
if (ppwperm != null) {
|
||||
if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid)
|
||||
== PERMISSION_GRANTED) {
|
||||
writeMet = true;
|
||||
} else {
|
||||
allowDefaultWrite = false;
|
||||
}
|
||||
}
|
||||
if (!writeMet) {
|
||||
final String ppwperm = pp.getWritePermission();
|
||||
if (DEBUG) Slog.v(TAG,
|
||||
"Checking write perm " + ppwperm + " for " + pp.getPath()
|
||||
+ ": match=" + pp.match(path)
|
||||
+ " check=" + checkUidPermission(ppwperm, uid));
|
||||
if (ppwperm != null) {
|
||||
if (considerUidPermissions && checkUidPermission(ppwperm, uid)
|
||||
== PERMISSION_GRANTED) {
|
||||
writeMet = true;
|
||||
} else {
|
||||
allowDefaultWrite = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// grant unprotected <provider> read/write, if not blocked by
|
||||
// <path-permission> above
|
||||
if (allowDefaultRead) readMet = true;
|
||||
if (allowDefaultWrite) writeMet = true;
|
||||
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// grant unprotected <provider> read/write, if not blocked by
|
||||
// <path-permission> above
|
||||
if (allowDefaultRead) readMet = true;
|
||||
if (allowDefaultWrite) writeMet = true;
|
||||
|
||||
// If this provider says that grants are always required, we need to
|
||||
// consult it directly to determine if the UID has permission
|
||||
final boolean forceMet;
|
||||
@@ -1013,14 +1019,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
}
|
||||
|
||||
private ProviderInfo getProviderInfo(String authority, int userHandle, int pmFlags) {
|
||||
ProviderInfo pi = null;
|
||||
try {
|
||||
pi = AppGlobals.getPackageManager().resolveContentProvider(
|
||||
authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags,
|
||||
userHandle);
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
return pi;
|
||||
return mPmInternal.resolveContentProvider(authority,
|
||||
PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1033,7 +1033,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
* lastTargetUid else set that to -1.
|
||||
*/
|
||||
int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
|
||||
final int modeFlags, int lastTargetUid) {
|
||||
int modeFlags, int lastTargetUid) {
|
||||
if (!Intent.isAccessUriMode(modeFlags)) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1042,8 +1042,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
if (DEBUG) Slog.v(TAG, "Checking grant " + targetPkg + " permission to " + grantUri);
|
||||
}
|
||||
|
||||
final IPackageManager pm = AppGlobals.getPackageManager();
|
||||
|
||||
// If this is not a content: uri, we can't do anything with it.
|
||||
if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
|
||||
if (DEBUG) Slog.v(TAG, "Can't grant URI permission for non-content URI: " + grantUri);
|
||||
@@ -1079,39 +1077,22 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
|
||||
int targetUid = lastTargetUid;
|
||||
if (targetUid < 0 && targetPkg != null) {
|
||||
try {
|
||||
targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
|
||||
UserHandle.getUserId(callingUid));
|
||||
if (targetUid < 0) {
|
||||
if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg);
|
||||
return -1;
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
targetUid = mPmInternal.getPackageUidInternal(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
|
||||
UserHandle.getUserId(callingUid));
|
||||
if (targetUid < 0) {
|
||||
if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out the value returned when access is allowed
|
||||
final int allowedResult;
|
||||
if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0
|
||||
|| pi.forceUriPermissions) {
|
||||
// If we're extending a persistable grant or need to force, then we need to return
|
||||
// "targetUid" so that we always create a grant data structure to
|
||||
// support take/release APIs
|
||||
allowedResult = targetUid;
|
||||
} else {
|
||||
// Otherwise, we can return "-1" to indicate that no grant data
|
||||
// structures need to be created
|
||||
allowedResult = -1;
|
||||
}
|
||||
|
||||
boolean targetHoldsPermission = false;
|
||||
if (targetUid >= 0) {
|
||||
// First... does the target actually need this permission?
|
||||
if (checkHoldingPermissions(pm, pi, grantUri, targetUid, modeFlags)) {
|
||||
if (checkHoldingPermissions(pi, grantUri, targetUid, modeFlags)) {
|
||||
// No need to grant the target this permission.
|
||||
if (DEBUG) Slog.v(TAG,
|
||||
"Target " + targetPkg + " already has full permission to " + grantUri);
|
||||
return allowedResult;
|
||||
targetHoldsPermission = true;
|
||||
}
|
||||
} else {
|
||||
// First... there is no target package, so can anyone access it?
|
||||
@@ -1145,9 +1126,25 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allowed) {
|
||||
return allowedResult;
|
||||
if (pi.forceUriPermissions) {
|
||||
// When provider requires dynamic permission checks, the only
|
||||
// way to be safe is to issue permission grants for each item by
|
||||
// assuming no generic access
|
||||
allowed = false;
|
||||
}
|
||||
if (allowed) {
|
||||
targetHoldsPermission = true;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean basicGrant = (modeFlags & ~(FLAG_GRANT_READ_URI_PERMISSION
|
||||
| FLAG_GRANT_WRITE_URI_PERMISSION)) == 0;
|
||||
if (basicGrant && targetHoldsPermission) {
|
||||
// When caller holds permission, and this is a simple permission
|
||||
// grant, we can skip generating any bookkeeping; when any advanced
|
||||
// features have been requested, we proceed below to make sure the
|
||||
// provider supports granting permissions
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* There is a special cross user grant if:
|
||||
@@ -1158,38 +1155,42 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
*/
|
||||
boolean specialCrossUserGrant = targetUid >= 0
|
||||
&& UserHandle.getUserId(targetUid) != grantUri.sourceUserId
|
||||
&& checkHoldingPermissionsInternal(pm, pi, grantUri, callingUid,
|
||||
&& checkHoldingPermissionsInternal(pi, grantUri, callingUid,
|
||||
modeFlags, false /*without considering the uid permissions*/);
|
||||
|
||||
// Second... is the provider allowing granting of URI permissions?
|
||||
if (!specialCrossUserGrant) {
|
||||
if (!pi.grantUriPermissions) {
|
||||
boolean grantAllowed = pi.grantUriPermissions;
|
||||
if (!ArrayUtils.isEmpty(pi.uriPermissionPatterns)) {
|
||||
final int N = pi.uriPermissionPatterns.length;
|
||||
grantAllowed = false;
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (pi.uriPermissionPatterns[i] != null
|
||||
&& pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
|
||||
grantAllowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!grantAllowed) {
|
||||
if (specialCrossUserGrant) {
|
||||
// We're only okay issuing basic grant access across user
|
||||
// boundaries; advanced flags are blocked here
|
||||
if (!basicGrant) {
|
||||
throw new SecurityException("Provider " + pi.packageName
|
||||
+ "/" + pi.name
|
||||
+ " does not allow granting of advanced Uri permissions (uri "
|
||||
+ grantUri + ")");
|
||||
}
|
||||
} else {
|
||||
throw new SecurityException("Provider " + pi.packageName
|
||||
+ "/" + pi.name
|
||||
+ " does not allow granting of Uri permissions (uri "
|
||||
+ grantUri + ")");
|
||||
}
|
||||
if (pi.uriPermissionPatterns != null) {
|
||||
final int N = pi.uriPermissionPatterns.length;
|
||||
boolean allowed = false;
|
||||
for (int i=0; i<N; i++) {
|
||||
if (pi.uriPermissionPatterns[i] != null
|
||||
&& pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allowed) {
|
||||
throw new SecurityException("Provider " + pi.packageName
|
||||
+ "/" + pi.name
|
||||
+ " does not allow granting of permission to path of Uri "
|
||||
+ grantUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Third... does the caller itself have permission to access this uri?
|
||||
if (!checkHoldingPermissions(pm, pi, grantUri, callingUid, modeFlags)) {
|
||||
if (!checkHoldingPermissions(pi, grantUri, callingUid, modeFlags)) {
|
||||
// Require they hold a strong enough Uri permission
|
||||
if (!checkUriPermission(grantUri, callingUid, modeFlags)) {
|
||||
if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(pi.readPermission)) {
|
||||
@@ -1203,6 +1204,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targetUid;
|
||||
}
|
||||
|
||||
@@ -1212,7 +1214,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags,
|
||||
int userId) {
|
||||
return checkGrantUriPermission(callingUid, targetPkg,
|
||||
new GrantUri(userId, uri, false), modeFlags, -1);
|
||||
new GrantUri(userId, uri, modeFlags), modeFlags, -1);
|
||||
}
|
||||
|
||||
boolean checkUriPermission(GrantUri grantUri, int uid, final int modeFlags) {
|
||||
@@ -1297,14 +1299,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private PackageManagerInternal getPmInternal() {
|
||||
// Don't need to synchonize; worst-case scenario LocalServices will be called twice.
|
||||
if (mPmInternal == null) {
|
||||
mPmInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
}
|
||||
return mPmInternal;
|
||||
}
|
||||
|
||||
final class H extends Handler {
|
||||
static final int PERSIST_URI_GRANTS_MSG = 1;
|
||||
|
||||
@@ -1324,7 +1318,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
}
|
||||
|
||||
final class LocalService implements UriGrantsManagerInternal {
|
||||
|
||||
@Override
|
||||
public void removeUriPermissionIfNeeded(UriPermission perm) {
|
||||
synchronized (mLock) {
|
||||
@@ -1418,13 +1411,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityManagerInternalAdded() {
|
||||
synchronized (mLock) {
|
||||
UriGrantsManagerService.this.onActivityManagerInternalAdded();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder newUriPermissionOwner(String name) {
|
||||
enforceNotIsolatedCaller("newUriPermissionOwner");
|
||||
@@ -1454,8 +1440,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
if (uri == null) {
|
||||
owner.removeUriPermissions(mode);
|
||||
} else {
|
||||
final boolean prefix = (mode & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
|
||||
owner.removeUriPermission(new GrantUri(userId, uri, prefix), mode);
|
||||
owner.removeUriPermission(new GrantUri(userId, uri, mode), mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1478,12 +1463,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
|
||||
boolean printed = false;
|
||||
int dumpUid = -2;
|
||||
if (dumpPackage != null) {
|
||||
try {
|
||||
dumpUid = mContext.getPackageManager().getPackageUidAsUser(dumpPackage,
|
||||
MATCH_ANY_USER, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
dumpUid = -1;
|
||||
}
|
||||
dumpUid = mPmInternal.getPackageUidInternal(dumpPackage, MATCH_ANY_USER, 0);
|
||||
}
|
||||
for (int i = 0; i < mGrantedUriPermissions.size(); i++) {
|
||||
int uid = mGrantedUriPermissions.keyAt(i);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.uri;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.GrantedUriPermission;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
@@ -79,7 +80,7 @@ final class UriPermission {
|
||||
*/
|
||||
long persistedCreateTime = INVALID_TIME;
|
||||
|
||||
private static final long INVALID_TIME = Long.MIN_VALUE;
|
||||
static final long INVALID_TIME = Long.MIN_VALUE;
|
||||
|
||||
private ArraySet<UriPermissionOwner> mReadOwners;
|
||||
private ArraySet<UriPermissionOwner> mWriteOwners;
|
||||
@@ -96,7 +97,7 @@ final class UriPermission {
|
||||
|
||||
private void updateModeFlags() {
|
||||
final int oldModeFlags = modeFlags;
|
||||
modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
|
||||
modeFlags = ownedModeFlags | globalModeFlags | persistedModeFlags;
|
||||
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE) && (modeFlags != oldModeFlags)) {
|
||||
Slog.d(TAG,
|
||||
@@ -123,7 +124,7 @@ final class UriPermission {
|
||||
updateModeFlags();
|
||||
}
|
||||
|
||||
void grantModes(int modeFlags, UriPermissionOwner owner) {
|
||||
boolean grantModes(int modeFlags, @Nullable UriPermissionOwner owner) {
|
||||
final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
|
||||
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
@@ -144,6 +145,7 @@ final class UriPermission {
|
||||
}
|
||||
|
||||
updateModeFlags();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,8 +178,6 @@ final class UriPermission {
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
final int before = persistedModeFlags;
|
||||
|
||||
persistableModeFlags &= ~modeFlags;
|
||||
persistedModeFlags &= ~modeFlags;
|
||||
|
||||
if (persistedModeFlags == 0) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.util.ArraySet;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.server.am.UriPermissionOwnerProto;
|
||||
|
||||
import com.google.android.collect.Sets;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.uri;
|
||||
|
||||
import static com.android.server.uri.UriGrantsMockContext.FLAG_PERSISTABLE;
|
||||
import static com.android.server.uri.UriGrantsMockContext.FLAG_PREFIX;
|
||||
import static com.android.server.uri.UriGrantsMockContext.FLAG_READ;
|
||||
import static com.android.server.uri.UriGrantsMockContext.PKG_CAMERA;
|
||||
import static com.android.server.uri.UriGrantsMockContext.PKG_COMPLEX;
|
||||
import static com.android.server.uri.UriGrantsMockContext.PKG_SOCIAL;
|
||||
import static com.android.server.uri.UriGrantsMockContext.UID_PRIMARY_CAMERA;
|
||||
import static com.android.server.uri.UriGrantsMockContext.UID_PRIMARY_COMPLEX;
|
||||
import static com.android.server.uri.UriGrantsMockContext.UID_PRIMARY_PRIVATE;
|
||||
import static com.android.server.uri.UriGrantsMockContext.UID_PRIMARY_PUBLIC;
|
||||
import static com.android.server.uri.UriGrantsMockContext.UID_PRIMARY_SOCIAL;
|
||||
import static com.android.server.uri.UriGrantsMockContext.UID_SECONDARY_CAMERA;
|
||||
import static com.android.server.uri.UriGrantsMockContext.UID_SECONDARY_SOCIAL;
|
||||
import static com.android.server.uri.UriGrantsMockContext.URI_PHOTO_1;
|
||||
import static com.android.server.uri.UriGrantsMockContext.URI_PRIVATE;
|
||||
import static com.android.server.uri.UriGrantsMockContext.URI_PUBLIC;
|
||||
import static com.android.server.uri.UriGrantsMockContext.USER_PRIMARY;
|
||||
import static com.android.server.uri.UriGrantsMockContext.USER_SECONDARY;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.net.Uri;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
public class UriGrantsManagerServiceTest {
|
||||
private UriGrantsMockContext mContext;
|
||||
private UriGrantsManagerService mService;
|
||||
private UriGrantsManagerInternal mLocalService;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext());
|
||||
mService = UriGrantsManagerService.createForTest(mContext.getFilesDir());
|
||||
mLocalService = mService.getLocalService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNeeded_normal() {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PHOTO_1).addFlags(FLAG_READ);
|
||||
final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
|
||||
|
||||
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null,
|
||||
USER_PRIMARY);
|
||||
assertEquals(PKG_SOCIAL, needed.targetPkg);
|
||||
assertEquals(UID_PRIMARY_SOCIAL, needed.targetUid);
|
||||
assertEquals(FLAG_READ, needed.flags);
|
||||
assertEquals(asSet(expectedGrant), needed.uris);
|
||||
}
|
||||
|
||||
/**
|
||||
* No need to issue grants for public authorities.
|
||||
*/
|
||||
@Test
|
||||
public void testNeeded_public() {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PUBLIC).addFlags(FLAG_READ);
|
||||
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_PUBLIC, PKG_SOCIAL, intent, intent.getFlags(), null,
|
||||
USER_PRIMARY);
|
||||
assertNull(needed);
|
||||
}
|
||||
|
||||
/**
|
||||
* But we're willing to issue grants to public authorities when crossing
|
||||
* user boundaries.
|
||||
*/
|
||||
@Test
|
||||
public void testNeeded_public_differentUser() {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PUBLIC).addFlags(FLAG_READ);
|
||||
final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PUBLIC, FLAG_READ);
|
||||
|
||||
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_PUBLIC, PKG_SOCIAL, intent, intent.getFlags(), null, USER_SECONDARY);
|
||||
assertEquals(PKG_SOCIAL, needed.targetPkg);
|
||||
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
|
||||
assertEquals(FLAG_READ, needed.flags);
|
||||
assertEquals(asSet(expectedGrant), needed.uris);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refuse to issue grants for private authorities.
|
||||
*/
|
||||
@Test
|
||||
public void testNeeded_private() {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PRIVATE).addFlags(FLAG_READ);
|
||||
try {
|
||||
mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_PRIVATE, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY);
|
||||
fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we can't grant permissions to top level of a provider with
|
||||
* complex permission model.
|
||||
*/
|
||||
@Test
|
||||
public void testNeeded_complex_top() {
|
||||
final Uri uri = Uri.parse("content://" + PKG_COMPLEX + "/");
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ);
|
||||
assertNull(mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL,
|
||||
intent, intent.getFlags(), null, USER_PRIMARY));
|
||||
}
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ | FLAG_PREFIX);
|
||||
try {
|
||||
mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL,
|
||||
intent, intent.getFlags(), null, USER_PRIMARY);
|
||||
fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ | FLAG_PERSISTABLE);
|
||||
try {
|
||||
mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL,
|
||||
intent, intent.getFlags(), null, USER_PRIMARY);
|
||||
fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we allow special cross-user grants to top level of a provider
|
||||
* that normally wouldn't allow it. Only basic permission modes are allowed;
|
||||
* advanced modes throw.
|
||||
*/
|
||||
@Test
|
||||
public void testNeeded_complex_top_differentUser() {
|
||||
final Uri uri = Uri.parse("content://" + PKG_COMPLEX + "/");
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ);
|
||||
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null,
|
||||
USER_SECONDARY);
|
||||
assertEquals(FLAG_READ, needed.flags);
|
||||
}
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ | FLAG_PREFIX);
|
||||
try {
|
||||
mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null,
|
||||
USER_SECONDARY);
|
||||
fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ | FLAG_PERSISTABLE);
|
||||
try {
|
||||
mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null,
|
||||
USER_SECONDARY);
|
||||
fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we can grant permissions to middle level of a provider with
|
||||
* complex permission model.
|
||||
*/
|
||||
@Test
|
||||
public void testNeeded_complex_middle() {
|
||||
final Uri uri = Uri.parse("content://" + PKG_COMPLEX + "/secure/12");
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ);
|
||||
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY);
|
||||
assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, 0)), needed.uris);
|
||||
}
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ | FLAG_PREFIX);
|
||||
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY);
|
||||
assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, FLAG_PREFIX)), needed.uris);
|
||||
}
|
||||
{
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
||||
.addFlags(FLAG_READ | FLAG_PERSISTABLE);
|
||||
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY);
|
||||
assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, 0)), needed.uris);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that when we try sending a list of mixed items that the actual
|
||||
* grants are verified based on the capabilities of the caller.
|
||||
*/
|
||||
@Test
|
||||
public void testNeeded_mixedPersistable() {
|
||||
final ClipData clip = ClipData.newRawUri("test", URI_PHOTO_1);
|
||||
clip.addItem(new ClipData.Item(URI_PUBLIC));
|
||||
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.addFlags(FLAG_READ | FLAG_PERSISTABLE);
|
||||
intent.setClipData(clip);
|
||||
|
||||
{
|
||||
// When granting towards primary, persistable can't be honored so
|
||||
// the entire grant fails
|
||||
try {
|
||||
mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_CAMERA, PKG_SOCIAL, intent,
|
||||
intent.getFlags(), null, USER_PRIMARY);
|
||||
fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
{
|
||||
// When granting towards secondary, persistable can't be honored so
|
||||
// the entire grant fails
|
||||
try {
|
||||
mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_CAMERA, PKG_SOCIAL, intent,
|
||||
intent.getFlags(), null, USER_SECONDARY);
|
||||
fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that two overlapping owners require separate grants and that they
|
||||
* don't interfere with each other.
|
||||
*/
|
||||
@Test
|
||||
public void testGrant_overlap() {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PHOTO_1).addFlags(FLAG_READ);
|
||||
|
||||
final UriPermissionOwner activity = new UriPermissionOwner(mLocalService, "activity");
|
||||
final UriPermissionOwner service = new UriPermissionOwner(mLocalService, "service");
|
||||
|
||||
final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
|
||||
|
||||
// Grant read via activity and write via service
|
||||
mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY),
|
||||
activity);
|
||||
mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY),
|
||||
service);
|
||||
|
||||
// Verify that everything is good with the world
|
||||
assertTrue(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ));
|
||||
|
||||
// Finish activity; service should hold permission
|
||||
activity.removeUriPermissions();
|
||||
assertTrue(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ));
|
||||
|
||||
// And finishing service should wrap things up
|
||||
service.removeUriPermissions();
|
||||
assertFalse(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckAuthorityGrants() {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PHOTO_1).addFlags(FLAG_READ);
|
||||
final UriPermissionOwner owner = new UriPermissionOwner(mLocalService, "primary");
|
||||
|
||||
final ProviderInfo cameraInfo = mContext.mPmInternal.resolveContentProvider(
|
||||
PKG_CAMERA, 0, USER_PRIMARY);
|
||||
|
||||
// By default no social can see any camera
|
||||
assertFalse(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
|
||||
cameraInfo, USER_PRIMARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
|
||||
cameraInfo, USER_SECONDARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_SECONDARY_SOCIAL,
|
||||
cameraInfo, USER_PRIMARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_SECONDARY_SOCIAL,
|
||||
cameraInfo, USER_SECONDARY, true));
|
||||
|
||||
// Granting primary camera to primary social
|
||||
mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent(
|
||||
UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY),
|
||||
owner);
|
||||
assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
|
||||
cameraInfo, USER_PRIMARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
|
||||
cameraInfo, USER_SECONDARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_SECONDARY_SOCIAL,
|
||||
cameraInfo, USER_PRIMARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_SECONDARY_SOCIAL,
|
||||
cameraInfo, USER_SECONDARY, true));
|
||||
|
||||
// Granting secondary camera to primary social
|
||||
mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent(
|
||||
UID_SECONDARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY),
|
||||
owner);
|
||||
assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
|
||||
cameraInfo, USER_PRIMARY, true));
|
||||
assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
|
||||
cameraInfo, USER_SECONDARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_SECONDARY_SOCIAL,
|
||||
cameraInfo, USER_PRIMARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_SECONDARY_SOCIAL,
|
||||
cameraInfo, USER_SECONDARY, true));
|
||||
|
||||
// And releasing the grant means we lose access
|
||||
owner.removeUriPermissions();
|
||||
assertFalse(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
|
||||
cameraInfo, USER_PRIMARY, true));
|
||||
assertFalse(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
|
||||
cameraInfo, USER_SECONDARY, true));
|
||||
}
|
||||
|
||||
private static <T> Set<T> asSet(T... values) {
|
||||
return new ArraySet<T>(Arrays.asList(values));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.uri;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PathPermission;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.FileUtils;
|
||||
import android.os.PatternMatcher;
|
||||
import android.os.UserHandle;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.test.mock.MockPackageManager;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class UriGrantsMockContext extends ContextWrapper {
|
||||
static final String TAG = "UriGrants";
|
||||
|
||||
static final int FLAG_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
static final int FLAG_WRITE = Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||
static final int FLAG_PERSISTABLE = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
|
||||
static final int FLAG_PREFIX = Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
|
||||
|
||||
static final int USER_PRIMARY = 10;
|
||||
static final int USER_SECONDARY = 11;
|
||||
|
||||
/** Typical social network app */
|
||||
static final String PKG_SOCIAL = "com.example.social";
|
||||
/** Typical camera app that allows grants */
|
||||
static final String PKG_CAMERA = "com.example.camera";
|
||||
/** Completely private app/provider that offers no grants */
|
||||
static final String PKG_PRIVATE = "com.example.private";
|
||||
/** Completely public app/provider that needs no grants */
|
||||
static final String PKG_PUBLIC = "com.example.public";
|
||||
/** Complex provider that offers nested grants */
|
||||
static final String PKG_COMPLEX = "com.example.complex";
|
||||
|
||||
private static final int UID_SOCIAL = android.os.Process.LAST_APPLICATION_UID - 1;
|
||||
private static final int UID_CAMERA = android.os.Process.LAST_APPLICATION_UID - 2;
|
||||
private static final int UID_PRIVATE = android.os.Process.LAST_APPLICATION_UID - 3;
|
||||
private static final int UID_PUBLIC = android.os.Process.LAST_APPLICATION_UID - 4;
|
||||
private static final int UID_COMPLEX = android.os.Process.LAST_APPLICATION_UID - 5;
|
||||
|
||||
static final int UID_PRIMARY_SOCIAL = UserHandle.getUid(USER_PRIMARY, UID_SOCIAL);
|
||||
static final int UID_PRIMARY_CAMERA = UserHandle.getUid(USER_PRIMARY, UID_CAMERA);
|
||||
static final int UID_PRIMARY_PRIVATE = UserHandle.getUid(USER_PRIMARY, UID_PRIVATE);
|
||||
static final int UID_PRIMARY_PUBLIC = UserHandle.getUid(USER_PRIMARY, UID_PUBLIC);
|
||||
static final int UID_PRIMARY_COMPLEX = UserHandle.getUid(USER_PRIMARY, UID_COMPLEX);
|
||||
|
||||
static final int UID_SECONDARY_SOCIAL = UserHandle.getUid(USER_SECONDARY, UID_SOCIAL);
|
||||
static final int UID_SECONDARY_CAMERA = UserHandle.getUid(USER_SECONDARY, UID_CAMERA);
|
||||
static final int UID_SECONDARY_PRIVATE = UserHandle.getUid(USER_SECONDARY, UID_PRIVATE);
|
||||
static final int UID_SECONDARY_PUBLIC = UserHandle.getUid(USER_SECONDARY, UID_PUBLIC);
|
||||
static final int UID_SECONDARY_COMPLEX = UserHandle.getUid(USER_PRIMARY, UID_COMPLEX);
|
||||
|
||||
static final Uri URI_PHOTO_1 = Uri.parse("content://" + PKG_CAMERA + "/1");
|
||||
static final Uri URI_PHOTO_2 = Uri.parse("content://" + PKG_CAMERA + "/2");
|
||||
static final Uri URI_PRIVATE = Uri.parse("content://" + PKG_PRIVATE + "/42");
|
||||
static final Uri URI_PUBLIC = Uri.parse("content://" + PKG_PUBLIC + "/42");
|
||||
|
||||
private final File mDir;
|
||||
|
||||
private final MockPackageManager mPackage;
|
||||
private final MockContentResolver mResolver;
|
||||
|
||||
final ActivityManagerInternal mAmInternal;
|
||||
final PackageManagerInternal mPmInternal;
|
||||
|
||||
public UriGrantsMockContext(@NonNull Context base) {
|
||||
super(base);
|
||||
mDir = new File(base.getFilesDir(), TAG);
|
||||
mDir.mkdirs();
|
||||
FileUtils.deleteContents(mDir);
|
||||
|
||||
mPackage = new MockPackageManager();
|
||||
mResolver = new MockContentResolver(this);
|
||||
|
||||
mAmInternal = mock(ActivityManagerInternal.class);
|
||||
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
|
||||
LocalServices.addService(ActivityManagerInternal.class, mAmInternal);
|
||||
|
||||
mPmInternal = mock(PackageManagerInternal.class);
|
||||
LocalServices.removeServiceForTest(PackageManagerInternal.class);
|
||||
LocalServices.addService(PackageManagerInternal.class, mPmInternal);
|
||||
|
||||
for (int userId : new int[] { USER_PRIMARY, USER_SECONDARY }) {
|
||||
when(mPmInternal.getPackageUidInternal(eq(PKG_SOCIAL), anyInt(), eq(userId)))
|
||||
.thenReturn(UserHandle.getUid(userId, UID_SOCIAL));
|
||||
when(mPmInternal.getPackageUidInternal(eq(PKG_CAMERA), anyInt(), eq(userId)))
|
||||
.thenReturn(UserHandle.getUid(userId, UID_CAMERA));
|
||||
when(mPmInternal.getPackageUidInternal(eq(PKG_PRIVATE), anyInt(), eq(userId)))
|
||||
.thenReturn(UserHandle.getUid(userId, UID_PRIVATE));
|
||||
when(mPmInternal.getPackageUidInternal(eq(PKG_PUBLIC), anyInt(), eq(userId)))
|
||||
.thenReturn(UserHandle.getUid(userId, UID_PUBLIC));
|
||||
when(mPmInternal.getPackageUidInternal(eq(PKG_COMPLEX), anyInt(), eq(userId)))
|
||||
.thenReturn(UserHandle.getUid(userId, UID_COMPLEX));
|
||||
|
||||
when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyInt(), eq(userId)))
|
||||
.thenReturn(buildCameraProvider(userId));
|
||||
when(mPmInternal.resolveContentProvider(eq(PKG_PRIVATE), anyInt(), eq(userId)))
|
||||
.thenReturn(buildPrivateProvider(userId));
|
||||
when(mPmInternal.resolveContentProvider(eq(PKG_PUBLIC), anyInt(), eq(userId)))
|
||||
.thenReturn(buildPublicProvider(userId));
|
||||
when(mPmInternal.resolveContentProvider(eq(PKG_COMPLEX), anyInt(), eq(userId)))
|
||||
.thenReturn(buildComplexProvider(userId));
|
||||
}
|
||||
}
|
||||
|
||||
private static ProviderInfo buildCameraProvider(int userId) {
|
||||
final ProviderInfo pi = new ProviderInfo();
|
||||
pi.packageName = PKG_CAMERA;
|
||||
pi.authority = PKG_CAMERA;
|
||||
pi.readPermission = android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||
pi.writePermission = android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||
pi.grantUriPermissions = true;
|
||||
pi.applicationInfo = new ApplicationInfo();
|
||||
pi.applicationInfo.uid = UserHandle.getUid(userId, UID_CAMERA);
|
||||
return pi;
|
||||
}
|
||||
|
||||
private static ProviderInfo buildPrivateProvider(int userId) {
|
||||
final ProviderInfo pi = new ProviderInfo();
|
||||
pi.packageName = PKG_PRIVATE;
|
||||
pi.authority = PKG_PRIVATE;
|
||||
pi.exported = false;
|
||||
pi.grantUriPermissions = false;
|
||||
pi.applicationInfo = new ApplicationInfo();
|
||||
pi.applicationInfo.uid = UserHandle.getUid(userId, UID_PRIVATE);
|
||||
return pi;
|
||||
}
|
||||
|
||||
private static ProviderInfo buildPublicProvider(int userId) {
|
||||
final ProviderInfo pi = new ProviderInfo();
|
||||
pi.packageName = PKG_PUBLIC;
|
||||
pi.authority = PKG_PUBLIC;
|
||||
pi.exported = true;
|
||||
pi.grantUriPermissions = false;
|
||||
pi.applicationInfo = new ApplicationInfo();
|
||||
pi.applicationInfo.uid = UserHandle.getUid(userId, UID_PUBLIC);
|
||||
return pi;
|
||||
}
|
||||
|
||||
private static ProviderInfo buildComplexProvider(int userId) {
|
||||
final ProviderInfo pi = new ProviderInfo();
|
||||
pi.packageName = PKG_COMPLEX;
|
||||
pi.authority = PKG_COMPLEX;
|
||||
pi.exported = true;
|
||||
pi.grantUriPermissions = true;
|
||||
pi.applicationInfo = new ApplicationInfo();
|
||||
pi.applicationInfo.uid = UserHandle.getUid(userId, UID_COMPLEX);
|
||||
pi.pathPermissions = new PathPermission[] {
|
||||
new PathPermission("/secure", PathPermission.PATTERN_PREFIX,
|
||||
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
};
|
||||
pi.uriPermissionPatterns = new PatternMatcher[] {
|
||||
new PatternMatcher("/secure", PathPermission.PATTERN_PREFIX),
|
||||
new PatternMatcher("/insecure", PathPermission.PATTERN_PREFIX),
|
||||
};
|
||||
return pi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageManager getPackageManager() {
|
||||
return mPackage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentResolver getContentResolver() {
|
||||
return mResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFilesDir() {
|
||||
return mDir;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.uri;
|
||||
|
||||
import static com.android.server.uri.UriGrantsMockContext.FLAG_PERSISTABLE;
|
||||
import static com.android.server.uri.UriGrantsMockContext.FLAG_READ;
|
||||
import static com.android.server.uri.UriGrantsMockContext.FLAG_WRITE;
|
||||
import static com.android.server.uri.UriGrantsMockContext.PKG_CAMERA;
|
||||
import static com.android.server.uri.UriGrantsMockContext.PKG_SOCIAL;
|
||||
import static com.android.server.uri.UriGrantsMockContext.UID_PRIMARY_SOCIAL;
|
||||
import static com.android.server.uri.UriGrantsMockContext.URI_PHOTO_1;
|
||||
import static com.android.server.uri.UriGrantsMockContext.URI_PHOTO_2;
|
||||
import static com.android.server.uri.UriGrantsMockContext.USER_PRIMARY;
|
||||
import static com.android.server.uri.UriPermission.INVALID_TIME;
|
||||
import static com.android.server.uri.UriPermission.STRENGTH_GLOBAL;
|
||||
import static com.android.server.uri.UriPermission.STRENGTH_NONE;
|
||||
import static com.android.server.uri.UriPermission.STRENGTH_OWNED;
|
||||
import static com.android.server.uri.UriPermission.STRENGTH_PERSISTABLE;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
public class UriPermissionTest {
|
||||
@Mock
|
||||
private UriGrantsManagerInternal mService;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNone() {
|
||||
final GrantUri grant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
|
||||
final UriPermission perm = new UriPermission(PKG_CAMERA,
|
||||
PKG_SOCIAL, UID_PRIMARY_SOCIAL, grant);
|
||||
assertEquals(STRENGTH_NONE, perm.getStrength(FLAG_READ));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlobal() {
|
||||
final GrantUri grant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
|
||||
final UriPermission perm = new UriPermission(PKG_CAMERA,
|
||||
PKG_SOCIAL, UID_PRIMARY_SOCIAL, grant);
|
||||
|
||||
assertFalse(perm.grantModes(FLAG_READ, null));
|
||||
assertEquals(STRENGTH_GLOBAL, perm.getStrength(FLAG_READ));
|
||||
|
||||
assertFalse(perm.revokeModes(FLAG_READ, true));
|
||||
assertEquals(STRENGTH_NONE, perm.getStrength(FLAG_READ));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOwned() {
|
||||
final GrantUri grant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
|
||||
final UriPermission perm = new UriPermission(PKG_CAMERA,
|
||||
PKG_SOCIAL, UID_PRIMARY_SOCIAL, grant);
|
||||
final UriPermissionOwner owner = new UriPermissionOwner(mService, "test");
|
||||
|
||||
assertFalse(perm.grantModes(FLAG_READ, owner));
|
||||
assertEquals(STRENGTH_OWNED, perm.getStrength(FLAG_READ));
|
||||
|
||||
assertFalse(perm.revokeModes(FLAG_READ, false));
|
||||
assertEquals(STRENGTH_OWNED, perm.getStrength(FLAG_READ));
|
||||
|
||||
assertFalse(perm.revokeModes(FLAG_READ, true));
|
||||
assertEquals(STRENGTH_NONE, perm.getStrength(FLAG_READ));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlap() {
|
||||
final GrantUri grant1 = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
|
||||
final GrantUri grant2 = new GrantUri(USER_PRIMARY, URI_PHOTO_2, FLAG_READ);
|
||||
|
||||
final UriPermission photo1 = new UriPermission(PKG_CAMERA,
|
||||
PKG_SOCIAL, UID_PRIMARY_SOCIAL, grant1);
|
||||
final UriPermission photo2 = new UriPermission(PKG_CAMERA,
|
||||
PKG_SOCIAL, UID_PRIMARY_SOCIAL, grant2);
|
||||
|
||||
// Verify behavior when we have multiple owners within the same app
|
||||
final UriPermissionOwner activity = new UriPermissionOwner(mService, "activity");
|
||||
final UriPermissionOwner service = new UriPermissionOwner(mService, "service");
|
||||
|
||||
photo1.grantModes(FLAG_READ | FLAG_WRITE, activity);
|
||||
photo1.grantModes(FLAG_READ, service);
|
||||
photo2.grantModes(FLAG_READ, activity);
|
||||
photo2.grantModes(FLAG_WRITE, null);
|
||||
|
||||
assertEquals(FLAG_READ | FLAG_WRITE, photo1.modeFlags);
|
||||
assertEquals(FLAG_READ | FLAG_WRITE, photo2.modeFlags);
|
||||
|
||||
// Shutting down activity should only trim away write access
|
||||
activity.removeUriPermissions();
|
||||
assertEquals(FLAG_READ, photo1.modeFlags);
|
||||
assertEquals(FLAG_WRITE, photo2.modeFlags);
|
||||
|
||||
// Shutting down service should bring everything else down
|
||||
service.removeUriPermissions();
|
||||
assertEquals(0, photo1.modeFlags);
|
||||
assertEquals(FLAG_WRITE, photo2.modeFlags);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersist() {
|
||||
final GrantUri grant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
|
||||
final UriPermission perm = new UriPermission(PKG_CAMERA,
|
||||
PKG_SOCIAL, UID_PRIMARY_SOCIAL, grant);
|
||||
|
||||
assertFalse(perm.grantModes(FLAG_READ, null));
|
||||
assertFalse(perm.grantModes(FLAG_WRITE | FLAG_PERSISTABLE, null));
|
||||
assertEquals(STRENGTH_GLOBAL, perm.getStrength(FLAG_READ));
|
||||
assertEquals(STRENGTH_PERSISTABLE, perm.getStrength(FLAG_WRITE));
|
||||
|
||||
// Verify behavior of non-persistable mode; nothing happens
|
||||
{
|
||||
assertFalse(perm.takePersistableModes(FLAG_READ));
|
||||
assertEquals(0, perm.persistedModeFlags);
|
||||
|
||||
assertFalse(perm.releasePersistableModes(FLAG_READ));
|
||||
assertEquals(0, perm.persistedModeFlags);
|
||||
}
|
||||
|
||||
// Verify behavior of persistable mode
|
||||
{
|
||||
assertEquals(FLAG_WRITE, perm.persistableModeFlags);
|
||||
assertEquals(0, perm.persistedModeFlags);
|
||||
assertTrue(perm.takePersistableModes(FLAG_WRITE));
|
||||
assertEquals(FLAG_WRITE, perm.persistableModeFlags);
|
||||
assertEquals(FLAG_WRITE, perm.persistedModeFlags);
|
||||
|
||||
// Attempting to take a second time should be a no-op
|
||||
final long createTime = perm.persistedCreateTime;
|
||||
assertFalse(perm.takePersistableModes(FLAG_WRITE));
|
||||
assertEquals(createTime, perm.persistedCreateTime);
|
||||
|
||||
assertTrue(perm.releasePersistableModes(FLAG_WRITE));
|
||||
assertEquals(FLAG_WRITE, perm.persistableModeFlags);
|
||||
assertEquals(0, perm.persistedModeFlags);
|
||||
assertEquals(INVALID_TIME, perm.persistedCreateTime);
|
||||
|
||||
// Attempting to release a second time should be a no-op
|
||||
assertFalse(perm.releasePersistableModes(FLAG_WRITE));
|
||||
|
||||
// We should still be able to take again
|
||||
assertTrue(perm.takePersistableModes(FLAG_WRITE));
|
||||
assertEquals(FLAG_WRITE, perm.persistableModeFlags);
|
||||
assertEquals(FLAG_WRITE, perm.persistedModeFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user