blob: 20ad63683acd8b3983210b4f23517ec3feaf573e [file] [log] [blame]
Dianne Hackborna06de0f2012-12-11 16:34:47 -08001/*
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 com.android.server;
18
19import java.io.File;
20import java.io.FileDescriptor;
Dianne Hackborn35654b62013-01-14 17:38:02 -080021import java.io.FileInputStream;
22import java.io.FileNotFoundException;
23import java.io.FileOutputStream;
24import java.io.IOException;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080025import java.io.PrintWriter;
Dianne Hackborn35654b62013-01-14 17:38:02 -080026import java.util.ArrayList;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080027import java.util.HashMap;
Dianne Hackbornc2293022013-02-06 23:14:49 -080028import java.util.Iterator;
Dianne Hackborn35654b62013-01-14 17:38:02 -080029import java.util.List;
Dianne Hackborn607b4142013-08-02 18:10:10 -070030import java.util.Map;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080031
32import android.app.AppOpsManager;
33import android.content.Context;
34import android.content.pm.PackageManager;
35import android.content.pm.PackageManager.NameNotFoundException;
Dianne Hackborn35654b62013-01-14 17:38:02 -080036import android.os.AsyncTask;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080037import android.os.Binder;
Dianne Hackborn35654b62013-01-14 17:38:02 -080038import android.os.Handler;
Dianne Hackbornc2293022013-02-06 23:14:49 -080039import android.os.IBinder;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080040import android.os.Process;
Dianne Hackbornc2293022013-02-06 23:14:49 -080041import android.os.RemoteException;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080042import android.os.ServiceManager;
43import android.os.UserHandle;
44import android.util.AtomicFile;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080045import android.util.Log;
Dianne Hackborn607b4142013-08-02 18:10:10 -070046import android.util.Pair;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080047import android.util.Slog;
48import android.util.SparseArray;
49import android.util.TimeUtils;
Dianne Hackborn35654b62013-01-14 17:38:02 -080050import android.util.Xml;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080051
52import com.android.internal.app.IAppOpsService;
Dianne Hackbornc2293022013-02-06 23:14:49 -080053import com.android.internal.app.IAppOpsCallback;
Dianne Hackborn35654b62013-01-14 17:38:02 -080054import com.android.internal.util.FastXmlSerializer;
55import com.android.internal.util.XmlUtils;
56
57import org.xmlpull.v1.XmlPullParser;
58import org.xmlpull.v1.XmlPullParserException;
59import org.xmlpull.v1.XmlSerializer;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080060
61public class AppOpsService extends IAppOpsService.Stub {
62 static final String TAG = "AppOps";
Dianne Hackborn35654b62013-01-14 17:38:02 -080063 static final boolean DEBUG = false;
64
65 // Write at most every 30 minutes.
66 static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080067
68 Context mContext;
69 final AtomicFile mFile;
Dianne Hackborn35654b62013-01-14 17:38:02 -080070 final Handler mHandler;
71
72 boolean mWriteScheduled;
73 final Runnable mWriteRunner = new Runnable() {
74 public void run() {
75 synchronized (AppOpsService.this) {
76 mWriteScheduled = false;
77 AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
78 @Override protected Void doInBackground(Void... params) {
79 writeState();
80 return null;
81 }
82 };
83 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
84 }
85 }
86 };
Dianne Hackborna06de0f2012-12-11 16:34:47 -080087
88 final SparseArray<HashMap<String, Ops>> mUidOps
89 = new SparseArray<HashMap<String, Ops>>();
90
Dianne Hackbornc2293022013-02-06 23:14:49 -080091 public final static class Ops extends SparseArray<Op> {
Dianne Hackborna06de0f2012-12-11 16:34:47 -080092 public final String packageName;
Dianne Hackborn35654b62013-01-14 17:38:02 -080093 public final int uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080094
Dianne Hackborn35654b62013-01-14 17:38:02 -080095 public Ops(String _packageName, int _uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -080096 packageName = _packageName;
Dianne Hackborn35654b62013-01-14 17:38:02 -080097 uid = _uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080098 }
99 }
100
Dianne Hackbornc2293022013-02-06 23:14:49 -0800101 public final static class Op {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800102 public final int op;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800103 public int mode;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800104 public int duration;
105 public long time;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800106 public long rejectTime;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800107 public int nesting;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800108
109 public Op(int _op) {
110 op = _op;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800111 mode = AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800112 }
113 }
114
Dianne Hackbornc2293022013-02-06 23:14:49 -0800115 final SparseArray<ArrayList<Callback>> mOpModeWatchers
116 = new SparseArray<ArrayList<Callback>>();
117 final HashMap<String, ArrayList<Callback>> mPackageModeWatchers
118 = new HashMap<String, ArrayList<Callback>>();
119 final HashMap<IBinder, Callback> mModeWatchers
120 = new HashMap<IBinder, Callback>();
121
122 public final class Callback implements DeathRecipient {
123 final IAppOpsCallback mCallback;
124
125 public Callback(IAppOpsCallback callback) {
126 mCallback = callback;
127 try {
128 mCallback.asBinder().linkToDeath(this, 0);
129 } catch (RemoteException e) {
130 }
131 }
132
133 public void unlinkToDeath() {
134 mCallback.asBinder().unlinkToDeath(this, 0);
135 }
136
137 @Override
138 public void binderDied() {
139 stopWatchingMode(mCallback);
140 }
141 }
142
Dianne Hackborn35654b62013-01-14 17:38:02 -0800143 public AppOpsService(File storagePath) {
144 mFile = new AtomicFile(storagePath);
145 mHandler = new Handler();
146 readState();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800147 }
148
149 public void publish(Context context) {
150 mContext = context;
151 ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
152 }
153
Dianne Hackborn514074f2013-02-11 10:52:46 -0800154 public void systemReady() {
155 synchronized (this) {
156 boolean changed = false;
157 for (int i=0; i<mUidOps.size(); i++) {
158 HashMap<String, Ops> pkgs = mUidOps.valueAt(i);
159 Iterator<Ops> it = pkgs.values().iterator();
160 while (it.hasNext()) {
161 Ops ops = it.next();
162 int curUid;
163 try {
164 curUid = mContext.getPackageManager().getPackageUid(ops.packageName,
165 UserHandle.getUserId(ops.uid));
166 } catch (NameNotFoundException e) {
167 curUid = -1;
168 }
169 if (curUid != ops.uid) {
170 Slog.i(TAG, "Pruning old package " + ops.packageName
171 + "/" + ops.uid + ": new uid=" + curUid);
172 it.remove();
173 changed = true;
174 }
175 }
176 if (pkgs.size() <= 0) {
177 mUidOps.removeAt(i);
178 }
179 }
180 if (changed) {
181 scheduleWriteLocked();
182 }
183 }
184 }
185
186 public void packageRemoved(int uid, String packageName) {
187 synchronized (this) {
188 HashMap<String, Ops> pkgs = mUidOps.get(uid);
189 if (pkgs != null) {
190 if (pkgs.remove(packageName) != null) {
191 if (pkgs.size() <= 0) {
192 mUidOps.remove(uid);
193 }
194 scheduleWriteLocked();
195 }
196 }
197 }
198 }
199
200 public void uidRemoved(int uid) {
201 synchronized (this) {
202 if (mUidOps.indexOfKey(uid) >= 0) {
203 mUidOps.remove(uid);
204 scheduleWriteLocked();
205 }
206 }
207 }
208
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800209 public void shutdown() {
210 Slog.w(TAG, "Writing app ops before shutdown...");
Dianne Hackborn35654b62013-01-14 17:38:02 -0800211 boolean doWrite = false;
212 synchronized (this) {
213 if (mWriteScheduled) {
214 mWriteScheduled = false;
215 doWrite = true;
216 }
217 }
218 if (doWrite) {
219 writeState();
220 }
221 }
222
Dianne Hackborn72e39832013-01-18 18:36:09 -0800223 private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
224 ArrayList<AppOpsManager.OpEntry> resOps = null;
225 if (ops == null) {
226 resOps = new ArrayList<AppOpsManager.OpEntry>();
227 for (int j=0; j<pkgOps.size(); j++) {
228 Op curOp = pkgOps.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800229 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
230 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800231 }
232 } else {
233 for (int j=0; j<ops.length; j++) {
234 Op curOp = pkgOps.get(ops[j]);
235 if (curOp != null) {
236 if (resOps == null) {
237 resOps = new ArrayList<AppOpsManager.OpEntry>();
238 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800239 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
240 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800241 }
242 }
243 }
244 return resOps;
245 }
246
Dianne Hackborn35654b62013-01-14 17:38:02 -0800247 @Override
248 public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
249 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
250 Binder.getCallingPid(), Binder.getCallingUid(), null);
251 ArrayList<AppOpsManager.PackageOps> res = null;
252 synchronized (this) {
253 for (int i=0; i<mUidOps.size(); i++) {
254 HashMap<String, Ops> packages = mUidOps.valueAt(i);
255 for (Ops pkgOps : packages.values()) {
Dianne Hackborn72e39832013-01-18 18:36:09 -0800256 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800257 if (resOps != null) {
258 if (res == null) {
259 res = new ArrayList<AppOpsManager.PackageOps>();
260 }
261 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
262 pkgOps.packageName, pkgOps.uid, resOps);
263 res.add(resPackage);
264 }
265 }
266 }
267 }
268 return res;
269 }
270
271 @Override
Dianne Hackborn72e39832013-01-18 18:36:09 -0800272 public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
273 int[] ops) {
274 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
275 Binder.getCallingPid(), Binder.getCallingUid(), null);
276 synchronized (this) {
277 Ops pkgOps = getOpsLocked(uid, packageName, false);
278 if (pkgOps == null) {
279 return null;
280 }
281 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
282 if (resOps == null) {
283 return null;
284 }
285 ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
286 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
287 pkgOps.packageName, pkgOps.uid, resOps);
288 res.add(resPackage);
289 return res;
290 }
291 }
292
Dianne Hackborn607b4142013-08-02 18:10:10 -0700293 private void pruneOp(Op op, int uid, String packageName) {
294 if (op.time == 0 && op.rejectTime == 0) {
295 Ops ops = getOpsLocked(uid, packageName, false);
296 if (ops != null) {
297 ops.remove(op.op);
298 if (ops.size() <= 0) {
299 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
300 if (pkgOps != null) {
301 pkgOps.remove(ops.packageName);
302 if (pkgOps.size() <= 0) {
303 mUidOps.remove(uid);
304 }
305 }
306 }
307 }
308 }
309 }
310
Dianne Hackborn72e39832013-01-18 18:36:09 -0800311 @Override
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800312 public void setMode(int code, int uid, String packageName, int mode) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800313 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800314 verifyIncomingOp(code);
Dianne Hackbornc2293022013-02-06 23:14:49 -0800315 ArrayList<Callback> repCbs = null;
316 code = AppOpsManager.opToSwitch(code);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800317 synchronized (this) {
Dianne Hackbornc2293022013-02-06 23:14:49 -0800318 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800319 if (op != null) {
320 if (op.mode != mode) {
321 op.mode = mode;
Dianne Hackbornc2293022013-02-06 23:14:49 -0800322 ArrayList<Callback> cbs = mOpModeWatchers.get(code);
323 if (cbs != null) {
324 if (repCbs == null) {
325 repCbs = new ArrayList<Callback>();
326 }
327 repCbs.addAll(cbs);
328 }
329 cbs = mPackageModeWatchers.get(packageName);
330 if (cbs != null) {
331 if (repCbs == null) {
332 repCbs = new ArrayList<Callback>();
333 }
334 repCbs.addAll(cbs);
335 }
Dianne Hackborn514074f2013-02-11 10:52:46 -0800336 if (mode == AppOpsManager.MODE_ALLOWED) {
337 // If going into the default mode, prune this op
338 // if there is nothing else interesting in it.
Dianne Hackborn607b4142013-08-02 18:10:10 -0700339 pruneOp(op, uid, packageName);
Dianne Hackborn514074f2013-02-11 10:52:46 -0800340 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800341 scheduleWriteNowLocked();
342 }
343 }
344 }
Dianne Hackbornc2293022013-02-06 23:14:49 -0800345 if (repCbs != null) {
346 for (int i=0; i<repCbs.size(); i++) {
347 try {
348 repCbs.get(i).mCallback.opChanged(code, packageName);
349 } catch (RemoteException e) {
350 }
351 }
352 }
353 }
354
Dianne Hackborn607b4142013-08-02 18:10:10 -0700355 private static HashMap<Callback, ArrayList<Pair<String, Integer>>> addCallbacks(
356 HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks,
357 String packageName, int op, ArrayList<Callback> cbs) {
358 if (cbs == null) {
359 return callbacks;
360 }
361 if (callbacks == null) {
362 callbacks = new HashMap<Callback, ArrayList<Pair<String, Integer>>>();
363 }
364 for (int i=0; i<cbs.size(); i++) {
365 Callback cb = cbs.get(i);
366 ArrayList<Pair<String, Integer>> reports = callbacks.get(cb);
367 if (reports == null) {
368 reports = new ArrayList<Pair<String, Integer>>();
369 callbacks.put(cb, reports);
370 }
371 reports.add(new Pair<String, Integer>(packageName, op));
372 }
373 return callbacks;
374 }
375
376 @Override
377 public void resetAllModes() {
378 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
379 Binder.getCallingPid(), Binder.getCallingUid(), null);
380 HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
381 synchronized (this) {
382 boolean changed = false;
383 for (int i=0; i<mUidOps.size(); i++) {
384 HashMap<String, Ops> packages = mUidOps.valueAt(i);
385 for (Map.Entry<String, Ops> ent : packages.entrySet()) {
386 String packageName = ent.getKey();
387 Ops pkgOps = ent.getValue();
388 for (int j=0; j<pkgOps.size(); j++) {
389 Op curOp = pkgOps.valueAt(j);
390 if (curOp.mode != AppOpsManager.MODE_ALLOWED) {
391 curOp.mode = AppOpsManager.MODE_ALLOWED;
392 changed = true;
393 callbacks = addCallbacks(callbacks, packageName, curOp.op,
394 mOpModeWatchers.get(curOp.op));
395 callbacks = addCallbacks(callbacks, packageName, curOp.op,
396 mPackageModeWatchers.get(packageName));
397 pruneOp(curOp, mUidOps.keyAt(i), packageName);
398 }
399 }
400 }
401 }
402 if (changed) {
403 scheduleWriteNowLocked();
404 }
405 }
406 if (callbacks != null) {
407 for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
408 Callback cb = ent.getKey();
409 ArrayList<Pair<String, Integer>> reports = ent.getValue();
410 for (int i=0; i<reports.size(); i++) {
411 Pair<String, Integer> rep = reports.get(i);
412 try {
413 cb.mCallback.opChanged(rep.second, rep.first);
414 } catch (RemoteException e) {
415 }
416 }
417 }
418 }
419 }
420
Dianne Hackbornc2293022013-02-06 23:14:49 -0800421 @Override
422 public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
423 synchronized (this) {
424 op = AppOpsManager.opToSwitch(op);
425 Callback cb = mModeWatchers.get(callback.asBinder());
426 if (cb == null) {
427 cb = new Callback(callback);
428 mModeWatchers.put(callback.asBinder(), cb);
429 }
430 if (op != AppOpsManager.OP_NONE) {
431 ArrayList<Callback> cbs = mOpModeWatchers.get(op);
432 if (cbs == null) {
433 cbs = new ArrayList<Callback>();
434 mOpModeWatchers.put(op, cbs);
435 }
436 cbs.add(cb);
437 }
438 if (packageName != null) {
439 ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
440 if (cbs == null) {
441 cbs = new ArrayList<Callback>();
442 mPackageModeWatchers.put(packageName, cbs);
443 }
444 cbs.add(cb);
445 }
446 }
447 }
448
449 @Override
450 public void stopWatchingMode(IAppOpsCallback callback) {
451 synchronized (this) {
452 Callback cb = mModeWatchers.remove(callback.asBinder());
453 if (cb != null) {
454 cb.unlinkToDeath();
455 for (int i=0; i<mOpModeWatchers.size(); i++) {
456 ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
457 cbs.remove(cb);
458 if (cbs.size() <= 0) {
459 mOpModeWatchers.removeAt(i);
460 }
461 }
462 if (mPackageModeWatchers.size() > 0) {
463 Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
464 while (it.hasNext()) {
465 ArrayList<Callback> cbs = it.next();
466 cbs.remove(cb);
467 if (cbs.size() <= 0) {
468 it.remove();
469 }
470 }
471 }
472 }
473 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800474 }
475
476 @Override
Dianne Hackborn35654b62013-01-14 17:38:02 -0800477 public int checkOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800478 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800479 verifyIncomingOp(code);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800480 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800481 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800482 if (op == null) {
483 return AppOpsManager.MODE_ALLOWED;
484 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800485 return op.mode;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800486 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800487 }
488
489 @Override
490 public int noteOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800491 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800492 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800493 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800494 Ops ops = getOpsLocked(uid, packageName, true);
495 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800496 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
497 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800498 return AppOpsManager.MODE_IGNORED;
499 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800500 Op op = getOpLocked(ops, code, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800501 if (op.duration == -1) {
502 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
503 + " code " + code + " time=" + op.time + " duration=" + op.duration);
504 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800505 op.duration = 0;
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800506 final int switchCode = AppOpsManager.opToSwitch(code);
507 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
508 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
509 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
510 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800511 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800512 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800513 }
514 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
515 + " package " + packageName);
516 op.time = System.currentTimeMillis();
Dianne Hackborn514074f2013-02-11 10:52:46 -0800517 op.rejectTime = 0;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800518 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800519 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800520 }
521
522 @Override
523 public int startOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800524 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800525 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800526 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800527 Ops ops = getOpsLocked(uid, packageName, true);
528 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800529 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
530 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800531 return AppOpsManager.MODE_IGNORED;
532 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800533 Op op = getOpLocked(ops, code, true);
534 final int switchCode = AppOpsManager.opToSwitch(code);
535 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
536 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
537 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
538 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800539 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800540 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800541 }
542 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
543 + " package " + packageName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800544 if (op.nesting == 0) {
545 op.time = System.currentTimeMillis();
Dianne Hackborn514074f2013-02-11 10:52:46 -0800546 op.rejectTime = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800547 op.duration = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800548 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800549 op.nesting++;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800550 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800551 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800552 }
553
554 @Override
555 public void finishOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800556 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800557 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800558 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800559 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800560 if (op == null) {
561 return;
562 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800563 if (op.nesting <= 1) {
564 if (op.nesting == 1) {
565 op.duration = (int)(System.currentTimeMillis() - op.time);
Dianne Hackborn514074f2013-02-11 10:52:46 -0800566 op.time += op.duration;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800567 } else {
568 Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
569 + " code " + code + " time=" + op.time + " duration=" + op.duration
570 + " nesting=" + op.nesting);
571 }
Dianne Hackborne7991752013-01-16 17:56:46 -0800572 op.nesting = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800573 } else {
574 op.nesting--;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800575 }
576 }
577 }
578
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800579 private void verifyIncomingUid(int uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800580 if (uid == Binder.getCallingUid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800581 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800582 }
583 if (Binder.getCallingPid() == Process.myPid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800584 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800585 }
586 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
587 Binder.getCallingPid(), Binder.getCallingUid(), null);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800588 }
589
Dianne Hackborn961321f2013-02-05 17:22:41 -0800590 private void verifyIncomingOp(int op) {
591 if (op >= 0 && op < AppOpsManager._NUM_OP) {
592 return;
593 }
594 throw new IllegalArgumentException("Bad operation #" + op);
595 }
596
Dianne Hackborn72e39832013-01-18 18:36:09 -0800597 private Ops getOpsLocked(int uid, String packageName, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800598 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
599 if (pkgOps == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800600 if (!edit) {
601 return null;
602 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800603 pkgOps = new HashMap<String, Ops>();
604 mUidOps.put(uid, pkgOps);
605 }
Dianne Hackborn514074f2013-02-11 10:52:46 -0800606 if (uid == 0) {
607 packageName = "root";
608 } else if (uid == Process.SHELL_UID) {
609 packageName = "com.android.shell";
610 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800611 Ops ops = pkgOps.get(packageName);
612 if (ops == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800613 if (!edit) {
614 return null;
615 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800616 // This is the first time we have seen this package name under this uid,
617 // so let's make sure it is valid.
Dianne Hackborn514074f2013-02-11 10:52:46 -0800618 if (uid != 0) {
619 final long ident = Binder.clearCallingIdentity();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800620 try {
Dianne Hackborn514074f2013-02-11 10:52:46 -0800621 int pkgUid = -1;
622 try {
623 pkgUid = mContext.getPackageManager().getPackageUid(packageName,
624 UserHandle.getUserId(uid));
625 } catch (NameNotFoundException e) {
626 }
627 if (pkgUid != uid) {
628 // Oops! The package name is not valid for the uid they are calling
629 // under. Abort.
630 Slog.w(TAG, "Bad call: specified package " + packageName
631 + " under uid " + uid + " but it is really " + pkgUid);
632 return null;
633 }
634 } finally {
635 Binder.restoreCallingIdentity(ident);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800636 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800637 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800638 ops = new Ops(packageName, uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800639 pkgOps.put(packageName, ops);
640 }
Dianne Hackborn72e39832013-01-18 18:36:09 -0800641 return ops;
642 }
643
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800644 private void scheduleWriteLocked() {
645 if (!mWriteScheduled) {
646 mWriteScheduled = true;
647 mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
648 }
649 }
650
651 private void scheduleWriteNowLocked() {
652 if (!mWriteScheduled) {
653 mWriteScheduled = true;
654 }
655 mHandler.removeCallbacks(mWriteRunner);
656 mHandler.post(mWriteRunner);
657 }
658
Dianne Hackborn72e39832013-01-18 18:36:09 -0800659 private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
660 Ops ops = getOpsLocked(uid, packageName, edit);
661 if (ops == null) {
662 return null;
663 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800664 return getOpLocked(ops, code, edit);
665 }
666
667 private Op getOpLocked(Ops ops, int code, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800668 Op op = ops.get(code);
669 if (op == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800670 if (!edit) {
671 return null;
672 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800673 op = new Op(code);
674 ops.put(code, op);
675 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800676 if (edit) {
677 scheduleWriteLocked();
Dianne Hackborn35654b62013-01-14 17:38:02 -0800678 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800679 return op;
680 }
681
Dianne Hackborn35654b62013-01-14 17:38:02 -0800682 void readState() {
683 synchronized (mFile) {
684 synchronized (this) {
685 FileInputStream stream;
686 try {
687 stream = mFile.openRead();
688 } catch (FileNotFoundException e) {
689 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
690 return;
691 }
692 boolean success = false;
693 try {
694 XmlPullParser parser = Xml.newPullParser();
695 parser.setInput(stream, null);
696 int type;
697 while ((type = parser.next()) != XmlPullParser.START_TAG
698 && type != XmlPullParser.END_DOCUMENT) {
699 ;
700 }
701
702 if (type != XmlPullParser.START_TAG) {
703 throw new IllegalStateException("no start tag found");
704 }
705
706 int outerDepth = parser.getDepth();
707 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
708 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
709 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
710 continue;
711 }
712
713 String tagName = parser.getName();
714 if (tagName.equals("pkg")) {
Dave Burke0997c5bd2013-08-02 20:25:02 +0000715 readPackage(parser);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800716 } else {
717 Slog.w(TAG, "Unknown element under <app-ops>: "
718 + parser.getName());
719 XmlUtils.skipCurrentTag(parser);
720 }
721 }
722 success = true;
723 } catch (IllegalStateException e) {
724 Slog.w(TAG, "Failed parsing " + e);
725 } catch (NullPointerException e) {
726 Slog.w(TAG, "Failed parsing " + e);
727 } catch (NumberFormatException e) {
728 Slog.w(TAG, "Failed parsing " + e);
729 } catch (XmlPullParserException e) {
730 Slog.w(TAG, "Failed parsing " + e);
731 } catch (IOException e) {
732 Slog.w(TAG, "Failed parsing " + e);
733 } catch (IndexOutOfBoundsException e) {
734 Slog.w(TAG, "Failed parsing " + e);
735 } finally {
736 if (!success) {
737 mUidOps.clear();
738 }
739 try {
740 stream.close();
741 } catch (IOException e) {
742 }
743 }
744 }
745 }
746 }
747
Dave Burke0997c5bd2013-08-02 20:25:02 +0000748 void readPackage(XmlPullParser parser) throws NumberFormatException,
Dianne Hackborn35654b62013-01-14 17:38:02 -0800749 XmlPullParserException, IOException {
750 String pkgName = parser.getAttributeValue(null, "n");
751 int outerDepth = parser.getDepth();
752 int type;
753 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
754 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
755 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
756 continue;
757 }
758
759 String tagName = parser.getName();
760 if (tagName.equals("uid")) {
Dave Burke0997c5bd2013-08-02 20:25:02 +0000761 readUid(parser, pkgName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800762 } else {
763 Slog.w(TAG, "Unknown element under <pkg>: "
764 + parser.getName());
765 XmlUtils.skipCurrentTag(parser);
766 }
767 }
768 }
769
Dave Burke0997c5bd2013-08-02 20:25:02 +0000770 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
Dianne Hackborn35654b62013-01-14 17:38:02 -0800771 XmlPullParserException, IOException {
772 int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
773 int outerDepth = parser.getDepth();
774 int type;
775 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
776 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
777 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
778 continue;
779 }
780
781 String tagName = parser.getName();
782 if (tagName.equals("op")) {
783 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800784 String mode = parser.getAttributeValue(null, "m");
785 if (mode != null) {
Dave Burke0997c5bd2013-08-02 20:25:02 +0000786 op.mode = Integer.parseInt(mode);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800787 }
788 String time = parser.getAttributeValue(null, "t");
789 if (time != null) {
790 op.time = Long.parseLong(time);
791 }
792 time = parser.getAttributeValue(null, "r");
793 if (time != null) {
794 op.rejectTime = Long.parseLong(time);
795 }
796 String dur = parser.getAttributeValue(null, "d");
797 if (dur != null) {
798 op.duration = Integer.parseInt(dur);
799 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800800 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
801 if (pkgOps == null) {
802 pkgOps = new HashMap<String, Ops>();
803 mUidOps.put(uid, pkgOps);
804 }
805 Ops ops = pkgOps.get(pkgName);
806 if (ops == null) {
807 ops = new Ops(pkgName, uid);
808 pkgOps.put(pkgName, ops);
809 }
810 ops.put(op.op, op);
811 } else {
812 Slog.w(TAG, "Unknown element under <pkg>: "
813 + parser.getName());
814 XmlUtils.skipCurrentTag(parser);
815 }
816 }
817 }
818
819 void writeState() {
820 synchronized (mFile) {
821 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
822
823 FileOutputStream stream;
824 try {
825 stream = mFile.startWrite();
826 } catch (IOException e) {
827 Slog.w(TAG, "Failed to write state: " + e);
828 return;
829 }
830
831 try {
832 XmlSerializer out = new FastXmlSerializer();
833 out.setOutput(stream, "utf-8");
834 out.startDocument(null, true);
835 out.startTag(null, "app-ops");
836
837 if (allOps != null) {
838 String lastPkg = null;
839 for (int i=0; i<allOps.size(); i++) {
840 AppOpsManager.PackageOps pkg = allOps.get(i);
841 if (!pkg.getPackageName().equals(lastPkg)) {
842 if (lastPkg != null) {
843 out.endTag(null, "pkg");
844 }
845 lastPkg = pkg.getPackageName();
846 out.startTag(null, "pkg");
847 out.attribute(null, "n", lastPkg);
848 }
849 out.startTag(null, "uid");
850 out.attribute(null, "n", Integer.toString(pkg.getUid()));
851 List<AppOpsManager.OpEntry> ops = pkg.getOps();
852 for (int j=0; j<ops.size(); j++) {
853 AppOpsManager.OpEntry op = ops.get(j);
854 out.startTag(null, "op");
855 out.attribute(null, "n", Integer.toString(op.getOp()));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800856 if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
857 out.attribute(null, "m", Integer.toString(op.getMode()));
858 }
859 long time = op.getTime();
860 if (time != 0) {
861 out.attribute(null, "t", Long.toString(time));
862 }
863 time = op.getRejectTime();
864 if (time != 0) {
865 out.attribute(null, "r", Long.toString(time));
866 }
867 int dur = op.getDuration();
868 if (dur != 0) {
869 out.attribute(null, "d", Integer.toString(dur));
870 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800871 out.endTag(null, "op");
872 }
873 out.endTag(null, "uid");
874 }
875 if (lastPkg != null) {
876 out.endTag(null, "pkg");
877 }
878 }
879
880 out.endTag(null, "app-ops");
881 out.endDocument();
882 mFile.finishWrite(stream);
883 } catch (IOException e) {
884 Slog.w(TAG, "Failed to write state, restoring backup.", e);
885 mFile.failWrite(stream);
886 }
887 }
888 }
889
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800890 @Override
891 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
892 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
893 != PackageManager.PERMISSION_GRANTED) {
894 pw.println("Permission Denial: can't dump ApOps service from from pid="
895 + Binder.getCallingPid()
896 + ", uid=" + Binder.getCallingUid());
897 return;
898 }
899
900 synchronized (this) {
901 pw.println("Current AppOps Service state:");
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800902 final long now = System.currentTimeMillis();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800903 for (int i=0; i<mUidOps.size(); i++) {
904 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
905 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
906 for (Ops ops : pkgOps.values()) {
907 pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
908 for (int j=0; j<ops.size(); j++) {
909 Op op = ops.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800910 pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
911 pw.print(": mode="); pw.print(op.mode);
912 if (op.time != 0) {
913 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
914 pw.print(" ago");
915 }
916 if (op.rejectTime != 0) {
917 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
918 pw.print(" ago");
919 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800920 if (op.duration == -1) {
921 pw.println(" (running)");
922 } else {
923 pw.print("; duration=");
924 TimeUtils.formatDuration(op.duration, pw);
925 pw.println();
926 }
927 }
928 }
929 }
930 }
931 }
932}