diff options
12 files changed, 663 insertions, 132 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 96d59b80b479..9bd6c750fb13 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -4804,6 +4804,16 @@ public class AppOpsManager { public static final int HISTORY_FLAG_DISCRETE = 1 << 1; /** + * Flag for querying app op history: assemble attribution chains, and attach the last visible + * node in the chain to the start as a proxy info. This only applies to discrete accesses. + * + * TODO 191512294: Add to @SystemApi + * + * @hide + */ + public static final int HISTORY_FLAG_GET_ATTRIBUTION_CHAINS = 1 << 2; + + /** * Flag for querying app op history: get all types of historical access information. * * @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer) @@ -4819,7 +4829,8 @@ public class AppOpsManager { @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "HISTORY_FLAG_" }, value = { HISTORY_FLAG_AGGREGATE, - HISTORY_FLAG_DISCRETE + HISTORY_FLAG_DISCRETE, + HISTORY_FLAG_GET_ATTRIBUTION_CHAINS }) public @interface OpHistoryFlags {} @@ -5037,7 +5048,8 @@ public class AppOpsManager { * @return This builder. */ public @NonNull Builder setHistoryFlags(@OpHistoryFlags int flags) { - Preconditions.checkFlagsArgument(flags, HISTORY_FLAGS_ALL); + Preconditions.checkFlagsArgument(flags, + HISTORY_FLAGS_ALL | HISTORY_FLAG_GET_ATTRIBUTION_CHAINS); mHistoryFlags = flags; return this; } @@ -5290,8 +5302,17 @@ public class AppOpsManager { @Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag, long discreteAccessTime, long discreteAccessDuration) { getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag, - uidState, opFlag, discreteAccessTime, discreteAccessDuration); - }; + uidState, opFlag, discreteAccessTime, discreteAccessDuration, null); + } + + /** @hide */ + public void addDiscreteAccess(int opCode, int uid, @NonNull String packageName, + @Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag, + long discreteAccessTime, long discreteAccessDuration, + @Nullable OpEventProxyInfo proxy) { + getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag, + uidState, opFlag, discreteAccessTime, discreteAccessDuration, proxy); + } /** @hide */ @@ -5623,9 +5644,10 @@ public class AppOpsManager { private void addDiscreteAccess(int opCode, @NonNull String packageName, @Nullable String attributionTag, @UidState int uidState, - @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration) { + @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration, + @Nullable OpEventProxyInfo proxy) { getOrCreateHistoricalPackageOps(packageName).addDiscreteAccess(opCode, attributionTag, - uidState, flag, discreteAccessTime, discreteAccessDuration); + uidState, flag, discreteAccessTime, discreteAccessDuration, proxy); }; /** @@ -5889,9 +5911,9 @@ public class AppOpsManager { private void addDiscreteAccess(int opCode, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flag, long discreteAccessTime, - long discreteAccessDuration) { + long discreteAccessDuration, @Nullable OpEventProxyInfo proxy) { getOrCreateAttributedHistoricalOps(attributionTag).addDiscreteAccess(opCode, uidState, - flag, discreteAccessTime, discreteAccessDuration); + flag, discreteAccessTime, discreteAccessDuration, proxy); } /** @@ -6212,9 +6234,10 @@ public class AppOpsManager { } private void addDiscreteAccess(int opCode, @UidState int uidState, @OpFlags int flag, - long discreteAccessTime, long discreteAccessDuration) { + long discreteAccessTime, long discreteAccessDuration, + @Nullable OpEventProxyInfo proxy) { getOrCreateHistoricalOp(opCode).addDiscreteAccess(uidState,flag, discreteAccessTime, - discreteAccessDuration); + discreteAccessDuration, proxy); } /** @@ -6583,11 +6606,12 @@ public class AppOpsManager { } private void addDiscreteAccess(@UidState int uidState, @OpFlags int flag, - long discreteAccessTime, long discreteAccessDuration) { + long discreteAccessTime, long discreteAccessDuration, + @Nullable OpEventProxyInfo proxy) { List<AttributedOpEntry> discreteAccesses = getOrCreateDiscreteAccesses(); LongSparseArray<NoteOpEvent> accessEvents = new LongSparseArray<>(); long key = makeKey(uidState, flag); - NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, null); + NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, proxy); accessEvents.append(key, note); AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null); int insertionPoint = discreteAccesses.size() - 1; @@ -10022,6 +10046,8 @@ public class AppOpsManager { NoteOpEvent existingAccess = accessEvents.get(key); if (existingAccess == null || existingAccess.getDuration() == -1) { accessEvents.append(key, access); + } else if (existingAccess.mProxy == null && access.mProxy != null ) { + existingAccess.mProxy = access.mProxy; } } if (reject != null) { diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index c5d0cd40bc6d..4ef0e6e785e8 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -908,9 +908,32 @@ public final class PermissionManager { */ public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context, @NonNull String packageName) { - if (SYSTEM_PKG.equals(packageName)) { - return false; + return !getIndicatorExemptedPackages(context).contains(packageName); + } + + /** + * Get the list of packages that are not shown by the indicators. Only a select few roles, and + * the system app itself, are hidden. These values are updated at most every 15 seconds. + * @hide + */ + public static Set<String> getIndicatorExemptedPackages(@NonNull Context context) { + updateIndicatorExemptedPackages(context); + ArraySet<String> pkgNames = new ArraySet<>(); + pkgNames.add(SYSTEM_PKG); + for (int i = 0; i < INDICATOR_EXEMPTED_PACKAGES.length; i++) { + String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i]; + if (exemptedPackage != null) { + pkgNames.add(exemptedPackage); + } } + return pkgNames; + } + + /** + * Update the cached indicator exempted packages + * @hide + */ + public static void updateIndicatorExemptedPackages(@NonNull Context context) { long now = SystemClock.elapsedRealtime(); if (sLastIndicatorUpdateTime == -1 || (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) { @@ -919,14 +942,6 @@ public final class PermissionManager { INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]); } } - for (int i = 0; i < EXEMPTED_ROLES.length; i++) { - String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i]; - if (exemptedPackage != null && exemptedPackage.equals(packageName)) { - return false; - } - } - - return true; } /** * Gets the list of packages that have permissions that specified diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index d4e548e1df1e..791764b4342f 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -410,7 +410,9 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis int usageAttr = usage.getPackageIdHash(); // If this usage has a proxy, but is not a proxy, it is the end of a chain. - if (!proxies.containsKey(usageAttr) && usage.proxy != null) { + // TODO remove once camera converted + if (!proxies.containsKey(usageAttr) && usage.proxy != null + && !usage.op.equals(OPSTR_RECORD_AUDIO)) { proxyLabels.put(usage, new ArrayList<>()); proxyPackages.add(usage.getPackageIdHash()); } diff --git a/core/java/com/android/internal/util/function/DodecConsumer.java b/core/java/com/android/internal/util/function/DodecConsumer.java new file mode 100644 index 000000000000..b4d2fb94d245 --- /dev/null +++ b/core/java/com/android/internal/util/function/DodecConsumer.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.function; + +import java.util.function.Consumer; + + +/** + * A 12-argument {@link Consumer} + * + * @hide + */ +public interface DodecConsumer<A, B, C, D, E, F, G, H, I, J, K, L> { + void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l); +} diff --git a/core/java/com/android/internal/util/function/DodecFunction.java b/core/java/com/android/internal/util/function/DodecFunction.java new file mode 100644 index 000000000000..178b2c111fd7 --- /dev/null +++ b/core/java/com/android/internal/util/function/DodecFunction.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.function; + +import java.util.function.Function; + +/** + * A 12-argument {@link Function} + * + * @hide + */ +public interface DodecFunction<A, B, C, D, E, F, G, H, I, J, K, L, R> { + R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l); +} diff --git a/core/java/com/android/internal/util/function/DodecPredicate.java b/core/java/com/android/internal/util/function/DodecPredicate.java new file mode 100644 index 000000000000..d3a2b856100f --- /dev/null +++ b/core/java/com/android/internal/util/function/DodecPredicate.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.function; + +import java.util.function.Predicate; + +/** + * A 12-argument {@link Predicate} + * + * @hide + */ +public interface DodecPredicate<A, B, C, D, E, F, G, H, I, J, K, L> { + boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l); +} diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java index a60cc0fb101f..f073c1c046c5 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java @@ -23,6 +23,8 @@ import android.os.Message; import com.android.internal.util.function.DecConsumer; import com.android.internal.util.function.DecFunction; +import com.android.internal.util.function.DodecConsumer; +import com.android.internal.util.function.DodecFunction; import com.android.internal.util.function.HeptConsumer; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexConsumer; @@ -188,7 +190,7 @@ public interface PooledLambda { A arg1) { return acquire(PooledLambdaImpl.sPool, function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -205,7 +207,7 @@ public interface PooledLambda { A arg1) { return acquire(PooledLambdaImpl.sPool, function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -222,7 +224,7 @@ public interface PooledLambda { A arg1) { return acquire(PooledLambdaImpl.sPool, function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -253,7 +255,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null, - null, null, null); + null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -273,7 +275,7 @@ public interface PooledLambda { A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -291,7 +293,7 @@ public interface PooledLambda { A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -309,7 +311,7 @@ public interface PooledLambda { A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -327,7 +329,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -345,7 +347,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -364,7 +366,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -384,7 +386,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -405,7 +407,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, function, 5, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, arg5, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -423,7 +425,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -441,7 +443,7 @@ public interface PooledLambda { A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -459,7 +461,7 @@ public interface PooledLambda { A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -477,7 +479,7 @@ public interface PooledLambda { A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -509,7 +511,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, - null, null, null); + null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -530,7 +532,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -549,7 +551,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -568,7 +570,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -587,7 +589,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -606,7 +608,7 @@ public interface PooledLambda { A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -625,7 +627,7 @@ public interface PooledLambda { A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -644,7 +646,7 @@ public interface PooledLambda { A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -663,7 +665,7 @@ public interface PooledLambda { A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -696,7 +698,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, - null, null, null); + null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -718,7 +720,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -738,7 +740,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -758,7 +760,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -778,7 +780,7 @@ public interface PooledLambda { ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -798,7 +800,7 @@ public interface PooledLambda { A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -818,7 +820,7 @@ public interface PooledLambda { A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -838,7 +840,7 @@ public interface PooledLambda { A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -858,7 +860,7 @@ public interface PooledLambda { A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -878,7 +880,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -898,7 +900,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -932,7 +934,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, - null, null, null); + null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -955,7 +957,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -976,7 +978,7 @@ public interface PooledLambda { function, A arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null, - null, null, null); + null, null, null, null); } /** @@ -1012,7 +1014,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null, - null, null, null); + null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -1036,7 +1038,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { return acquire(PooledLambdaImpl.sPool, function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null, - null, null, null); + null, null, null, null); } /** @@ -1058,7 +1060,7 @@ public interface PooledLambda { ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { return acquire(PooledLambdaImpl.sPool, function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null, - null, null, null); + null, null, null, null); } /** @@ -1095,7 +1097,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null, - null, null, null); + null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -1120,7 +1122,7 @@ public interface PooledLambda { ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { return acquire(PooledLambdaImpl.sPool, function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, - null, null, null); + null, null, null, null); } /** @@ -1144,7 +1146,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { return acquire(PooledLambdaImpl.sPool, function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, - null, null, null); + null, null, null, null); } /** @@ -1182,7 +1184,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, - null, null, null); + null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -1209,7 +1211,7 @@ public interface PooledLambda { H arg8) { return acquire(PooledLambdaImpl.sPool, function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - null, null, null); + null, null, null, null); } /** @@ -1234,7 +1236,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) { return acquire(PooledLambdaImpl.sPool, function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - null, null, null); + null, null, null, null); } /** @@ -1274,7 +1276,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - null, null, null); + null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -1302,7 +1304,7 @@ public interface PooledLambda { E arg5, F arg6, G arg7, H arg8, I arg9) { return acquire(PooledLambdaImpl.sPool, function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - arg9, null, null); + arg9, null, null, null); } /** @@ -1328,7 +1330,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) { return acquire(PooledLambdaImpl.sPool, function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - arg9, null, null); + arg9, null, null, null); } /** @@ -1369,7 +1371,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - arg9, null, null); + arg9, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -1398,7 +1400,7 @@ public interface PooledLambda { D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) { return acquire(PooledLambdaImpl.sPool, function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - arg9, arg10, null); + arg9, arg10, null, null); } /** @@ -1425,7 +1427,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) { return acquire(PooledLambdaImpl.sPool, function, 10, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - arg9, arg10, null); + arg9, arg10, null, null); } /** @@ -1467,7 +1469,7 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, - arg8, arg9, arg10, null); + arg8, arg9, arg10, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -1498,7 +1500,7 @@ public interface PooledLambda { C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, K arg11) { return acquire(PooledLambdaImpl.sPool, function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - arg9, arg10, arg11); + arg9, arg10, arg11, null); } /** @@ -1528,7 +1530,7 @@ public interface PooledLambda { K arg11) { return acquire(PooledLambdaImpl.sPool, function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, - arg9, arg10, arg11); + arg9, arg10, arg11, null); } /** @@ -1571,7 +1573,118 @@ public interface PooledLambda { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, - arg8, arg9, arg10, arg11); + arg8, arg9, arg10, arg11, null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @param arg10 parameter supplied to {@code function} on call + * @param arg11 parameter supplied to {@code function} on call + * @param arg12 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, + * arg11, arg12) } + */ + static <A, B, C, D, E, F, G, H, I, J, K, L> PooledRunnable obtainRunnable( + DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I, ? super J, ? super K, + ? super L> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, + K arg11, L arg12) { + return acquire(PooledLambdaImpl.sPool, + function, 12, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @param arg10 parameter supplied to {@code function} on call + * @param arg11 parameter supplied to {@code function} on call + * @param arg12 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, + * arg11) } + */ + static <A, B, C, D, E, F, G, H, I, J, K, L, R> PooledSupplier<R> obtainSupplier( + DodecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I, ? super J, ? super K, ? extends L, + ? extends R> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, + K arg11, L arg12) { + return acquire(PooledLambdaImpl.sPool, + function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @param arg10 parameter supplied to {@code function} on call + * @param arg11 parameter supplied to {@code function} on call + * @param arg12 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6, + * arg7, arg8, arg9, arg10, arg11) } when handled + */ + static <A, B, C, D, E, F, G, H, I, J, K, L> Message obtainMessage( + DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I, ? super J, ? super K, ? super L> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, + K arg11, L arg12) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12); return Message.obtain().setCallback(callback.recycleOnUse()); } } diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index 1646a07b8001..19f0816e3e48 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -28,6 +28,9 @@ import com.android.internal.util.FunctionalUtils; import com.android.internal.util.function.DecConsumer; import com.android.internal.util.function.DecFunction; import com.android.internal.util.function.DecPredicate; +import com.android.internal.util.function.DodecConsumer; +import com.android.internal.util.function.DodecFunction; +import com.android.internal.util.function.DodecPredicate; import com.android.internal.util.function.HeptConsumer; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HeptPredicate; @@ -458,6 +461,28 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } } } break; + + case 12: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((DodecConsumer) mFunc).accept(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), popArg(5), + popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((DodecPredicate) mFunc).test(popArg(0), + popArg(1), popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), + popArg(11)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((DodecFunction) mFunc).apply(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), popArg(5), + popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11)); + } + } + } break; } throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType)); } @@ -523,7 +548,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, */ static <E extends PooledLambda> E acquire(Pool pool, Object func, int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c, - Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k) { + Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k, + Object l) { PooledLambdaImpl r = acquire(pool); if (DEBUG) { Log.i(LOG_TAG, @@ -543,6 +569,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, + ", i = " + i + ", j = " + j + ", k = " + k + + ", l = " + l + ")"); } r.mFunc = Objects.requireNonNull(func); @@ -560,6 +587,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, setIfInBounds(r.mArgs, 8, i); setIfInBounds(r.mArgs, 9, j); setIfInBounds(r.mArgs, 10, k); + setIfInBounds(r.mArgs, 11, l); return (E) r; } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 99a33e4462e2..5ba75d3ac312 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -19,11 +19,13 @@ package com.android.server.appop; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; +import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP; import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG; import static android.app.AppOpsManager.FILTER_BY_OP_NAMES; import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME; import static android.app.AppOpsManager.FILTER_BY_UID; +import static android.app.AppOpsManager.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS; import static android.app.AppOpsManager.HistoricalOpsRequestFilter; import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME; import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME; @@ -130,6 +132,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManagerInternal; +import android.permission.PermissionManager; import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; @@ -200,8 +203,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Scanner; +import java.util.Set; import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; public class AppOpsService extends IAppOpsService.Stub { @@ -2357,10 +2360,21 @@ public class AppOpsService extends IAppOpsService.Stub { final String[] opNamesArray = (opNames != null) ? opNames.toArray(new String[opNames.size()]) : null; + Set<String> attributionChainExemptPackages = null; + if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) { + attributionChainExemptPackages = + PermissionManager.getIndicatorExemptedPackages(mContext); + } + + final String[] chainExemptPkgArray = attributionChainExemptPackages != null + ? attributionChainExemptPackages.toArray( + new String[attributionChainExemptPackages.size()]) : null; + // Must not hold the appops lock mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps, mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType, - filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse()); + filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray, + callback).recycleOnUse()); } @Override @@ -2377,10 +2391,21 @@ public class AppOpsService extends IAppOpsService.Stub { final String[] opNamesArray = (opNames != null) ? opNames.toArray(new String[opNames.size()]) : null; + Set<String> attributionChainExemptPackages = null; + if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) { + attributionChainExemptPackages = + PermissionManager.getIndicatorExemptedPackages(mContext); + } + + final String[] chainExemptPkgArray = attributionChainExemptPackages != null + ? attributionChainExemptPackages.toArray( + new String[attributionChainExemptPackages.size()]) : null; + // Must not hold the appops lock mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw, mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType, - filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse()); + filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray, + callback).recycleOnUse()); } @Override @@ -3838,7 +3863,8 @@ public class AppOpsService extends IAppOpsService.Stub { final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid; final boolean isProxyTrusted = mContext.checkPermission( Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid) - == PackageManager.PERMISSION_GRANTED || isSelfBlame; + == PackageManager.PERMISSION_GRANTED || isSelfBlame + || attributionChainId != ATTRIBUTION_CHAIN_ID_NONE; String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid, proxiedPackageName); @@ -5174,8 +5200,6 @@ public class AppOpsService extends IAppOpsService.Stub { } static class Shell extends ShellCommand { - static final AtomicInteger sAttributionChainIds = new AtomicInteger(0); - final IAppOpsService mInterface; final AppOpsService mInternal; @@ -5645,8 +5669,7 @@ public class AppOpsService extends IAppOpsService.Stub { shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid, shell.packageName, shell.attributionTag, true, true, "appops start shell command", true, - AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, - shell.sAttributionChainIds.incrementAndGet()); + AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, ATTRIBUTION_CHAIN_ID_NONE); } else { return -1; } diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java index 1e32129dcee1..8b2d9e7d071d 100644 --- a/services/core/java/com/android/server/appop/DiscreteRegistry.java +++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java @@ -17,6 +17,8 @@ package com.android.server.appop; import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; +import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR; +import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER; import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG; import static android.app.AppOpsManager.FILTER_BY_OP_NAMES; import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME; @@ -72,6 +74,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Objects; +import java.util.Set; /** * This class manages information about recent accesses to ops for permission usage timeline. @@ -270,13 +274,19 @@ final class DiscreteRegistry { long beginTimeMillis, long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter, - @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) { + @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter, + Set<String> attributionExemptPkgs) { + boolean assembleChains = attributionExemptPkgs != null; DiscreteOps discreteOps = getAllDiscreteOps(); + ArrayMap<Integer, AttributionChain> attributionChains = new ArrayMap<>(); + if (assembleChains) { + attributionChains = createAttributionChains(discreteOps, attributionExemptPkgs); + } beginTimeMillis = max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff, ChronoUnit.MILLIS).toEpochMilli()); discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter, - opNamesFilter, attributionTagFilter, flagsFilter); - discreteOps.applyToHistoricalOps(result); + opNamesFilter, attributionTagFilter, flagsFilter, attributionChains); + discreteOps.applyToHistoricalOps(result, attributionChains); return; } @@ -317,13 +327,56 @@ final class DiscreteRegistry { } finally { try { stream.close(); - } catch (IOException e) { } + } catch (IOException e) { + } } } else { return 0; } } + private ArrayMap<Integer, AttributionChain> createAttributionChains( + DiscreteOps discreteOps, Set<String> attributionExemptPkgs) { + ArrayMap<Integer, AttributionChain> chains = new ArrayMap<>(); + int nUids = discreteOps.mUids.size(); + for (int uidNum = 0; uidNum < nUids; uidNum++) { + ArrayMap<String, DiscretePackageOps> pkgs = discreteOps.mUids.valueAt(uidNum).mPackages; + int uid = discreteOps.mUids.keyAt(uidNum); + int nPackages = pkgs.size(); + for (int pkgNum = 0; pkgNum < nPackages; pkgNum++) { + ArrayMap<Integer, DiscreteOp> ops = pkgs.valueAt(pkgNum).mPackageOps; + String pkg = pkgs.keyAt(pkgNum); + int nOps = ops.size(); + for (int opNum = 0; opNum < nOps; opNum++) { + ArrayMap<String, List<DiscreteOpEvent>> attrOps = + ops.valueAt(opNum).mAttributedOps; + int op = ops.keyAt(opNum); + int nAttrOps = attrOps.size(); + for (int attrOpNum = 0; attrOpNum < nAttrOps; attrOpNum++) { + List<DiscreteOpEvent> opEvents = attrOps.valueAt(attrOpNum); + String attributionTag = attrOps.keyAt(attrOpNum); + int nOpEvents = opEvents.size(); + for (int opEventNum = 0; opEventNum < nOpEvents; opEventNum++) { + DiscreteOpEvent event = opEvents.get(opEventNum); + if (event == null + || event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE) { + continue; + } + + if (!chains.containsKey(event.mAttributionChainId)) { + chains.put(event.mAttributionChainId, + new AttributionChain(attributionExemptPkgs)); + } + chains.get(event.mAttributionChainId) + .addEvent(pkg, uid, attributionTag, op, event); + } + } + } + } + } + return chains; + } + private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) { synchronized (mOnDiskLock) { long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff, @@ -389,7 +442,7 @@ final class DiscreteRegistry { String[] opNamesFilter = dumpOp == OP_NONE ? null : new String[]{AppOpsManager.opToPublicName(dumpOp)}; discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter, - opNamesFilter, attributionTagFilter, OP_FLAGS_ALL); + opNamesFilter, attributionTagFilter, OP_FLAGS_ALL, new ArrayMap<>()); pw.print(prefix); pw.print("Largest chain id: "); pw.print(mDiscreteOps.mLargestChainId); @@ -419,6 +472,134 @@ final class DiscreteRegistry { } } + /** + * Represents a chain of usages, each attributing its usage to the one before it + */ + private static final class AttributionChain { + private static final class OpEvent { + String mPkgName; + int mUid; + String mAttributionTag; + int mOpCode; + DiscreteOpEvent mOpEvent; + + OpEvent(String pkgName, int uid, String attributionTag, int opCode, + DiscreteOpEvent event) { + mPkgName = pkgName; + mUid = uid; + mAttributionTag = attributionTag; + mOpCode = opCode; + mOpEvent = event; + } + + public boolean matches(String pkgName, int uid, String attributionTag, int opCode, + DiscreteOpEvent event) { + return Objects.equals(pkgName, mPkgName) && mUid == uid + && Objects.equals(attributionTag, mAttributionTag) && mOpCode == opCode + && mOpEvent.mAttributionChainId == event.mAttributionChainId + && mOpEvent.mAttributionFlags == event.mAttributionFlags + && mOpEvent.mNoteTime == event.mNoteTime; + } + + public boolean packageOpEquals(OpEvent other) { + return Objects.equals(other.mPkgName, mPkgName) && other.mUid == mUid + && Objects.equals(other.mAttributionTag, mAttributionTag) + && mOpCode == other.mOpCode; + } + + public boolean equalsExceptDuration(OpEvent other) { + if (other.mOpEvent.mNoteDuration == mOpEvent.mNoteDuration) { + return false; + } + return packageOpEquals(other) && mOpEvent.equalsExceptDuration(other.mOpEvent); + } + } + + ArrayList<OpEvent> mChain = new ArrayList<>(); + Set<String> mExemptPkgs; + OpEvent mStartEvent = null; + OpEvent mLastVisibleEvent = null; + + AttributionChain(Set<String> exemptPkgs) { + mExemptPkgs = exemptPkgs; + } + + boolean isComplete() { + return !mChain.isEmpty() && getStart() != null && isEnd(mChain.get(mChain.size() - 1)); + } + + boolean isStart(String pkgName, int uid, String attributionTag, int op, + DiscreteOpEvent opEvent) { + if (mStartEvent == null || opEvent == null) { + return false; + } + return mStartEvent.matches(pkgName, uid, attributionTag, op, opEvent); + } + + private OpEvent getStart() { + return mChain.isEmpty() || !isStart(mChain.get(0)) ? null : mChain.get(0); + } + + private OpEvent getLastVisible() { + // Search all nodes but the first one, which is the start node + for (int i = mChain.size() - 1; i > 0; i++) { + OpEvent event = mChain.get(i); + if (!mExemptPkgs.contains(event.mPkgName)) { + return event; + } + } + return null; + } + + void addEvent(String pkgName, int uid, String attributionTag, int op, + DiscreteOpEvent opEvent) { + OpEvent event = new OpEvent(pkgName, uid, attributionTag, op, opEvent); + + // check if we have a matching event, without duration, replacing duration otherwise + for (int i = 0; i < mChain.size(); i++) { + OpEvent item = mChain.get(i); + if (item.equalsExceptDuration(event)) { + if (event.mOpEvent.mNoteDuration != -1) { + item.mOpEvent = event.mOpEvent; + } + return; + } + } + + if (mChain.isEmpty() || isEnd(event)) { + mChain.add(event); + } else if (isStart(event)) { + mChain.add(0, event); + + } else { + for (int i = 0; i < mChain.size(); i++) { + OpEvent currEvent = mChain.get(i); + if ((!isStart(currEvent) + && currEvent.mOpEvent.mNoteTime > event.mOpEvent.mNoteTime) + || i == mChain.size() - 1 && isEnd(currEvent)) { + mChain.add(i, event); + break; + } else if (i == mChain.size() - 1) { + mChain.add(event); + break; + } + } + } + mStartEvent = isComplete() ? getStart() : null; + mLastVisibleEvent = isComplete() ? getLastVisible() : null; + } + + private boolean isEnd(OpEvent event) { + return event != null + && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_ACCESSOR) != 0; + } + + private boolean isStart(OpEvent event) { + return event != null + && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_RECEIVER) != 0; + } + } + private final class DiscreteOps { ArrayMap<Integer, DiscreteUidOps> mUids; int mChainIdOffset; @@ -448,25 +629,27 @@ final class DiscreteRegistry { @Nullable String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime, long accessDuration, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { + int offsetChainId = attributionChainId; if (attributionChainId != ATTRIBUTION_CHAIN_ID_NONE) { - attributionChainId += mChainIdOffset; - if (attributionChainId < 0) { - attributionChainId -= mChainIdOffset; - mChainIdOffset = 0; - mLargestChainId = attributionChainId; - } - if (attributionChainId > mLargestChainId) { - mLargestChainId = attributionChainId; + offsetChainId = attributionChainId + mChainIdOffset; + if (offsetChainId > mLargestChainId) { + mLargestChainId = offsetChainId; + } else if (offsetChainId < 0) { + // handle overflow + offsetChainId = 0; + mLargestChainId = 0; + mChainIdOffset = -1 * attributionChainId; } } getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags, - uidState, accessTime, accessDuration, attributionFlags, attributionChainId); + uidState, accessTime, accessDuration, attributionFlags, offsetChainId); } private void filter(long beginTimeMillis, long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter, - @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) { + @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter, + ArrayMap<Integer, AttributionChain> attributionChains) { if ((filter & FILTER_BY_UID) != 0) { ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>(); uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter)); @@ -475,7 +658,8 @@ final class DiscreteRegistry { int nUids = mUids.size(); for (int i = nUids - 1; i >= 0; i--) { mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter, - opNamesFilter, attributionTagFilter, flagsFilter); + opNamesFilter, attributionTagFilter, flagsFilter, mUids.keyAt(i), + attributionChains); if (mUids.valueAt(i).isEmpty()) { mUids.removeAt(i); } @@ -498,10 +682,11 @@ final class DiscreteRegistry { } } - private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) { + private void applyToHistoricalOps(AppOpsManager.HistoricalOps result, + ArrayMap<Integer, AttributionChain> attributionChains) { int nUids = mUids.size(); for (int i = 0; i < nUids; i++) { - mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i)); + mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i), attributionChains); } } @@ -668,7 +853,8 @@ final class DiscreteRegistry { private void filter(long beginTimeMillis, long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter, - @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) { + @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter, + int currentUid, ArrayMap<Integer, AttributionChain> attributionChains) { if ((filter & FILTER_BY_PACKAGE_NAME) != 0) { ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>(); packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter)); @@ -677,7 +863,8 @@ final class DiscreteRegistry { int nPackages = mPackages.size(); for (int i = nPackages - 1; i >= 0; i--) { mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter, - attributionTagFilter, flagsFilter); + attributionTagFilter, flagsFilter, currentUid, mPackages.keyAt(i), + attributionChains); if (mPackages.valueAt(i).isEmpty()) { mPackages.removeAt(i); } @@ -712,10 +899,12 @@ final class DiscreteRegistry { return result; } - private void applyToHistory(AppOpsManager.HistoricalOps result, int uid) { + private void applyToHistory(AppOpsManager.HistoricalOps result, int uid, + @NonNull ArrayMap<Integer, AttributionChain> attributionChains) { int nPackages = mPackages.size(); for (int i = 0; i < nPackages; i++) { - mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i)); + mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i), + attributionChains); } } @@ -783,7 +972,8 @@ final class DiscreteRegistry { private void filter(long beginTimeMillis, long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter, - @AppOpsManager.OpFlags int flagsFilter) { + @AppOpsManager.OpFlags int flagsFilter, int currentUid, String currentPkgName, + ArrayMap<Integer, AttributionChain> attributionChains) { int nOps = mPackageOps.size(); for (int i = nOps - 1; i >= 0; i--) { int opId = mPackageOps.keyAt(i); @@ -793,7 +983,8 @@ final class DiscreteRegistry { continue; } mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, - attributionTagFilter, flagsFilter); + attributionTagFilter, flagsFilter, currentUid, currentPkgName, + mPackageOps.keyAt(i), attributionChains); if (mPackageOps.valueAt(i).isEmpty()) { mPackageOps.removeAt(i); } @@ -817,11 +1008,12 @@ final class DiscreteRegistry { } private void applyToHistory(AppOpsManager.HistoricalOps result, int uid, - @NonNull String packageName) { + @NonNull String packageName, + @NonNull ArrayMap<Integer, AttributionChain> attributionChains) { int nPackageOps = mPackageOps.size(); for (int i = 0; i < nPackageOps; i++) { mPackageOps.valueAt(i).applyToHistory(result, uid, packageName, - mPackageOps.keyAt(i)); + mPackageOps.keyAt(i), attributionChains); } } @@ -880,7 +1072,9 @@ final class DiscreteRegistry { private void filter(long beginTimeMillis, long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, - @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) { + @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter, + int currentUid, String currentPkgName, int currentOp, + ArrayMap<Integer, AttributionChain> attributionChains) { if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) { ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>(); attributedOps.put(attributionTagFilter, @@ -892,7 +1086,9 @@ final class DiscreteRegistry { for (int i = nTags - 1; i >= 0; i--) { String tag = mAttributedOps.keyAt(i); List<DiscreteOpEvent> list = mAttributedOps.valueAt(i); - list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter); + list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter, + currentUid, currentPkgName, currentOp, mAttributedOps.keyAt(i), + attributionChains); mAttributedOps.put(tag, list); if (list.size() == 0) { mAttributedOps.removeAt(i); @@ -954,7 +1150,8 @@ final class DiscreteRegistry { } private void applyToHistory(AppOpsManager.HistoricalOps result, int uid, - @NonNull String packageName, int op) { + @NonNull String packageName, int op, + @NonNull ArrayMap<Integer, AttributionChain> attributionChains) { int nOps = mAttributedOps.size(); for (int i = 0; i < nOps; i++) { String tag = mAttributedOps.keyAt(i); @@ -962,9 +1159,21 @@ final class DiscreteRegistry { int nEvents = events.size(); for (int j = 0; j < nEvents; j++) { DiscreteOpEvent event = events.get(j); + AppOpsManager.OpEventProxyInfo proxy = null; + if (event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE + && attributionChains != null) { + AttributionChain chain = attributionChains.get(event.mAttributionChainId); + if (chain != null && chain.isComplete() + && chain.isStart(packageName, uid, tag, op, event) + && chain.mLastVisibleEvent != null) { + AttributionChain.OpEvent proxyEvent = chain.mLastVisibleEvent; + proxy = new AppOpsManager.OpEventProxyInfo(proxyEvent.mUid, + proxyEvent.mPkgName, proxyEvent.mAttributionTag); + } + } result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState, event.mOpFlag, discretizeTimeStamp(event.mNoteTime), - discretizeDuration(event.mNoteDuration)); + discretizeDuration(event.mNoteDuration), proxy); } } } @@ -1059,6 +1268,13 @@ final class DiscreteRegistry { mAttributionChainId = attributionChainId; } + public boolean equalsExceptDuration(DiscreteOpEvent o) { + return mNoteTime == o.mNoteTime && mUidState == o.mUidState && mOpFlag == o.mOpFlag + && mAttributionFlags == o.mAttributionFlags + && mAttributionChainId == o.mAttributionChainId; + + } + private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) { pw.print(prefix); @@ -1141,11 +1357,20 @@ final class DiscreteRegistry { } private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list, - long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) { + long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter, + int currentUid, String currentPackageName, int currentOp, String currentAttrTag, + ArrayMap<Integer, AttributionChain> attributionChains) { int n = list.size(); List<DiscreteOpEvent> result = new ArrayList<>(n); for (int i = 0; i < n; i++) { DiscreteOpEvent event = list.get(i); + AttributionChain chain = attributionChains.get(event.mAttributionChainId); + // If we have an attribution chain, and this event isn't the beginning node, remove it + if (chain != null && !chain.isStart(currentPackageName, currentUid, currentAttrTag, + currentOp, event) && chain.isComplete() + && event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE) { + continue; + } if ((event.mOpFlag & flagsFilter) != 0 && event.mNoteTime + event.mNoteDuration > beginTimeMillis && event.mNoteTime < endTimeMillis) { diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 62472b5f1d75..2c68aaf4f3e5 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -370,7 +370,7 @@ final class HistoricalRegistry { @Nullable String attributionTag, @Nullable String[] opNames, @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, - @NonNull RemoteCallback callback) { + String[] attributionExemptedPackages, @NonNull RemoteCallback callback) { if (!isApiEnabled()) { callback.sendResult(new Bundle()); return; @@ -396,7 +396,7 @@ final class HistoricalRegistry { if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) { mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis, endTimeMillis, filter, uid, packageName, opNames, attributionTag, - flags); + flags, new ArraySet<>(attributionExemptedPackages)); } final Bundle payload = new Bundle(); @@ -407,7 +407,8 @@ final class HistoricalRegistry { void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, - @OpFlags int flags, @NonNull RemoteCallback callback) { + @OpFlags int flags, @Nullable String[] attributionExemptPkgs, + @NonNull RemoteCallback callback) { if (!isApiEnabled()) { callback.sendResult(new Bundle()); return; @@ -429,7 +430,8 @@ final class HistoricalRegistry { if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) { mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis, - endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags); + endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags, + new ArraySet<>(attributionExemptPkgs)); } if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index a391dbc1fa47..38e9d3ec34e3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -5720,10 +5720,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean fromDatasource, int attributedOp) { // Now let's check the identity chain... final int op = AppOpsManager.permissionToOpCode(permission); - final int attributionChainId = (startDataDelivery) - ? sAttributionChainIds.incrementAndGet() - : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; - + final int attributionChainId = + getAttributionChainId(startDataDelivery, attributionSource); AttributionSource current = attributionSource; AttributionSource next = null; @@ -5879,9 +5877,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PermissionChecker.PERMISSION_HARD_DENIED; } - final int attributionChainId = (startDataDelivery) - ? sAttributionChainIds.incrementAndGet() - : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; + final int attributionChainId = + getAttributionChainId(startDataDelivery, attributionSource); AttributionSource current = attributionSource; AttributionSource next = null; @@ -6064,6 +6061,21 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + private static int getAttributionChainId(boolean startDataDelivery, + AttributionSource source) { + if (source == null || source.getNext() == null || !startDataDelivery) { + return AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; + } + int attributionChainId = sAttributionChainIds.incrementAndGet(); + + // handle overflow + if (attributionChainId < 0) { + attributionChainId = 0; + sAttributionChainIds.set(0); + } + return attributionChainId; + } + private static @Nullable String resolvePackageName(@NonNull Context context, @NonNull AttributionSource attributionSource) { if (attributionSource.getPackageName() != null) { |