summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/am/UriPermission.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java/com/android/server/am/UriPermission.java')
-rw-r--r--services/core/java/com/android/server/am/UriPermission.java357
1 files changed, 357 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
new file mode 100644
index 0000000..1f12b74
--- /dev/null
+++ b/services/core/java/com/android/server/am/UriPermission.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2006 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.am;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.google.android.collect.Sets;
+
+import java.io.PrintWriter;
+import java.util.Comparator;
+import java.util.HashSet;
+
+/**
+ * Description of a permission granted to an app to access a particular URI.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+ * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ */
+final class UriPermission {
+ private static final String TAG = "UriPermission";
+
+ public static final int STRENGTH_NONE = 0;
+ public static final int STRENGTH_OWNED = 1;
+ public static final int STRENGTH_GLOBAL = 2;
+ public static final int STRENGTH_PERSISTABLE = 3;
+
+ final int userHandle;
+ final String sourcePkg;
+ final String targetPkg;
+
+ /** Cached UID of {@link #targetPkg}; should not be persisted */
+ final int targetUid;
+
+ final Uri uri;
+
+ /**
+ * Allowed modes. All permission enforcement should use this field. Must
+ * always be a combination of {@link #ownedModeFlags},
+ * {@link #globalModeFlags}, {@link #persistableModeFlags}, and
+ * {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by
+ * the owning class.
+ */
+ int modeFlags = 0;
+
+ /** Allowed modes with explicit owner. */
+ int ownedModeFlags = 0;
+ /** Allowed modes without explicit owner. */
+ int globalModeFlags = 0;
+ /** Allowed modes that have been offered for possible persisting. */
+ int persistableModeFlags = 0;
+ /** Allowed modes that should be persisted across device boots. */
+ int persistedModeFlags = 0;
+
+ /**
+ * Timestamp when {@link #persistedModeFlags} was first defined in
+ * {@link System#currentTimeMillis()} time base.
+ */
+ long persistedCreateTime = INVALID_TIME;
+
+ private static final long INVALID_TIME = Long.MIN_VALUE;
+
+ private HashSet<UriPermissionOwner> mReadOwners;
+ private HashSet<UriPermissionOwner> mWriteOwners;
+
+ private String stringName;
+
+ UriPermission(String sourcePkg, String targetPkg, int targetUid, Uri uri) {
+ this.userHandle = UserHandle.getUserId(targetUid);
+ this.sourcePkg = sourcePkg;
+ this.targetPkg = targetPkg;
+ this.targetUid = targetUid;
+ this.uri = uri;
+ }
+
+ private void updateModeFlags() {
+ modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
+ }
+
+ /**
+ * Initialize persisted modes as read from file. This doesn't issue any
+ * global or owner grants.
+ */
+ void initPersistedModes(int modeFlags, long createdTime) {
+ persistableModeFlags = modeFlags;
+ persistedModeFlags = modeFlags;
+ persistedCreateTime = createdTime;
+
+ updateModeFlags();
+ }
+
+ void grantModes(int modeFlags, boolean persistable, UriPermissionOwner owner) {
+ if (persistable) {
+ persistableModeFlags |= modeFlags;
+ }
+
+ if (owner == null) {
+ globalModeFlags |= modeFlags;
+ } else {
+ if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ addReadOwner(owner);
+ }
+ if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ addWriteOwner(owner);
+ }
+ }
+
+ updateModeFlags();
+ }
+
+ /**
+ * @return if mode changes should trigger persisting.
+ */
+ boolean takePersistableModes(int modeFlags) {
+ if ((modeFlags & persistableModeFlags) != modeFlags) {
+ throw new SecurityException("Requested flags 0x"
+ + Integer.toHexString(modeFlags) + ", but only 0x"
+ + Integer.toHexString(persistableModeFlags) + " are allowed");
+ }
+
+ final int before = persistedModeFlags;
+ persistedModeFlags |= (persistableModeFlags & modeFlags);
+
+ if (persistedModeFlags != 0) {
+ persistedCreateTime = System.currentTimeMillis();
+ }
+
+ updateModeFlags();
+ return persistedModeFlags != before;
+ }
+
+ boolean releasePersistableModes(int modeFlags) {
+ final int before = persistedModeFlags;
+
+ persistableModeFlags &= ~modeFlags;
+ persistedModeFlags &= ~modeFlags;
+
+ if (persistedModeFlags == 0) {
+ persistedCreateTime = INVALID_TIME;
+ }
+
+ updateModeFlags();
+ return persistedModeFlags != before;
+ }
+
+ /**
+ * @return if mode changes should trigger persisting.
+ */
+ boolean clearModes(int modeFlags, boolean persistable) {
+ final int before = persistedModeFlags;
+
+ if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (persistable) {
+ persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ }
+ globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ if (mReadOwners != null) {
+ ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ for (UriPermissionOwner r : mReadOwners) {
+ r.removeReadPermission(this);
+ }
+ mReadOwners = null;
+ }
+ }
+ if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (persistable) {
+ persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ }
+ globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ if (mWriteOwners != null) {
+ ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ for (UriPermissionOwner r : mWriteOwners) {
+ r.removeWritePermission(this);
+ }
+ mWriteOwners = null;
+ }
+ }
+
+ if (persistedModeFlags == 0) {
+ persistedCreateTime = INVALID_TIME;
+ }
+
+ updateModeFlags();
+ return persistedModeFlags != before;
+ }
+
+ /**
+ * Return strength of this permission grant for the given flags.
+ */
+ public int getStrength(int modeFlags) {
+ if ((persistableModeFlags & modeFlags) == modeFlags) {
+ return STRENGTH_PERSISTABLE;
+ } else if ((globalModeFlags & modeFlags) == modeFlags) {
+ return STRENGTH_GLOBAL;
+ } else if ((ownedModeFlags & modeFlags) == modeFlags) {
+ return STRENGTH_OWNED;
+ } else {
+ return STRENGTH_NONE;
+ }
+ }
+
+ private void addReadOwner(UriPermissionOwner owner) {
+ if (mReadOwners == null) {
+ mReadOwners = Sets.newHashSet();
+ ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ updateModeFlags();
+ }
+ if (mReadOwners.add(owner)) {
+ owner.addReadPermission(this);
+ }
+ }
+
+ /**
+ * Remove given read owner, updating {@Link #modeFlags} as needed.
+ */
+ void removeReadOwner(UriPermissionOwner owner) {
+ if (!mReadOwners.remove(owner)) {
+ Log.wtf(TAG, "Unknown read owner " + owner + " in " + this);
+ }
+ if (mReadOwners.size() == 0) {
+ mReadOwners = null;
+ ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ updateModeFlags();
+ }
+ }
+
+ private void addWriteOwner(UriPermissionOwner owner) {
+ if (mWriteOwners == null) {
+ mWriteOwners = Sets.newHashSet();
+ ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ updateModeFlags();
+ }
+ if (mWriteOwners.add(owner)) {
+ owner.addWritePermission(this);
+ }
+ }
+
+ /**
+ * Remove given write owner, updating {@Link #modeFlags} as needed.
+ */
+ void removeWriteOwner(UriPermissionOwner owner) {
+ if (!mWriteOwners.remove(owner)) {
+ Log.wtf(TAG, "Unknown write owner " + owner + " in " + this);
+ }
+ if (mWriteOwners.size() == 0) {
+ mWriteOwners = null;
+ ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ updateModeFlags();
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (stringName != null) {
+ return stringName;
+ }
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("UriPermission{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ sb.append(uri);
+ sb.append('}');
+ return stringName = sb.toString();
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print("userHandle=" + userHandle);
+ pw.print(" sourcePkg=" + sourcePkg);
+ pw.println(" targetPkg=" + targetPkg);
+
+ pw.print(prefix);
+ pw.print("mode=0x" + Integer.toHexString(modeFlags));
+ pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
+ pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
+ pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
+ pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
+ if (persistedCreateTime != INVALID_TIME) {
+ pw.print(" persistedCreate=" + persistedCreateTime);
+ }
+ pw.println();
+
+ if (mReadOwners != null) {
+ pw.print(prefix);
+ pw.println("readOwners:");
+ for (UriPermissionOwner owner : mReadOwners) {
+ pw.print(prefix);
+ pw.println(" * " + owner);
+ }
+ }
+ if (mWriteOwners != null) {
+ pw.print(prefix);
+ pw.println("writeOwners:");
+ for (UriPermissionOwner owner : mReadOwners) {
+ pw.print(prefix);
+ pw.println(" * " + owner);
+ }
+ }
+ }
+
+ public static class PersistedTimeComparator implements Comparator<UriPermission> {
+ @Override
+ public int compare(UriPermission lhs, UriPermission rhs) {
+ return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
+ }
+ }
+
+ /**
+ * Snapshot of {@link UriPermission} with frozen
+ * {@link UriPermission#persistedModeFlags} state.
+ */
+ public static class Snapshot {
+ final int userHandle;
+ final String sourcePkg;
+ final String targetPkg;
+ final Uri uri;
+ final int persistedModeFlags;
+ final long persistedCreateTime;
+
+ private Snapshot(UriPermission perm) {
+ this.userHandle = perm.userHandle;
+ this.sourcePkg = perm.sourcePkg;
+ this.targetPkg = perm.targetPkg;
+ this.uri = perm.uri;
+ this.persistedModeFlags = perm.persistedModeFlags;
+ this.persistedCreateTime = perm.persistedCreateTime;
+ }
+ }
+
+ public Snapshot snapshot() {
+ return new Snapshot(this);
+ }
+
+ public android.content.UriPermission buildPersistedPublicApiObject() {
+ return new android.content.UriPermission(uri, persistedModeFlags, persistedCreateTime);
+ }
+}