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.Manifest;
20import com.android.internal.app.IAppOpsService;
21import com.android.internal.app.IAppOpsCallback;
22
23import java.util.ArrayList;
24import java.util.HashMap;
25import java.util.List;
26
27import android.content.Context;
28import android.os.Parcel;
29import android.os.Parcelable;
30import android.os.Process;
31import android.os.RemoteException;
32
33/**
34 * API for interacting with "application operation" tracking.  Allows you to:
35 *
36 * - Note when operations are happening, and find out if they are allowed for the current caller.
37 * - Disallow specific apps from doing specific operations.
38 * - Collect all of the current information about operations that have been executed or are not
39 * being allowed.
40 * - Monitor for changes in whether an operation is allowed.
41 *
42 * Each operation is identified by a single integer; these integers are a fixed set of
43 * operations, enumerated by the OP_* constants.
44 *
45 * When checking operations, the result is a "mode" integer indicating the current setting
46 * for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute the operation but
47 * fake its behavior enough so that the caller doesn't crash), MODE_ERRORED (through a
48 * SecurityException back to the caller; the normal operation calls will do this for you).
49 *
50 * @hide
51 */
52public class AppOpsManager {
53    final Context mContext;
54    final IAppOpsService mService;
55    final HashMap<Callback, IAppOpsCallback> mModeWatchers
56            = new HashMap<Callback, IAppOpsCallback>();
57
58    public static final int MODE_ALLOWED = 0;
59    public static final int MODE_IGNORED = 1;
60    public static final int MODE_ERRORED = 2;
61
62    // when adding one of these:
63    //  - increment _NUM_OP
64    //  - add rows to sOpToSwitch, sOpNames, sOpPerms
65    //  - add descriptive strings to Settings/res/values/arrays.xml
66    public static final int OP_NONE = -1;
67    public static final int OP_COARSE_LOCATION = 0;
68    public static final int OP_FINE_LOCATION = 1;
69    public static final int OP_GPS = 2;
70    public static final int OP_VIBRATE = 3;
71    public static final int OP_READ_CONTACTS = 4;
72    public static final int OP_WRITE_CONTACTS = 5;
73    public static final int OP_READ_CALL_LOG = 6;
74    public static final int OP_WRITE_CALL_LOG = 7;
75    public static final int OP_READ_CALENDAR = 8;
76    public static final int OP_WRITE_CALENDAR = 9;
77    public static final int OP_WIFI_SCAN = 10;
78    public static final int OP_POST_NOTIFICATION = 11;
79    public static final int OP_NEIGHBORING_CELLS = 12;
80    public static final int OP_CALL_PHONE = 13;
81    public static final int OP_READ_SMS = 14;
82    public static final int OP_WRITE_SMS = 15;
83    public static final int OP_RECEIVE_SMS = 16;
84    public static final int OP_RECEIVE_EMERGECY_SMS = 17;
85    public static final int OP_RECEIVE_MMS = 18;
86    public static final int OP_RECEIVE_WAP_PUSH = 19;
87    public static final int OP_SEND_SMS = 20;
88    public static final int OP_READ_ICC_SMS = 21;
89    public static final int OP_WRITE_ICC_SMS = 22;
90    public static final int OP_WRITE_SETTINGS = 23;
91    public static final int OP_SYSTEM_ALERT_WINDOW = 24;
92    public static final int OP_ACCESS_NOTIFICATIONS = 25;
93    public static final int OP_CAMERA = 26;
94    public static final int OP_RECORD_AUDIO = 27;
95    public static final int OP_PLAY_AUDIO = 28;
96    public static final int OP_READ_CLIPBOARD = 29;
97    public static final int OP_WRITE_CLIPBOARD = 30;
98    /** @hide */
99    public static final int _NUM_OP = 31;
100
101    /**
102     * This maps each operation to the operation that serves as the
103     * switch to determine whether it is allowed.  Generally this is
104     * a 1:1 mapping, but for some things (like location) that have
105     * multiple low-level operations being tracked that should be
106     * presented to hte user as one switch then this can be used to
107     * make them all controlled by the same single operation.
108     */
109    private static int[] sOpToSwitch = new int[] {
110            OP_COARSE_LOCATION,
111            OP_COARSE_LOCATION,
112            OP_COARSE_LOCATION,
113            OP_VIBRATE,
114            OP_READ_CONTACTS,
115            OP_WRITE_CONTACTS,
116            OP_READ_CALL_LOG,
117            OP_WRITE_CALL_LOG,
118            OP_READ_CALENDAR,
119            OP_WRITE_CALENDAR,
120            OP_COARSE_LOCATION,
121            OP_POST_NOTIFICATION,
122            OP_COARSE_LOCATION,
123            OP_CALL_PHONE,
124            OP_READ_SMS,
125            OP_WRITE_SMS,
126            OP_READ_SMS,
127            OP_READ_SMS,
128            OP_READ_SMS,
129            OP_READ_SMS,
130            OP_WRITE_SMS,
131            OP_READ_SMS,
132            OP_WRITE_SMS,
133            OP_WRITE_SETTINGS,
134            OP_SYSTEM_ALERT_WINDOW,
135            OP_ACCESS_NOTIFICATIONS,
136            OP_CAMERA,
137            OP_RECORD_AUDIO,
138            OP_PLAY_AUDIO,
139            OP_READ_CLIPBOARD,
140            OP_WRITE_CLIPBOARD,
141    };
142
143    /**
144     * This provides a simple name for each operation to be used
145     * in debug output.
146     */
147    private static String[] sOpNames = new String[] {
148            "COARSE_LOCATION",
149            "FINE_LOCATION",
150            "GPS",
151            "VIBRATE",
152            "READ_CONTACTS",
153            "WRITE_CONTACTS",
154            "READ_CALL_LOG",
155            "WRITE_CALL_LOG",
156            "READ_CALENDAR",
157            "WRITE_CALENDAR",
158            "WIFI_SCAN",
159            "POST_NOTIFICATION",
160            "NEIGHBORING_CELLS",
161            "CALL_PHONE",
162            "READ_SMS",
163            "WRITE_SMS",
164            "RECEIVE_SMS",
165            "RECEIVE_EMERGECY_SMS",
166            "RECEIVE_MMS",
167            "RECEIVE_WAP_PUSH",
168            "SEND_SMS",
169            "READ_ICC_SMS",
170            "WRITE_ICC_SMS",
171            "WRITE_SETTINGS",
172            "SYSTEM_ALERT_WINDOW",
173            "ACCESS_NOTIFICATIONS",
174            "CAMERA",
175            "RECORD_AUDIO",
176            "PLAY_AUDIO",
177            "READ_CLIPBOARD",
178            "WRITE_CLIPBOARD",
179    };
180
181    /**
182     * This optionally maps a permission to an operation.  If there
183     * is no permission associated with an operation, it is null.
184     */
185    private static String[] sOpPerms = new String[] {
186            android.Manifest.permission.ACCESS_COARSE_LOCATION,
187            android.Manifest.permission.ACCESS_FINE_LOCATION,
188            null,
189            android.Manifest.permission.VIBRATE,
190            android.Manifest.permission.READ_CONTACTS,
191            android.Manifest.permission.WRITE_CONTACTS,
192            android.Manifest.permission.READ_CALL_LOG,
193            android.Manifest.permission.WRITE_CALL_LOG,
194            android.Manifest.permission.READ_CALENDAR,
195            android.Manifest.permission.WRITE_CALENDAR,
196            null, // no permission required for notifications
197            android.Manifest.permission.ACCESS_WIFI_STATE,
198            null, // neighboring cells shares the coarse location perm
199            android.Manifest.permission.CALL_PHONE,
200            android.Manifest.permission.READ_SMS,
201            android.Manifest.permission.WRITE_SMS,
202            android.Manifest.permission.RECEIVE_SMS,
203            android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
204            android.Manifest.permission.RECEIVE_MMS,
205            android.Manifest.permission.RECEIVE_WAP_PUSH,
206            android.Manifest.permission.SEND_SMS,
207            android.Manifest.permission.READ_SMS,
208            android.Manifest.permission.WRITE_SMS,
209            android.Manifest.permission.WRITE_SETTINGS,
210            android.Manifest.permission.SYSTEM_ALERT_WINDOW,
211            android.Manifest.permission.ACCESS_NOTIFICATIONS,
212            android.Manifest.permission.CAMERA,
213            android.Manifest.permission.RECORD_AUDIO,
214            null, // no permission for playing audio
215            null, // no permission for reading clipboard
216            null, // no permission for writing clipboard
217    };
218
219    /**
220     * Retrieve the op switch that controls the given operation.
221     */
222    public static int opToSwitch(int op) {
223        return sOpToSwitch[op];
224    }
225
226    /**
227     * Retrieve a non-localized name for the operation, for debugging output.
228     */
229    public static String opToName(int op) {
230        if (op == OP_NONE) return "NONE";
231        return op < sOpNames.length ? sOpNames[op] : ("Unknown(" + op + ")");
232    }
233
234    /**
235     * Retrieve the permission associated with an operation, or null if there is not one.
236     */
237    public static String opToPermission(int op) {
238        return sOpPerms[op];
239    }
240
241    /**
242     * Class holding all of the operation information associated with an app.
243     */
244    public static class PackageOps implements Parcelable {
245        private final String mPackageName;
246        private final int mUid;
247        private final List<OpEntry> mEntries;
248
249        public PackageOps(String packageName, int uid, List<OpEntry> entries) {
250            mPackageName = packageName;
251            mUid = uid;
252            mEntries = entries;
253        }
254
255        public String getPackageName() {
256            return mPackageName;
257        }
258
259        public int getUid() {
260            return mUid;
261        }
262
263        public List<OpEntry> getOps() {
264            return mEntries;
265        }
266
267        @Override
268        public int describeContents() {
269            return 0;
270        }
271
272        @Override
273        public void writeToParcel(Parcel dest, int flags) {
274            dest.writeString(mPackageName);
275            dest.writeInt(mUid);
276            dest.writeInt(mEntries.size());
277            for (int i=0; i<mEntries.size(); i++) {
278                mEntries.get(i).writeToParcel(dest, flags);
279            }
280        }
281
282        PackageOps(Parcel source) {
283            mPackageName = source.readString();
284            mUid = source.readInt();
285            mEntries = new ArrayList<OpEntry>();
286            final int N = source.readInt();
287            for (int i=0; i<N; i++) {
288                mEntries.add(OpEntry.CREATOR.createFromParcel(source));
289            }
290        }
291
292        public static final Creator<PackageOps> CREATOR = new Creator<PackageOps>() {
293            @Override public PackageOps createFromParcel(Parcel source) {
294                return new PackageOps(source);
295            }
296
297            @Override public PackageOps[] newArray(int size) {
298                return new PackageOps[size];
299            }
300        };
301    }
302
303    /**
304     * Class holding the information about one unique operation of an application.
305     */
306    public static class OpEntry implements Parcelable {
307        private final int mOp;
308        private final int mMode;
309        private final long mTime;
310        private final long mRejectTime;
311        private final int mDuration;
312
313        public OpEntry(int op, int mode, long time, long rejectTime, int duration) {
314            mOp = op;
315            mMode = mode;
316            mTime = time;
317            mRejectTime = rejectTime;
318            mDuration = duration;
319        }
320
321        public int getOp() {
322            return mOp;
323        }
324
325        public int getMode() {
326            return mMode;
327        }
328
329        public long getTime() {
330            return mTime;
331        }
332
333        public long getRejectTime() {
334            return mRejectTime;
335        }
336
337        public boolean isRunning() {
338            return mDuration == -1;
339        }
340
341        public int getDuration() {
342            return mDuration == -1 ? (int)(System.currentTimeMillis()-mTime) : mDuration;
343        }
344
345        @Override
346        public int describeContents() {
347            return 0;
348        }
349
350        @Override
351        public void writeToParcel(Parcel dest, int flags) {
352            dest.writeInt(mOp);
353            dest.writeInt(mMode);
354            dest.writeLong(mTime);
355            dest.writeLong(mRejectTime);
356            dest.writeInt(mDuration);
357        }
358
359        OpEntry(Parcel source) {
360            mOp = source.readInt();
361            mMode = source.readInt();
362            mTime = source.readLong();
363            mRejectTime = source.readLong();
364            mDuration = source.readInt();
365        }
366
367        public static final Creator<OpEntry> CREATOR = new Creator<OpEntry>() {
368            @Override public OpEntry createFromParcel(Parcel source) {
369                return new OpEntry(source);
370            }
371
372            @Override public OpEntry[] newArray(int size) {
373                return new OpEntry[size];
374            }
375        };
376    }
377
378    /**
379     * Callback for notification of changes to operation state.
380     */
381    public interface Callback {
382        public void opChanged(int op, String packageName);
383    }
384
385    public AppOpsManager(Context context, IAppOpsService service) {
386        mContext = context;
387        mService = service;
388    }
389
390    /**
391     * Retrieve current operation state for all applications.
392     *
393     * @param ops The set of operations you are interested in, or null if you want all of them.
394     */
395    public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
396        try {
397            return mService.getPackagesForOps(ops);
398        } catch (RemoteException e) {
399        }
400        return null;
401    }
402
403    /**
404     * Retrieve current operation state for one application.
405     *
406     * @param uid The uid of the application of interest.
407     * @param packageName The name of the application of interest.
408     * @param ops The set of operations you are interested in, or null if you want all of them.
409     */
410    public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
411        try {
412            return mService.getOpsForPackage(uid, packageName, ops);
413        } catch (RemoteException e) {
414        }
415        return null;
416    }
417
418    public void setMode(int code, int uid, String packageName, int mode) {
419        try {
420            mService.setMode(code, uid, packageName, mode);
421        } catch (RemoteException e) {
422        }
423    }
424
425    public void startWatchingMode(int op, String packageName, final Callback callback) {
426        synchronized (mModeWatchers) {
427            IAppOpsCallback cb = mModeWatchers.get(callback);
428            if (cb == null) {
429                cb = new IAppOpsCallback.Stub() {
430                    public void opChanged(int op, String packageName) {
431                        callback.opChanged(op, packageName);
432                    }
433                };
434                mModeWatchers.put(callback, cb);
435            }
436            try {
437                mService.startWatchingMode(op, packageName, cb);
438            } catch (RemoteException e) {
439            }
440        }
441    }
442
443    public void stopWatchingMode(Callback callback) {
444        synchronized (mModeWatchers) {
445            IAppOpsCallback cb = mModeWatchers.get(callback);
446            if (cb != null) {
447                try {
448                    mService.stopWatchingMode(cb);
449                } catch (RemoteException e) {
450                }
451            }
452        }
453    }
454
455    public int checkOp(int op, int uid, String packageName) {
456        try {
457            int mode = mService.checkOperation(op, uid, packageName);
458            if (mode == MODE_ERRORED) {
459                throw new SecurityException("Operation not allowed");
460            }
461            return mode;
462        } catch (RemoteException e) {
463        }
464        return MODE_IGNORED;
465    }
466
467    public int checkOpNoThrow(int op, int uid, String packageName) {
468        try {
469            return mService.checkOperation(op, uid, packageName);
470        } catch (RemoteException e) {
471        }
472        return MODE_IGNORED;
473    }
474
475    public int noteOp(int op, int uid, String packageName) {
476        try {
477            int mode = mService.noteOperation(op, uid, packageName);
478            if (mode == MODE_ERRORED) {
479                throw new SecurityException("Operation not allowed");
480            }
481            return mode;
482        } catch (RemoteException e) {
483        }
484        return MODE_IGNORED;
485    }
486
487    public int noteOpNoThrow(int op, int uid, String packageName) {
488        try {
489            return mService.noteOperation(op, uid, packageName);
490        } catch (RemoteException e) {
491        }
492        return MODE_IGNORED;
493    }
494
495    public int noteOp(int op) {
496        return noteOp(op, Process.myUid(), mContext.getBasePackageName());
497    }
498
499    public int startOp(int op, int uid, String packageName) {
500        try {
501            int mode = mService.startOperation(op, uid, packageName);
502            if (mode == MODE_ERRORED) {
503                throw new SecurityException("Operation not allowed");
504            }
505            return mode;
506        } catch (RemoteException e) {
507        }
508        return MODE_IGNORED;
509    }
510
511    public int startOpNoThrow(int op, int uid, String packageName) {
512        try {
513            return mService.startOperation(op, uid, packageName);
514        } catch (RemoteException e) {
515        }
516        return MODE_IGNORED;
517    }
518
519    public int startOp(int op) {
520        return startOp(op, Process.myUid(), mContext.getBasePackageName());
521    }
522
523    public void finishOp(int op, int uid, String packageName) {
524        try {
525            mService.finishOperation(op, uid, packageName);
526        } catch (RemoteException e) {
527        }
528    }
529
530    public void finishOp(int op) {
531        finishOp(op, Process.myUid(), mContext.getBasePackageName());
532    }
533}
534