AppOpsManager.java revision 0b8374501975aecd7a628336e2f7e53c272ebeea
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import android.os.Binder;
20import android.os.IBinder;
21import android.util.ArrayMap;
22import com.android.internal.app.IAppOpsService;
23import com.android.internal.app.IAppOpsCallback;
24
25import java.util.ArrayList;
26import java.util.List;
27
28import android.content.Context;
29import android.os.Parcel;
30import android.os.Parcelable;
31import android.os.Process;
32import android.os.RemoteException;
33
34/**
35 * API for interacting with "application operation" tracking.  Allows you to:
36 *
37 * <ul>
38 * <li> Note when operations are happening, and find out if they are allowed for the current
39 * caller.</li>
40 * <li> Disallow specific apps from doing specific operations.</li>
41 * <li> Collect all of the current information about operations that have been executed or are not
42 * being allowed.</li>
43 * <li> Monitor for changes in whether an operation is allowed.</li>
44 * </ul>
45 *
46 * <p>Each operation is identified by a single integer; these integers are a fixed set of
47 * operations, enumerated by the OP_* constants.
48 *
49 * <p></p>When checking operations, the result is a "mode" integer indicating the current setting
50 * for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute the operation but
51 * fake its behavior enough so that the caller doesn't crash), MODE_ERRORED (through a
52 * SecurityException back to the caller; the normal operation calls will do this for you).
53 */
54public class AppOpsManager {
55    final Context mContext;
56    final IAppOpsService mService;
57    final ArrayMap<Callback, IAppOpsCallback> mModeWatchers
58            = new ArrayMap<Callback, IAppOpsCallback>();
59
60    static IBinder sToken;
61
62    public static final int MODE_ALLOWED = 0;
63    public static final int MODE_IGNORED = 1;
64    public static final int MODE_ERRORED = 2;
65
66    // when adding one of these:
67    //  - increment _NUM_OP
68    //  - add rows to sOpToSwitch, sOpNames, sOpPerms
69    //  - add descriptive strings to Settings/res/values/arrays.xml
70    //  - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
71
72    /** No operation specified. */
73    public static final int OP_NONE = -1;
74    /** Access to coarse location information. */
75    public static final int OP_COARSE_LOCATION = 0;
76    /** Access to fine location information. */
77    public static final int OP_FINE_LOCATION = 1;
78    /** Causing GPS to run. */
79    public static final int OP_GPS = 2;
80    /** @hide */
81    public static final int OP_VIBRATE = 3;
82    /** @hide */
83    public static final int OP_READ_CONTACTS = 4;
84    /** @hide */
85    public static final int OP_WRITE_CONTACTS = 5;
86    /** @hide */
87    public static final int OP_READ_CALL_LOG = 6;
88    /** @hide */
89    public static final int OP_WRITE_CALL_LOG = 7;
90    /** @hide */
91    public static final int OP_READ_CALENDAR = 8;
92    /** @hide */
93    public static final int OP_WRITE_CALENDAR = 9;
94    /** @hide */
95    public static final int OP_WIFI_SCAN = 10;
96    /** @hide */
97    public static final int OP_POST_NOTIFICATION = 11;
98    /** @hide */
99    public static final int OP_NEIGHBORING_CELLS = 12;
100    /** @hide */
101    public static final int OP_CALL_PHONE = 13;
102    /** @hide */
103    public static final int OP_READ_SMS = 14;
104    /** @hide */
105    public static final int OP_WRITE_SMS = 15;
106    /** @hide */
107    public static final int OP_RECEIVE_SMS = 16;
108    /** @hide */
109    public static final int OP_RECEIVE_EMERGECY_SMS = 17;
110    /** @hide */
111    public static final int OP_RECEIVE_MMS = 18;
112    /** @hide */
113    public static final int OP_RECEIVE_WAP_PUSH = 19;
114    /** @hide */
115    public static final int OP_SEND_SMS = 20;
116    /** @hide */
117    public static final int OP_READ_ICC_SMS = 21;
118    /** @hide */
119    public static final int OP_WRITE_ICC_SMS = 22;
120    /** @hide */
121    public static final int OP_WRITE_SETTINGS = 23;
122    /** @hide */
123    public static final int OP_SYSTEM_ALERT_WINDOW = 24;
124    /** @hide */
125    public static final int OP_ACCESS_NOTIFICATIONS = 25;
126    /** @hide */
127    public static final int OP_CAMERA = 26;
128    /** @hide */
129    public static final int OP_RECORD_AUDIO = 27;
130    /** @hide */
131    public static final int OP_PLAY_AUDIO = 28;
132    /** @hide */
133    public static final int OP_READ_CLIPBOARD = 29;
134    /** @hide */
135    public static final int OP_WRITE_CLIPBOARD = 30;
136    /** @hide */
137    public static final int OP_TAKE_MEDIA_BUTTONS = 31;
138    /** @hide */
139    public static final int OP_TAKE_AUDIO_FOCUS = 32;
140    /** @hide */
141    public static final int OP_AUDIO_MASTER_VOLUME = 33;
142    /** @hide */
143    public static final int OP_AUDIO_VOICE_VOLUME = 34;
144    /** @hide */
145    public static final int OP_AUDIO_RING_VOLUME = 35;
146    /** @hide */
147    public static final int OP_AUDIO_MEDIA_VOLUME = 36;
148    /** @hide */
149    public static final int OP_AUDIO_ALARM_VOLUME = 37;
150    /** @hide */
151    public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38;
152    /** @hide */
153    public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39;
154    /** @hide */
155    public static final int OP_WAKE_LOCK = 40;
156    /** Continually monitoring location data. */
157    public static final int OP_MONITOR_LOCATION = 41;
158    /** Continually monitoring location data with a relatively high power request. */
159    public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42;
160    /** @hide */
161    public static final int _NUM_OP = 43;
162
163    /**
164     * This maps each operation to the operation that serves as the
165     * switch to determine whether it is allowed.  Generally this is
166     * a 1:1 mapping, but for some things (like location) that have
167     * multiple low-level operations being tracked that should be
168     * presented to the user as one switch then this can be used to
169     * make them all controlled by the same single operation.
170     */
171    private static int[] sOpToSwitch = new int[] {
172            OP_COARSE_LOCATION,
173            OP_COARSE_LOCATION,
174            OP_COARSE_LOCATION,
175            OP_VIBRATE,
176            OP_READ_CONTACTS,
177            OP_WRITE_CONTACTS,
178            OP_READ_CALL_LOG,
179            OP_WRITE_CALL_LOG,
180            OP_READ_CALENDAR,
181            OP_WRITE_CALENDAR,
182            OP_COARSE_LOCATION,
183            OP_POST_NOTIFICATION,
184            OP_COARSE_LOCATION,
185            OP_CALL_PHONE,
186            OP_READ_SMS,
187            OP_WRITE_SMS,
188            OP_READ_SMS,
189            OP_READ_SMS,
190            OP_READ_SMS,
191            OP_READ_SMS,
192            OP_WRITE_SMS,
193            OP_READ_SMS,
194            OP_WRITE_SMS,
195            OP_WRITE_SETTINGS,
196            OP_SYSTEM_ALERT_WINDOW,
197            OP_ACCESS_NOTIFICATIONS,
198            OP_CAMERA,
199            OP_RECORD_AUDIO,
200            OP_PLAY_AUDIO,
201            OP_READ_CLIPBOARD,
202            OP_WRITE_CLIPBOARD,
203            OP_TAKE_MEDIA_BUTTONS,
204            OP_TAKE_AUDIO_FOCUS,
205            OP_AUDIO_MASTER_VOLUME,
206            OP_AUDIO_VOICE_VOLUME,
207            OP_AUDIO_RING_VOLUME,
208            OP_AUDIO_MEDIA_VOLUME,
209            OP_AUDIO_ALARM_VOLUME,
210            OP_AUDIO_NOTIFICATION_VOLUME,
211            OP_AUDIO_BLUETOOTH_VOLUME,
212            OP_WAKE_LOCK,
213            OP_COARSE_LOCATION,
214            OP_COARSE_LOCATION,
215    };
216
217    /**
218     * This provides a simple name for each operation to be used
219     * in debug output.
220     */
221    private static String[] sOpNames = new String[] {
222            "COARSE_LOCATION",
223            "FINE_LOCATION",
224            "GPS",
225            "VIBRATE",
226            "READ_CONTACTS",
227            "WRITE_CONTACTS",
228            "READ_CALL_LOG",
229            "WRITE_CALL_LOG",
230            "READ_CALENDAR",
231            "WRITE_CALENDAR",
232            "WIFI_SCAN",
233            "POST_NOTIFICATION",
234            "NEIGHBORING_CELLS",
235            "CALL_PHONE",
236            "READ_SMS",
237            "WRITE_SMS",
238            "RECEIVE_SMS",
239            "RECEIVE_EMERGECY_SMS",
240            "RECEIVE_MMS",
241            "RECEIVE_WAP_PUSH",
242            "SEND_SMS",
243            "READ_ICC_SMS",
244            "WRITE_ICC_SMS",
245            "WRITE_SETTINGS",
246            "SYSTEM_ALERT_WINDOW",
247            "ACCESS_NOTIFICATIONS",
248            "CAMERA",
249            "RECORD_AUDIO",
250            "PLAY_AUDIO",
251            "READ_CLIPBOARD",
252            "WRITE_CLIPBOARD",
253            "TAKE_MEDIA_BUTTONS",
254            "TAKE_AUDIO_FOCUS",
255            "AUDIO_MASTER_VOLUME",
256            "AUDIO_VOICE_VOLUME",
257            "AUDIO_RING_VOLUME",
258            "AUDIO_MEDIA_VOLUME",
259            "AUDIO_ALARM_VOLUME",
260            "AUDIO_NOTIFICATION_VOLUME",
261            "AUDIO_BLUETOOTH_VOLUME",
262            "WAKE_LOCK",
263            "MONITOR_LOCATION",
264            "MONITOR_HIGH_POWER_LOCATION",
265    };
266
267    /**
268     * This optionally maps a permission to an operation.  If there
269     * is no permission associated with an operation, it is null.
270     */
271    private static String[] sOpPerms = new String[] {
272            android.Manifest.permission.ACCESS_COARSE_LOCATION,
273            android.Manifest.permission.ACCESS_FINE_LOCATION,
274            null,
275            android.Manifest.permission.VIBRATE,
276            android.Manifest.permission.READ_CONTACTS,
277            android.Manifest.permission.WRITE_CONTACTS,
278            android.Manifest.permission.READ_CALL_LOG,
279            android.Manifest.permission.WRITE_CALL_LOG,
280            android.Manifest.permission.READ_CALENDAR,
281            android.Manifest.permission.WRITE_CALENDAR,
282            null, // no permission required for notifications
283            android.Manifest.permission.ACCESS_WIFI_STATE,
284            null, // neighboring cells shares the coarse location perm
285            android.Manifest.permission.CALL_PHONE,
286            android.Manifest.permission.READ_SMS,
287            android.Manifest.permission.WRITE_SMS,
288            android.Manifest.permission.RECEIVE_SMS,
289            android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
290            android.Manifest.permission.RECEIVE_MMS,
291            android.Manifest.permission.RECEIVE_WAP_PUSH,
292            android.Manifest.permission.SEND_SMS,
293            android.Manifest.permission.READ_SMS,
294            android.Manifest.permission.WRITE_SMS,
295            android.Manifest.permission.WRITE_SETTINGS,
296            android.Manifest.permission.SYSTEM_ALERT_WINDOW,
297            android.Manifest.permission.ACCESS_NOTIFICATIONS,
298            android.Manifest.permission.CAMERA,
299            android.Manifest.permission.RECORD_AUDIO,
300            null, // no permission for playing audio
301            null, // no permission for reading clipboard
302            null, // no permission for writing clipboard
303            null, // no permission for taking media buttons
304            null, // no permission for taking audio focus
305            null, // no permission for changing master volume
306            null, // no permission for changing voice volume
307            null, // no permission for changing ring volume
308            null, // no permission for changing media volume
309            null, // no permission for changing alarm volume
310            null, // no permission for changing notification volume
311            null, // no permission for changing bluetooth volume
312            android.Manifest.permission.WAKE_LOCK,
313            null, // no permission for generic location monitoring
314            null, // no permission for high power location monitoring
315    };
316
317    /**
318     * Retrieve the op switch that controls the given operation.
319     * @hide
320     */
321    public static int opToSwitch(int op) {
322        return sOpToSwitch[op];
323    }
324
325    /**
326     * Retrieve a non-localized name for the operation, for debugging output.
327     */
328    public static String opToName(int op) {
329        if (op == OP_NONE) return "NONE";
330        return op < sOpNames.length ? sOpNames[op] : ("Unknown(" + op + ")");
331    }
332
333    /**
334     * Retrieve the permission associated with an operation, or null if there is not one.
335     * @hide
336     */
337    public static String opToPermission(int op) {
338        return sOpPerms[op];
339    }
340
341    /**
342     * Class holding all of the operation information associated with an app.
343     * @hide
344     */
345    public static class PackageOps implements Parcelable {
346        private final String mPackageName;
347        private final int mUid;
348        private final List<OpEntry> mEntries;
349
350        public PackageOps(String packageName, int uid, List<OpEntry> entries) {
351            mPackageName = packageName;
352            mUid = uid;
353            mEntries = entries;
354        }
355
356        public String getPackageName() {
357            return mPackageName;
358        }
359
360        public int getUid() {
361            return mUid;
362        }
363
364        public List<OpEntry> getOps() {
365            return mEntries;
366        }
367
368        @Override
369        public int describeContents() {
370            return 0;
371        }
372
373        @Override
374        public void writeToParcel(Parcel dest, int flags) {
375            dest.writeString(mPackageName);
376            dest.writeInt(mUid);
377            dest.writeInt(mEntries.size());
378            for (int i=0; i<mEntries.size(); i++) {
379                mEntries.get(i).writeToParcel(dest, flags);
380            }
381        }
382
383        PackageOps(Parcel source) {
384            mPackageName = source.readString();
385            mUid = source.readInt();
386            mEntries = new ArrayList<OpEntry>();
387            final int N = source.readInt();
388            for (int i=0; i<N; i++) {
389                mEntries.add(OpEntry.CREATOR.createFromParcel(source));
390            }
391        }
392
393        public static final Creator<PackageOps> CREATOR = new Creator<PackageOps>() {
394            @Override public PackageOps createFromParcel(Parcel source) {
395                return new PackageOps(source);
396            }
397
398            @Override public PackageOps[] newArray(int size) {
399                return new PackageOps[size];
400            }
401        };
402    }
403
404    /**
405     * Class holding the information about one unique operation of an application.
406     * @hide
407     */
408    public static class OpEntry implements Parcelable {
409        private final int mOp;
410        private final int mMode;
411        private final long mTime;
412        private final long mRejectTime;
413        private final int mDuration;
414
415        public OpEntry(int op, int mode, long time, long rejectTime, int duration) {
416            mOp = op;
417            mMode = mode;
418            mTime = time;
419            mRejectTime = rejectTime;
420            mDuration = duration;
421        }
422
423        public int getOp() {
424            return mOp;
425        }
426
427        public int getMode() {
428            return mMode;
429        }
430
431        public long getTime() {
432            return mTime;
433        }
434
435        public long getRejectTime() {
436            return mRejectTime;
437        }
438
439        public boolean isRunning() {
440            return mDuration == -1;
441        }
442
443        public int getDuration() {
444            return mDuration == -1 ? (int)(System.currentTimeMillis()-mTime) : mDuration;
445        }
446
447        @Override
448        public int describeContents() {
449            return 0;
450        }
451
452        @Override
453        public void writeToParcel(Parcel dest, int flags) {
454            dest.writeInt(mOp);
455            dest.writeInt(mMode);
456            dest.writeLong(mTime);
457            dest.writeLong(mRejectTime);
458            dest.writeInt(mDuration);
459        }
460
461        OpEntry(Parcel source) {
462            mOp = source.readInt();
463            mMode = source.readInt();
464            mTime = source.readLong();
465            mRejectTime = source.readLong();
466            mDuration = source.readInt();
467        }
468
469        public static final Creator<OpEntry> CREATOR = new Creator<OpEntry>() {
470            @Override public OpEntry createFromParcel(Parcel source) {
471                return new OpEntry(source);
472            }
473
474            @Override public OpEntry[] newArray(int size) {
475                return new OpEntry[size];
476            }
477        };
478    }
479
480    /**
481     * Callback for notification of changes to operation state.
482     */
483    public interface Callback {
484        public void opChanged(int op, String packageName);
485    }
486
487    AppOpsManager(Context context, IAppOpsService service) {
488        mContext = context;
489        mService = service;
490    }
491
492    /**
493     * Retrieve current operation state for all applications.
494     *
495     * @param ops The set of operations you are interested in, or null if you want all of them.
496     * @hide
497     */
498    public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
499        try {
500            return mService.getPackagesForOps(ops);
501        } catch (RemoteException e) {
502        }
503        return null;
504    }
505
506    /**
507     * Retrieve current operation state for one application.
508     *
509     * @param uid The uid of the application of interest.
510     * @param packageName The name of the application of interest.
511     * @param ops The set of operations you are interested in, or null if you want all of them.
512     * @hide
513     */
514    public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
515        try {
516            return mService.getOpsForPackage(uid, packageName, ops);
517        } catch (RemoteException e) {
518        }
519        return null;
520    }
521
522    /** @hide */
523    public void setMode(int code, int uid, String packageName, int mode) {
524        try {
525            mService.setMode(code, uid, packageName, mode);
526        } catch (RemoteException e) {
527        }
528    }
529
530    /**
531     * Monitor for changes to the operating mode for the given op in the given app package.
532     * @param op The operation to monitor, one of OP_*.
533     * @param packageName The name of the application to monitor.
534     * @param callback Where to report changes.
535     */
536    public void startWatchingMode(int op, String packageName, final Callback callback) {
537        synchronized (mModeWatchers) {
538            IAppOpsCallback cb = mModeWatchers.get(callback);
539            if (cb == null) {
540                cb = new IAppOpsCallback.Stub() {
541                    public void opChanged(int op, String packageName) {
542                        callback.opChanged(op, packageName);
543                    }
544                };
545                mModeWatchers.put(callback, cb);
546            }
547            try {
548                mService.startWatchingMode(op, packageName, cb);
549            } catch (RemoteException e) {
550            }
551        }
552    }
553
554    /**
555     * Stop monitoring that was previously started with {@link #startWatchingMode}.  All
556     * monitoring associated with this callback will be removed.
557     */
558    public void stopWatchingMode(Callback callback) {
559        synchronized (mModeWatchers) {
560            IAppOpsCallback cb = mModeWatchers.get(callback);
561            if (cb != null) {
562                try {
563                    mService.stopWatchingMode(cb);
564                } catch (RemoteException e) {
565                }
566            }
567        }
568    }
569
570    /**
571     * Do a quick check for whether an application might be able to perform an operation.
572     * This is <em>not</em> a security check; you must use {@link #noteOp(int, int, String)}
573     * or {@link #startOp(int, int, String)} for your actual security checks, which also
574     * ensure that the given uid and package name are consistent.  This function can just be
575     * used for a quick check to see if an operation has been disabled for the application,
576     * as an early reject of some work.  This does not modify the time stamp or other data
577     * about the operation.
578     * @param op The operation to check.  One of the OP_* constants.
579     * @param uid The user id of the application attempting to perform the operation.
580     * @param packageName The name of the application attempting to perform the operation.
581     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
582     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
583     * causing the app to crash).
584     * @throws SecurityException If the app has been configured to crash on this op.
585     */
586    public int checkOp(int op, int uid, String packageName) {
587        try {
588            int mode = mService.checkOperation(op, uid, packageName);
589            if (mode == MODE_ERRORED) {
590                throw new SecurityException("Operation not allowed");
591            }
592            return mode;
593        } catch (RemoteException e) {
594        }
595        return MODE_IGNORED;
596    }
597
598    /**
599     * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
600     * returns {@link #MODE_ERRORED}.
601     */
602    public int checkOpNoThrow(int op, int uid, String packageName) {
603        try {
604            return mService.checkOperation(op, uid, packageName);
605        } catch (RemoteException e) {
606        }
607        return MODE_IGNORED;
608    }
609
610    /**
611     * Make note of an application performing an operation.  Note that you must pass
612     * in both the uid and name of the application to be checked; this function will verify
613     * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
614     * succeeds, the last execution time of the operation for this app will be updated to
615     * the current time.
616     * @param op The operation to note.  One of the OP_* constants.
617     * @param uid The user id of the application attempting to perform the operation.
618     * @param packageName The name of the application attempting to perform the operation.
619     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
620     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
621     * causing the app to crash).
622     * @throws SecurityException If the app has been configured to crash on this op.
623     */
624    public int noteOp(int op, int uid, String packageName) {
625        try {
626            int mode = mService.noteOperation(op, uid, packageName);
627            if (mode == MODE_ERRORED) {
628                throw new SecurityException("Operation not allowed");
629            }
630            return mode;
631        } catch (RemoteException e) {
632        }
633        return MODE_IGNORED;
634    }
635
636    /**
637     * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it
638     * returns {@link #MODE_ERRORED}.
639     */
640    public int noteOpNoThrow(int op, int uid, String packageName) {
641        try {
642            return mService.noteOperation(op, uid, packageName);
643        } catch (RemoteException e) {
644        }
645        return MODE_IGNORED;
646    }
647
648    /** @hide */
649    public int noteOp(int op) {
650        return noteOp(op, Process.myUid(), mContext.getBasePackageName());
651    }
652
653    /** @hide */
654    public static IBinder getToken(IAppOpsService service) {
655        synchronized (AppOpsManager.class) {
656            if (sToken != null) {
657                return sToken;
658            }
659            try {
660                sToken = service.getToken(new Binder());
661            } catch (RemoteException e) {
662                // System is dead, whatevs.
663            }
664            return sToken;
665        }
666    }
667
668    /**
669     * Report that an application has started executing a long-running operation.  Note that you
670     * must pass in both the uid and name of the application to be checked; this function will
671     * verify that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
672     * succeeds, the last execution time of the operation for this app will be updated to
673     * the current time and the operation will be marked as "running".  In this case you must
674     * later call {@link #finishOp(int, int, String)} to report when the application is no
675     * longer performing the operation.
676     * @param op The operation to start.  One of the OP_* constants.
677     * @param uid The user id of the application attempting to perform the operation.
678     * @param packageName The name of the application attempting to perform the operation.
679     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
680     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
681     * causing the app to crash).
682     * @throws SecurityException If the app has been configured to crash on this op.
683     */
684    public int startOp(int op, int uid, String packageName) {
685        try {
686            int mode = mService.startOperation(getToken(mService), op, uid, packageName);
687            if (mode == MODE_ERRORED) {
688                throw new SecurityException("Operation not allowed");
689            }
690            return mode;
691        } catch (RemoteException e) {
692        }
693        return MODE_IGNORED;
694    }
695
696    /**
697     * Like {@link #startOp} but instead of throwing a {@link SecurityException} it
698     * returns {@link #MODE_ERRORED}.
699     */
700    public int startOpNoThrow(int op, int uid, String packageName) {
701        try {
702            return mService.startOperation(getToken(mService), op, uid, packageName);
703        } catch (RemoteException e) {
704        }
705        return MODE_IGNORED;
706    }
707
708    /** @hide */
709    public int startOp(int op) {
710        return startOp(op, Process.myUid(), mContext.getBasePackageName());
711    }
712
713    /**
714     * Report that an application is no longer performing an operation that had previously
715     * been started with {@link #startOp(int, int, String)}.  There is no validation of input
716     * or result; the parameters supplied here must be the exact same ones previously passed
717     * in when starting the operation.
718     */
719    public void finishOp(int op, int uid, String packageName) {
720        try {
721            mService.finishOperation(getToken(mService), op, uid, packageName);
722        } catch (RemoteException e) {
723        }
724    }
725
726    public void finishOp(int op) {
727        finishOp(op, Process.myUid(), mContext.getBasePackageName());
728    }
729}
730