| /* |
| * 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.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. Allows you to: |
| * |
| * <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 (through a |
| * SecurityException back to the caller; the normal operation calls will do this for you). |
| */ |
| public class AppOpsManager { |
| final Context mContext; |
| final IAppOpsService mService; |
| final ArrayMap<Callback, IAppOpsCallback> mModeWatchers |
| = new ArrayMap<Callback, IAppOpsCallback>(); |
| |
| static IBinder sToken; |
| |
| public static final int MODE_ALLOWED = 0; |
| public static final int MODE_IGNORED = 1; |
| public static final int MODE_ERRORED = 2; |
| |
| // when adding one of these: |
| // - increment _NUM_OP |
| // - add rows to sOpToSwitch, sOpNames, sOpPerms |
| // - add descriptive strings to Settings/res/values/arrays.xml |
| // - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app) |
| |
| /** No operation specified. */ |
| public static final int OP_NONE = -1; |
| /** Access to coarse location information. */ |
| public static final int OP_COARSE_LOCATION = 0; |
| /** Access to fine location information. */ |
| public static final int OP_FINE_LOCATION = 1; |
| /** 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; |
| /** Continually monitoring location data. */ |
| public static final int OP_MONITOR_LOCATION = 41; |
| /** 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; |
| |
| /** |
| * 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 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 |
| }; |
| |
| /** |
| * 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. |
| */ |
| 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]; |
| } |
| |
| /** |
| * 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 Callback { |
| public void opChanged(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) { |
| } |
| } |
| |
| /** @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 OP_*. |
| * @param packageName The name of the application to monitor. |
| * @param callback Where to report changes. |
| */ |
| public void startWatchingMode(int op, String packageName, final Callback callback) { |
| synchronized (mModeWatchers) { |
| IAppOpsCallback cb = mModeWatchers.get(callback); |
| if (cb == null) { |
| cb = new IAppOpsCallback.Stub() { |
| public void opChanged(int op, String packageName) { |
| callback.opChanged(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(Callback 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]; |
| } |
| |
| /** |
| * 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. |
| */ |
| 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}. |
| */ |
| 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. |
| */ |
| 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}. |
| */ |
| 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. |
| */ |
| 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}. |
| */ |
| 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. |
| */ |
| public void finishOp(int op, int uid, String packageName) { |
| try { |
| mService.finishOperation(getToken(mService), op, uid, packageName); |
| } catch (RemoteException e) { |
| } |
| } |
| |
| public void finishOp(int op) { |
| finishOp(op, Process.myUid(), mContext.getOpPackageName()); |
| } |
| } |