diff options
| author | Philip P. Moltmann <moltmann@google.com> | 2019-10-18 02:26:08 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-10-18 02:26:08 +0000 |
| commit | c9bb90c0e0d2ea0b285547a140f323e136edc04d (patch) | |
| tree | 319b928a79cd50ca4f98506802afec8e799cc99c /core/java/android | |
| parent | 291a46e4f4a72c3cb07c9f2c8539ea1a5bc6228c (diff) | |
| parent | 59076d89573ff4a155032b70d13dcfee12fd5f2b (diff) | |
Merge "Add feature context"
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/app/AppOpsManager.java | 1108 | ||||
| -rw-r--r-- | core/java/android/app/AppOpsManagerInternal.java | 11 | ||||
| -rw-r--r-- | core/java/android/app/AsyncNotedAppOp.java | 27 | ||||
| -rw-r--r-- | core/java/android/app/ContextImpl.java | 67 | ||||
| -rw-r--r-- | core/java/android/app/SyncNotedAppOp.java | 13 | ||||
| -rw-r--r-- | core/java/android/content/Context.java | 25 | ||||
| -rw-r--r-- | core/java/android/content/ContextWrapper.java | 7 | ||||
| -rw-r--r-- | core/java/android/content/PermissionChecker.java | 5 | ||||
| -rw-r--r-- | core/java/android/util/LongSparseArray.java | 93 | ||||
| -rw-r--r-- | core/java/android/util/LongSparseLongArray.java | 47 |
10 files changed, 1152 insertions, 251 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 1649e8be8b3a..d5e41f0eacbe 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -50,6 +50,7 @@ import android.os.UserManager; import android.util.ArrayMap; import android.util.LongSparseArray; import android.util.LongSparseLongArray; +import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -77,10 +78,12 @@ import java.util.BitSet; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.function.ToLongFunction; /** * API for interacting with "application operation" tracking. @@ -2052,7 +2055,7 @@ public class AppOpsManager { * * @see #markAppOpNoted */ - private static final ThreadLocal<long[]> sAppOpsNotedInThisBinderTransaction = + private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction = new ThreadLocal<>(); /** Whether noting for an appop should be collected */ @@ -2329,51 +2332,42 @@ public class AppOpsManager { } /** - * Class holding the information about one unique operation of an application. + * Class holding the information about one unique operation of a + * {@link Context#createFeatureContext(String) feature}. + * * @hide */ @TestApi @Immutable @SystemApi - public static final class OpEntry implements Parcelable { - private final int mOp; + public static final class OpFeatureEntry { + private final @NonNull OpEntry mParent; private final boolean mRunning; - private final @Mode int mMode; + private final @Nullable LongSparseLongArray mAccessTimes; private final @Nullable LongSparseLongArray mRejectTimes; private final @Nullable LongSparseLongArray mDurations; private final @Nullable LongSparseLongArray mProxyUids; private final @Nullable LongSparseArray<String> mProxyPackageNames; + private final @Nullable LongSparseArray<String> mProxyFeatureIds; /** * @hide */ - public OpEntry(int op, boolean running, @Mode int mode, - @Nullable LongSparseLongArray accessTimes, @Nullable LongSparseLongArray rejectTimes, + public OpFeatureEntry(@NonNull OpEntry parent, boolean running, + @Nullable LongSparseLongArray accessTimes, + @Nullable LongSparseLongArray rejectTimes, @Nullable LongSparseLongArray durations, @Nullable LongSparseLongArray proxyUids, - @Nullable LongSparseArray<String> proxyPackageNames) { - mOp = op; + @Nullable LongSparseArray<String> proxyPackageNames, + @Nullable LongSparseArray<String> proxyFeatureIds) { + mParent = Preconditions.checkNotNull(parent); mRunning = running; - mMode = mode; mAccessTimes = accessTimes; mRejectTimes = rejectTimes; mDurations = durations; mProxyUids = proxyUids; mProxyPackageNames = proxyPackageNames; - } - - /** - * @hide - */ - public OpEntry(int op, @Mode int mode) { - mOp = op; - mMode = mode; - mRunning = false; - mAccessTimes = null; - mRejectTimes = null; - mDurations = null; - mProxyUids = null; - mProxyPackageNames = null; + mProxyFeatureIds = proxyFeatureIds; } /** @@ -2390,29 +2384,6 @@ public class AppOpsManager { /** * @hide */ - @UnsupportedAppUsage - public int getOp() { - return mOp; - } - - /** - * @return This entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}. - */ - public @NonNull String getOpStr() { - return sOpToString[mOp]; - } - - /** - * @return this entry's current mode, such as {@link #MODE_ALLOWED}. - */ - public @Mode int getMode() { - return mMode; - } - - /** - * @hide - */ - @UnsupportedAppUsage public long getTime() { return getLastAccessTime(OP_FLAGS_ALL); } @@ -2455,7 +2426,7 @@ public class AppOpsManager { */ public long getLastAccessForegroundTime(@OpFlags int flags) { return maxForFlagsInStates(mAccessTimes, MAX_PRIORITY_UID_STATE, - resolveFirstUnrestrictedUidState(mOp), flags); + resolveFirstUnrestrictedUidState(mParent.mOp), flags); } /** @@ -2475,7 +2446,7 @@ public class AppOpsManager { * @see #getLastAccessTime(int, int, int) */ public long getLastAccessBackgroundTime(@OpFlags int flags) { - return maxForFlagsInStates(mAccessTimes, resolveLastRestrictedUidState(mOp), + return maxForFlagsInStates(mAccessTimes, resolveLastRestrictedUidState(mParent.mOp), MIN_PRIORITY_UID_STATE, flags); } @@ -2509,7 +2480,6 @@ public class AppOpsManager { /** * @hide */ - @UnsupportedAppUsage public long getRejectTime() { return getLastRejectTime(OP_FLAGS_ALL); } @@ -2553,7 +2523,7 @@ public class AppOpsManager { */ public long getLastRejectForegroundTime(@OpFlags int flags) { return maxForFlagsInStates(mRejectTimes, MAX_PRIORITY_UID_STATE, - resolveFirstUnrestrictedUidState(mOp), flags); + resolveFirstUnrestrictedUidState(mParent.mOp), flags); } /** @@ -2573,7 +2543,7 @@ public class AppOpsManager { * @see #getLastRejectTime(int) */ public long getLastRejectBackgroundTime(@OpFlags int flags) { - return maxForFlagsInStates(mRejectTimes, resolveLastRestrictedUidState(mOp), + return maxForFlagsInStates(mRejectTimes, resolveLastRestrictedUidState(mParent.mOp), MIN_PRIORITY_UID_STATE, flags); } @@ -2633,7 +2603,7 @@ public class AppOpsManager { */ public long getLastForegroundDuration(@OpFlags int flags) { return sumForFlagsInStates(mDurations, MAX_PRIORITY_UID_STATE, - resolveFirstUnrestrictedUidState(mOp), flags); + resolveFirstUnrestrictedUidState(mParent.mOp), flags); } /** @@ -2651,7 +2621,7 @@ public class AppOpsManager { * @see #getLastDuration(int, int, int) */ public long getLastBackgroundDuration(@OpFlags int flags) { - return sumForFlagsInStates(mDurations, resolveLastRestrictedUidState(mOp), + return sumForFlagsInStates(mDurations, resolveLastRestrictedUidState(mParent.mOp), MIN_PRIORITY_UID_STATE, flags); } @@ -2736,12 +2706,663 @@ public class AppOpsManager { * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} * for any flag. - * @return The proxy package name. + * @return The feature id. */ public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) { return findFirstNonNullForFlagsInStates(mProxyPackageNames, uidState, uidState, flags); } + /** + * Gets the feature of the app that performed the op on behalf of this + * app and as a result blamed the op on this app or {@code null} + * if there is no proxy. + * + * @return The proxy package name. + */ + public @Nullable String getProxyFeatureId() { + return findFirstNonNullForFlagsInStates(mProxyFeatureIds, MAX_PRIORITY_UID_STATE, + MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL); + } + + /** + * Gets the feature of the app that performed the op on behalf of this + * app and as a result blamed the op on this app for a UID state or + * {@code null} if there is no proxy. + * + * @param uidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return The feature id. + */ + public @Nullable String getProxyFeatureId(@UidState int uidState, @OpFlags int flags) { + return findFirstNonNullForFlagsInStates(mProxyFeatureIds, uidState, uidState, flags); + } + + /** + * @hide + */ + public static class Builder { + private final boolean mRunning; + + private final @Nullable LongSparseLongArray mAccessTimes; + private final @Nullable LongSparseLongArray mRejectTimes; + private final @Nullable LongSparseLongArray mDurations; + private final @Nullable LongSparseLongArray mProxyUids; + private final @Nullable LongSparseArray<String> mProxyPackageNames; + private final @Nullable LongSparseArray<String> mProxyFeatureIds; + private @NonNull OpEntry mParent; + + public Builder(boolean running, @Nullable LongSparseLongArray accessTimes, + @Nullable LongSparseLongArray rejectTimes, + @Nullable LongSparseLongArray durations, + @Nullable LongSparseLongArray proxyUids, + @Nullable LongSparseArray<String> proxyPackageNames, + @Nullable LongSparseArray<String> proxyFeatureIds) { + mRunning = running; + mAccessTimes = accessTimes; + mRejectTimes = rejectTimes; + mDurations = durations; + mProxyUids = proxyUids; + mProxyPackageNames = proxyPackageNames; + mProxyFeatureIds = proxyFeatureIds; + } + + public Builder setParent(@NonNull OpEntry parent) { + mParent = parent; + + return this; + } + + /** + * Create OpFeatureEntry from builder + */ + public OpFeatureEntry build() { + Preconditions.checkNotNull(mParent); + + return new OpFeatureEntry(mParent, mRunning, mAccessTimes, mRejectTimes, + mDurations, mProxyUids, mProxyPackageNames, mProxyFeatureIds); + } + } + + /** + * @hide + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + LongSparseLongArray.Parcelling longSparseLongArrayParcelling = + LongSparseLongArray.Parcelling.Cache.getOrCreate( + LongSparseLongArray.Parcelling.class); + LongSparseArray.StringParcelling longSparseStringArrayParcelling = + LongSparseArray.StringParcelling.Cache.getOrCreate( + LongSparseArray.StringParcelling.class); + + dest.writeBoolean(mRunning); + longSparseLongArrayParcelling.parcel(mAccessTimes, dest, flags); + longSparseLongArrayParcelling.parcel(mRejectTimes, dest, flags); + longSparseLongArrayParcelling.parcel(mDurations, dest, flags); + longSparseLongArrayParcelling.parcel(mProxyUids, dest, flags); + longSparseStringArrayParcelling.parcel(mProxyPackageNames, dest, flags); + longSparseStringArrayParcelling.parcel(mProxyFeatureIds, dest, flags); + } + + /** + * @hide + */ + public static OpFeatureEntry.Builder createFromParcel(@NonNull Parcel source) { + LongSparseLongArray.Parcelling longSparseLongArrayParcelling = + LongSparseLongArray.Parcelling.Cache.getOrCreate( + LongSparseLongArray.Parcelling.class); + LongSparseArray.StringParcelling longSparseStringArrayParcelling = + LongSparseArray.StringParcelling.Cache.getOrCreate( + LongSparseArray.StringParcelling.class); + + return new OpFeatureEntry.Builder(source.readBoolean(), + (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source), + (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source), + (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source), + (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source), + (LongSparseArray<String>) longSparseStringArrayParcelling.unparcel(source), + (LongSparseArray<String>) longSparseStringArrayParcelling.unparcel(source)); + } + } + + /** + * Class holding the information about one unique operation of an application. + * @hide + */ + @TestApi + @Immutable + @SystemApi + public static final class OpEntry implements Parcelable { + private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp; + private final @Mode int mMode; + private final @NonNull ArrayMap<String, OpFeatureEntry> mFeatures; + + /** + * @hide + */ + public OpEntry(@IntRange(from = 0, to = _NUM_OP - 1) int op, @Mode int mode, + @NonNull Pair<String, OpFeatureEntry.Builder>[] featureBuilders) { + mOp = Preconditions.checkArgumentInRange(op, 0, _NUM_OP - 1, "op"); + mMode = Preconditions.checkArgumentInRange(mode, 0, MODE_FOREGROUND, "mode"); + + mFeatures = new ArrayMap<>(featureBuilders.length); + for (Pair<String, OpFeatureEntry.Builder> feature : featureBuilders) { + mFeatures.put(feature.first, feature.second.setParent(this).build()); + } + } + + /** + * @return The mapping from the feature ids to the feature state + */ + public @NonNull Map<String, OpFeatureEntry> getFeatures() { + return mFeatures; + } + + /** + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code " + + "#getOpStr()}") + public int getOp() { + return mOp; + } + + /** + * @return This entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}. + */ + public @NonNull String getOpStr() { + return sOpToString[mOp]; + } + + /** + * @return this entry's current mode, such as {@link #MODE_ALLOWED}. + */ + public @Mode int getMode() { + return mMode; + } + + /** + * @deprecated Use {@link OpEntry#getLastAccessTime(int)} instead + * + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code " + + "#getLastAccessTime(int)}") + public long getTime() { + return getLastAccessTime(OP_FLAGS_ALL); + } + + private long getMaxOfFeatures(@NonNull ToLongFunction<OpFeatureEntry> timeGetter) { + long max = 0; + + int numFeatures = mFeatures.size(); + for (int i = 0; i < numFeatures; i++) { + max = Math.max(max, timeGetter.applyAsLong(mFeatures.valueAt(i))); + } + + return max; + } + + private long getSumOfFeatures(@NonNull ToLongFunction<OpFeatureEntry> getter) { + long sum = 0; + + int numFeatures = mFeatures.size(); + for (int i = 0; i < numFeatures; i++) { + sum += getter.applyAsLong(mFeatures.valueAt(i)); + } + + return sum; + } + + /** + * Return the last wall clock time in milliseconds this op was accessed + * by the app for a given range of UID states. + * + * @param fromUidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state for which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastAccessForegroundTime(int) + * @see #getLastAccessBackgroundTime(int) + * @see #getLastAccessTime(int) + */ + public long getLastAccessTime(@OpFlags int flags) { + return getMaxOfFeatures( + (featureEntry -> featureEntry.getLastAccessTime(flags))); + } + + /** + * Return the last wall clock time in milliseconds this op was accessed + * by the app while in the foreground. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastAccessBackgroundTime(int) + * @see #getLastAccessTime(int) + * @see #getLastAccessTime(int, int, int) + */ + public long getLastAccessForegroundTime(@OpFlags int flags) { + return getMaxOfFeatures( + (featureEntry -> featureEntry.getLastAccessForegroundTime(flags))); + } + + /** + * Return the last wall clock time in milliseconds this op was accessed + * by the app while in the background. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastAccessForegroundTime(int) + * @see #getLastAccessTime(int) + * @see #getLastAccessTime(int, int, int) + */ + public long getLastAccessBackgroundTime(@OpFlags int flags) { + return getMaxOfFeatures( + (featureEntry -> featureEntry.getLastAccessBackgroundTime(flags))); + } + + /** + * Return the last wall clock time in milliseconds this op was accessed + * by the app for a given range of UID states. + * + * @param fromUidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state for which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastAccessForegroundTime(int) + * @see #getLastAccessBackgroundTime(int) + * @see #getLastAccessTime(int) + */ + public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return getMaxOfFeatures( + (featureEntry -> featureEntry.getLastAccessTime(fromUidState, + toUidState, flags))); + } + + /** + * @deprecated Use {@link OpEntry#getLastRejectTime(int)} instead + * + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code " + + "#getLastRejectTime(int)}") + public long getRejectTime() { + return getLastRejectTime(OP_FLAGS_ALL); + } + + /** + * Return the last wall clock time in milliseconds the app made an attempt + * to access this op but was rejected. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last reject time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastRejectBackgroundTime(int) + * @see #getLastRejectForegroundTime(int) + * @see #getLastRejectTime(int, int, int) + */ + public long getLastRejectTime(@OpFlags int flags) { + return getMaxOfFeatures( + (featureEntry -> featureEntry.getLastRejectTime(flags))); + } + + /** + * Return the last wall clock time in milliseconds the app made an attempt + * to access this op while in the foreground but was rejected. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last foreground reject time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastRejectBackgroundTime(int) + * @see #getLastRejectTime(int, int, int) + * @see #getLastRejectTime(int) + */ + public long getLastRejectForegroundTime(@OpFlags int flags) { + return getMaxOfFeatures( + (featureEntry -> featureEntry.getLastRejectForegroundTime(flags))); + } + + /** + * Return the last wall clock time in milliseconds the app made an attempt + * to access this op while in the background but was rejected. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last background reject time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastRejectForegroundTime(int) + * @see #getLastRejectTime(int, int, int) + * @see #getLastRejectTime(int) + */ + public long getLastRejectBackgroundTime(@OpFlags int flags) { + return getMaxOfFeatures( + (featureEntry -> featureEntry.getLastRejectBackgroundTime(flags))); + } + + /** + * Return the last wall clock time state in milliseconds the app made an + * attempt to access this op for a given range of UID states. + * + * @param fromUidState The UID state from which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state to which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastRejectForegroundTime(int) + * @see #getLastRejectBackgroundTime(int) + * @see #getLastRejectTime(int) + */ + public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return getMaxOfFeatures( + (featureEntry -> featureEntry.getLastRejectTime(fromUidState, + toUidState, flags))); + } + + /** + * @return Whether the operation is running. + */ + public boolean isRunning() { + int numFeatures = mFeatures.size(); + if (mFeatures.isEmpty()) { + return false; + } + + for (int i = 0; i < numFeatures; i++) { + if (mFeatures.valueAt(i).mRunning) { + return true; + } + } + + return false; + } + + /** + * @return The duration of the operation in milliseconds. The duration is in wall time. + */ + public long getDuration() { + return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL); + } + + /** + * Return the duration in milliseconds the app accessed this op while + * in the foreground. The duration is in wall time. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the foreground access duration in milliseconds. + * + * @see #getLastBackgroundDuration(int) + * @see #getLastDuration(int, int, int) + */ + public long getLastForegroundDuration(@OpFlags int flags) { + return getSumOfFeatures((featureEntry) -> + featureEntry.getLastForegroundDuration(flags)); + } + + /** + * Return the duration in milliseconds the app accessed this op while + * in the background. The duration is in wall time. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the background access duration in milliseconds. + * + * @see #getLastForegroundDuration(int) + * @see #getLastDuration(int, int, int) + */ + public long getLastBackgroundDuration(@OpFlags int flags) { + return getSumOfFeatures((featureEntry) -> + featureEntry.getLastBackgroundDuration(flags)); + } + + /** + * Return the duration in milliseconds the app accessed this op for + * a given range of UID states. The duration is in wall time. + * + * @param fromUidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state for which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the access duration in milliseconds. + */ + public long getLastDuration(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return getSumOfFeatures((featureEntry) -> + featureEntry.getLastDuration(fromUidState, toUidState, flags)); + } + + /** + * Like {@link #findFirstNonNegativeForFlagsInStates(LongSparseLongArray, int, int, int)} + * but for all proxy uid in all features. + */ + private long findFirstNonNegativeProxyUidInFeatureStates(@UidState int beginUidState, + @UidState int endUidState, @OpFlags int flags) { + int numFeatures = mFeatures.size(); + + if (numFeatures == 0) { + return -1; + } + + while (flags != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + for (int uidState : UID_STATES) { + if (uidState < beginUidState || uidState > endUidState) { + continue; + } + + final long key = makeKey(uidState, flag); + + for (int i = 0; i < numFeatures; i++) { + OpFeatureEntry featureEntry = mFeatures.valueAt(i); + + if (featureEntry.mProxyUids == null) { + continue; + } + + final long proxyUid = featureEntry.mProxyUids.get(key); + if (proxyUid >= 0) { + return proxyUid; + } + } + } + } + + return -1; + } + + /** + * Like {@link #findFirstNonNullForFlagsInStates(LongSparseArray, int, int, int)} but + * for all proxyPackageNames in all features. + */ + private @Nullable String findFirstNonNullProxyPackageNameInFeatureStates( + @OpFlags int flags, @UidState int beginUidState, @UidState int endUidState) { + int numFeatures = mFeatures.size(); + + if (numFeatures == 0) { + return null; + } + + while (flags != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + for (int uidState : UID_STATES) { + if (uidState < beginUidState || uidState > endUidState) { + continue; + } + final long key = makeKey(uidState, flag); + + for (int i = 0; i < numFeatures; i++) { + OpFeatureEntry featureEntry = mFeatures.valueAt(i); + + if (featureEntry.mProxyPackageNames == null) { + continue; + } + + final String proxyName = featureEntry.mProxyPackageNames.get(key); + if (proxyName != null) { + return proxyName; + } + } + } + } + return null; + } + + /** + * @deprecated Use {@link #getProxyUid(int, int)} instead + */ + @Deprecated + public int getProxyUid() { + return (int) findFirstNonNegativeProxyUidInFeatureStates(MAX_PRIORITY_UID_STATE, + MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL); + } + + /** + * Gets the UID of the app that performed the op on behalf of this app and + * as a result blamed the op on this app or {@link Process#INVALID_UID} if + * there is no proxy. + * + * @param uidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * + * @return The proxy UID. + */ + public int getProxyUid(@UidState int uidState, @OpFlags int flags) { + return (int) findFirstNonNegativeProxyUidInFeatureStates(uidState, uidState, flags); + } + + /** + * @deprecated Use {@link #getProxyPackageName(int, int)} instead + */ + @Deprecated + public @Nullable String getProxyPackageName() { + return findFirstNonNullProxyPackageNameInFeatureStates(MAX_PRIORITY_UID_STATE, + MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL); + } + + /** + * Gets the package name of the app that performed the op on behalf of this + * app and as a result blamed the op on this app for a UID state or + * {@code null} if there is no proxy. + * + * @param uidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return The proxy package name. + */ + public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) { + return findFirstNonNullProxyPackageNameInFeatureStates(uidState, uidState, flags); + } + + /** + * Create OpEntry from parcel. + * + * @hide + */ + public static OpEntry createFromParcel(@NonNull Parcel source) { + int op = source.readInt(); + int mode = source.readInt(); + + int numFeatures = source.readInt(); + Pair<String, OpFeatureEntry.Builder>[] features = new Pair[numFeatures]; + for (int i = 0; i < numFeatures; i++) { + features[i] = new Pair<>(source.readString(), + OpFeatureEntry.createFromParcel(source)); + } + + return new OpEntry(op, mode, features); + } + @Override public int describeContents() { return 0; @@ -2751,31 +3372,23 @@ public class AppOpsManager { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mOp); dest.writeInt(mMode); - dest.writeBoolean(mRunning); - writeLongSparseLongArrayToParcel(mAccessTimes, dest); - writeLongSparseLongArrayToParcel(mRejectTimes, dest); - writeLongSparseLongArrayToParcel(mDurations, dest); - writeLongSparseLongArrayToParcel(mProxyUids, dest); - writeLongSparseStringArrayToParcel(mProxyPackageNames, dest); - } - OpEntry(Parcel source) { - mOp = source.readInt(); - mMode = source.readInt(); - mRunning = source.readBoolean(); - mAccessTimes = readLongSparseLongArrayFromParcel(source); - mRejectTimes = readLongSparseLongArrayFromParcel(source); - mDurations = readLongSparseLongArrayFromParcel(source); - mProxyUids = readLongSparseLongArrayFromParcel(source); - mProxyPackageNames = readLongSparseStringArrayFromParcel(source); + int numFeatures = mFeatures.size(); + dest.writeInt(numFeatures); + for (int i = 0; i < numFeatures; i++) { + dest.writeString(mFeatures.keyAt(i)); + mFeatures.valueAt(i).writeToParcel(dest, flags); + } } - public static final @android.annotation.NonNull Creator<OpEntry> CREATOR = new Creator<OpEntry>() { - @Override public OpEntry createFromParcel(Parcel source) { - return new OpEntry(source); + public static final @NonNull Creator<OpEntry> CREATOR = new Creator<OpEntry>() { + @Override + public @NonNull OpEntry createFromParcel(@NonNull Parcel parcel) { + return OpEntry.createFromParcel(parcel); } - @Override public OpEntry[] newArray(int size) { + @Override + public @NonNull OpEntry[] newArray(int size) { return new OpEntry[size]; } }; @@ -4428,7 +5041,7 @@ public class AppOpsManager { * state due to UID policy or because it's controlled by a different master op. * * Use {@link #unsafeCheckOp(String, int, String)}} or - * {@link #noteOp(String, int, String, String)} if the effective mode is needed. + * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. * * @param ops The set of operations you are interested in, or null if you want all of them. * @hide @@ -4452,7 +5065,7 @@ public class AppOpsManager { * state due to UID policy or because it's controlled by a different master op. * * Use {@link #unsafeCheckOp(String, int, String)}} or - * {@link #noteOp(String, int, String, String)} if the effective mode is needed. + * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. * * @param ops The set of operations you are interested in, or null if you want all of them. * @hide @@ -4474,7 +5087,7 @@ public class AppOpsManager { * state due to UID policy or because it's controlled by a different master op. * * Use {@link #unsafeCheckOp(String, int, String)}} or - * {@link #noteOp(String, int, String, String)} if the effective mode is needed. + * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. * * @param uid The uid of the application of interest. * @param packageName The name of the application of interest. @@ -4507,7 +5120,7 @@ public class AppOpsManager { * state due to UID policy or because it's controlled by a different master op. * * Use {@link #unsafeCheckOp(String, int, String)}} or - * {@link #noteOp(String, int, String, String)} if the effective mode is needed. + * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. * * @param uid The uid of the application of interest. * @param packageName The name of the application of interest. @@ -4897,8 +5510,8 @@ public class AppOpsManager { * * @see #isOperationActive * @see #stopWatchingActive - * @see #startOp(int, int, String, boolean, String) - * @see #finishOp(int, int, String) + * @see #startOp(int, int, String, boolean, String, String) + * @see #finishOp(int, int, String, String) */ // TODO: Uncomment below annotation once b/73559440 is fixed // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) @@ -4948,8 +5561,8 @@ public class AppOpsManager { * * @see #isOperationActive * @see #startWatchingActive - * @see #startOp(int, int, String, boolean, String) - * @see #finishOp(int, int, String) + * @see #startOp(int, int, String, boolean, String, String) + * @see #finishOp(int, int, String, String) */ public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) { synchronized (mActiveWatchers) { @@ -4979,7 +5592,7 @@ public class AppOpsManager { * * @see #startWatchingActive(int[], OnOpActiveChangedListener) * @see #stopWatchingNoted(OnOpNotedListener) - * @see #noteOp(String, int, String, String) + * @see #noteOp(String, int, String, String, String) * * @hide */ @@ -5011,7 +5624,7 @@ public class AppOpsManager { * Unregistering a non-registered callback has no effect. * * @see #startWatchingNoted(int[], OnOpNotedListener) - * @see #noteOp(String, int, String, String) + * @see #noteOp(String, int, String, String, String) * * @hide */ @@ -5047,15 +5660,15 @@ public class AppOpsManager { /** * Do a quick check for whether an application might be able to perform an operation. * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String, - * String)} or {@link #startOp(String, int, String, String)} for your actual security checks, - * which also ensure that the given uid and package name are consistent. This function can just - * be used for a quick check to see if an operation has been disabled for the application, - * as an early reject of some work. This does not modify the time stamp or other data - * about the operation. + * String, String)} or {@link #startOp(String, int, String, String, String)} for your actual + * security checks, which also ensure that the given uid and package name are consistent. This + * function can just be used for a quick check to see if an operation has been disabled for the + * application, as an early reject of some work. This does not modify the time stamp or other + * data about the operation. * * <p>Important things this will not do (which you need to ultimate use - * {@link #noteOp(String, int, String, String)} or - * {@link #startOp(String, int, String, String)} to cover):</p> + * {@link #noteOp(String, int, String, String, String)} or + * {@link #startOp(String, int, String, String, String)} to cover):</p> * <ul> * <li>Verifying the uid and package are consistent, so callers can't spoof * their identity.</li> @@ -5126,35 +5739,37 @@ public class AppOpsManager { } /** - * @deprecated Use {@link #noteOp(String, int, String, String)} instead + * @deprecated Use {@link #noteOp(String, int, String, String, String)} instead */ @Deprecated public int noteOp(@NonNull String op, int uid, @NonNull String packageName) { - return noteOp(op, uid, packageName, null); + return noteOp(op, uid, packageName, null, null); } /** - * @deprecated Use {@link #noteOp(String, int, String, String)} instead + * @deprecated Use {@link #noteOp(String, int, String, String, String)} instead * * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " - + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String)} instead") + + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String, " + + "java.lang.String)} instead") @Deprecated public int noteOp(int op) { - return noteOp(op, Process.myUid(), mContext.getOpPackageName(), null); + return noteOp(op, Process.myUid(), mContext.getOpPackageName(), null, null); } /** - * @deprecated Use {@link #noteOp(String, int, String, String)} instead + * @deprecated Use {@link #noteOp(String, int, String, String, String)} instead * * @hide */ @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " - + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String)} instead") + + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String, " + + "java.lang.String)} instead") public int noteOp(int op, int uid, @Nullable String packageName) { - return noteOp(op, uid, packageName, null); + return noteOp(op, uid, packageName, null, null); } /** @@ -5167,6 +5782,7 @@ public class AppOpsManager { * @param op The operation to note. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. + * @param featureId The feature in the app or {@code null} for default feature * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -5176,8 +5792,8 @@ public class AppOpsManager { * @throws SecurityException If the app has been configured to crash on this op. */ public int noteOp(@NonNull String op, int uid, @Nullable String packageName, - @Nullable String message) { - return noteOp(strOpToOp(op), uid, packageName, message); + @Nullable String featureId, @Nullable String message) { + return noteOp(strOpToOp(op), uid, packageName, featureId, message); } /** @@ -5190,6 +5806,7 @@ public class AppOpsManager { * @param op The operation to note. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. + * @param featureId The feature in the app or {@code null} for default feature * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -5200,8 +5817,9 @@ public class AppOpsManager { * * @hide */ - public int noteOp(int op, int uid, @Nullable String packageName, @Nullable String message) { - final int mode = noteOpNoThrow(op, uid, packageName, message); + public int noteOp(int op, int uid, @Nullable String packageName, @Nullable String featureId, + @Nullable String message) { + final int mode = noteOpNoThrow(op, uid, packageName, featureId, message); if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } @@ -5209,27 +5827,28 @@ public class AppOpsManager { } /** - * @deprecated Use {@link #noteOpNoThrow(String, int, String, String)} instead + * @deprecated Use {@link #noteOpNoThrow(String, int, String, String, String)} instead */ @Deprecated public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) { - return noteOpNoThrow(op, uid, packageName, null); + return noteOpNoThrow(op, uid, packageName, null, null); } /** - * @deprecated Use {@link #noteOpNoThrow(int, int, String, String)} instead + * @deprecated Use {@link #noteOpNoThrow(int, int, String, String, String)} instead * * @hide */ @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " - + "#noteOpNoThrow(java.lang.String, int, java.lang.String, java.lang.String)} instead") + + "#noteOpNoThrow(java.lang.String, int, java.lang.String, java.lang.String, " + + "java.lang.String)} instead") public int noteOpNoThrow(int op, int uid, String packageName) { - return noteOpNoThrow(op, uid, packageName, null); + return noteOpNoThrow(op, uid, packageName, null, null); } /** - * Like {@link #noteOp(String, int, String, String)} but instead of throwing a + * Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a * {@link SecurityException} it returns {@link #MODE_ERRORED}. * * @param op The operation to note. One of the OPSTR_* constants. @@ -5242,17 +5861,18 @@ public class AppOpsManager { * causing the app to crash). */ public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName, - @Nullable String message) { - return noteOpNoThrow(strOpToOp(op), uid, packageName, message); + @Nullable String feature, @Nullable String message) { + return noteOpNoThrow(strOpToOp(op), uid, packageName, feature, message); } /** - * Like {@link #noteOp(String, int, String, String)} but instead of throwing a + * Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a * {@link SecurityException} it returns {@link #MODE_ERRORED}. * * @param op The operation to note. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. + * @param featureId The feature in the app or {@code null} for default feature * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -5262,11 +5882,11 @@ public class AppOpsManager { * @hide */ public int noteOpNoThrow(int op, int uid, @Nullable String packageName, - @Nullable String message) { + @Nullable String featureId, @Nullable String message) { try { - int mode = mService.noteOperation(op, uid, packageName); + int mode = mService.noteOperation(op, uid, packageName, featureId); if (mode == MODE_ALLOWED) { - markAppOpNoted(uid, packageName, op, message); + markAppOpNoted(uid, packageName, op, featureId, message); } return mode; @@ -5276,23 +5896,24 @@ public class AppOpsManager { } /** - * @deprecated Use {@link #noteProxyOp(String, String, int, String)} instead + * @deprecated Use {@link #noteProxyOp(String, String, int, String, String)} instead */ @Deprecated public int noteProxyOp(@NonNull String op, @NonNull String proxiedPackageName) { - return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null); + return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null, null); } /** - * @deprecated Use {@link #noteProxyOp(String, String, int, String)} instead + * @deprecated Use {@link #noteProxyOp(String, String, int, String, String)} instead * * @hide */ @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " - + "#noteProxyOp(java.lang.String, java.lang.String, int, java.lang.String)} instead") + + "#noteProxyOp(java.lang.String, java.lang.String, int, java.lang.String, " + + "java.lang.String)} instead") public int noteProxyOp(int op, @Nullable String proxiedPackageName) { - return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null); + return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null, null); } /** @@ -5304,6 +5925,8 @@ public class AppOpsManager { * @param op The operation to note. One of the OP_* constants. * @param proxiedPackageName The name of the application calling into the proxy application. * @param proxiedUid The uid of the proxied application + * @param proxiedFeatureId The feature in the proxied app or {@code null} for default + * feature * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED} @@ -5315,8 +5938,9 @@ public class AppOpsManager { * @hide */ public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid, - @Nullable String message) { - int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, message); + @Nullable String proxiedFeatureId, @Nullable String message) { + int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedFeatureId, + message); if (mode == MODE_ERRORED) { throw new SecurityException("Proxy package " + mContext.getOpPackageName() + " from uid " + Process.myUid() + " or calling package " + proxiedPackageName @@ -5334,6 +5958,8 @@ public class AppOpsManager { * @param op The operation to note. One of the OPSTR_* constants. * @param proxiedPackageName The name of the application calling into the proxy application. * @param proxiedUid The uid of the proxied application + * @param proxiedFeatureId The feature in the proxied app or {@code null} for default + * feature * @param message A message describing the reason the op was noted * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED} @@ -5343,29 +5969,30 @@ public class AppOpsManager { * op. */ public int noteProxyOp(@NonNull String op, @Nullable String proxiedPackageName, int proxiedUid, - @Nullable String message) { - return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, message); + @Nullable String proxiedFeatureId, @Nullable String message) { + return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedFeatureId, + message); } /** - * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String)} instead + * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String, String)} instead */ @Deprecated public int noteProxyOpNoThrow(@NonNull String op, @NonNull String proxiedPackageName) { - return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid(), null); + return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid(), null, null); } /** - * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String)} instead + * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String, String)} instead */ @Deprecated public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName, int proxiedUid) { - return noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, null); + return noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, null, null); } /** - * Like {@link #noteProxyOp(String, String, int, String)} but instead + * Like {@link #noteProxyOp(String, String, int, String, String)} but instead * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}. * * <p>This API requires package with the {@code proxiedPackageName} to belong to @@ -5374,37 +6001,43 @@ public class AppOpsManager { * @param op The op to note * @param proxiedPackageName The package to note the op for * @param proxiedUid The uid the package belongs to + * @param proxiedFeatureId The feature in the proxied app or {@code null} for default + * feature * @param message A message describing the reason the op was noted */ public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName, - int proxiedUid, @Nullable String message) { - return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid, message); + int proxiedUid, @Nullable String proxiedFeatureId, @Nullable String message) { + return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid, + proxiedFeatureId, message); } /** - * Like {@link #noteProxyOp(int, String, int, String)} but instead + * Like {@link #noteProxyOp(int, String, int, String, String)} but instead * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}. * * @param op The op to note * @param proxiedPackageName The package to note the op for or {@code null} if the op should be * noted for the "android" package * @param proxiedUid The uid the package belongs to + * @param proxiedFeatureId The feature in the proxied app or {@code null} for default + * feature * @param message A message describing the reason the op was noted * * @hide */ public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid, - @Nullable String message) { + @Nullable String proxiedFeatureId, @Nullable String message) { int myUid = Process.myUid(); try { - int mode = mService.noteProxyOperation(op, myUid, mContext.getOpPackageName(), - proxiedUid, proxiedPackageName); + int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName, + proxiedFeatureId, myUid, mContext.getOpPackageName(), + mContext.getFeatureId()); if (mode == MODE_ALLOWED // Only collect app-ops when the proxy is trusted && mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, myUid) == PackageManager.PERMISSION_GRANTED) { - markAppOpNoted(proxiedUid, proxiedPackageName, op, message); + markAppOpNoted(proxiedUid, proxiedPackageName, op, proxiedFeatureId, message); } return mode; @@ -5416,15 +6049,15 @@ public class AppOpsManager { /** * Do a quick check for whether an application might be able to perform an operation. * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String, - * String)} or {@link #startOp(int, int, String, boolean, String)} for your actual security - * checks, which also ensure that the given uid and package name are consistent. This function - * can just be used for a quick check to see if an operation has been disabled for the - * application, as an early reject of some work. This does not modify the time stamp or other - * data about the operation. + * String, String)} or {@link #startOp(int, int, String, boolean, String, String)} for your + * actual security checks, which also ensure that the given uid and package name are consistent. + * This function can just be used for a quick check to see if an operation has been disabled for + * the application, as an early reject of some work. This does not modify the time stamp or + * other data about the operation. * * <p>Important things this will not do (which you need to ultimate use - * {@link #noteOp(String, int, String, String)} or - * {@link #startOp(int, int, String, boolean, String)} to cover):</p> + * {@link #noteOp(String, int, String, String, String)} or + * {@link #startOp(int, int, String, boolean, String, String)} to cover):</p> * <ul> * <li>Verifying the uid and package are consistent, so callers can't spoof * their identity.</li> @@ -5534,41 +6167,41 @@ public class AppOpsManager { /** - * @deprecated use {@link #startOp(String, int, String, String)} instead + * @deprecated use {@link #startOp(String, int, String, String, String)} instead */ @Deprecated public int startOp(@NonNull String op, int uid, @NonNull String packageName) { - return startOp(op, uid, packageName, null); + return startOp(op, uid, packageName, null, null); } /** - * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead + * @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead * * @hide */ @Deprecated public int startOp(int op) { - return startOp(op, Process.myUid(), mContext.getOpPackageName(), false, null); + return startOp(op, Process.myUid(), mContext.getOpPackageName(), false, null, null); } /** - * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead + * @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead * * @hide */ @Deprecated public int startOp(int op, int uid, String packageName) { - return startOp(op, uid, packageName, false, null); + return startOp(op, uid, packageName, false, null, null); } /** - * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead + * @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead * * @hide */ @Deprecated public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) { - return startOp(op, uid, packageName, startIfModeDefault, null); + return startOp(op, uid, packageName, startIfModeDefault, null, null); } /** @@ -5577,6 +6210,7 @@ public class AppOpsManager { * @param op The operation to start. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. + * @param featureId The feature in the app or {@code null} for default feature * @param message Description why op was started * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -5587,8 +6221,8 @@ public class AppOpsManager { * the package is not in the passed in UID. */ public int startOp(@NonNull String op, int uid, @Nullable String packageName, - @Nullable String message) { - return startOp(strOpToOp(op), uid, packageName, false, message); + @NonNull String featureId, @Nullable String message) { + return startOp(strOpToOp(op), uid, packageName, false, featureId, message); } /** @@ -5597,6 +6231,7 @@ public class AppOpsManager { * @param op The operation to start. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. + * @param featureId The feature in the app or {@code null} for default feature * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}. * @param message Description why op was started * @@ -5610,8 +6245,9 @@ public class AppOpsManager { * @hide */ public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault, - @Nullable String message) { - final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, message); + @NonNull String featureId, @Nullable String message) { + final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, featureId, + message); if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } @@ -5619,40 +6255,41 @@ public class AppOpsManager { } /** - * @deprecated use {@link #startOpNoThrow(String, int, String, String)} instead + * @deprecated use {@link #startOpNoThrow(String, int, String, String, String)} instead */ @Deprecated public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) { - return startOpNoThrow(op, uid, packageName, null); + return startOpNoThrow(op, uid, packageName, null, null); } /** - * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String} instead + * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String, String} instead * * @hide */ @Deprecated public int startOpNoThrow(int op, int uid, String packageName) { - return startOpNoThrow(op, uid, packageName, false, null); + return startOpNoThrow(op, uid, packageName, false, null, null); } /** - * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String} instead + * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String, String} instead * * @hide */ @Deprecated public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) { - return startOpNoThrow(op, uid, packageName, startIfModeDefault, null); + return startOpNoThrow(op, uid, packageName, startIfModeDefault, null, null); } /** - * Like {@link #startOp(String, int, String, String)} but instead of throwing a + * Like {@link #startOp(String, int, String, String, String)} but instead of throwing a * {@link SecurityException} it returns {@link #MODE_ERRORED}. * * @param op The operation to start. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. + * @param featureId The feature in the app or {@code null} for default feature * @param message Description why op was started * * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or @@ -5660,17 +6297,18 @@ public class AppOpsManager { * causing the app to crash). */ public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName, - @Nullable String message) { - return startOpNoThrow(strOpToOp(op), uid, packageName, false, message); + @NonNull String featureId, @Nullable String message) { + return startOpNoThrow(strOpToOp(op), uid, packageName, false, featureId, message); } /** - * Like {@link #startOp(int, int, String, boolean, String)} but instead of throwing a + * Like {@link #startOp(int, int, String, boolean, String, String)} but instead of throwing a * {@link SecurityException} it returns {@link #MODE_ERRORED}. * * @param op The operation to start. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. + * @param featureId The feature in the app or {@code null} for default feature * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}. * @param message Description why op was started * @@ -5681,12 +6319,12 @@ public class AppOpsManager { * @hide */ public int startOpNoThrow(int op, int uid, @NonNull String packageName, - boolean startIfModeDefault, @Nullable String message) { + boolean startIfModeDefault, @Nullable String featureId, @Nullable String message) { try { int mode = mService.startOperation(getToken(mService), op, uid, packageName, - startIfModeDefault); + featureId, startIfModeDefault); if (mode == MODE_ALLOWED) { - markAppOpNoted(uid, packageName, op, message); + markAppOpNoted(uid, packageName, op, featureId, message); } return mode; @@ -5696,36 +6334,54 @@ public class AppOpsManager { } /** - * @deprecated Use {@link #finishOp(String, int, String)} instead + * @deprecated Use {@link #finishOp(String, int, String, String)} instead * * @hide */ @Deprecated public void finishOp(int op) { - finishOp(op, Process.myUid(), mContext.getOpPackageName()); + finishOp(op, Process.myUid(), mContext.getOpPackageName(), null); } /** - * Report that an application is no longer performing an operation that had previously - * been started with {@link #startOp(String, int, String, String)}. There is no validation of - * input or result; the parameters supplied here must be the exact same ones previously passed - * in when starting the operation. + * @deprecated Use {@link #finishOp(String, int, String, String)} instead */ public void finishOp(@NonNull String op, int uid, @NonNull String packageName) { - finishOp(strOpToOp(op), uid, packageName); + finishOp(strOpToOp(op), uid, packageName, null); } /** * Report that an application is no longer performing an operation that had previously - * been started with {@link #startOp(int, int, String, boolean, String)}. There is no + * been started with {@link #startOp(String, int, String, String, String)}. There is no * validation of input or result; the parameters supplied here must be the exact same ones * previously passed in when starting the operation. + */ + public void finishOp(@NonNull String op, int uid, @NonNull String packageName, + @Nullable String featureId) { + finishOp(strOpToOp(op), uid, packageName, featureId); + } + + /** + * @deprecated Use {@link #finishOp(int, int, String, String)} instead * * @hide */ public void finishOp(int op, int uid, @NonNull String packageName) { + finishOp(op, uid, packageName, null); + } + + /** + * Report that an application is no longer performing an operation that had previously + * been started with {@link #startOp(int, int, String, boolean, String, String)}. There is no + * validation of input or result; the parameters supplied here must be the exact same ones + * previously passed in when starting the operation. + * + * @hide + */ + public void finishOp(int op, int uid, @NonNull String packageName, + @Nullable String featureId) { try { - mService.finishOperation(getToken(mService), op, uid, packageName); + mService.finishOperation(getToken(mService), op, uid, packageName, featureId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -5737,8 +6393,8 @@ public class AppOpsManager { * If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS} * permission you can query only for your UID. * - * @see #finishOp(String, int, String) - * @see #startOp(String, int, String, String) + * @see #finishOp(String, int, String, String) + * @see #startOp(String, int, String, String, String) */ public boolean isOpActive(@NonNull String op, int uid, @NonNull String packageName) { return isOperationActive(strOpToOp(op), uid, packageName); @@ -5766,9 +6422,10 @@ public class AppOpsManager { */ public static class PausedNotedAppOpsCollection { final int mUid; - final @Nullable long[] mCollectedNotedAppOps; + final @Nullable ArrayMap<String, long[]> mCollectedNotedAppOps; - PausedNotedAppOpsCollection(int uid, @Nullable long[] collectedNotedAppOps) { + PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String, + long[]> collectedNotedAppOps) { mUid = uid; mCollectedNotedAppOps = collectedNotedAppOps; } @@ -5786,7 +6443,8 @@ public class AppOpsManager { public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() { Integer previousUid = sBinderThreadCallingUid.get(); if (previousUid != null) { - long[] previousCollectedNotedAppOps = sAppOpsNotedInThisBinderTransaction.get(); + ArrayMap<String, long[]> previousCollectedNotedAppOps = + sAppOpsNotedInThisBinderTransaction.get(); sBinderThreadCallingUid.remove(); sAppOpsNotedInThisBinderTransaction.remove(); @@ -5832,8 +6490,12 @@ public class AppOpsManager { /** * Mark an app-op as noted */ - private void markAppOpNoted(int uid, @NonNull String packageName, int code, - @Nullable String message) { + private void markAppOpNoted(int uid, @Nullable String packageName, int code, + @Nullable String featureId, @Nullable String message) { + if (packageName == null) { + packageName = "android"; + } + // check it the appops needs to be collected and cache result if (sAppOpsToNote[code] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) { boolean shouldCollectNotes; @@ -5860,7 +6522,7 @@ public class AppOpsManager { if (sNotedAppOpsCollector != null && uid == Process.myUid() && packageName.equals( ActivityThread.currentOpPackageName())) { // This app is noting an app-op for itself. Deliver immediately. - sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(code)); + sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(code, featureId)); return; } @@ -5869,18 +6531,24 @@ public class AppOpsManager { if (binderUid != null && binderUid == uid) { // If this is inside of a two-way binder call: Delivered to caller via // {@link #prefixParcelWithAppOpsIfNeeded} - long[] appOpsNotedInThisBinderTransaction; + // We are inside of a two-way binder call. Delivered to caller via + // {@link #prefixParcelWithAppOpsIfNeeded} + ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get(); + if (appOpsNoted == null) { + appOpsNoted = new ArrayMap<>(1); + sAppOpsNotedInThisBinderTransaction.set(appOpsNoted); + } - appOpsNotedInThisBinderTransaction = sAppOpsNotedInThisBinderTransaction.get(); - if (appOpsNotedInThisBinderTransaction == null) { - appOpsNotedInThisBinderTransaction = new long[2]; - sAppOpsNotedInThisBinderTransaction.set(appOpsNotedInThisBinderTransaction); + long[] appOpsNotedForFeature = appOpsNoted.get(featureId); + if (appOpsNotedForFeature == null) { + appOpsNotedForFeature = new long[2]; + appOpsNoted.put(featureId, appOpsNotedForFeature); } if (code < 64) { - appOpsNotedInThisBinderTransaction[0] |= 1L << code; + appOpsNotedForFeature[0] |= 1L << code; } else { - appOpsNotedInThisBinderTransaction[1] |= 1L << (code - 64); + appOpsNotedForFeature[1] |= 1L << (code - 64); } } else { // Cannot deliver the note synchronous: Hence send it to the system server to @@ -5892,7 +6560,8 @@ public class AppOpsManager { long token = Binder.clearCallingIdentity(); try { - mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code, message); + mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code, + featureId, message); } catch (RemoteException e) { e.rethrowFromSystemServer(); } finally { @@ -5912,14 +6581,21 @@ public class AppOpsManager { * @hide */ public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) { - long[] notedAppOps = sAppOpsNotedInThisBinderTransaction.get(); - if (notedAppOps == null || (notedAppOps[0] == 0 && notedAppOps[1] == 0)) { + ArrayMap<String, long[]> notedAppOps = sAppOpsNotedInThisBinderTransaction.get(); + if (notedAppOps == null) { return; } p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER); - p.writeLong(notedAppOps[0]); - p.writeLong(notedAppOps[1]); + + int numFeatureWithNotesAppOps = notedAppOps.size(); + p.writeInt(numFeatureWithNotesAppOps); + + for (int i = 0; i < numFeatureWithNotesAppOps; i++) { + p.writeString(notedAppOps.keyAt(i)); + p.writeLong(notedAppOps.valueAt(i)[0]); + p.writeLong(notedAppOps.valueAt(i)[1]); + } } /** @@ -5928,26 +6604,28 @@ public class AppOpsManager { * <p>This is called on the calling side of a two way binder transaction just after the * transaction returns. * - * <p>Note: Make sure to keep frameworks/native/libs/binder/Status.cpp::readAndLogNotedAppops - * in sync. - * * @param p The parcel to read from * * @hide */ public static void readAndLogNotedAppops(@NonNull Parcel p) { - long[] rawNotedAppOps = new long[2]; - rawNotedAppOps[0] = p.readLong(); - rawNotedAppOps[1] = p.readLong(); - - if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) { - BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps); - - synchronized (sLock) { - for (int code = notedAppOps.nextSetBit(0); code != -1; - code = notedAppOps.nextSetBit(code + 1)) { - if (sNotedAppOpsCollector != null) { - sNotedAppOpsCollector.onNoted(new SyncNotedAppOp(code)); + int numFeaturesWithNotedAppOps = p.readInt(); + + for (int i = 0; i < numFeaturesWithNotedAppOps; i++) { + String featureId = p.readString(); + long[] rawNotedAppOps = new long[2]; + rawNotedAppOps[0] = p.readLong(); + rawNotedAppOps[1] = p.readLong(); + + if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) { + BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps); + + synchronized (sLock) { + for (int code = notedAppOps.nextSetBit(0); code != -1; + code = notedAppOps.nextSetBit(code + 1)) { + if (sNotedAppOpsCollector != null) { + sNotedAppOpsCollector.onNoted(new SyncNotedAppOp(code, featureId)); + } } } } @@ -6134,8 +6812,8 @@ public class AppOpsManager { * * @see #startWatchingActive(int[], OnOpActiveChangedListener) * @see #stopWatchingMode(OnOpChangedListener) - * @see #finishOp(int) - * @see #startOp(int, int, String, boolean, String) + * @see #finishOp(int, int, String, String) + * @see #startOp(int, int, String, boolean, String, String) * * @hide */ @TestApi diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index 19be6c92e91c..bd7ef2ac3f68 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -16,10 +16,11 @@ package android.app; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.util.SparseIntArray; import com.android.internal.util.function.QuadFunction; -import com.android.internal.util.function.TriFunction; /** * App ops service local interface. @@ -60,12 +61,14 @@ public abstract class AppOpsManagerInternal { * * @param code The op code to note. * @param uid The UID for which to note. - * @param packageName The package for which to note. + * @param packageName The package for which to note. {@code null} for system package. + * @param featureId Id of the feature in the package * @param superImpl The super implementation. * @return The app op note result. */ - int noteOperation(int code, int uid, String packageName, - TriFunction<Integer, Integer, String, Integer> superImpl); + int noteOperation(int code, int uid, @Nullable String packageName, + @Nullable String featureId, + @NonNull QuadFunction<Integer, Integer, String, String, Integer> superImpl); } /** diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java index 241895c9ff64..958ebae003ea 100644 --- a/core/java/android/app/AsyncNotedAppOp.java +++ b/core/java/android/app/AsyncNotedAppOp.java @@ -25,7 +25,7 @@ import com.android.internal.annotations.Immutable; import com.android.internal.util.DataClass; /** - * When an {@link AppOpsManager#noteOp(String, int, String, String) app-op is noted} and the + * When an {@link AppOpsManager#noteOp(String, int, String, String, String) app-op is noted} and the * app the app-op is noted for has a {@link AppOpsManager.AppOpsCollector} registered the note-event * needs to be delivered to the collector. Usually this is done via an {@link SyncNotedAppOp}, but * in some cases this is not possible. In this case an {@link AsyncNotedAppOp} is send to the system @@ -51,6 +51,9 @@ public final class AsyncNotedAppOp implements Parcelable { */ private final @Nullable String mNotingPackageName; + /** {@link android.content.Context#createFeatureContext Feature} in the app */ + private final @Nullable String mFeatureId; + /** Message associated with the noteOp. This message is set by the app noting the op */ private final @NonNull String mMessage; @@ -89,6 +92,8 @@ public final class AsyncNotedAppOp implements Parcelable { * @param notingPackageName * Package that noted the op. {@code null} if the package name that noted the op could be not * be determined (e.g. when the op is noted from native code). + * @param featureId + * {@link android.content.Context#createFeatureContext Feature} in the app * @param message * Message associated with the noteOp. This message is set by the app noting the op * @param time @@ -100,6 +105,7 @@ public final class AsyncNotedAppOp implements Parcelable { @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode, @IntRange(from = 0) int notingUid, @Nullable String notingPackageName, + @Nullable String featureId, @NonNull String message, @IntRange(from = 0) long time) { this.mOpCode = opCode; @@ -112,6 +118,7 @@ public final class AsyncNotedAppOp implements Parcelable { IntRange.class, null, mNotingUid, "from", 0); this.mNotingPackageName = notingPackageName; + this.mFeatureId = featureId; this.mMessage = message; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mMessage); @@ -141,6 +148,14 @@ public final class AsyncNotedAppOp implements Parcelable { } /** + * {@link android.content.Context#createFeatureContext Feature} in the app + */ + @DataClass.Generated.Member + public @Nullable String getFeatureId() { + return mFeatureId; + } + + /** * Message associated with the noteOp. This message is set by the app noting the op */ @DataClass.Generated.Member @@ -172,6 +187,7 @@ public final class AsyncNotedAppOp implements Parcelable { && mOpCode == that.mOpCode && mNotingUid == that.mNotingUid && java.util.Objects.equals(mNotingPackageName, that.mNotingPackageName) + && java.util.Objects.equals(mFeatureId, that.mFeatureId) && java.util.Objects.equals(mMessage, that.mMessage) && mTime == that.mTime; } @@ -186,6 +202,7 @@ public final class AsyncNotedAppOp implements Parcelable { _hash = 31 * _hash + mOpCode; _hash = 31 * _hash + mNotingUid; _hash = 31 * _hash + java.util.Objects.hashCode(mNotingPackageName); + _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId); _hash = 31 * _hash + java.util.Objects.hashCode(mMessage); _hash = 31 * _hash + Long.hashCode(mTime); return _hash; @@ -199,10 +216,12 @@ public final class AsyncNotedAppOp implements Parcelable { byte flg = 0; if (mNotingPackageName != null) flg |= 0x4; + if (mFeatureId != null) flg |= 0x8; dest.writeByte(flg); dest.writeInt(mOpCode); dest.writeInt(mNotingUid); if (mNotingPackageName != null) dest.writeString(mNotingPackageName); + if (mFeatureId != null) dest.writeString(mFeatureId); dest.writeString(mMessage); dest.writeLong(mTime); } @@ -222,6 +241,7 @@ public final class AsyncNotedAppOp implements Parcelable { int opCode = in.readInt(); int notingUid = in.readInt(); String notingPackageName = (flg & 0x4) == 0 ? null : in.readString(); + String featureId = (flg & 0x8) == 0 ? null : in.readString(); String message = in.readString(); long time = in.readLong(); @@ -235,6 +255,7 @@ public final class AsyncNotedAppOp implements Parcelable { IntRange.class, null, mNotingUid, "from", 0); this.mNotingPackageName = notingPackageName; + this.mFeatureId = featureId; this.mMessage = message; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mMessage); @@ -261,10 +282,10 @@ public final class AsyncNotedAppOp implements Parcelable { }; @DataClass.Generated( - time = 1571246617363L, + time = 1571327470155L, codegenVersion = "1.0.9", sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L, to=91L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.IntRange(from=0L, to=91L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index d5bc9b0b213a..eb2b2bca8ca9 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -204,6 +204,9 @@ class ContextImpl extends Context { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final String mOpPackageName; + /** If of feature this context is for */ + private final @Nullable String mFeatureId; + private final @NonNull ResourcesManager mResourcesManager; @UnsupportedAppUsage private @NonNull Resources mResources; @@ -395,6 +398,12 @@ class ContextImpl extends Context { return mOpPackageName != null ? mOpPackageName : getBasePackageName(); } + /** @hide */ + @Override + public @Nullable String getFeatureId() { + return mFeatureId; + } + @Override public ApplicationInfo getApplicationInfo() { if (mPackageInfo != null) { @@ -2159,7 +2168,7 @@ class ContextImpl extends Context { LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE); if (pi != null) { - ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, + ContextImpl c = new ContextImpl(this, mMainThread, pi, null, null, mActivityToken, new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null); final int displayId = getDisplayId(); @@ -2187,15 +2196,15 @@ class ContextImpl extends Context { if (packageName.equals("system") || packageName.equals("android")) { // The system resources are loaded in every application, so we can safely copy // the context without reloading Resources. - return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user, - flags, null, null); + return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, null, + mActivityToken, user, flags, null, null); } LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { - ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user, - flags, null, null); + ContextImpl c = new ContextImpl(this, mMainThread, pi, mFeatureId, null, + mActivityToken, user, flags, null, null); final int displayId = getDisplayId(); @@ -2230,8 +2239,8 @@ class ContextImpl extends Context { final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName); final String[] paths = mPackageInfo.getSplitPaths(splitName); - final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName, - mActivityToken, mUser, mFlags, classLoader, null); + final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, + mFeatureId, splitName, mActivityToken, mUser, mFlags, classLoader, null); final int displayId = getDisplayId(); @@ -2254,8 +2263,8 @@ class ContextImpl extends Context { throw new IllegalArgumentException("overrideConfiguration must not be null"); } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, - mActivityToken, mUser, mFlags, mClassLoader, null); + ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, + mSplitName, mActivityToken, mUser, mFlags, mClassLoader, null); final int displayId = getDisplayId(); context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, @@ -2269,8 +2278,8 @@ class ContextImpl extends Context { throw new IllegalArgumentException("display must not be null"); } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, - mActivityToken, mUser, mFlags, mClassLoader, null); + ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, + mSplitName, mActivityToken, mUser, mFlags, mClassLoader, null); final int displayId = display.getDisplayId(); context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, @@ -2280,19 +2289,25 @@ class ContextImpl extends Context { } @Override + public @NonNull Context createFeatureContext(@Nullable String featureId) { + return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName, + mActivityToken, mUser, mFlags, mClassLoader, null); + } + + @Override public Context createDeviceProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE) | Context.CONTEXT_DEVICE_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser, - flags, mClassLoader, null); + return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName, + mActivityToken, mUser, flags, mClassLoader, null); } @Override public Context createCredentialProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE) | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser, - flags, mClassLoader, null); + return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName, + mActivityToken, mUser, flags, mClassLoader, null); } @Override @@ -2436,8 +2451,8 @@ class ContextImpl extends Context { @UnsupportedAppUsage static ContextImpl createSystemContext(ActivityThread mainThread) { LoadedApk packageInfo = new LoadedApk(mainThread); - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, - null, null); + ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null, + 0, null, null); context.setResources(packageInfo.getResources()); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetrics()); @@ -2454,7 +2469,7 @@ class ContextImpl extends Context { static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) { final LoadedApk packageInfo = systemContext.mPackageInfo; ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null, - null, null, 0, null, null); + null, null, null, 0, null, null); context.setResources(createResources(null, packageInfo, null, displayId, null, packageInfo.getCompatibilityInfo())); context.updateDisplay(displayId); @@ -2477,8 +2492,8 @@ class ContextImpl extends Context { static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo, String opPackageName) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, - null, opPackageName); + ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null, + 0, null, opPackageName); context.setResources(packageInfo.getResources()); return context; } @@ -2505,8 +2520,8 @@ class ContextImpl extends Context { } } - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName, - activityToken, null, 0, classLoader, null); + ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, + activityInfo.splitName, activityToken, null, 0, classLoader, null); // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; @@ -2534,9 +2549,9 @@ class ContextImpl extends Context { } private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread, - @NonNull LoadedApk packageInfo, @Nullable String splitName, - @Nullable IBinder activityToken, @Nullable UserHandle user, int flags, - @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) { + @NonNull LoadedApk packageInfo, @Nullable String featureId, + @Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user, + int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) { mOuterContext = this; // If creator didn't specify which storage to use, use the default @@ -2587,7 +2602,7 @@ class ContextImpl extends Context { } mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName; - + mFeatureId = featureId; mContentResolver = new ApplicationContentResolver(this, mainThread); } diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java index f7b83d409a02..065d5de368ae 100644 --- a/core/java/android/app/SyncNotedAppOp.java +++ b/core/java/android/app/SyncNotedAppOp.java @@ -18,6 +18,7 @@ package android.app; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import com.android.internal.annotations.Immutable; @@ -33,6 +34,7 @@ import com.android.internal.annotations.Immutable; @Immutable public final class SyncNotedAppOp { private final int mOpCode; + private final @Nullable String mFeatureId; /** * @return The op that was noted. @@ -42,14 +44,23 @@ public final class SyncNotedAppOp { } /** + * @return The {@link android.content.Context#createFeatureContext Feature} in the app + */ + public @Nullable String getFeatureId() { + return mFeatureId; + } + + /** * Create a new sync op description * * @param opCode The op that was noted * * @hide */ - public SyncNotedAppOp(@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode) { + public SyncNotedAppOp(@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode, + @Nullable String featureId) { mOpCode = opCode; + mFeatureId = featureId; } @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3eb066e9d99e..0aab2086b911 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -811,6 +811,17 @@ public abstract class Context { throw new RuntimeException("Not implemented. Must override in a subclass."); } + /** + * <p>Features are used in complex apps to logically separate parts of the app. E.g. a + * blogging app might also have a instant messaging app built in. + * + * @return the feature id this context is for or {@code null} if this is the default + * feature. + */ + public @Nullable String getFeatureId() { + return null; + } + /** Return the full application info for this context's package. */ public abstract ApplicationInfo getApplicationInfo(); @@ -5332,6 +5343,20 @@ public abstract class Context { public abstract Context createDisplayContext(@NonNull Display display); /** + * Return a new Context object for the current Context but for a different feature in the app. + * Features can be used by complex apps to separate logical parts. + * + * @param featureId The feature id or {@code null} to create a context for the default feature. + * + * @return A {@link Context} for the feature + * + * @see #getFeatureId() + */ + public @NonNull Context createFeatureContext(@Nullable String featureId) { + return this; + } + + /** * Return a new Context object for the current Context but whose storage * APIs are backed by device-protected storage. * <p> diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 7993ea192424..d5d0dcea9438 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; @@ -158,6 +159,12 @@ public class ContextWrapper extends Context { return mBase.getOpPackageName(); } + /** @hide */ + @Override + public @Nullable String getFeatureId() { + return mBase.getFeatureId(); + } + @Override public ApplicationInfo getApplicationInfo() { return mBase.getApplicationInfo(); diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index c3daad188792..85f96fc61681 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -401,7 +401,8 @@ public final class PermissionChecker { } if (forDataDelivery) { - if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid, message) + // TODO moltmann: Set correct feature id + if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid, null, message) != AppOpsManager.MODE_ALLOWED) { return PERMISSION_DENIED_APP_OP; } @@ -414,4 +415,4 @@ public final class PermissionChecker { return PERMISSION_GRANTED; } -}
\ No newline at end of file +} diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index d3b2c46ff1c3..e78b79695d3d 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -16,8 +16,12 @@ package android.util; +import android.os.Parcel; +import android.os.Parcelable; + import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import com.android.internal.util.Preconditions; import libcore.util.EmptyArray; @@ -442,4 +446,93 @@ public class LongSparseArray<E> implements Cloneable { buffer.append('}'); return buffer.toString(); } + + /** + * @hide + */ + public static class StringParcelling implements com.android.internal.util.Parcelling { + @Override + public void parcel(Object item, Parcel dest, int parcelFlags) { + if (item == null) { + dest.writeInt(-1); + return; + } + + LongSparseArray<String> array = (LongSparseArray<String>) item; + dest.writeInt(array.mSize); + dest.writeLongArray(array.mKeys); + dest.writeStringArray((String[]) array.mValues); + } + + @Override + public Object unparcel(Parcel source) { + int size = source.readInt(); + if (size == -1) { + return null; + } + + LongSparseArray<String> array = new LongSparseArray<>(0); + array.mSize = size; + array.mKeys = source.createLongArray(); + array.mValues = source.createStringArray(); + + // Make sure array is sane + Preconditions.checkArgument(array.mKeys.length >= size); + Preconditions.checkArgument(array.mValues.length >= size); + + if (size > 0) { + long last = array.mKeys[0]; + for (int i = 1; i < size; i++) { + Preconditions.checkArgument(last < array.mKeys[i]); + } + } + + return array; + } + } + + /** + * @hide + */ + public static class Parcelling<T extends Parcelable> implements + com.android.internal.util.Parcelling { + @Override + public void parcel(Object item, Parcel dest, int parcelFlags) { + if (item == null) { + dest.writeInt(-1); + return; + } + + LongSparseArray<T> array = (LongSparseArray<T>) item; + dest.writeInt(array.mSize); + dest.writeLongArray(array.mKeys); + dest.writeParcelableArray((T[]) array.mValues, parcelFlags); + } + + @Override + public Object unparcel(Parcel source) { + int size = source.readInt(); + if (size == -1) { + return null; + } + + LongSparseArray<T> array = new LongSparseArray<>(0); + array.mSize = size; + array.mKeys = source.createLongArray(); + array.mValues = source.readParcelableArray(null); + + // Make sure array is sane + Preconditions.checkArgument(array.mKeys.length >= size); + Preconditions.checkArgument(array.mValues.length >= size); + + if (size > 0) { + long last = array.mKeys[0]; + for (int i = 1; i < size; i++) { + Preconditions.checkArgument(last < array.mKeys[i]); + } + } + + return array; + } + } } diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index 7b7eea09e884..9ffd4f05bd47 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -17,9 +17,11 @@ package android.util; import android.annotation.UnsupportedAppUsage; +import android.os.Parcel; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import com.android.internal.util.Preconditions; import libcore.util.EmptyArray; @@ -283,4 +285,49 @@ public class LongSparseLongArray implements Cloneable { buffer.append('}'); return buffer.toString(); } + + /** + * @hide + */ + public static class Parcelling implements com.android.internal.util.Parcelling { + @Override + public void parcel(Object item, Parcel dest, int parcelFlags) { + if (item == null) { + dest.writeInt(-1); + return; + } + + LongSparseLongArray array = (LongSparseLongArray) item; + dest.writeInt(array.mSize); + dest.writeLongArray(array.mKeys); + dest.writeLongArray(array.mValues); + } + + @Override + public Object unparcel(Parcel source) { + int size = source.readInt(); + if (size == -1) { + return null; + } + + LongSparseLongArray array = new LongSparseLongArray(0); + + array.mSize = size; + array.mKeys = source.createLongArray(); + array.mValues = source.createLongArray(); + + // Make sure array is sane + Preconditions.checkArgument(array.mKeys.length >= size); + Preconditions.checkArgument(array.mValues.length >= size); + + if (size > 0) { + long last = array.mKeys[0]; + for (int i = 1; i < size; i++) { + Preconditions.checkArgument(last < array.mKeys[i]); + } + } + + return array; + } + } } |
