blob: a55fddcd7ed8c79af90a808fa491f6853dbe8520 [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;
Dianne Hackborn7f09ec32013-08-07 15:36:08 -0700383 for (int i=mUidOps.size()-1; i>=0; i--) {
Dianne Hackborn607b4142013-08-02 18:10:10 -0700384 HashMap<String, Ops> packages = mUidOps.valueAt(i);
Dianne Hackborn7f09ec32013-08-07 15:36:08 -0700385 Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
386 while (it.hasNext()) {
387 Map.Entry<String, Ops> ent = it.next();
Dianne Hackborn607b4142013-08-02 18:10:10 -0700388 String packageName = ent.getKey();
389 Ops pkgOps = ent.getValue();
Dianne Hackborn7f09ec32013-08-07 15:36:08 -0700390 for (int j=pkgOps.size()-1; j>=0; j--) {
Dianne Hackborn607b4142013-08-02 18:10:10 -0700391 Op curOp = pkgOps.valueAt(j);
392 if (curOp.mode != AppOpsManager.MODE_ALLOWED) {
393 curOp.mode = AppOpsManager.MODE_ALLOWED;
394 changed = true;
395 callbacks = addCallbacks(callbacks, packageName, curOp.op,
396 mOpModeWatchers.get(curOp.op));
397 callbacks = addCallbacks(callbacks, packageName, curOp.op,
398 mPackageModeWatchers.get(packageName));
Dianne Hackborn7f09ec32013-08-07 15:36:08 -0700399 if (curOp.time == 0 && curOp.rejectTime == 0) {
400 pkgOps.removeAt(j);
401 }
Dianne Hackborn607b4142013-08-02 18:10:10 -0700402 }
403 }
Dianne Hackborn7f09ec32013-08-07 15:36:08 -0700404 if (pkgOps.size() == 0) {
405 it.remove();
406 }
407 }
408 if (packages.size() == 0) {
409 mUidOps.removeAt(i);
Dianne Hackborn607b4142013-08-02 18:10:10 -0700410 }
411 }
412 if (changed) {
413 scheduleWriteNowLocked();
414 }
415 }
416 if (callbacks != null) {
417 for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
418 Callback cb = ent.getKey();
419 ArrayList<Pair<String, Integer>> reports = ent.getValue();
420 for (int i=0; i<reports.size(); i++) {
421 Pair<String, Integer> rep = reports.get(i);
422 try {
423 cb.mCallback.opChanged(rep.second, rep.first);
424 } catch (RemoteException e) {
425 }
426 }
427 }
428 }
429 }
430
Dianne Hackbornc2293022013-02-06 23:14:49 -0800431 @Override
432 public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
433 synchronized (this) {
434 op = AppOpsManager.opToSwitch(op);
435 Callback cb = mModeWatchers.get(callback.asBinder());
436 if (cb == null) {
437 cb = new Callback(callback);
438 mModeWatchers.put(callback.asBinder(), cb);
439 }
440 if (op != AppOpsManager.OP_NONE) {
441 ArrayList<Callback> cbs = mOpModeWatchers.get(op);
442 if (cbs == null) {
443 cbs = new ArrayList<Callback>();
444 mOpModeWatchers.put(op, cbs);
445 }
446 cbs.add(cb);
447 }
448 if (packageName != null) {
449 ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
450 if (cbs == null) {
451 cbs = new ArrayList<Callback>();
452 mPackageModeWatchers.put(packageName, cbs);
453 }
454 cbs.add(cb);
455 }
456 }
457 }
458
459 @Override
460 public void stopWatchingMode(IAppOpsCallback callback) {
461 synchronized (this) {
462 Callback cb = mModeWatchers.remove(callback.asBinder());
463 if (cb != null) {
464 cb.unlinkToDeath();
465 for (int i=0; i<mOpModeWatchers.size(); i++) {
466 ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
467 cbs.remove(cb);
468 if (cbs.size() <= 0) {
469 mOpModeWatchers.removeAt(i);
470 }
471 }
472 if (mPackageModeWatchers.size() > 0) {
473 Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
474 while (it.hasNext()) {
475 ArrayList<Callback> cbs = it.next();
476 cbs.remove(cb);
477 if (cbs.size() <= 0) {
478 it.remove();
479 }
480 }
481 }
482 }
483 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800484 }
485
486 @Override
Dianne Hackborn35654b62013-01-14 17:38:02 -0800487 public int checkOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800488 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800489 verifyIncomingOp(code);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800490 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800491 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800492 if (op == null) {
493 return AppOpsManager.MODE_ALLOWED;
494 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800495 return op.mode;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800496 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800497 }
498
499 @Override
500 public int noteOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800501 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800502 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800503 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800504 Ops ops = getOpsLocked(uid, packageName, true);
505 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800506 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
507 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800508 return AppOpsManager.MODE_IGNORED;
509 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800510 Op op = getOpLocked(ops, code, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800511 if (op.duration == -1) {
512 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
513 + " code " + code + " time=" + op.time + " duration=" + op.duration);
514 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800515 op.duration = 0;
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800516 final int switchCode = AppOpsManager.opToSwitch(code);
517 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
518 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
519 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
520 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800521 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800522 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800523 }
524 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
525 + " package " + packageName);
526 op.time = System.currentTimeMillis();
Dianne Hackborn514074f2013-02-11 10:52:46 -0800527 op.rejectTime = 0;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800528 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800529 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800530 }
531
532 @Override
533 public int startOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800534 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800535 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800536 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800537 Ops ops = getOpsLocked(uid, packageName, true);
538 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800539 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
540 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800541 return AppOpsManager.MODE_IGNORED;
542 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800543 Op op = getOpLocked(ops, code, true);
544 final int switchCode = AppOpsManager.opToSwitch(code);
545 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
546 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
547 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
548 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800549 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800550 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800551 }
552 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
553 + " package " + packageName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800554 if (op.nesting == 0) {
555 op.time = System.currentTimeMillis();
Dianne Hackborn514074f2013-02-11 10:52:46 -0800556 op.rejectTime = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800557 op.duration = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800558 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800559 op.nesting++;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800560 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800561 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800562 }
563
564 @Override
565 public void finishOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800566 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800567 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800568 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800569 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800570 if (op == null) {
571 return;
572 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800573 if (op.nesting <= 1) {
574 if (op.nesting == 1) {
575 op.duration = (int)(System.currentTimeMillis() - op.time);
Dianne Hackborn514074f2013-02-11 10:52:46 -0800576 op.time += op.duration;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800577 } else {
578 Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
579 + " code " + code + " time=" + op.time + " duration=" + op.duration
580 + " nesting=" + op.nesting);
581 }
Dianne Hackborne7991752013-01-16 17:56:46 -0800582 op.nesting = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800583 } else {
584 op.nesting--;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800585 }
586 }
587 }
588
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800589 private void verifyIncomingUid(int uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800590 if (uid == Binder.getCallingUid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800591 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800592 }
593 if (Binder.getCallingPid() == Process.myPid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800594 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800595 }
596 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
597 Binder.getCallingPid(), Binder.getCallingUid(), null);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800598 }
599
Dianne Hackborn961321f2013-02-05 17:22:41 -0800600 private void verifyIncomingOp(int op) {
601 if (op >= 0 && op < AppOpsManager._NUM_OP) {
602 return;
603 }
604 throw new IllegalArgumentException("Bad operation #" + op);
605 }
606
Dianne Hackborn72e39832013-01-18 18:36:09 -0800607 private Ops getOpsLocked(int uid, String packageName, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800608 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
609 if (pkgOps == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800610 if (!edit) {
611 return null;
612 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800613 pkgOps = new HashMap<String, Ops>();
614 mUidOps.put(uid, pkgOps);
615 }
Dianne Hackborn514074f2013-02-11 10:52:46 -0800616 if (uid == 0) {
617 packageName = "root";
618 } else if (uid == Process.SHELL_UID) {
619 packageName = "com.android.shell";
620 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800621 Ops ops = pkgOps.get(packageName);
622 if (ops == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800623 if (!edit) {
624 return null;
625 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800626 // This is the first time we have seen this package name under this uid,
627 // so let's make sure it is valid.
Dianne Hackborn514074f2013-02-11 10:52:46 -0800628 if (uid != 0) {
629 final long ident = Binder.clearCallingIdentity();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800630 try {
Dianne Hackborn514074f2013-02-11 10:52:46 -0800631 int pkgUid = -1;
632 try {
633 pkgUid = mContext.getPackageManager().getPackageUid(packageName,
634 UserHandle.getUserId(uid));
635 } catch (NameNotFoundException e) {
636 }
637 if (pkgUid != uid) {
638 // Oops! The package name is not valid for the uid they are calling
639 // under. Abort.
640 Slog.w(TAG, "Bad call: specified package " + packageName
641 + " under uid " + uid + " but it is really " + pkgUid);
642 return null;
643 }
644 } finally {
645 Binder.restoreCallingIdentity(ident);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800646 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800647 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800648 ops = new Ops(packageName, uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800649 pkgOps.put(packageName, ops);
650 }
Dianne Hackborn72e39832013-01-18 18:36:09 -0800651 return ops;
652 }
653
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800654 private void scheduleWriteLocked() {
655 if (!mWriteScheduled) {
656 mWriteScheduled = true;
657 mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
658 }
659 }
660
661 private void scheduleWriteNowLocked() {
662 if (!mWriteScheduled) {
663 mWriteScheduled = true;
664 }
665 mHandler.removeCallbacks(mWriteRunner);
666 mHandler.post(mWriteRunner);
667 }
668
Dianne Hackborn72e39832013-01-18 18:36:09 -0800669 private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
670 Ops ops = getOpsLocked(uid, packageName, edit);
671 if (ops == null) {
672 return null;
673 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800674 return getOpLocked(ops, code, edit);
675 }
676
677 private Op getOpLocked(Ops ops, int code, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800678 Op op = ops.get(code);
679 if (op == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800680 if (!edit) {
681 return null;
682 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800683 op = new Op(code);
684 ops.put(code, op);
685 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800686 if (edit) {
687 scheduleWriteLocked();
Dianne Hackborn35654b62013-01-14 17:38:02 -0800688 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800689 return op;
690 }
691
Dianne Hackborn35654b62013-01-14 17:38:02 -0800692 void readState() {
693 synchronized (mFile) {
694 synchronized (this) {
695 FileInputStream stream;
696 try {
697 stream = mFile.openRead();
698 } catch (FileNotFoundException e) {
699 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
700 return;
701 }
702 boolean success = false;
703 try {
704 XmlPullParser parser = Xml.newPullParser();
705 parser.setInput(stream, null);
706 int type;
707 while ((type = parser.next()) != XmlPullParser.START_TAG
708 && type != XmlPullParser.END_DOCUMENT) {
709 ;
710 }
711
712 if (type != XmlPullParser.START_TAG) {
713 throw new IllegalStateException("no start tag found");
714 }
715
716 int outerDepth = parser.getDepth();
717 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
718 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
719 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
720 continue;
721 }
722
723 String tagName = parser.getName();
724 if (tagName.equals("pkg")) {
Dave Burke0997c5bd2013-08-02 20:25:02 +0000725 readPackage(parser);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800726 } else {
727 Slog.w(TAG, "Unknown element under <app-ops>: "
728 + parser.getName());
729 XmlUtils.skipCurrentTag(parser);
730 }
731 }
732 success = true;
733 } catch (IllegalStateException e) {
734 Slog.w(TAG, "Failed parsing " + e);
735 } catch (NullPointerException e) {
736 Slog.w(TAG, "Failed parsing " + e);
737 } catch (NumberFormatException e) {
738 Slog.w(TAG, "Failed parsing " + e);
739 } catch (XmlPullParserException e) {
740 Slog.w(TAG, "Failed parsing " + e);
741 } catch (IOException e) {
742 Slog.w(TAG, "Failed parsing " + e);
743 } catch (IndexOutOfBoundsException e) {
744 Slog.w(TAG, "Failed parsing " + e);
745 } finally {
746 if (!success) {
747 mUidOps.clear();
748 }
749 try {
750 stream.close();
751 } catch (IOException e) {
752 }
753 }
754 }
755 }
756 }
757
Dave Burke0997c5bd2013-08-02 20:25:02 +0000758 void readPackage(XmlPullParser parser) throws NumberFormatException,
Dianne Hackborn35654b62013-01-14 17:38:02 -0800759 XmlPullParserException, IOException {
760 String pkgName = parser.getAttributeValue(null, "n");
761 int outerDepth = parser.getDepth();
762 int type;
763 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
764 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
765 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
766 continue;
767 }
768
769 String tagName = parser.getName();
770 if (tagName.equals("uid")) {
Dave Burke0997c5bd2013-08-02 20:25:02 +0000771 readUid(parser, pkgName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800772 } else {
773 Slog.w(TAG, "Unknown element under <pkg>: "
774 + parser.getName());
775 XmlUtils.skipCurrentTag(parser);
776 }
777 }
778 }
779
Dave Burke0997c5bd2013-08-02 20:25:02 +0000780 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
Dianne Hackborn35654b62013-01-14 17:38:02 -0800781 XmlPullParserException, IOException {
782 int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
783 int outerDepth = parser.getDepth();
784 int type;
785 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
786 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
787 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
788 continue;
789 }
790
791 String tagName = parser.getName();
792 if (tagName.equals("op")) {
793 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800794 String mode = parser.getAttributeValue(null, "m");
795 if (mode != null) {
Dave Burke0997c5bd2013-08-02 20:25:02 +0000796 op.mode = Integer.parseInt(mode);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800797 }
798 String time = parser.getAttributeValue(null, "t");
799 if (time != null) {
800 op.time = Long.parseLong(time);
801 }
802 time = parser.getAttributeValue(null, "r");
803 if (time != null) {
804 op.rejectTime = Long.parseLong(time);
805 }
806 String dur = parser.getAttributeValue(null, "d");
807 if (dur != null) {
808 op.duration = Integer.parseInt(dur);
809 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800810 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
811 if (pkgOps == null) {
812 pkgOps = new HashMap<String, Ops>();
813 mUidOps.put(uid, pkgOps);
814 }
815 Ops ops = pkgOps.get(pkgName);
816 if (ops == null) {
817 ops = new Ops(pkgName, uid);
818 pkgOps.put(pkgName, ops);
819 }
820 ops.put(op.op, op);
821 } else {
822 Slog.w(TAG, "Unknown element under <pkg>: "
823 + parser.getName());
824 XmlUtils.skipCurrentTag(parser);
825 }
826 }
827 }
828
829 void writeState() {
830 synchronized (mFile) {
831 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
832
833 FileOutputStream stream;
834 try {
835 stream = mFile.startWrite();
836 } catch (IOException e) {
837 Slog.w(TAG, "Failed to write state: " + e);
838 return;
839 }
840
841 try {
842 XmlSerializer out = new FastXmlSerializer();
843 out.setOutput(stream, "utf-8");
844 out.startDocument(null, true);
845 out.startTag(null, "app-ops");
846
847 if (allOps != null) {
848 String lastPkg = null;
849 for (int i=0; i<allOps.size(); i++) {
850 AppOpsManager.PackageOps pkg = allOps.get(i);
851 if (!pkg.getPackageName().equals(lastPkg)) {
852 if (lastPkg != null) {
853 out.endTag(null, "pkg");
854 }
855 lastPkg = pkg.getPackageName();
856 out.startTag(null, "pkg");
857 out.attribute(null, "n", lastPkg);
858 }
859 out.startTag(null, "uid");
860 out.attribute(null, "n", Integer.toString(pkg.getUid()));
861 List<AppOpsManager.OpEntry> ops = pkg.getOps();
862 for (int j=0; j<ops.size(); j++) {
863 AppOpsManager.OpEntry op = ops.get(j);
864 out.startTag(null, "op");
865 out.attribute(null, "n", Integer.toString(op.getOp()));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800866 if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
867 out.attribute(null, "m", Integer.toString(op.getMode()));
868 }
869 long time = op.getTime();
870 if (time != 0) {
871 out.attribute(null, "t", Long.toString(time));
872 }
873 time = op.getRejectTime();
874 if (time != 0) {
875 out.attribute(null, "r", Long.toString(time));
876 }
877 int dur = op.getDuration();
878 if (dur != 0) {
879 out.attribute(null, "d", Integer.toString(dur));
880 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800881 out.endTag(null, "op");
882 }
883 out.endTag(null, "uid");
884 }
885 if (lastPkg != null) {
886 out.endTag(null, "pkg");
887 }
888 }
889
890 out.endTag(null, "app-ops");
891 out.endDocument();
892 mFile.finishWrite(stream);
893 } catch (IOException e) {
894 Slog.w(TAG, "Failed to write state, restoring backup.", e);
895 mFile.failWrite(stream);
896 }
897 }
898 }
899
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800900 @Override
901 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
902 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
903 != PackageManager.PERMISSION_GRANTED) {
904 pw.println("Permission Denial: can't dump ApOps service from from pid="
905 + Binder.getCallingPid()
906 + ", uid=" + Binder.getCallingUid());
907 return;
908 }
909
910 synchronized (this) {
911 pw.println("Current AppOps Service state:");
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800912 final long now = System.currentTimeMillis();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800913 for (int i=0; i<mUidOps.size(); i++) {
914 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
915 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
916 for (Ops ops : pkgOps.values()) {
917 pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
918 for (int j=0; j<ops.size(); j++) {
919 Op op = ops.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800920 pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
921 pw.print(": mode="); pw.print(op.mode);
922 if (op.time != 0) {
923 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
924 pw.print(" ago");
925 }
926 if (op.rejectTime != 0) {
927 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
928 pw.print(" ago");
929 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800930 if (op.duration == -1) {
931 pw.println(" (running)");
932 } else {
933 pw.print("; duration=");
934 TimeUtils.formatDuration(op.duration, pw);
935 pw.println();
936 }
937 }
938 }
939 }
940 }
941 }
942}