APIs to watch active op changes
System singed components can watch for starting/finishing of long running app ops. Also protected the APIs to watch op mode changes with a singature permission for the cross-uid use case. Test: atest com.android.server.appops.AppOpsActiveWatcherTest bug:64085448 Change-Id: Id7fe79ce1de4c5690b4f52786424ec5a5d9eb0fa
This commit is contained in:
committed by
Svet Ganov
parent
7d5beeb255
commit
2d20fb47f4
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
public void onOpChanged(String op, String packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -1696,6 +1717,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.
|
||||
@@ -1707,11 +1730,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);
|
||||
@@ -1753,6 +1782,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];
|
||||
}
|
||||
@@ -2146,6 +2243,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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 static HashMap<Callback, ArrayList<ChangeRec>> addCallbacks(
|
||||
HashMap<Callback, ArrayList<ChangeRec>> callbacks,
|
||||
int op, int uid, String packageName, ArraySet<Callback> cbs) {
|
||||
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<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;
|
||||
@@ -1144,6 +1193,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);
|
||||
@@ -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;
|
||||
|
||||
@@ -2693,6 +2693,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) {
|
||||
@@ -2969,17 +2982,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");
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user