Loading Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -388,6 +388,7 @@ java_defaults { "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/IAppOpsNotedCallback.aidl", "core/java/com/android/internal/app/IAppOpsService.aidl", "core/java/com/android/internal/app/IBatteryStats.aidl", "core/java/com/android/internal/app/ISoundTriggerService.aidl", Loading core/java/android/app/AppOpsManager.java +101 −4 Original line number Diff line number Diff line Loading @@ -41,8 +41,10 @@ import android.os.UserManager; import android.provider.Settings; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsNotedCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.Preconditions; Loading Loading @@ -86,10 +88,20 @@ public class AppOpsManager { */ final Context mContext; @UnsupportedAppUsage final IAppOpsService mService; final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>(); final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers = @GuardedBy("mModeWatchers") private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>(); @GuardedBy("mActiveWatchers") private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers = new ArrayMap<>(); @GuardedBy("mNotedWatchers") private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers = new ArrayMap<>(); static IBinder sToken; Loading Loading @@ -2470,6 +2482,23 @@ public class AppOpsManager { void onOpActiveChanged(int code, int uid, String packageName, boolean active); } /** * Callback for notification of an op being noted. * * @hide */ public interface OnOpNotedListener { /** * Called when an op was noted. * * @param code The op code. * @param uid The UID performing the operation. * @param packageName The package performing the operation. * @param result The result of the note. */ void onOpNoted(String code, int uid, String packageName, int result); } /** * Callback for notification of changes to operation state. * This allows you to see the raw op codes instead of strings. Loading Loading @@ -2819,7 +2848,7 @@ public class AppOpsManager { */ public void stopWatchingMode(OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); IAppOpsCallback cb = mModeWatchers.remove(callback); if (cb != null) { try { mService.stopWatchingMode(cb); Loading Loading @@ -2893,7 +2922,7 @@ public class AppOpsManager { @TestApi public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) { synchronized (mActiveWatchers) { final IAppOpsActiveCallback cb = mActiveWatchers.get(callback); final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback); if (cb != null) { try { mService.stopWatchingActive(cb); Loading @@ -2904,6 +2933,74 @@ public class AppOpsManager { } } /** * Start watching for noted app ops. An app op may be immediate or long running. * Immediate ops are noted while long running ones are started and stopped. This * method allows registering a listener to be notified when an app op is noted. If * an op is being noted 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. * * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission * you can watch changes only for your UID. * * @param ops The ops to watch. * @param callback Where to report changes. * * @see #startWatchingActive(int[], OnOpActiveChangedListener) * @see #stopWatchingNoted(OnOpNotedListener) * @see #noteOp(String, int, String) * * @hide */ @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) { IAppOpsNotedCallback cb; synchronized (mNotedWatchers) { cb = mNotedWatchers.get(callback); if (cb != null) { return; } cb = new IAppOpsNotedCallback.Stub() { @Override public void opNoted(int op, int uid, String packageName, int mode) { callback.onOpNoted(sOpToString[op], uid, packageName, mode); } }; mNotedWatchers.put(callback, cb); } try { final int[] opCodes = new int[ops.length]; for (int i = 0; i < opCodes.length; i++) { opCodes[i] = strOpToOp(ops[i]); } mService.startWatchingNoted(opCodes, cb); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Stop watching for noted app ops. An app op may be immediate or long running. * Unregistering a non-registered callback has no effect. * * @see #startWatchingNoted(String[], OnOpNotedListener) * @see #noteOp(String, int, String) * * @hide */ public void stopWatchingNoted(@NonNull OnOpNotedListener callback) { synchronized (mNotedWatchers) { final IAppOpsNotedCallback cb = mNotedWatchers.get(callback); if (cb != null) { try { mService.stopWatchingNoted(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]; } Loading core/java/com/android/internal/app/IAppOpsNotedCallback.aidl 0 → 100644 +22 −0 Original line number Diff line number Diff line /* * 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 note/checks of ops oneway interface IAppOpsNotedCallback { void opNoted(int op, int uid, String packageName, int mode); } core/java/com/android/internal/app/IAppOpsService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.pm.ParceledListSlice; import android.os.Bundle; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsNotedCallback; interface IAppOpsService { // These first methods are also called by native code, so must Loading Loading @@ -61,4 +62,7 @@ interface IAppOpsService { boolean isOperationActive(int code, int uid, String packageName); void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback); void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback); void stopWatchingNoted(IAppOpsNotedCallback callback); } services/core/java/com/android/server/AppOpsService.java +186 −5 Original line number Diff line number Diff line Loading @@ -86,6 +86,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsNotedCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; Loading Loading @@ -456,6 +457,7 @@ public class AppOpsService extends IAppOpsService.Stub { final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>(); final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>(); final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>(); final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>(); final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>(); final class ModeCallback implements DeathRecipient { Loading @@ -475,6 +477,7 @@ public class AppOpsService extends IAppOpsService.Stub { try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { /*ignored*/ } } Loading Loading @@ -524,6 +527,7 @@ public class AppOpsService extends IAppOpsService.Stub { try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { /*ignored*/ } } Loading Loading @@ -552,6 +556,50 @@ public class AppOpsService extends IAppOpsService.Stub { } } final class NotedCallback implements DeathRecipient { final IAppOpsNotedCallback mCallback; final int mWatchingUid; final int mCallingUid; final int mCallingPid; NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid, int callingPid) { mCallback = callback; mWatchingUid = watchingUid; mCallingUid = callingUid; mCallingPid = callingPid; try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { /*ignored*/ } } @Override public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("NotedCallback{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" watchinguid="); UserHandle.formatUid(sb, mWatchingUid); sb.append(" from uid="); UserHandle.formatUid(sb, mCallingUid); sb.append(" pid="); sb.append(mCallingPid); sb.append('}'); return sb.toString(); } void destroy() { mCallback.asBinder().unlinkToDeath(this, 0); } @Override public void binderDied() { stopWatchingNoted(mCallback); } } final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); final class ClientState extends Binder implements DeathRecipient { Loading Loading @@ -1629,7 +1677,7 @@ public class AppOpsService extends IAppOpsService.Stub { UidState uidState = getUidStateLocked(uid, false); if (uidState != null && uidState.opModes != null && uidState.opModes.indexOfKey(code) >= 0) { return uidState.opModes.get(code); return uidState.evalMode(uidState.opModes.get(code)); } Op op = getOpLocked(code, uid, packageName, false, true, false); if (op == null) { Loading Loading @@ -1795,12 +1843,16 @@ public class AppOpsService extends IAppOpsService.Stub { final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, false /* uidMismatchExpected */); if (ops == null) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid + " package " + packageName); return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, true); if (isOpRestrictedLocked(uid, code, packageName)) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; } final UidState uidState = ops.uidState; Loading @@ -1820,6 +1872,7 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime[uidState.state] = System.currentTimeMillis(); scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode); return uidMode; } } else { Loading @@ -1830,6 +1883,7 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime[uidState.state] = System.currentTimeMillis(); scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode); return mode; } } Loading @@ -1839,6 +1893,8 @@ public class AppOpsService extends IAppOpsService.Stub { op.rejectTime[uidState.state] = 0; op.proxyUid = proxyUid; op.proxyPackageName = proxyPackageName; scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED); return AppOpsManager.MODE_ALLOWED; } } Loading Loading @@ -1886,12 +1942,52 @@ public class AppOpsService extends IAppOpsService.Stub { } 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 void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) { int watchedUid = Process.INVALID_UID; final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS) != PackageManager.PERMISSION_GRANTED) { watchedUid = callingUid; } Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty"); Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops)); Preconditions.checkNotNull(callback, "Callback cannot be null"); synchronized (this) { SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder()); if (callbacks == null) { callbacks = new SparseArray<>(); mNotedWatchers.put(callback.asBinder(), callbacks); } final NotedCallback notedCallback = new NotedCallback(callback, watchedUid, callingUid, callingPid); for (int op : ops) { callbacks.put(op, notedCallback); } } } @Override public void stopWatchingNoted(IAppOpsNotedCallback callback) { Preconditions.checkNotNull(callback, "Callback cannot be null"); synchronized (this) { final SparseArray<NotedCallback> notedCallbacks = mNotedWatchers.remove(callback.asBinder()); if (notedCallbacks == null) { return; } final int callbackCount = notedCallbacks.size(); for (int i = 0; i < callbackCount; i++) { notedCallbacks.valueAt(i).destroy(); } } } @Override Loading Loading @@ -2052,6 +2148,51 @@ public class AppOpsService extends IAppOpsService.Stub { } } private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName, int result) { ArraySet<NotedCallback> dispatchedCallbacks = null; final int callbackListCount = mNotedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i); final NotedCallback callback = callbacks.get(code); if (callback != null) { if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) { continue; } if (dispatchedCallbacks == null) { dispatchedCallbacks = new ArraySet<>(); } dispatchedCallbacks.add(callback); } } if (dispatchedCallbacks == null) { return; } mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChecked, this, dispatchedCallbacks, code, uid, packageName, result)); } private void notifyOpChecked(ArraySet<NotedCallback> callbacks, int code, int uid, String packageName, int result) { // There are components watching for checks 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 NotedCallback callback = callbacks.valueAt(i); try { callback.mCallback.opNoted(code, uid, packageName, result); } catch (RemoteException e) { /* do nothing */ } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public int permissionToOpCode(String permission) { if (permission == null) { Loading Loading @@ -3463,6 +3604,46 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(cb); } } if (mNotedWatchers.size() > 0 && dumpMode < 0) { needSep = true; boolean printedHeader = false; for (int i = 0; i < mNotedWatchers.size(); i++) { final SparseArray<NotedCallback> notedWatchers = mNotedWatchers.valueAt(i); if (notedWatchers.size() <= 0) { continue; } final NotedCallback cb = notedWatchers.valueAt(0); if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) { continue; } if (dumpPackage != null && cb.mWatchingUid >= 0 && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) { continue; } if (!printedHeader) { pw.println(" All op noted watchers:"); printedHeader = true; } pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode( mNotedWatchers.keyAt(i)))); pw.println(" ->"); pw.print(" ["); final int opCount = notedWatchers.size(); for (i = 0; i < opCount; i++) { if (i > 0) { pw.print(' '); } pw.print(AppOpsManager.opToName(notedWatchers.keyAt(i))); if (i < opCount - 1) { pw.print(','); } } pw.println("]"); pw.print(" "); pw.println(cb); } } if (mClients.size() > 0 && dumpMode < 0) { needSep = true; boolean printedHeader = false; Loading Loading
Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -388,6 +388,7 @@ java_defaults { "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/IAppOpsNotedCallback.aidl", "core/java/com/android/internal/app/IAppOpsService.aidl", "core/java/com/android/internal/app/IBatteryStats.aidl", "core/java/com/android/internal/app/ISoundTriggerService.aidl", Loading
core/java/android/app/AppOpsManager.java +101 −4 Original line number Diff line number Diff line Loading @@ -41,8 +41,10 @@ import android.os.UserManager; import android.provider.Settings; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsNotedCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.Preconditions; Loading Loading @@ -86,10 +88,20 @@ public class AppOpsManager { */ final Context mContext; @UnsupportedAppUsage final IAppOpsService mService; final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>(); final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers = @GuardedBy("mModeWatchers") private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>(); @GuardedBy("mActiveWatchers") private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers = new ArrayMap<>(); @GuardedBy("mNotedWatchers") private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers = new ArrayMap<>(); static IBinder sToken; Loading Loading @@ -2470,6 +2482,23 @@ public class AppOpsManager { void onOpActiveChanged(int code, int uid, String packageName, boolean active); } /** * Callback for notification of an op being noted. * * @hide */ public interface OnOpNotedListener { /** * Called when an op was noted. * * @param code The op code. * @param uid The UID performing the operation. * @param packageName The package performing the operation. * @param result The result of the note. */ void onOpNoted(String code, int uid, String packageName, int result); } /** * Callback for notification of changes to operation state. * This allows you to see the raw op codes instead of strings. Loading Loading @@ -2819,7 +2848,7 @@ public class AppOpsManager { */ public void stopWatchingMode(OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); IAppOpsCallback cb = mModeWatchers.remove(callback); if (cb != null) { try { mService.stopWatchingMode(cb); Loading Loading @@ -2893,7 +2922,7 @@ public class AppOpsManager { @TestApi public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) { synchronized (mActiveWatchers) { final IAppOpsActiveCallback cb = mActiveWatchers.get(callback); final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback); if (cb != null) { try { mService.stopWatchingActive(cb); Loading @@ -2904,6 +2933,74 @@ public class AppOpsManager { } } /** * Start watching for noted app ops. An app op may be immediate or long running. * Immediate ops are noted while long running ones are started and stopped. This * method allows registering a listener to be notified when an app op is noted. If * an op is being noted 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. * * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission * you can watch changes only for your UID. * * @param ops The ops to watch. * @param callback Where to report changes. * * @see #startWatchingActive(int[], OnOpActiveChangedListener) * @see #stopWatchingNoted(OnOpNotedListener) * @see #noteOp(String, int, String) * * @hide */ @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) { IAppOpsNotedCallback cb; synchronized (mNotedWatchers) { cb = mNotedWatchers.get(callback); if (cb != null) { return; } cb = new IAppOpsNotedCallback.Stub() { @Override public void opNoted(int op, int uid, String packageName, int mode) { callback.onOpNoted(sOpToString[op], uid, packageName, mode); } }; mNotedWatchers.put(callback, cb); } try { final int[] opCodes = new int[ops.length]; for (int i = 0; i < opCodes.length; i++) { opCodes[i] = strOpToOp(ops[i]); } mService.startWatchingNoted(opCodes, cb); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Stop watching for noted app ops. An app op may be immediate or long running. * Unregistering a non-registered callback has no effect. * * @see #startWatchingNoted(String[], OnOpNotedListener) * @see #noteOp(String, int, String) * * @hide */ public void stopWatchingNoted(@NonNull OnOpNotedListener callback) { synchronized (mNotedWatchers) { final IAppOpsNotedCallback cb = mNotedWatchers.get(callback); if (cb != null) { try { mService.stopWatchingNoted(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]; } Loading
core/java/com/android/internal/app/IAppOpsNotedCallback.aidl 0 → 100644 +22 −0 Original line number Diff line number Diff line /* * 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 note/checks of ops oneway interface IAppOpsNotedCallback { void opNoted(int op, int uid, String packageName, int mode); }
core/java/com/android/internal/app/IAppOpsService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.pm.ParceledListSlice; import android.os.Bundle; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsNotedCallback; interface IAppOpsService { // These first methods are also called by native code, so must Loading Loading @@ -61,4 +62,7 @@ interface IAppOpsService { boolean isOperationActive(int code, int uid, String packageName); void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback); void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback); void stopWatchingNoted(IAppOpsNotedCallback callback); }
services/core/java/com/android/server/AppOpsService.java +186 −5 Original line number Diff line number Diff line Loading @@ -86,6 +86,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsNotedCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; Loading Loading @@ -456,6 +457,7 @@ public class AppOpsService extends IAppOpsService.Stub { final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>(); final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>(); final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>(); final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>(); final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>(); final class ModeCallback implements DeathRecipient { Loading @@ -475,6 +477,7 @@ public class AppOpsService extends IAppOpsService.Stub { try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { /*ignored*/ } } Loading Loading @@ -524,6 +527,7 @@ public class AppOpsService extends IAppOpsService.Stub { try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { /*ignored*/ } } Loading Loading @@ -552,6 +556,50 @@ public class AppOpsService extends IAppOpsService.Stub { } } final class NotedCallback implements DeathRecipient { final IAppOpsNotedCallback mCallback; final int mWatchingUid; final int mCallingUid; final int mCallingPid; NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid, int callingPid) { mCallback = callback; mWatchingUid = watchingUid; mCallingUid = callingUid; mCallingPid = callingPid; try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { /*ignored*/ } } @Override public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("NotedCallback{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" watchinguid="); UserHandle.formatUid(sb, mWatchingUid); sb.append(" from uid="); UserHandle.formatUid(sb, mCallingUid); sb.append(" pid="); sb.append(mCallingPid); sb.append('}'); return sb.toString(); } void destroy() { mCallback.asBinder().unlinkToDeath(this, 0); } @Override public void binderDied() { stopWatchingNoted(mCallback); } } final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); final class ClientState extends Binder implements DeathRecipient { Loading Loading @@ -1629,7 +1677,7 @@ public class AppOpsService extends IAppOpsService.Stub { UidState uidState = getUidStateLocked(uid, false); if (uidState != null && uidState.opModes != null && uidState.opModes.indexOfKey(code) >= 0) { return uidState.opModes.get(code); return uidState.evalMode(uidState.opModes.get(code)); } Op op = getOpLocked(code, uid, packageName, false, true, false); if (op == null) { Loading Loading @@ -1795,12 +1843,16 @@ public class AppOpsService extends IAppOpsService.Stub { final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, false /* uidMismatchExpected */); if (ops == null) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid + " package " + packageName); return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, true); if (isOpRestrictedLocked(uid, code, packageName)) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; } final UidState uidState = ops.uidState; Loading @@ -1820,6 +1872,7 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime[uidState.state] = System.currentTimeMillis(); scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode); return uidMode; } } else { Loading @@ -1830,6 +1883,7 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime[uidState.state] = System.currentTimeMillis(); scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode); return mode; } } Loading @@ -1839,6 +1893,8 @@ public class AppOpsService extends IAppOpsService.Stub { op.rejectTime[uidState.state] = 0; op.proxyUid = proxyUid; op.proxyPackageName = proxyPackageName; scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED); return AppOpsManager.MODE_ALLOWED; } } Loading Loading @@ -1886,12 +1942,52 @@ public class AppOpsService extends IAppOpsService.Stub { } 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 void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) { int watchedUid = Process.INVALID_UID; final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS) != PackageManager.PERMISSION_GRANTED) { watchedUid = callingUid; } Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty"); Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops)); Preconditions.checkNotNull(callback, "Callback cannot be null"); synchronized (this) { SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder()); if (callbacks == null) { callbacks = new SparseArray<>(); mNotedWatchers.put(callback.asBinder(), callbacks); } final NotedCallback notedCallback = new NotedCallback(callback, watchedUid, callingUid, callingPid); for (int op : ops) { callbacks.put(op, notedCallback); } } } @Override public void stopWatchingNoted(IAppOpsNotedCallback callback) { Preconditions.checkNotNull(callback, "Callback cannot be null"); synchronized (this) { final SparseArray<NotedCallback> notedCallbacks = mNotedWatchers.remove(callback.asBinder()); if (notedCallbacks == null) { return; } final int callbackCount = notedCallbacks.size(); for (int i = 0; i < callbackCount; i++) { notedCallbacks.valueAt(i).destroy(); } } } @Override Loading Loading @@ -2052,6 +2148,51 @@ public class AppOpsService extends IAppOpsService.Stub { } } private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName, int result) { ArraySet<NotedCallback> dispatchedCallbacks = null; final int callbackListCount = mNotedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i); final NotedCallback callback = callbacks.get(code); if (callback != null) { if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) { continue; } if (dispatchedCallbacks == null) { dispatchedCallbacks = new ArraySet<>(); } dispatchedCallbacks.add(callback); } } if (dispatchedCallbacks == null) { return; } mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChecked, this, dispatchedCallbacks, code, uid, packageName, result)); } private void notifyOpChecked(ArraySet<NotedCallback> callbacks, int code, int uid, String packageName, int result) { // There are components watching for checks 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 NotedCallback callback = callbacks.valueAt(i); try { callback.mCallback.opNoted(code, uid, packageName, result); } catch (RemoteException e) { /* do nothing */ } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public int permissionToOpCode(String permission) { if (permission == null) { Loading Loading @@ -3463,6 +3604,46 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(cb); } } if (mNotedWatchers.size() > 0 && dumpMode < 0) { needSep = true; boolean printedHeader = false; for (int i = 0; i < mNotedWatchers.size(); i++) { final SparseArray<NotedCallback> notedWatchers = mNotedWatchers.valueAt(i); if (notedWatchers.size() <= 0) { continue; } final NotedCallback cb = notedWatchers.valueAt(0); if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) { continue; } if (dumpPackage != null && cb.mWatchingUid >= 0 && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) { continue; } if (!printedHeader) { pw.println(" All op noted watchers:"); printedHeader = true; } pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode( mNotedWatchers.keyAt(i)))); pw.println(" ->"); pw.print(" ["); final int opCount = notedWatchers.size(); for (i = 0; i < opCount; i++) { if (i > 0) { pw.print(' '); } pw.print(AppOpsManager.opToName(notedWatchers.keyAt(i))); if (i < opCount - 1) { pw.print(','); } } pw.println("]"); pw.print(" "); pw.println(cb); } } if (mClients.size() > 0 && dumpMode < 0) { needSep = true; boolean printedHeader = false; Loading