blob: a480219fbf408a06553dcb13d6f8bfb75a383264 [file] [log] [blame]
/*
* 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.Manifest;
import android.os.Binder;
import android.os.IBinder;
import android.os.UserManager;
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.
*
* <p>This API is not generally intended for third party application developers; most
* features are only available to system applications. Obtain an instance of it through
* {@link Context#getSystemService(String) Context.getSystemService} with
* {@link Context#APP_OPS_SERVICE Context.APP_OPS_SERVICE}.</p>
*/
public class AppOpsManager {
/**
* <p>App ops allows callers to:</p>
*
* <ul>
* <li> Note when operations are happening, and find out if they are allowed for the current
* caller.</li>
* <li> Disallow specific apps from doing specific operations.</li>
* <li> Collect all of the current information about operations that have been executed or
* are not being allowed.</li>
* <li> Monitor for changes in whether an operation is allowed.</li>
* </ul>
*
* <p>Each operation is identified by a single integer; these integers are a fixed set of
* operations, enumerated by the OP_* constants.
*
* <p></p>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<OnOpChangedListener, IAppOpsCallback> mModeWatchers
= new ArrayMap<OnOpChangedListener, IAppOpsCallback>();
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
* <em>silently fail</em> (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 Retrieve current usage stats via {@link UsageStatsManager}. */
public static final int OP_GET_USAGE_STATS = 43;
/** @hide */
public static final int OP_MUTE_MICROPHONE = 44;
/** @hide */
public static final int _NUM_OP = 45;
/** 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,
OP_GET_USAGE_STATS,
OP_MUTE_MICROPHONE
};
/**
* 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,
null,
null,
};
/**
* 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",
"GET_USAGE_STATS",
"OP_MUTE_MICROPHONE",
};
/**
* 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,
android.Manifest.permission.ACCESS_WIFI_STATE,
null, // no permission required for notifications
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
android.Manifest.permission.PACKAGE_USAGE_STATS,
null, // no permission for muting/unmuting microphone
};
/**
* Specifies whether an Op should be restricted by a user restriction.
* Each Op should be filled with a restriction string from UserManager or
* null to specify it is not affected by any user restriction.
*/
private static String[] sOpRestrictions = new String[] {
null, //COARSE_LOCATION
null, //FINE_LOCATION
null, //GPS
null, //VIBRATE
null, //READ_CONTACTS
null, //WRITE_CONTACTS
null, //READ_CALL_LOG
null, //WRITE_CALL_LOG
null, //READ_CALENDAR
null, //WRITE_CALENDAR
null, //WIFI_SCAN
null, //POST_NOTIFICATION
null, //NEIGHBORING_CELLS
null, //CALL_PHONE
null, //READ_SMS
null, //WRITE_SMS
null, //RECEIVE_SMS
null, //RECEIVE_EMERGECY_SMS
null, //RECEIVE_MMS
null, //RECEIVE_WAP_PUSH
null, //SEND_SMS
null, //READ_ICC_SMS
null, //WRITE_ICC_SMS
null, //WRITE_SETTINGS
null, //SYSTEM_ALERT_WINDOW
null, //ACCESS_NOTIFICATIONS
null, //CAMERA
null, //RECORD_AUDIO
null, //PLAY_AUDIO
null, //READ_CLIPBOARD
null, //WRITE_CLIPBOARD
null, //TAKE_MEDIA_BUTTONS
null, //TAKE_AUDIO_FOCUS
UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_MASTER_VOLUME
UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_VOICE_VOLUME
UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_RING_VOLUME
UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_MEDIA_VOLUME
UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_ALARM_VOLUME
UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_NOTIFICATION_VOLUME
UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_BLUETOOTH_VOLUME
null, //WAKE_LOCK
null, //MONITOR_LOCATION
null, //MONITOR_HIGH_POWER_LOCATION
null, //GET_USAGE_STATS
UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
};
/**
* 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,
AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
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,
false,
false,
};
private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
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);
}
if (sOpRestrictions.length != _NUM_OP) {
throw new IllegalStateException("sOpRestrictions length " + sOpRestrictions.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 user restriction associated with an operation, or null if there is not one.
* @hide
*/
public static String opToRestriction(int op) {
return sOpRestrictions[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<OpEntry> mEntries;
public PackageOps(String packageName, int uid, List<OpEntry> entries) {
mPackageName = packageName;
mUid = uid;
mEntries = entries;
}
public String getPackageName() {
return mPackageName;
}
public int getUid() {
return mUid;
}
public List<OpEntry> 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<mEntries.size(); i++) {
mEntries.get(i).writeToParcel(dest, flags);
}
}
PackageOps(Parcel source) {
mPackageName = source.readString();
mUid = source.readInt();
mEntries = new ArrayList<OpEntry>();
final int N = source.readInt();
for (int i=0; i<N; i++) {
mEntries.add(OpEntry.CREATOR.createFromParcel(source));
}
}
public static final Creator<PackageOps> CREATOR = new Creator<PackageOps>() {
@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<OpEntry> CREATOR = new Creator<OpEntry>() {
@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<AppOpsManager.PackageOps> 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<AppOpsManager.PackageOps> 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) {
}
}
/**
* Set a non-persisted restriction on an audio operation at a stream-level.
* Restrictions are temporary additional constraints imposed on top of the persisted rules
* defined by {@link #setMode}.
*
* @param code The operation to restrict.
* @param stream The {@link android.media.AudioManager} stream type.
* @param mode The restriction mode (MODE_IGNORED,MODE_ERRORED) or MODE_ALLOWED to unrestrict.
* @param exceptionPackages Optional list of packages to exclude from the restriction.
* @hide
*/
public void setRestriction(int code, int stream, int mode, String[] exceptionPackages) {
try {
final int uid = Binder.getCallingUid();
mService.setAudioRestriction(code, stream, uid, mode, exceptionPackages);
} 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 <em>not</em> 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 <em>not</em> 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);
}
}
/**
* Like {@link #checkOp} but at a stream-level for audio operations.
* @hide
*/
public int checkAudioOp(int op, int stream, int uid, String packageName) {
try {
final int mode = mService.checkAudioOperation(op, stream, uid, packageName);
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
return mode;
} catch (RemoteException e) {
}
return MODE_IGNORED;
}
/**
* Like {@link #checkAudioOp} but instead of throwing a {@link SecurityException} it
* returns {@link #MODE_ERRORED}.
* @hide
*/
public int checkAudioOpNoThrow(int op, int stream, int uid, String packageName) {
try {
return mService.checkAudioOperation(op, stream, uid, packageName);
} catch (RemoteException e) {
}
return MODE_IGNORED;
}
/**
* 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());
}
}