/* * Copyright (C) 2012 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 android.app; import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsCallback; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; /** * API for interacting with "application operation" tracking. * *

This API is not generally intended for third party application developers; most * features are only available to system applicatins. Obtain an instance of it through * {@link Context#getSystemService(String) Context.getSystemService} with * {@link Context#APP_OPS_SERVICE Context.APP_OPS_SERVICE}.

*/ public class AppOpsManager { /** *

App ops allows callers to:

* * * *

Each operation is identified by a single integer; these integers are a fixed set of * operations, enumerated by the OP_* constants. * *

When checking operations, the result is a "mode" integer indicating the current * setting for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute * the operation but fake its behavior enough so that the caller doesn't crash), * MODE_ERRORED (throw a SecurityException back to the caller; the normal operation calls * will do this for you). */ final Context mContext; final IAppOpsService mService; final ArrayMap mModeWatchers = new ArrayMap(); static IBinder sToken; /** * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is * allowed to perform the given operation. */ public static final int MODE_ALLOWED = 0; /** * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is * not allowed to perform the given operation, and this attempt should * silently fail (it should not cause the app to crash). */ public static final int MODE_IGNORED = 1; /** * Result from {@link #checkOpNoThrow}, {@link #noteOpNoThrow}, {@link #startOpNoThrow}: the * given caller is not allowed to perform the given operation, and this attempt should * cause it to have a fatal error, typically a {@link SecurityException}. */ public static final int MODE_ERRORED = 2; // when adding one of these: // - increment _NUM_OP // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode // - add descriptive strings to Settings/res/values/arrays.xml // - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app) /** @hide No operation specified. */ public static final int OP_NONE = -1; /** @hide Access to coarse location information. */ public static final int OP_COARSE_LOCATION = 0; /** @hide Access to fine location information. */ public static final int OP_FINE_LOCATION = 1; /** @hide Causing GPS to run. */ public static final int OP_GPS = 2; /** @hide */ public static final int OP_VIBRATE = 3; /** @hide */ public static final int OP_READ_CONTACTS = 4; /** @hide */ public static final int OP_WRITE_CONTACTS = 5; /** @hide */ public static final int OP_READ_CALL_LOG = 6; /** @hide */ public static final int OP_WRITE_CALL_LOG = 7; /** @hide */ public static final int OP_READ_CALENDAR = 8; /** @hide */ public static final int OP_WRITE_CALENDAR = 9; /** @hide */ public static final int OP_WIFI_SCAN = 10; /** @hide */ public static final int OP_POST_NOTIFICATION = 11; /** @hide */ public static final int OP_NEIGHBORING_CELLS = 12; /** @hide */ public static final int OP_CALL_PHONE = 13; /** @hide */ public static final int OP_READ_SMS = 14; /** @hide */ public static final int OP_WRITE_SMS = 15; /** @hide */ public static final int OP_RECEIVE_SMS = 16; /** @hide */ public static final int OP_RECEIVE_EMERGECY_SMS = 17; /** @hide */ public static final int OP_RECEIVE_MMS = 18; /** @hide */ public static final int OP_RECEIVE_WAP_PUSH = 19; /** @hide */ public static final int OP_SEND_SMS = 20; /** @hide */ public static final int OP_READ_ICC_SMS = 21; /** @hide */ public static final int OP_WRITE_ICC_SMS = 22; /** @hide */ public static final int OP_WRITE_SETTINGS = 23; /** @hide */ public static final int OP_SYSTEM_ALERT_WINDOW = 24; /** @hide */ public static final int OP_ACCESS_NOTIFICATIONS = 25; /** @hide */ public static final int OP_CAMERA = 26; /** @hide */ public static final int OP_RECORD_AUDIO = 27; /** @hide */ public static final int OP_PLAY_AUDIO = 28; /** @hide */ public static final int OP_READ_CLIPBOARD = 29; /** @hide */ public static final int OP_WRITE_CLIPBOARD = 30; /** @hide */ public static final int OP_TAKE_MEDIA_BUTTONS = 31; /** @hide */ public static final int OP_TAKE_AUDIO_FOCUS = 32; /** @hide */ public static final int OP_AUDIO_MASTER_VOLUME = 33; /** @hide */ public static final int OP_AUDIO_VOICE_VOLUME = 34; /** @hide */ public static final int OP_AUDIO_RING_VOLUME = 35; /** @hide */ public static final int OP_AUDIO_MEDIA_VOLUME = 36; /** @hide */ public static final int OP_AUDIO_ALARM_VOLUME = 37; /** @hide */ public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38; /** @hide */ public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39; /** @hide */ public static final int OP_WAKE_LOCK = 40; /** @hide Continually monitoring location data. */ public static final int OP_MONITOR_LOCATION = 41; /** @hide Continually monitoring location data with a relatively high power request. */ public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42; /** @hide */ public static final int _NUM_OP = 43; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; /** Access to fine location information. */ public static final String OPSTR_FINE_LOCATION = "android:fine_location"; /** Continually monitoring location data. */ public static final String OPSTR_MONITOR_LOCATION = "android:monitor_location"; /** Continually monitoring location data with a relatively high power request. */ public static final String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power"; /** * This maps each operation to the operation that serves as the * switch to determine whether it is allowed. Generally this is * a 1:1 mapping, but for some things (like location) that have * multiple low-level operations being tracked that should be * presented to the user as one switch then this can be used to * make them all controlled by the same single operation. */ private static int[] sOpToSwitch = new int[] { OP_COARSE_LOCATION, OP_COARSE_LOCATION, OP_COARSE_LOCATION, OP_VIBRATE, OP_READ_CONTACTS, OP_WRITE_CONTACTS, OP_READ_CALL_LOG, OP_WRITE_CALL_LOG, OP_READ_CALENDAR, OP_WRITE_CALENDAR, OP_COARSE_LOCATION, OP_POST_NOTIFICATION, OP_COARSE_LOCATION, OP_CALL_PHONE, OP_READ_SMS, OP_WRITE_SMS, OP_RECEIVE_SMS, OP_RECEIVE_SMS, OP_RECEIVE_SMS, OP_RECEIVE_SMS, OP_SEND_SMS, OP_READ_SMS, OP_WRITE_SMS, OP_WRITE_SETTINGS, OP_SYSTEM_ALERT_WINDOW, OP_ACCESS_NOTIFICATIONS, OP_CAMERA, OP_RECORD_AUDIO, OP_PLAY_AUDIO, OP_READ_CLIPBOARD, OP_WRITE_CLIPBOARD, OP_TAKE_MEDIA_BUTTONS, OP_TAKE_AUDIO_FOCUS, OP_AUDIO_MASTER_VOLUME, OP_AUDIO_VOICE_VOLUME, OP_AUDIO_RING_VOLUME, OP_AUDIO_MEDIA_VOLUME, OP_AUDIO_ALARM_VOLUME, OP_AUDIO_NOTIFICATION_VOLUME, OP_AUDIO_BLUETOOTH_VOLUME, OP_WAKE_LOCK, OP_COARSE_LOCATION, OP_COARSE_LOCATION, }; /** * This maps each operation to the public string constant for it. * If it doesn't have a public string constant, it maps to null. */ private static String[] sOpToString = new String[] { OPSTR_COARSE_LOCATION, OPSTR_FINE_LOCATION, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, OPSTR_MONITOR_LOCATION, OPSTR_MONITOR_HIGH_POWER_LOCATION, }; /** * This provides a simple name for each operation to be used * in debug output. */ private static String[] sOpNames = new String[] { "COARSE_LOCATION", "FINE_LOCATION", "GPS", "VIBRATE", "READ_CONTACTS", "WRITE_CONTACTS", "READ_CALL_LOG", "WRITE_CALL_LOG", "READ_CALENDAR", "WRITE_CALENDAR", "WIFI_SCAN", "POST_NOTIFICATION", "NEIGHBORING_CELLS", "CALL_PHONE", "READ_SMS", "WRITE_SMS", "RECEIVE_SMS", "RECEIVE_EMERGECY_SMS", "RECEIVE_MMS", "RECEIVE_WAP_PUSH", "SEND_SMS", "READ_ICC_SMS", "WRITE_ICC_SMS", "WRITE_SETTINGS", "SYSTEM_ALERT_WINDOW", "ACCESS_NOTIFICATIONS", "CAMERA", "RECORD_AUDIO", "PLAY_AUDIO", "READ_CLIPBOARD", "WRITE_CLIPBOARD", "TAKE_MEDIA_BUTTONS", "TAKE_AUDIO_FOCUS", "AUDIO_MASTER_VOLUME", "AUDIO_VOICE_VOLUME", "AUDIO_RING_VOLUME", "AUDIO_MEDIA_VOLUME", "AUDIO_ALARM_VOLUME", "AUDIO_NOTIFICATION_VOLUME", "AUDIO_BLUETOOTH_VOLUME", "WAKE_LOCK", "MONITOR_LOCATION", "MONITOR_HIGH_POWER_LOCATION", }; /** * This optionally maps a permission to an operation. If there * is no permission associated with an operation, it is null. */ private static String[] sOpPerms = new String[] { android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, null, android.Manifest.permission.VIBRATE, android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.WRITE_CONTACTS, android.Manifest.permission.READ_CALL_LOG, android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.READ_CALENDAR, android.Manifest.permission.WRITE_CALENDAR, null, // no permission required for notifications android.Manifest.permission.ACCESS_WIFI_STATE, null, // neighboring cells shares the coarse location perm android.Manifest.permission.CALL_PHONE, android.Manifest.permission.READ_SMS, android.Manifest.permission.WRITE_SMS, android.Manifest.permission.RECEIVE_SMS, android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST, android.Manifest.permission.RECEIVE_MMS, android.Manifest.permission.RECEIVE_WAP_PUSH, android.Manifest.permission.SEND_SMS, android.Manifest.permission.READ_SMS, android.Manifest.permission.WRITE_SMS, android.Manifest.permission.WRITE_SETTINGS, android.Manifest.permission.SYSTEM_ALERT_WINDOW, android.Manifest.permission.ACCESS_NOTIFICATIONS, android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO, null, // no permission for playing audio null, // no permission for reading clipboard null, // no permission for writing clipboard null, // no permission for taking media buttons null, // no permission for taking audio focus null, // no permission for changing master volume null, // no permission for changing voice volume null, // no permission for changing ring volume null, // no permission for changing media volume null, // no permission for changing alarm volume null, // no permission for changing notification volume null, // no permission for changing bluetooth volume android.Manifest.permission.WAKE_LOCK, null, // no permission for generic location monitoring null, // no permission for high power location monitoring }; /** * This specifies the default mode for each operation. */ private static int[] sOpDefaultMode = new int[] { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_IGNORED, // OP_WRITE_SMS AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, }; /** * This specifies whether each option is allowed to be reset * when resetting all app preferences. Disable reset for * app ops that are under strong control of some part of the * system (such as OP_WRITE_SMS, which should be allowed only * for whichever app is selected as the current SMS app). */ private static boolean[] sOpDisableReset = new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, // OP_WRITE_SMS false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, }; private static HashMap sOpStrToOp = new HashMap(); static { if (sOpToSwitch.length != _NUM_OP) { throw new IllegalStateException("sOpToSwitch length " + sOpToSwitch.length + " should be " + _NUM_OP); } if (sOpToString.length != _NUM_OP) { throw new IllegalStateException("sOpToString length " + sOpToString.length + " should be " + _NUM_OP); } if (sOpNames.length != _NUM_OP) { throw new IllegalStateException("sOpNames length " + sOpNames.length + " should be " + _NUM_OP); } if (sOpPerms.length != _NUM_OP) { throw new IllegalStateException("sOpPerms length " + sOpPerms.length + " should be " + _NUM_OP); } if (sOpDefaultMode.length != _NUM_OP) { throw new IllegalStateException("sOpDefaultMode length " + sOpDefaultMode.length + " should be " + _NUM_OP); } if (sOpDisableReset.length != _NUM_OP) { throw new IllegalStateException("sOpDisableReset length " + sOpDisableReset.length + " should be " + _NUM_OP); } for (int i=0; i<_NUM_OP; i++) { if (sOpToString[i] != null) { sOpStrToOp.put(sOpToString[i], i); } } } /** * Retrieve the op switch that controls the given operation. * @hide */ public static int opToSwitch(int op) { return sOpToSwitch[op]; } /** * Retrieve a non-localized name for the operation, for debugging output. * @hide */ public static String opToName(int op) { if (op == OP_NONE) return "NONE"; return op < sOpNames.length ? sOpNames[op] : ("Unknown(" + op + ")"); } /** * Retrieve the permission associated with an operation, or null if there is not one. * @hide */ public static String opToPermission(int op) { return sOpPerms[op]; } /** * Retrieve the default mode for the operation. * @hide */ public static int opToDefaultMode(int op) { return sOpDefaultMode[op]; } /** * Retrieve whether the op allows itself to be reset. * @hide */ public static boolean opAllowsReset(int op) { return !sOpDisableReset[op]; } /** * Class holding all of the operation information associated with an app. * @hide */ public static class PackageOps implements Parcelable { private final String mPackageName; private final int mUid; private final List mEntries; public PackageOps(String packageName, int uid, List entries) { mPackageName = packageName; mUid = uid; mEntries = entries; } public String getPackageName() { return mPackageName; } public int getUid() { return mUid; } public List getOps() { return mEntries; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackageName); dest.writeInt(mUid); dest.writeInt(mEntries.size()); for (int i=0; i(); final int N = source.readInt(); for (int i=0; i CREATOR = new Creator() { @Override public PackageOps createFromParcel(Parcel source) { return new PackageOps(source); } @Override public PackageOps[] newArray(int size) { return new PackageOps[size]; } }; } /** * Class holding the information about one unique operation of an application. * @hide */ public static class OpEntry implements Parcelable { private final int mOp; private final int mMode; private final long mTime; private final long mRejectTime; private final int mDuration; public OpEntry(int op, int mode, long time, long rejectTime, int duration) { mOp = op; mMode = mode; mTime = time; mRejectTime = rejectTime; mDuration = duration; } public int getOp() { return mOp; } public int getMode() { return mMode; } public long getTime() { return mTime; } public long getRejectTime() { return mRejectTime; } public boolean isRunning() { return mDuration == -1; } public int getDuration() { return mDuration == -1 ? (int)(System.currentTimeMillis()-mTime) : mDuration; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mOp); dest.writeInt(mMode); dest.writeLong(mTime); dest.writeLong(mRejectTime); dest.writeInt(mDuration); } OpEntry(Parcel source) { mOp = source.readInt(); mMode = source.readInt(); mTime = source.readLong(); mRejectTime = source.readLong(); mDuration = source.readInt(); } public static final Creator CREATOR = new Creator() { @Override public OpEntry createFromParcel(Parcel source) { return new OpEntry(source); } @Override public OpEntry[] newArray(int size) { return new OpEntry[size]; } }; } /** * Callback for notification of changes to operation state. */ public interface OnOpChangedListener { public void onOpChanged(String op, String packageName); } /** * Callback for notification of changes to operation state. * This allows you to see the raw op codes instead of strings. * @hide */ public static class OnOpChangedInternalListener implements OnOpChangedListener { public void onOpChanged(String op, String packageName) { } public void onOpChanged(int op, String packageName) { } } AppOpsManager(Context context, IAppOpsService service) { mContext = context; mService = service; } /** * Retrieve current operation state for all applications. * * @param ops The set of operations you are interested in, or null if you want all of them. * @hide */ public List getPackagesForOps(int[] ops) { try { return mService.getPackagesForOps(ops); } catch (RemoteException e) { } return null; } /** * Retrieve current operation state for one application. * * @param uid The uid of the application of interest. * @param packageName The name of the application of interest. * @param ops The set of operations you are interested in, or null if you want all of them. * @hide */ public List getOpsForPackage(int uid, String packageName, int[] ops) { try { return mService.getOpsForPackage(uid, packageName, ops); } catch (RemoteException e) { } return null; } /** @hide */ public void setMode(int code, int uid, String packageName, int mode) { try { mService.setMode(code, uid, packageName, mode); } catch (RemoteException e) { } } /** @hide */ public void resetAllModes() { try { mService.resetAllModes(); } catch (RemoteException e) { } } /** * Monitor for changes to the operating mode for the given op in the given app package. * @param op The operation to monitor, one of OPSTR_*. * @param packageName The name of the application to monitor. * @param callback Where to report changes. */ public void startWatchingMode(String op, String packageName, final OnOpChangedListener callback) { startWatchingMode(strOpToOp(op), packageName, callback); } /** * Monitor for changes to the operating mode for the given op in the given app package. * @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 */ public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); if (cb == null) { cb = new IAppOpsCallback.Stub() { public void opChanged(int op, String packageName) { if (callback instanceof OnOpChangedInternalListener) { ((OnOpChangedInternalListener)callback).onOpChanged(op, packageName); } if (sOpToString[op] != null) { callback.onOpChanged(sOpToString[op], packageName); } } }; mModeWatchers.put(callback, cb); } try { mService.startWatchingMode(op, packageName, cb); } catch (RemoteException e) { } } } /** * Stop monitoring that was previously started with {@link #startWatchingMode}. All * monitoring associated with this callback will be removed. */ public void stopWatchingMode(OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); if (cb != null) { try { mService.stopWatchingMode(cb); } catch (RemoteException e) { } } } } private String buildSecurityExceptionMsg(int op, int uid, String packageName) { return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op]; } private int strOpToOp(String op) { Integer val = sOpStrToOp.get(op); if (val == null) { throw new IllegalArgumentException("Unknown operation string: " + op); } return val; } /** * Do a quick check for whether an application might be able to perform an operation. * This is not a security check; you must use {@link #noteOp(String, int, String)} * or {@link #startOp(String, int, String)} for your actual security checks, which also * ensure that the given uid and package name are consistent. This function can just be * used for a quick check to see if an operation has been disabled for the application, * as an early reject of some work. This does not modify the time stamp or other data * about the operation. * @param op The operation to check. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. */ public int checkOp(String op, int uid, String packageName) { return checkOp(strOpToOp(op), uid, packageName); } /** * Like {@link #checkOp but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. */ public int checkOpNoThrow(String op, int uid, String packageName) { return checkOpNoThrow(strOpToOp(op), uid, packageName); } /** * Make note of an application performing an operation. Note that you must pass * in both the uid and name of the application to be checked; this function will verify * that these two match, and if not, return {@link #MODE_IGNORED}. If this call * succeeds, the last execution time of the operation for this app will be updated to * the current time. * @param op The operation to note. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. */ public int noteOp(String op, int uid, String packageName) { return noteOp(strOpToOp(op), uid, packageName); } /** * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. */ public int noteOpNoThrow(String op, int uid, String packageName) { return noteOpNoThrow(strOpToOp(op), uid, packageName); } /** * Report that an application has started executing a long-running operation. Note that you * must pass in both the uid and name of the application to be checked; this function will * verify that these two match, and if not, return {@link #MODE_IGNORED}. If this call * succeeds, the last execution time of the operation for this app will be updated to * the current time and the operation will be marked as "running". In this case you must * later call {@link #finishOp(String, int, String)} to report when the application is no * longer performing the operation. * @param op The operation to start. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. */ public int startOp(String op, int uid, String packageName) { return startOp(strOpToOp(op), uid, packageName); } /** * Like {@link #startOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. */ public int startOpNoThrow(String op, int uid, String packageName) { return startOpNoThrow(strOpToOp(op), uid, packageName); } /** * Report that an application is no longer performing an operation that had previously * been started with {@link #startOp(String, int, String)}. There is no validation of input * or result; the parameters supplied here must be the exact same ones previously passed * in when starting the operation. */ public void finishOp(String op, int uid, String packageName) { finishOp(strOpToOp(op), uid, packageName); } /** * Do a quick check for whether an application might be able to perform an operation. * This is not a security check; you must use {@link #noteOp(int, int, String)} * or {@link #startOp(int, int, String)} for your actual security checks, which also * ensure that the given uid and package name are consistent. This function can just be * used for a quick check to see if an operation has been disabled for the application, * as an early reject of some work. This does not modify the time stamp or other data * about the operation. * @param op The operation to check. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. * @hide */ public int checkOp(int op, int uid, String packageName) { try { int mode = mService.checkOperation(op, uid, packageName); if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } return mode; } catch (RemoteException e) { } return MODE_IGNORED; } /** * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. * @hide */ public int checkOpNoThrow(int op, int uid, String packageName) { try { return mService.checkOperation(op, uid, packageName); } catch (RemoteException e) { } return MODE_IGNORED; } /** * Do a quick check to validate if a package name belongs to a UID. * * @throws SecurityException if the package name doesn't belong to the given * UID, or if ownership cannot be verified. */ public void checkPackage(int uid, String packageName) { try { if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) { throw new SecurityException( "Package " + packageName + " does not belong to " + uid); } } catch (RemoteException e) { throw new SecurityException("Unable to verify package ownership", e); } } /** * Make note of an application performing an operation. Note that you must pass * in both the uid and name of the application to be checked; this function will verify * that these two match, and if not, return {@link #MODE_IGNORED}. If this call * succeeds, the last execution time of the operation for this app will be updated to * the current time. * @param op The operation to note. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. * @hide */ public int noteOp(int op, int uid, String packageName) { try { int mode = mService.noteOperation(op, uid, packageName); if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } return mode; } catch (RemoteException e) { } return MODE_IGNORED; } /** * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. * @hide */ public int noteOpNoThrow(int op, int uid, String packageName) { try { return mService.noteOperation(op, uid, packageName); } catch (RemoteException e) { } return MODE_IGNORED; } /** @hide */ public int noteOp(int op) { return noteOp(op, Process.myUid(), mContext.getOpPackageName()); } /** @hide */ public static IBinder getToken(IAppOpsService service) { synchronized (AppOpsManager.class) { if (sToken != null) { return sToken; } try { sToken = service.getToken(new Binder()); } catch (RemoteException e) { // System is dead, whatevs. } return sToken; } } /** * Report that an application has started executing a long-running operation. Note that you * must pass in both the uid and name of the application to be checked; this function will * verify that these two match, and if not, return {@link #MODE_IGNORED}. If this call * succeeds, the last execution time of the operation for this app will be updated to * the current time and the operation will be marked as "running". In this case you must * later call {@link #finishOp(int, int, String)} to report when the application is no * longer performing the operation. * @param op The operation to start. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. * @hide */ public int startOp(int op, int uid, String packageName) { try { int mode = mService.startOperation(getToken(mService), op, uid, packageName); if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } return mode; } catch (RemoteException e) { } return MODE_IGNORED; } /** * Like {@link #startOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. * @hide */ public int startOpNoThrow(int op, int uid, String packageName) { try { return mService.startOperation(getToken(mService), op, uid, packageName); } catch (RemoteException e) { } return MODE_IGNORED; } /** @hide */ public int startOp(int op) { return startOp(op, Process.myUid(), mContext.getOpPackageName()); } /** * Report that an application is no longer performing an operation that had previously * been started with {@link #startOp(int, int, String)}. There is no validation of input * or result; the parameters supplied here must be the exact same ones previously passed * in when starting the operation. * @hide */ public void finishOp(int op, int uid, String packageName) { try { mService.finishOperation(getToken(mService), op, uid, packageName); } catch (RemoteException e) { } } /** @hide */ public void finishOp(int op) { finishOp(op, Process.myUid(), mContext.getOpPackageName()); } }