diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2018-02-20 18:46:57 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-02-20 18:46:57 +0000 |
commit | 414189fefe71beb998541cb2932b90f073ae8bcd (patch) | |
tree | b4be11d6537f477ec1914eaba919cfba11c6289a | |
parent | 3feba764d6fe59056f3e38b3d7d2088890a29087 (diff) | |
parent | 2d20fb47f4a7162450f993728876c74762b93112 (diff) |
Merge "APIs to watch active op changes"
21 files changed, 1005 insertions, 206 deletions
diff --git a/Android.bp b/Android.bp index e65ba0f6a95f..ea1ed91f1ded 100644 --- a/Android.bp +++ b/Android.bp @@ -356,6 +356,7 @@ java_library { "core/java/android/speech/IRecognitionService.aidl", "core/java/android/speech/tts/ITextToSpeechCallback.aidl", "core/java/android/speech/tts/ITextToSpeechService.aidl", + "core/java/com/android/internal/app/IAppOpsActiveCallback.aidl", "core/java/com/android/internal/app/IAppOpsCallback.aidl", "core/java/com/android/internal/app/IAppOpsService.aidl", "core/java/com/android/internal/app/IBatteryStats.aidl", diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 1026550b9b6b..c5b3a4acd339 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -17,6 +17,7 @@ package android.app; import android.Manifest; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -34,8 +35,10 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; +import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; +import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Arrays; @@ -74,8 +77,9 @@ public class AppOpsManager { final Context mContext; final IAppOpsService mService; - final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers - = new ArrayMap<OnOpChangedListener, IAppOpsCallback>(); + final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>(); + final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers = + new ArrayMap<>(); static IBinder sToken; @@ -1532,6 +1536,23 @@ public class AppOpsManager { } /** + * Callback for notification of changes to operation active state. + * + * @hide + */ + public interface OnOpActiveChangedListener { + /** + * Called when the active state of an app op changes. + * + * @param code The op code. + * @param uid The UID performing the operation. + * @param packageName The package performing the operation. + * @param active Whether the operation became active or inactive. + */ + void onOpActiveChanged(int code, int uid, String packageName, boolean active); + } + + /** * Callback for notification of changes to operation state. * This allows you to see the raw op codes instead of strings. * @hide @@ -1695,6 +1716,8 @@ public class AppOpsManager { /** * Monitor for changes to the operating mode for the given op in the given app package. + * You can watch op changes only for your UID. + * * @param op The operation to monitor, one of OPSTR_*. * @param packageName The name of the application to monitor. * @param callback Where to report changes. @@ -1706,11 +1729,17 @@ public class AppOpsManager { /** * Monitor for changes to the operating mode for the given op in the given app package. + * + * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission + * to watch changes only for your UID. + * * @param op The operation to monitor, one of OP_*. * @param packageName The name of the application to monitor. * @param callback Where to report changes. * @hide */ + // TODO: Uncomment below annotation once b/73559440 is fixed + // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); @@ -1752,6 +1781,74 @@ public class AppOpsManager { } } + /** + * Start watching for changes to the active state of app ops. An app op may be + * long running and it has a clear start and stop delimiters. If an op is being + * started or stopped by any package you will get a callback. To change the + * watched ops for a registered callback you need to unregister and register it + * again. + * + * @param ops The ops to watch. + * @param callback Where to report changes. + * + * @see #isOperationActive(int, int, String) + * @see #stopWatchingActive(OnOpActiveChangedListener) + * @see #startOp(int, int, String) + * @see #finishOp(int, int, String) + * + * @hide + */ + @RequiresPermission(Manifest.permission.WATCH_APPOPS) + public void startWatchingActive(@NonNull int[] ops, + @NonNull OnOpActiveChangedListener callback) { + Preconditions.checkNotNull(ops, "ops cannot be null"); + Preconditions.checkNotNull(callback, "callback cannot be null"); + IAppOpsActiveCallback cb; + synchronized (mActiveWatchers) { + cb = mActiveWatchers.get(callback); + if (cb != null) { + return; + } + cb = new IAppOpsActiveCallback.Stub() { + @Override + public void opActiveChanged(int op, int uid, String packageName, boolean active) { + callback.onOpActiveChanged(op, uid, packageName, active); + } + }; + mActiveWatchers.put(callback, cb); + } + try { + mService.startWatchingActive(ops, cb); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Stop watching for changes to the active state of an app op. An app op may be + * long running and it has a clear start and stop delimiters. Unregistering a + * non-registered callback has no effect. + * + * @see #isOperationActive#(int, int, String) + * @see #startWatchingActive(int[], OnOpActiveChangedListener) + * @see #startOp(int, int, String) + * @see #finishOp(int, int, String) + * + * @hide + */ + public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) { + synchronized (mActiveWatchers) { + final IAppOpsActiveCallback cb = mActiveWatchers.get(callback); + if (cb != null) { + try { + mService.stopWatchingActive(cb); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + private String buildSecurityExceptionMsg(int op, int uid, String packageName) { return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op]; } @@ -2145,6 +2242,7 @@ public class AppOpsManager { } /** @hide */ + @RequiresPermission(Manifest.permission.WATCH_APPOPS) public boolean isOperationActive(int code, int uid, String packageName) { try { return mService.isOperationActive(code, uid, packageName); diff --git a/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl b/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl new file mode 100644 index 000000000000..510af770d126 --- /dev/null +++ b/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 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.app; + +// Iterface to observe op active changes +oneway interface IAppOpsActiveCallback { + void opActiveChanged(int op, int uid, String packageName, boolean active); +} diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 2b975fe03bf2..fabda4a5c15e 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -19,6 +19,7 @@ package com.android.internal.app; import android.app.AppOpsManager; import android.os.Bundle; import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsActiveCallback; interface IAppOpsService { // These first methods are also called by native code, so must @@ -49,5 +50,7 @@ interface IAppOpsService { void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in String[] exceptionPackages); void removeUser(int userHandle); + void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback); + void stopWatchingActive(IAppOpsActiveCallback callback); boolean isOperationActive(int code, int uid, String packageName); } diff --git a/core/java/com/android/internal/util/function/HexConsumer.java b/core/java/com/android/internal/util/function/HexConsumer.java new file mode 100644 index 000000000000..ef6aee24afc1 --- /dev/null +++ b/core/java/com/android/internal/util/function/HexConsumer.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 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 6-argument {@link Consumer} + * + * @hide + */ +public interface HexConsumer<A, B, C, D, E, F> { + void accept(A a, B b, C c, D d, E e, F f); +} diff --git a/core/java/com/android/internal/util/function/HexFunction.java b/core/java/com/android/internal/util/function/HexFunction.java new file mode 100644 index 000000000000..6268daf88bc7 --- /dev/null +++ b/core/java/com/android/internal/util/function/HexFunction.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 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 6-argument {@link Function} + * + * @hide + */ +public interface HexFunction<A, B, C, D, E, F, R> { + R apply(A a, B b, C c, D d, E e, F f); +} diff --git a/core/java/com/android/internal/util/function/HexPredicate.java b/core/java/com/android/internal/util/function/HexPredicate.java new file mode 100644 index 000000000000..c6ebf6ad1718 --- /dev/null +++ b/core/java/com/android/internal/util/function/HexPredicate.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 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 6-argument {@link Predicate} + * + * @hide + */ +public interface HexPredicate<A, B, C, D, E, F> { + boolean test(A a, B b, C c, D d, E e, F f); +} diff --git a/core/java/com/android/internal/util/function/QuintConsumer.java b/core/java/com/android/internal/util/function/QuintConsumer.java new file mode 100644 index 000000000000..ebbc5ad7da80 --- /dev/null +++ b/core/java/com/android/internal/util/function/QuintConsumer.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 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 5-argument {@link Consumer} + * + * @hide + */ +public interface QuintConsumer<A, B, C, D, E> { + void accept(A a, B b, C c, D d, E e); +} diff --git a/core/java/com/android/internal/util/function/QuintFunction.java b/core/java/com/android/internal/util/function/QuintFunction.java new file mode 100644 index 000000000000..1b58f1f10810 --- /dev/null +++ b/core/java/com/android/internal/util/function/QuintFunction.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 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 5-argument {@link Function} + * + * @hide + */ +public interface QuintFunction<A, B, C, D, E, R> { + R apply(A a, B b, C c, D d, E e); +} diff --git a/core/java/com/android/internal/util/function/QuintPredicate.java b/core/java/com/android/internal/util/function/QuintPredicate.java new file mode 100644 index 000000000000..5e1f11debbf5 --- /dev/null +++ b/core/java/com/android/internal/util/function/QuintPredicate.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 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 5-argument {@link Predicate} + * + * @hide + */ +public interface QuintPredicate<A, B, C, D, E> { + boolean test(A a, B b, C c, D d, E e); +} diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java index c0f506ec889f..9378869d6934 100755 --- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java +++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java @@ -18,8 +18,12 @@ package com.android.internal.util.function.pooled; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.FunctionalUtils.ThrowingSupplier; +import com.android.internal.util.function.HexConsumer; +import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; +import com.android.internal.util.function.QuintConsumer; +import com.android.internal.util.function.QuintFunction; import com.android.internal.util.function.TriConsumer; import com.android.internal.util.function.TriFunction; @@ -33,58 +37,59 @@ import java.util.function.Function; * * @hide */ -abstract class OmniFunction<A, B, C, D, R> implements +abstract class OmniFunction<A, B, C, D, E, F, R> implements PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>, - QuadFunction<A, B, C, D, R>, - PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>, - PooledPredicate<A>, BiPredicate<A, B>, - PooledSupplier<R>, PooledRunnable, - ThrowingRunnable, ThrowingSupplier<R>, + QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>, + HexFunction<A, B, C, D, E, F, R>, PooledConsumer<A>, BiConsumer<A, B>, + TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>, QuintConsumer<A, B, C, D, E>, + HexConsumer<A, B, C, D, E, F>, PooledPredicate<A>, BiPredicate<A, B>, + PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>, PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble { - abstract R invoke(A a, B b, C c, D d); + abstract R invoke(A a, B b, C c, D d, E e, F f); @Override public R apply(A o, B o2) { - return invoke(o, o2, null, null); + return invoke(o, o2, null, null, null, null); } @Override public R apply(A o) { - return invoke(o, null, null, null); + return invoke(o, null, null, null, null, null); } - abstract public <V> OmniFunction<A, B, C, D, V> andThen(Function<? super R, ? extends V> after); - abstract public OmniFunction<A, B, C, D, R> negate(); + abstract public <V> OmniFunction<A, B, C, D, E, F, V> andThen( + Function<? super R, ? extends V> after); + abstract public OmniFunction<A, B, C, D, E, F, R> negate(); @Override public void accept(A o, B o2) { - invoke(o, o2, null, null); + invoke(o, o2, null, null, null, null); } @Override public void accept(A o) { - invoke(o, null, null, null); + invoke(o, null, null, null, null, null); } @Override public void run() { - invoke(null, null, null, null); + invoke(null, null, null, null, null, null); } @Override public R get() { - return invoke(null, null, null, null); + return invoke(null, null, null, null, null, null); } @Override public boolean test(A o, B o2) { - return (Boolean) invoke(o, o2, null, null); + return (Boolean) invoke(o, o2, null, null, null, null); } @Override public boolean test(A o) { - return (Boolean) invoke(o, null, null, null); + return (Boolean) invoke(o, null, null, null, null, null); } @Override @@ -99,22 +104,42 @@ abstract class OmniFunction<A, B, C, D, R> implements @Override public R apply(A a, B b, C c) { - return invoke(a, b, c, null); + return invoke(a, b, c, null, null, null); } @Override public void accept(A a, B b, C c) { - invoke(a, b, c, null); + invoke(a, b, c, null, null, null); } @Override public R apply(A a, B b, C c, D d) { - return invoke(a, b, c, d); + return invoke(a, b, c, d, null, null); + } + + @Override + public R apply(A a, B b, C c, D d, E e) { + return invoke(a, b, c, d, e, null); + } + + @Override + public R apply(A a, B b, C c, D d, E e, F f) { + return invoke(a, b, c, d, e, f); } @Override public void accept(A a, B b, C c, D d) { - invoke(a, b, c, d); + invoke(a, b, c, d, null, null); + } + + @Override + public void accept(A a, B b, C c, D d, E e) { + invoke(a, b, c, d, e, null); + } + + @Override + public void accept(A a, B b, C c, D d, E e, F f) { + invoke(a, b, c, d, e, f); } @Override @@ -128,5 +153,5 @@ abstract class OmniFunction<A, B, C, D, R> implements } @Override - abstract public OmniFunction<A, B, C, D, R> recycleOnUse(); + abstract public OmniFunction<A, B, C, D, E, F, R> recycleOnUse(); } 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 87c25e9663d8..15698cc67e51 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java @@ -21,8 +21,12 @@ import static com.android.internal.util.function.pooled.PooledLambdaImpl.acquire import android.os.Message; +import com.android.internal.util.function.HexConsumer; +import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; +import com.android.internal.util.function.QuintConsumer; +import com.android.internal.util.function.QuintFunction; import com.android.internal.util.function.TriConsumer; import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.pooled.PooledLambdaImpl.LambdaType.ReturnType; @@ -170,7 +174,7 @@ public interface PooledLambda { Consumer<? super A> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.VOID, arg1, null, null, null); + function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null); } /** @@ -186,7 +190,7 @@ public interface PooledLambda { Predicate<? super A> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null); + function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null); } /** @@ -202,7 +206,7 @@ public interface PooledLambda { Function<? super A, ? extends R> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.OBJECT, arg1, null, null, null); + function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null); } /** @@ -232,7 +236,7 @@ public interface PooledLambda { A arg1) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 1, 0, ReturnType.VOID, arg1, null, null, null); + function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -251,7 +255,7 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.VOID, arg1, arg2, null, null); + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null); } /** @@ -268,7 +272,7 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null); + function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null); } /** @@ -285,7 +289,7 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null); + function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null); } /** @@ -302,7 +306,7 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.VOID, arg1, arg2, null, null); + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null); } /** @@ -319,7 +323,7 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null); + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null); } /** @@ -336,7 +340,7 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null); + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null); } /** @@ -353,7 +357,7 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.VOID, arg1, arg2, null, null); + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null); } /** @@ -370,7 +374,7 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null); + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null); } /** @@ -387,7 +391,7 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null); + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null); } /** @@ -418,7 +422,7 @@ public interface PooledLambda { A arg1, B arg2) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 2, 0, ReturnType.VOID, arg1, arg2, null, null); + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -438,7 +442,7 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null); + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null); } /** @@ -456,7 +460,7 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null); + function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null); } /** @@ -474,7 +478,7 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null); } /** @@ -492,7 +496,7 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null); } /** @@ -510,7 +514,7 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null); } /** @@ -528,7 +532,7 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null); } /** @@ -546,7 +550,7 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null); } /** @@ -564,7 +568,7 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null); } /** @@ -596,7 +600,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null); + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -617,7 +621,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4); + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); } /** @@ -636,7 +640,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); } /** @@ -655,7 +659,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); } /** @@ -674,7 +678,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); } /** @@ -693,7 +697,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); } /** @@ -712,7 +716,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); } /** @@ -731,7 +735,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); } /** @@ -750,7 +754,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); } /** @@ -769,7 +773,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); } /** @@ -788,7 +792,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); } /** @@ -821,7 +825,164 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4); + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, 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 + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5) } + */ + static <A, B, C, D, E> PooledRunnable obtainRunnable( + QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function, + 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); + } + + /** + * {@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 + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5) } + */ + static <A, B, C, D, E, R> PooledSupplier<R> obtainSupplier( + QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R> + 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); + } + + /** + * 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 + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5) } when + * handled + */ + static <A, B, C, D, E> Message obtainMessage( + QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function, + A arg1, B arg2, C arg3, D arg4, E arg5) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, 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 + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6) } + */ + static <A, B, C, D, E, F> PooledRunnable obtainRunnable( + HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function, + 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); + } + + /** + * {@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 + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6) } + */ + static <A, B, C, D, E, F, R> PooledSupplier<R> obtainSupplier( + HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? 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); + } + + /** + * 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 + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6) } + * when handled + */ + static <A, B, C, D, E, F> Message obtainMessage( + HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6); 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 03e013cd46b8..565ae1129cb4 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -24,9 +24,15 @@ import android.util.Pools; import com.android.internal.util.ArrayUtils; import com.android.internal.util.BitUtils; +import com.android.internal.util.function.HexConsumer; +import com.android.internal.util.function.HexFunction; +import com.android.internal.util.function.HexPredicate; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuadPredicate; +import com.android.internal.util.function.QuintConsumer; +import com.android.internal.util.function.QuintFunction; +import com.android.internal.util.function.QuintPredicate; import com.android.internal.util.function.TriConsumer; import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.TriPredicate; @@ -44,12 +50,13 @@ import java.util.function.Supplier; * @see PooledLambda * @hide */ -final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Object, R> { +final class PooledLambdaImpl<R> extends OmniFunction<Object, + Object, Object, Object, Object, Object, R> { private static final boolean DEBUG = false; private static final String LOG_TAG = "PooledLambdaImpl"; - private static final int MAX_ARGS = 4; + private static final int MAX_ARGS = 5; private static final int MAX_POOL_SIZE = 50; @@ -151,16 +158,17 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Obj } @Override - R invoke(Object a1, Object a2, Object a3, Object a4) { + R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { checkNotRecycled(); if (DEBUG) { Log.i(LOG_TAG, this + ".invoke(" + commaSeparateFirstN( - new Object[] { a1, a2, a3, a4 }, + new Object[] { a1, a2, a3, a4, a5, a6 }, LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS))) + ")"); } - boolean ignored = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4); + final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) + && fillInArg(a4) && fillInArg(a5) && fillInArg(a6); int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)); if (argCount != LambdaType.MASK_ARG_COUNT) { for (int i = 0; i < argCount; i++) { @@ -289,6 +297,42 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Obj } } } break; + + case 5: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((QuintConsumer) mFunc).accept(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((QuintPredicate) mFunc).test( + popArg(0), popArg(1), popArg(2), popArg(3), popArg(4)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((QuintFunction) mFunc).apply( + popArg(0), popArg(1), popArg(2), popArg(3), popArg(4)); + } + } + } break; + + case 6: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((HexConsumer) mFunc).accept(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), popArg(5)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((HexPredicate) mFunc).test(popArg(0), + popArg(1), popArg(2), popArg(3), popArg(4), popArg(5)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((HexFunction) mFunc).apply(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), popArg(5)); + } + } + } } throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType)); } @@ -350,14 +394,14 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Obj /** * Internal non-typesafe factory method for {@link PooledLambdaImpl} */ - static <E extends PooledLambda> E acquire(Pool pool, Object f, + 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 a, Object b, Object c, Object d, Object e, Object f) { PooledLambdaImpl r = acquire(pool); if (DEBUG) { Log.i(LOG_TAG, "acquire(this = @" + hashCodeHex(r) - + ", f = " + f + + ", func = " + func + ", fNumArgs = " + fNumArgs + ", numPlaceholders = " + numPlaceholders + ", fReturnType = " + LambdaType.ReturnType.toString(fReturnType) @@ -365,9 +409,11 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Obj + ", b = " + b + ", c = " + c + ", d = " + d + + ", e = " + e + + ", f = " + f + ")"); } - r.mFunc = f; + r.mFunc = func; r.setFlags(MASK_FUNC_TYPE, LambdaType.encode(fNumArgs, fReturnType)); r.setFlags(MASK_EXPOSED_AS, LambdaType.encode(numPlaceholders, fReturnType)); if (ArrayUtils.size(r.mArgs) < fNumArgs) r.mArgs = new Object[fNumArgs]; @@ -375,6 +421,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Obj setIfInBounds(r.mArgs, 1, b); setIfInBounds(r.mArgs, 2, c); setIfInBounds(r.mArgs, 3, d); + setIfInBounds(r.mArgs, 4, e); + setIfInBounds(r.mArgs, 5, f); return (E) r; } @@ -400,12 +448,12 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Obj } @Override - public OmniFunction<Object, Object, Object, Object, R> negate() { + public OmniFunction<Object, Object, Object, Object, Object, Object, R> negate() { throw new UnsupportedOperationException(); } @Override - public <V> OmniFunction<Object, Object, Object, Object, V> andThen( + public <V> OmniFunction<Object, Object, Object, Object, Object, Object, V> andThen( Function<? super R, ? extends V> after) { throw new UnsupportedOperationException(); } @@ -426,7 +474,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Obj } @Override - public OmniFunction<Object, Object, Object, Object, R> recycleOnUse() { + public OmniFunction<Object, Object, Object, Object, Object, Object, R> recycleOnUse() { if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()"); mFlags |= FLAG_RECYCLE_ON_USE; return this; @@ -507,6 +555,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Obj case 2: return "Bi"; case 3: return "Tri"; case 4: return "Quad"; + case 5: return "Quint"; + case 6: return "Hex"; default: throw new IllegalArgumentException("" + argCount); } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3a527b59d074..caeca5927808 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3839,6 +3839,11 @@ <permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to watch changes and/or active state of app ops. + @hide <p>Not for use by third-party applications. --> + <permission android:name="android.permission.WATCH_APPOPS" + android:protectionLevel="signature" /> + <application android:process="system" android:persistent="true" android:hasCode="false" diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 3021555d30cc..ed290283a2d2 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -162,6 +162,7 @@ <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" /> <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="cameraserver" /> <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" /> + <assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" /> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 5c8d74516274..d919d6ac9d2c 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -143,6 +143,7 @@ android:name="android.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED" /> <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED" /> + <uses-permission android:name="android.permission.WATCH_APPOPS" /> <application android:label="@string/app_label" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 35c4d59752d3..3d49e5c37cf8 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -202,6 +202,9 @@ <!-- to change themes - light or dark --> <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" /> + <!-- Listen app op changes --> + <uses-permission android:name="android.permission.WATCH_APPOPS" /> + <application android:name=".SystemUIApplication" android:persistent="true" diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 894106afc30a..ca67a34101d5 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -39,14 +39,12 @@ import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; -import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManagerInternal; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; -import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -54,6 +52,7 @@ import android.util.TimeUtils; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.os.Zygote; @@ -63,6 +62,7 @@ import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; +import com.android.internal.util.function.pooled.PooledLambda; import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; @@ -180,16 +180,19 @@ public class AppOpsService extends IAppOpsService.Stub { } } - final SparseArray<ArraySet<Callback>> mOpModeWatchers = new SparseArray<>(); - final ArrayMap<String, ArraySet<Callback>> mPackageModeWatchers = new ArrayMap<>(); - final ArrayMap<IBinder, Callback> mModeWatchers = new ArrayMap<>(); + final SparseArray<ArraySet<ModeCallback>> mOpModeWatchers = new SparseArray<>(); + final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>(); + final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>(); + final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>(); final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>(); - public final class Callback implements DeathRecipient { + public final class ModeCallback implements DeathRecipient { final IAppOpsCallback mCallback; + final int mUid; - public Callback(IAppOpsCallback callback) { + public ModeCallback(IAppOpsCallback callback, int uid) { mCallback = callback; + mUid = uid; try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { @@ -206,6 +209,27 @@ public class AppOpsService extends IAppOpsService.Stub { } } + public final class ActiveCallback implements DeathRecipient { + final IAppOpsActiveCallback mCallback; + + public ActiveCallback(IAppOpsActiveCallback callback) { + mCallback = callback; + try { + mCallback.asBinder().linkToDeath(this, 0); + } catch (RemoteException e) { + } + } + + public void destroy() { + mCallback.asBinder().unlinkToDeath(this, 0); + } + + @Override + public void binderDied() { + stopWatchingActive(mCallback); + } + } + final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>(); public final class ClientState extends Binder implements DeathRecipient { @@ -360,21 +384,30 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - boolean changed = false; + Ops ops = null; // Remove any package state if such. - if (uidState.pkgOps != null && uidState.pkgOps.remove(packageName) != null) { - changed = true; + if (uidState.pkgOps != null) { + ops = uidState.pkgOps.remove(packageName); } // If we just nuked the last package state check if the UID is valid. - if (changed && uidState.pkgOps.isEmpty() + if (ops != null && uidState.pkgOps.isEmpty() && getPackagesForUid(uid).length <= 0) { mUidStates.remove(uid); } - if (changed) { + if (ops != null) { scheduleFastWriteLocked(); + + final int opCount = ops.size(); + for (int i = 0; i < opCount; i++) { + final Op op = ops.valueAt(i); + if (op.duration == -1) { + scheduleOpActiveChangedIfNeededLocked( + op.op, op.uid, op.packageName, false); + } + } } } } @@ -598,14 +631,14 @@ public class AppOpsService extends IAppOpsService.Stub { } String[] uidPackageNames = getPackagesForUid(uid); - ArrayMap<Callback, ArraySet<String>> callbackSpecs = null; + ArrayMap<ModeCallback, ArraySet<String>> callbackSpecs = null; synchronized (this) { - ArraySet<Callback> callbacks = mOpModeWatchers.get(code); + ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code); if (callbacks != null) { final int callbackCount = callbacks.size(); for (int i = 0; i < callbackCount; i++) { - Callback callback = callbacks.valueAt(i); + ModeCallback callback = callbacks.valueAt(i); ArraySet<String> changedPackages = new ArraySet<>(); Collections.addAll(changedPackages, uidPackageNames); callbackSpecs = new ArrayMap<>(); @@ -621,7 +654,7 @@ public class AppOpsService extends IAppOpsService.Stub { } final int callbackCount = callbacks.size(); for (int i = 0; i < callbackCount; i++) { - Callback callback = callbacks.valueAt(i); + ModeCallback callback = callbacks.valueAt(i); ArraySet<String> changedPackages = callbackSpecs.get(callback); if (changedPackages == null) { changedPackages = new ArraySet<>(); @@ -637,30 +670,23 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - // There are components watching for mode changes such as window manager - // and location manager which are in our process. The callbacks in these - // components may require permissions our remote caller does not have. - final long identity = Binder.clearCallingIdentity(); - try { - for (int i = 0; i < callbackSpecs.size(); i++) { - Callback callback = callbackSpecs.keyAt(i); - ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i); - try { - if (reportedPackageNames == null) { - callback.mCallback.opChanged(code, uid, null); - } else { - final int reportedPackageCount = reportedPackageNames.size(); - for (int j = 0; j < reportedPackageCount; j++) { - String reportedPackageName = reportedPackageNames.valueAt(j); - callback.mCallback.opChanged(code, uid, reportedPackageName); - } - } - } catch (RemoteException e) { - Log.w(TAG, "Error dispatching op op change", e); + for (int i = 0; i < callbackSpecs.size(); i++) { + final ModeCallback callback = callbackSpecs.keyAt(i); + final ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i); + if (reportedPackageNames == null) { + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChanged, + this, callback, code, uid, (String) null)); + + } else { + final int reportedPackageCount = reportedPackageNames.size(); + for (int j = 0; j < reportedPackageCount; j++) { + final String reportedPackageName = reportedPackageNames.valueAt(j); + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChanged, + this, callback, code, uid, reportedPackageName)); } } - } finally { - Binder.restoreCallingIdentity(identity); } } @@ -671,7 +697,7 @@ public class AppOpsService extends IAppOpsService.Stub { Binder.getCallingPid(), Binder.getCallingUid(), null); } verifyIncomingOp(code); - ArrayList<Callback> repCbs = null; + ArraySet<ModeCallback> repCbs = null; code = AppOpsManager.opToSwitch(code); synchronized (this) { UidState uidState = getUidStateLocked(uid, false); @@ -679,17 +705,17 @@ public class AppOpsService extends IAppOpsService.Stub { if (op != null) { if (op.mode != mode) { op.mode = mode; - ArraySet<Callback> cbs = mOpModeWatchers.get(code); + ArraySet<ModeCallback> cbs = mOpModeWatchers.get(code); if (cbs != null) { if (repCbs == null) { - repCbs = new ArrayList<>(); + repCbs = new ArraySet<>(); } repCbs.addAll(cbs); } cbs = mPackageModeWatchers.get(packageName); if (cbs != null) { if (repCbs == null) { - repCbs = new ArrayList<>(); + repCbs = new ArraySet<>(); } repCbs.addAll(cbs); } @@ -703,26 +729,41 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (repCbs != null) { - // There are components watching for mode changes such as window manager - // and location manager which are in our process. The callbacks in these - // components may require permissions our remote caller does not have. - final long identity = Binder.clearCallingIdentity(); - try { - for (int i = 0; i < repCbs.size(); i++) { - try { - repCbs.get(i).mCallback.opChanged(code, uid, packageName); - } catch (RemoteException e) { - } - } - } finally { - Binder.restoreCallingIdentity(identity); - } + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChanged, + this, repCbs, code, uid, packageName)); + } + } + + private void notifyOpChanged(ArraySet<ModeCallback> callbacks, int code, + int uid, String packageName) { + for (int i = 0; i < callbacks.size(); i++) { + final ModeCallback callback = callbacks.valueAt(i); + notifyOpChanged(callback, code, uid, packageName); + } + } + + private void notifyOpChanged(ModeCallback callback, int code, + int uid, String packageName) { + if (callback.mUid >= 0 && callback.mUid != uid) { + return; + } + // There are components watching for mode changes such as window manager + // and location manager which are in our process. The callbacks in these + // components may require permissions our remote caller does not have. + final long identity = Binder.clearCallingIdentity(); + try { + callback.mCallback.opChanged(code, uid, packageName); + } catch (RemoteException e) { + /* ignore */ + } finally { + Binder.restoreCallingIdentity(identity); } } - private static HashMap<Callback, ArrayList<ChangeRec>> addCallbacks( - HashMap<Callback, ArrayList<ChangeRec>> callbacks, - int op, int uid, String packageName, ArraySet<Callback> cbs) { + private static HashMap<ModeCallback, ArrayList<ChangeRec>> addCallbacks( + HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks, + int op, int uid, String packageName, ArraySet<ModeCallback> cbs) { if (cbs == null) { return callbacks; } @@ -732,7 +773,7 @@ public class AppOpsService extends IAppOpsService.Stub { boolean duplicate = false; final int N = cbs.size(); for (int i=0; i<N; i++) { - Callback cb = cbs.valueAt(i); + ModeCallback cb = cbs.valueAt(i); ArrayList<ChangeRec> reports = callbacks.get(cb); if (reports == null) { reports = new ArrayList<>(); @@ -785,7 +826,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - HashMap<Callback, ArrayList<ChangeRec>> callbacks = null; + HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks = null; synchronized (this) { boolean changed = false; for (int i = mUidStates.size() - 1; i >= 0; i--) { @@ -860,15 +901,14 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (callbacks != null) { - for (Map.Entry<Callback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) { - Callback cb = ent.getKey(); + for (Map.Entry<ModeCallback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) { + ModeCallback cb = ent.getKey(); ArrayList<ChangeRec> reports = ent.getValue(); for (int i=0; i<reports.size(); i++) { ChangeRec rep = reports.get(i); - try { - cb.mCallback.opChanged(rep.op, rep.uid, rep.pkg); - } catch (RemoteException e) { - } + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChanged, + this, cb, rep.op, rep.uid, rep.pkg)); } } } @@ -876,18 +916,25 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) { + int watchedUid = -1; + if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS) + != PackageManager.PERMISSION_GRANTED) { + watchedUid = Binder.getCallingUid(); + } + Preconditions.checkArgumentInRange(op, AppOpsManager.OP_NONE, + AppOpsManager._NUM_OP - 1, "Invalid op code: " + op); if (callback == null) { return; } synchronized (this) { op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op; - Callback cb = mModeWatchers.get(callback.asBinder()); + ModeCallback cb = mModeWatchers.get(callback.asBinder()); if (cb == null) { - cb = new Callback(callback); + cb = new ModeCallback(callback, watchedUid); mModeWatchers.put(callback.asBinder(), cb); } if (op != AppOpsManager.OP_NONE) { - ArraySet<Callback> cbs = mOpModeWatchers.get(op); + ArraySet<ModeCallback> cbs = mOpModeWatchers.get(op); if (cbs == null) { cbs = new ArraySet<>(); mOpModeWatchers.put(op, cbs); @@ -895,7 +942,7 @@ public class AppOpsService extends IAppOpsService.Stub { cbs.add(cb); } if (packageName != null) { - ArraySet<Callback> cbs = mPackageModeWatchers.get(packageName); + ArraySet<ModeCallback> cbs = mPackageModeWatchers.get(packageName); if (cbs == null) { cbs = new ArraySet<>(); mPackageModeWatchers.put(packageName, cbs); @@ -911,18 +958,18 @@ public class AppOpsService extends IAppOpsService.Stub { return; } synchronized (this) { - Callback cb = mModeWatchers.remove(callback.asBinder()); + ModeCallback cb = mModeWatchers.remove(callback.asBinder()); if (cb != null) { cb.unlinkToDeath(); for (int i=mOpModeWatchers.size()-1; i>=0; i--) { - ArraySet<Callback> cbs = mOpModeWatchers.valueAt(i); + ArraySet<ModeCallback> cbs = mOpModeWatchers.valueAt(i); cbs.remove(cb); if (cbs.size() <= 0) { mOpModeWatchers.removeAt(i); } } for (int i=mPackageModeWatchers.size()-1; i>=0; i--) { - ArraySet<Callback> cbs = mPackageModeWatchers.valueAt(i); + ArraySet<ModeCallback> cbs = mPackageModeWatchers.valueAt(i); cbs.remove(cb); if (cbs.size() <= 0) { mPackageModeWatchers.removeAt(i); @@ -981,7 +1028,7 @@ public class AppOpsService extends IAppOpsService.Stub { } if (suspended) { - Log.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid); + Slog.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid); return AppOpsManager.MODE_IGNORED; } @@ -1042,7 +1089,9 @@ public class AppOpsService extends IAppOpsService.Stub { usageRestrictions.put(usage, r); } } - notifyWatchersOfChange(code); + + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyWatchersOfChange, this, code)); } @Override @@ -1098,7 +1147,7 @@ public class AppOpsService extends IAppOpsService.Stub { Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, false /* uidMismatchExpected */); if (ops == null) { - if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid + if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid + " package " + packageName); return AppOpsManager.MODE_ERRORED; } @@ -1118,7 +1167,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) { final int uidMode = uidState.opModes.get(switchCode); if (uidMode != AppOpsManager.MODE_ALLOWED) { - if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " + if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime = System.currentTimeMillis(); @@ -1127,14 +1176,14 @@ public class AppOpsService extends IAppOpsService.Stub { } else { final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { - if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " + if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime = System.currentTimeMillis(); return switchOp.mode; } } - if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid + if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid + " package " + packageName); op.time = System.currentTimeMillis(); op.rejectTime = 0; @@ -1145,6 +1194,51 @@ public class AppOpsService extends IAppOpsService.Stub { } @Override + public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS, + "startWatchingActive"); + if (ops != null) { + Preconditions.checkArrayElementsInRange(ops, 0, + AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops)); + } + if (callback == null) { + return; + } + synchronized (this) { + SparseArray<ActiveCallback> callbacks = mActiveWatchers.get(callback.asBinder()); + if (callbacks == null) { + callbacks = new SparseArray<>(); + mActiveWatchers.put(callback.asBinder(), callbacks); + } + final ActiveCallback activeCallback = new ActiveCallback(callback); + for (int op : ops) { + callbacks.put(op, activeCallback); + } + } + } + + @Override + public void stopWatchingActive(IAppOpsActiveCallback callback) { + if (callback == null) { + return; + } + synchronized (this) { + final SparseArray<ActiveCallback> activeCallbacks = + mActiveWatchers.remove(callback.asBinder()); + if (activeCallbacks == null) { + return; + } + final int callbackCount = activeCallbacks.size(); + for (int i = 0; i < callbackCount; i++) { + // Apps ops are mapped to a singleton + if (i == 0) { + activeCallbacks.valueAt(i).destroy(); + } + } + } + } + + @Override public int startOperation(IBinder token, int code, int uid, String packageName) { verifyIncomingUid(uid); verifyIncomingOp(code); @@ -1157,7 +1251,7 @@ public class AppOpsService extends IAppOpsService.Stub { Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */, false /* uidMismatchExpected */); if (ops == null) { - if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid + if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid + " package " + resolvedPackageName); return AppOpsManager.MODE_ERRORED; } @@ -1167,37 +1261,42 @@ public class AppOpsService extends IAppOpsService.Stub { } final int switchCode = AppOpsManager.opToSwitch(code); UidState uidState = ops.uidState; - if (uidState.opModes != null) { + // If there is a non-default per UID policy (we set UID op mode only if + // non-default) it takes over, otherwise use the per package policy. + if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) { final int uidMode = uidState.opModes.get(switchCode); if (uidMode != AppOpsManager.MODE_ALLOWED) { - if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " + if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + resolvedPackageName); op.rejectTime = System.currentTimeMillis(); return uidMode; } + } else { + final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; + if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { + if (DEBUG) Slog.d(TAG, "startOperation: reject #" + op.mode + " for code " + + switchCode + " (" + code + ") uid " + uid + " package " + + resolvedPackageName); + op.rejectTime = System.currentTimeMillis(); + return switchOp.mode; + } } - final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; - if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { - if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code " - + switchCode + " (" + code + ") uid " + uid + " package " - + resolvedPackageName); - op.rejectTime = System.currentTimeMillis(); - return switchOp.mode; - } - if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid + if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid + " package " + resolvedPackageName); if (op.nesting == 0) { op.time = System.currentTimeMillis(); op.rejectTime = 0; op.duration = -1; + scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true); } op.nesting++; if (client.mStartedOps != null) { client.mStartedOps.add(op); } - return AppOpsManager.MODE_ALLOWED; } + + return AppOpsManager.MODE_ALLOWED; } @Override @@ -1224,6 +1323,52 @@ public class AppOpsService extends IAppOpsService.Stub { } } finishOperationLocked(op); + if (op.nesting <= 0) { + scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, false); + } + } + } + + private void scheduleOpActiveChangedIfNeededLocked(int code, int uid, String packageName, + boolean active) { + ArraySet<ActiveCallback> dispatchedCallbacks = null; + final int callbackListCount = mActiveWatchers.size(); + for (int i = 0; i < callbackListCount; i++) { + final SparseArray<ActiveCallback> callbacks = mActiveWatchers.valueAt(i); + ActiveCallback callback = callbacks.get(code); + if (callback != null) { + if (dispatchedCallbacks == null) { + dispatchedCallbacks = new ArraySet<>(); + } + dispatchedCallbacks.add(callback); + } + } + if (dispatchedCallbacks == null) { + return; + } + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpActiveChanged, + this, dispatchedCallbacks, code, uid, packageName, active)); + } + + private void notifyOpActiveChanged(ArraySet<ActiveCallback> callbacks, + int code, int uid, String packageName, boolean active) { + // There are components watching for mode changes such as window manager + // and location manager which are in our process. The callbacks in these + // components may require permissions our remote caller does not have. + final long identity = Binder.clearCallingIdentity(); + try { + final int callbackCount = callbacks.size(); + for (int i = 0; i < callbackCount; i++) { + final ActiveCallback callback = callbacks.valueAt(i); + try { + callback.mCallback.opActiveChanged(code, uid, packageName, active); + } catch (RemoteException e) { + /* do nothing */ + } + } + } finally { + Binder.restoreCallingIdentity(identity); } } @@ -2220,7 +2365,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i=0; i<mOpModeWatchers.size(); i++) { pw.print(" Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i))); pw.println(":"); - ArraySet<Callback> callbacks = mOpModeWatchers.valueAt(i); + ArraySet<ModeCallback> callbacks = mOpModeWatchers.valueAt(i); for (int j=0; j<callbacks.size(); j++) { pw.print(" #"); pw.print(j); pw.print(": "); pw.println(callbacks.valueAt(j)); @@ -2233,7 +2378,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i=0; i<mPackageModeWatchers.size(); i++) { pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i)); pw.println(":"); - ArraySet<Callback> callbacks = mPackageModeWatchers.valueAt(i); + ArraySet<ModeCallback> callbacks = mPackageModeWatchers.valueAt(i); for (int j=0; j<callbacks.size(); j++) { pw.print(" #"); pw.print(j); pw.print(": "); pw.println(callbacks.valueAt(j)); @@ -2242,12 +2387,32 @@ public class AppOpsService extends IAppOpsService.Stub { } if (mModeWatchers.size() > 0) { needSep = true; - pw.println(" All mode watchers:"); + pw.println(" All op mode watchers:"); for (int i=0; i<mModeWatchers.size(); i++) { pw.print(" "); pw.print(mModeWatchers.keyAt(i)); pw.print(" -> "); pw.println(mModeWatchers.valueAt(i)); } } + if (mActiveWatchers.size() > 0) { + needSep = true; + pw.println(" All op active watchers:"); + for (int i = 0; i < mActiveWatchers.size(); i++) { + final SparseArray<ActiveCallback> activeWatchers = mActiveWatchers.valueAt(i); + if (activeWatchers.size() <= 0) { + continue; + } + pw.print(" "); pw.print(mActiveWatchers.keyAt(i)); + pw.print(" -> ["); + final int opCount = activeWatchers.size(); + for (i = 0; i < opCount; i++) { + pw.print(AppOpsManager.opToName(activeWatchers.keyAt(i))); + if (i < opCount - 1) { + pw.print(','); + } + } + pw.print("]" ); pw.println(activeWatchers.valueAt(0)); + } + } if (mClients.size() > 0) { needSep = true; pw.println(" Clients:"); @@ -2434,8 +2599,6 @@ public class AppOpsService extends IAppOpsService.Stub { private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, int userHandle, String[] exceptionPackages) { - boolean notifyChange = false; - synchronized (AppOpsService.this) { ClientRestrictionState restrictionState = mOpUserRestrictions.get(token); @@ -2449,7 +2612,8 @@ public class AppOpsService extends IAppOpsService.Stub { } if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) { - notifyChange = true; + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyWatchersOfChange, this, code)); } if (restrictionState.isDefault()) { @@ -2457,39 +2621,19 @@ public class AppOpsService extends IAppOpsService.Stub { restrictionState.destroy(); } } - - if (notifyChange) { - notifyWatchersOfChange(code); - } } private void notifyWatchersOfChange(int code) { - final ArraySet<Callback> clonedCallbacks; + final ArraySet<ModeCallback> clonedCallbacks; synchronized (this) { - ArraySet<Callback> callbacks = mOpModeWatchers.get(code); + ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code); if (callbacks == null) { return; } clonedCallbacks = new ArraySet<>(callbacks); } - // There are components watching for mode changes such as window manager - // and location manager which are in our process. The callbacks in these - // components may require permissions our remote caller does not have. - final long identity = Binder.clearCallingIdentity(); - try { - final int callbackCount = clonedCallbacks.size(); - for (int i = 0; i < callbackCount; i++) { - Callback callback = clonedCallbacks.valueAt(i); - try { - callback.mCallback.opChanged(code, -1, null); - } catch (RemoteException e) { - Log.w(TAG, "Error dispatching op op change", e); - } - } - } finally { - Binder.restoreCallingIdentity(identity); - } + notifyOpChanged(clonedCallbacks, code, -1, null); } @Override @@ -2507,13 +2651,14 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public boolean isOperationActive(int code, int uid, String packageName) { - verifyIncomingUid(uid); + mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS, + "isOperationActive"); verifyIncomingOp(code); - String resolvedPackageName = resolvePackageName(uid, packageName); + final String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { return false; } - synchronized (this) { + synchronized (AppOpsService.this) { for (int i = mClients.size() - 1; i >= 0; i--) { final ClientState client = mClients.valueAt(i); if (client.mStartedOps == null) continue; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2af284c12288..183862b8b8bc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2711,6 +2711,19 @@ public class ActivityManagerService extends IActivityManager.Stub throw new RuntimeException( "Unable to find android system package", e); } + + // Start watching app ops after we and the package manager are up and running. + mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null, + new IAppOpsCallback.Stub() { + @Override public void opChanged(int op, int uid, String packageName) { + if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) { + if (mAppOpsService.checkOperation(op, uid, packageName) + != AppOpsManager.MODE_ALLOWED) { + runInBackgroundDisabled(uid); + } + } + } + }); } public void setWindowManager(WindowManagerService wm) { @@ -2987,17 +3000,6 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats")); mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler); - mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null, - new IAppOpsCallback.Stub() { - @Override public void opChanged(int op, int uid, String packageName) { - if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) { - if (mAppOpsService.checkOperation(op, uid, packageName) - != AppOpsManager.MODE_ALLOWED) { - runInBackgroundDisabled(uid); - } - } - } - }); mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants"); diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 5d8aca195ef8..568d7a733a19 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -60,6 +60,7 @@ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WATCH_APPOPS" /> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java new file mode 100644 index 000000000000..ea0fe455672b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appops; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.app.AppOpsManager; +import android.app.AppOpsManager.OnOpActiveChangedListener; +import android.content.Context; +import android.os.Process; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests app ops version upgrades + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AppOpsActiveWatcherTest { + + private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000; + + @Test + public void testWatchActiveOps() { + // Create a mock listener + final OnOpActiveChangedListener listener = mock(OnOpActiveChangedListener.class); + + // Start watching active ops + final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); + appOpsManager.startWatchingActive(new int[] {AppOpsManager.OP_CAMERA, + AppOpsManager.OP_RECORD_AUDIO}, listener); + + // Start the op + appOpsManager.startOp(AppOpsManager.OP_CAMERA); + + // Verify that we got called for the op being active + verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) + .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA), + eq(Process.myUid()), eq(getContext().getPackageName()), eq(true)); + + // This should be the only callback we got + verifyNoMoreInteractions(listener); + + // Start with a clean slate + reset(listener); + + // Verify that the op is active + assertThat(appOpsManager.isOperationActive(AppOpsManager.OP_CAMERA, + Process.myUid(), getContext().getPackageName())).isTrue(); + + // Finish the op + appOpsManager.finishOp(AppOpsManager.OP_CAMERA); + + // Verify that we got called for the op being active + verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) + .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA), + eq(Process.myUid()), eq(getContext().getPackageName()), eq(false)); + + // Verify that the op is not active + assertThat(appOpsManager.isOperationActive(AppOpsManager.OP_CAMERA, + Process.myUid(), getContext().getPackageName())).isFalse(); + + // This should be the only callback we got + verifyNoMoreInteractions(listener); + + // Start with a clean slate + reset(listener); + + // Stop watching active ops + appOpsManager.stopWatchingActive(listener); + + // Start the op + appOpsManager.startOp(AppOpsManager.OP_CAMERA); + + // We should not be getting any callbacks + verifyNoMoreInteractions(listener); + + // Finish the op + appOpsManager.finishOp(AppOpsManager.OP_CAMERA); + + // We should not be getting any callbacks + verifyNoMoreInteractions(listener); + } + + private static Context getContext() { + return InstrumentationRegistry.getContext(); + } +}
\ No newline at end of file |