blob: a402642bc0de9a964c06a1ea11a964b5c0448e82 [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 Hackborna06de0f2012-12-11 16:34:47 -080030
31import android.app.AppOpsManager;
32import android.content.Context;
33import android.content.pm.PackageManager;
34import android.content.pm.PackageManager.NameNotFoundException;
Dianne Hackborn35654b62013-01-14 17:38:02 -080035import android.os.AsyncTask;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080036import android.os.Binder;
Dianne Hackborn35654b62013-01-14 17:38:02 -080037import android.os.Handler;
Dianne Hackbornc2293022013-02-06 23:14:49 -080038import android.os.IBinder;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080039import android.os.Process;
Dianne Hackbornc2293022013-02-06 23:14:49 -080040import android.os.RemoteException;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080041import android.os.ServiceManager;
42import android.os.UserHandle;
43import android.util.AtomicFile;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080044import android.util.Log;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080045import android.util.Slog;
46import android.util.SparseArray;
47import android.util.TimeUtils;
Dianne Hackborn35654b62013-01-14 17:38:02 -080048import android.util.Xml;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080049
50import com.android.internal.app.IAppOpsService;
Dianne Hackbornc2293022013-02-06 23:14:49 -080051import com.android.internal.app.IAppOpsCallback;
Dianne Hackborn35654b62013-01-14 17:38:02 -080052import com.android.internal.util.FastXmlSerializer;
53import com.android.internal.util.XmlUtils;
54
55import org.xmlpull.v1.XmlPullParser;
56import org.xmlpull.v1.XmlPullParserException;
57import org.xmlpull.v1.XmlSerializer;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080058
59public class AppOpsService extends IAppOpsService.Stub {
60 static final String TAG = "AppOps";
Dianne Hackborn35654b62013-01-14 17:38:02 -080061 static final boolean DEBUG = false;
62
63 // Write at most every 30 minutes.
64 static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080065
66 Context mContext;
67 final AtomicFile mFile;
Dianne Hackborn35654b62013-01-14 17:38:02 -080068 final Handler mHandler;
69
70 boolean mWriteScheduled;
71 final Runnable mWriteRunner = new Runnable() {
72 public void run() {
73 synchronized (AppOpsService.this) {
74 mWriteScheduled = false;
75 AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
76 @Override protected Void doInBackground(Void... params) {
77 writeState();
78 return null;
79 }
80 };
81 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
82 }
83 }
84 };
Dianne Hackborna06de0f2012-12-11 16:34:47 -080085
86 final SparseArray<HashMap<String, Ops>> mUidOps
87 = new SparseArray<HashMap<String, Ops>>();
88
Dianne Hackbornc2293022013-02-06 23:14:49 -080089 public final static class Ops extends SparseArray<Op> {
Dianne Hackborna06de0f2012-12-11 16:34:47 -080090 public final String packageName;
Dianne Hackborn35654b62013-01-14 17:38:02 -080091 public final int uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080092
Dianne Hackborn35654b62013-01-14 17:38:02 -080093 public Ops(String _packageName, int _uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -080094 packageName = _packageName;
Dianne Hackborn35654b62013-01-14 17:38:02 -080095 uid = _uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080096 }
97 }
98
Dianne Hackbornc2293022013-02-06 23:14:49 -080099 public final static class Op {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800100 public final int op;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800101 public int mode;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800102 public int duration;
103 public long time;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800104 public long rejectTime;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800105 public int nesting;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800106
107 public Op(int _op) {
108 op = _op;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800109 mode = AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800110 }
111 }
112
Dianne Hackbornc2293022013-02-06 23:14:49 -0800113 final SparseArray<ArrayList<Callback>> mOpModeWatchers
114 = new SparseArray<ArrayList<Callback>>();
115 final HashMap<String, ArrayList<Callback>> mPackageModeWatchers
116 = new HashMap<String, ArrayList<Callback>>();
117 final HashMap<IBinder, Callback> mModeWatchers
118 = new HashMap<IBinder, Callback>();
119
120 public final class Callback implements DeathRecipient {
121 final IAppOpsCallback mCallback;
122
123 public Callback(IAppOpsCallback callback) {
124 mCallback = callback;
125 try {
126 mCallback.asBinder().linkToDeath(this, 0);
127 } catch (RemoteException e) {
128 }
129 }
130
131 public void unlinkToDeath() {
132 mCallback.asBinder().unlinkToDeath(this, 0);
133 }
134
135 @Override
136 public void binderDied() {
137 stopWatchingMode(mCallback);
138 }
139 }
140
Dianne Hackborn35654b62013-01-14 17:38:02 -0800141 public AppOpsService(File storagePath) {
142 mFile = new AtomicFile(storagePath);
143 mHandler = new Handler();
144 readState();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800145 }
146
147 public void publish(Context context) {
148 mContext = context;
149 ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
150 }
151
Dianne Hackborn514074f2013-02-11 10:52:46 -0800152 public void systemReady() {
153 synchronized (this) {
154 boolean changed = false;
155 for (int i=0; i<mUidOps.size(); i++) {
156 HashMap<String, Ops> pkgs = mUidOps.valueAt(i);
157 Iterator<Ops> it = pkgs.values().iterator();
158 while (it.hasNext()) {
159 Ops ops = it.next();
160 int curUid;
161 try {
162 curUid = mContext.getPackageManager().getPackageUid(ops.packageName,
163 UserHandle.getUserId(ops.uid));
164 } catch (NameNotFoundException e) {
165 curUid = -1;
166 }
167 if (curUid != ops.uid) {
168 Slog.i(TAG, "Pruning old package " + ops.packageName
169 + "/" + ops.uid + ": new uid=" + curUid);
170 it.remove();
171 changed = true;
172 }
173 }
174 if (pkgs.size() <= 0) {
175 mUidOps.removeAt(i);
176 }
177 }
178 if (changed) {
179 scheduleWriteLocked();
180 }
181 }
182 }
183
184 public void packageRemoved(int uid, String packageName) {
185 synchronized (this) {
186 HashMap<String, Ops> pkgs = mUidOps.get(uid);
187 if (pkgs != null) {
188 if (pkgs.remove(packageName) != null) {
189 if (pkgs.size() <= 0) {
190 mUidOps.remove(uid);
191 }
192 scheduleWriteLocked();
193 }
194 }
195 }
196 }
197
198 public void uidRemoved(int uid) {
199 synchronized (this) {
200 if (mUidOps.indexOfKey(uid) >= 0) {
201 mUidOps.remove(uid);
202 scheduleWriteLocked();
203 }
204 }
205 }
206
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800207 public void shutdown() {
208 Slog.w(TAG, "Writing app ops before shutdown...");
Dianne Hackborn35654b62013-01-14 17:38:02 -0800209 boolean doWrite = false;
210 synchronized (this) {
211 if (mWriteScheduled) {
212 mWriteScheduled = false;
213 doWrite = true;
214 }
215 }
216 if (doWrite) {
217 writeState();
218 }
219 }
220
Dianne Hackborn72e39832013-01-18 18:36:09 -0800221 private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
222 ArrayList<AppOpsManager.OpEntry> resOps = null;
223 if (ops == null) {
224 resOps = new ArrayList<AppOpsManager.OpEntry>();
225 for (int j=0; j<pkgOps.size(); j++) {
226 Op curOp = pkgOps.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800227 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
228 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800229 }
230 } else {
231 for (int j=0; j<ops.length; j++) {
232 Op curOp = pkgOps.get(ops[j]);
233 if (curOp != null) {
234 if (resOps == null) {
235 resOps = new ArrayList<AppOpsManager.OpEntry>();
236 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800237 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
238 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800239 }
240 }
241 }
242 return resOps;
243 }
244
Dianne Hackborn35654b62013-01-14 17:38:02 -0800245 @Override
246 public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
247 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
248 Binder.getCallingPid(), Binder.getCallingUid(), null);
249 ArrayList<AppOpsManager.PackageOps> res = null;
250 synchronized (this) {
251 for (int i=0; i<mUidOps.size(); i++) {
252 HashMap<String, Ops> packages = mUidOps.valueAt(i);
253 for (Ops pkgOps : packages.values()) {
Dianne Hackborn72e39832013-01-18 18:36:09 -0800254 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800255 if (resOps != null) {
256 if (res == null) {
257 res = new ArrayList<AppOpsManager.PackageOps>();
258 }
259 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
260 pkgOps.packageName, pkgOps.uid, resOps);
261 res.add(resPackage);
262 }
263 }
264 }
265 }
266 return res;
267 }
268
269 @Override
Dianne Hackborn72e39832013-01-18 18:36:09 -0800270 public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
271 int[] ops) {
272 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
273 Binder.getCallingPid(), Binder.getCallingUid(), null);
274 synchronized (this) {
275 Ops pkgOps = getOpsLocked(uid, packageName, false);
276 if (pkgOps == null) {
277 return null;
278 }
279 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
280 if (resOps == null) {
281 return null;
282 }
283 ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
284 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
285 pkgOps.packageName, pkgOps.uid, resOps);
286 res.add(resPackage);
287 return res;
288 }
289 }
290
291 @Override
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800292 public void setMode(int code, int uid, String packageName, int mode) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800293 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800294 verifyIncomingOp(code);
Dianne Hackbornc2293022013-02-06 23:14:49 -0800295 ArrayList<Callback> repCbs = null;
296 code = AppOpsManager.opToSwitch(code);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800297 synchronized (this) {
Dianne Hackbornc2293022013-02-06 23:14:49 -0800298 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800299 if (op != null) {
300 if (op.mode != mode) {
301 op.mode = mode;
Dianne Hackbornc2293022013-02-06 23:14:49 -0800302 ArrayList<Callback> cbs = mOpModeWatchers.get(code);
303 if (cbs != null) {
304 if (repCbs == null) {
305 repCbs = new ArrayList<Callback>();
306 }
307 repCbs.addAll(cbs);
308 }
309 cbs = mPackageModeWatchers.get(packageName);
310 if (cbs != null) {
311 if (repCbs == null) {
312 repCbs = new ArrayList<Callback>();
313 }
314 repCbs.addAll(cbs);
315 }
Dianne Hackborn514074f2013-02-11 10:52:46 -0800316 if (mode == AppOpsManager.MODE_ALLOWED) {
317 // If going into the default mode, prune this op
318 // if there is nothing else interesting in it.
319 if (op.time == 0 && op.rejectTime == 0) {
320 Ops ops = getOpsLocked(uid, packageName, false);
321 if (ops != null) {
322 ops.remove(op.op);
323 if (ops.size() <= 0) {
324 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
325 if (pkgOps != null) {
326 pkgOps.remove(ops.packageName);
327 if (pkgOps.size() <= 0) {
328 mUidOps.remove(uid);
329 }
330 }
331 }
332 }
333 }
334 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800335 scheduleWriteNowLocked();
336 }
337 }
338 }
Dianne Hackbornc2293022013-02-06 23:14:49 -0800339 if (repCbs != null) {
340 for (int i=0; i<repCbs.size(); i++) {
341 try {
342 repCbs.get(i).mCallback.opChanged(code, packageName);
343 } catch (RemoteException e) {
344 }
345 }
346 }
347 }
348
349 @Override
350 public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
351 synchronized (this) {
352 op = AppOpsManager.opToSwitch(op);
353 Callback cb = mModeWatchers.get(callback.asBinder());
354 if (cb == null) {
355 cb = new Callback(callback);
356 mModeWatchers.put(callback.asBinder(), cb);
357 }
358 if (op != AppOpsManager.OP_NONE) {
359 ArrayList<Callback> cbs = mOpModeWatchers.get(op);
360 if (cbs == null) {
361 cbs = new ArrayList<Callback>();
362 mOpModeWatchers.put(op, cbs);
363 }
364 cbs.add(cb);
365 }
366 if (packageName != null) {
367 ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
368 if (cbs == null) {
369 cbs = new ArrayList<Callback>();
370 mPackageModeWatchers.put(packageName, cbs);
371 }
372 cbs.add(cb);
373 }
374 }
375 }
376
377 @Override
378 public void stopWatchingMode(IAppOpsCallback callback) {
379 synchronized (this) {
380 Callback cb = mModeWatchers.remove(callback.asBinder());
381 if (cb != null) {
382 cb.unlinkToDeath();
383 for (int i=0; i<mOpModeWatchers.size(); i++) {
384 ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
385 cbs.remove(cb);
386 if (cbs.size() <= 0) {
387 mOpModeWatchers.removeAt(i);
388 }
389 }
390 if (mPackageModeWatchers.size() > 0) {
391 Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
392 while (it.hasNext()) {
393 ArrayList<Callback> cbs = it.next();
394 cbs.remove(cb);
395 if (cbs.size() <= 0) {
396 it.remove();
397 }
398 }
399 }
400 }
401 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800402 }
403
404 @Override
Dianne Hackborn35654b62013-01-14 17:38:02 -0800405 public int checkOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800406 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800407 verifyIncomingOp(code);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800408 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800409 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800410 if (op == null) {
411 return AppOpsManager.MODE_ALLOWED;
412 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800413 return op.mode;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800414 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800415 }
416
417 @Override
418 public int noteOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800419 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800420 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800421 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800422 Ops ops = getOpsLocked(uid, packageName, true);
423 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800424 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
425 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800426 return AppOpsManager.MODE_IGNORED;
427 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800428 Op op = getOpLocked(ops, code, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800429 if (op.duration == -1) {
430 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
431 + " code " + code + " time=" + op.time + " duration=" + op.duration);
432 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800433 op.duration = 0;
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800434 final int switchCode = AppOpsManager.opToSwitch(code);
435 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
436 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
437 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
438 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800439 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800440 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800441 }
442 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
443 + " package " + packageName);
444 op.time = System.currentTimeMillis();
Dianne Hackborn514074f2013-02-11 10:52:46 -0800445 op.rejectTime = 0;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800446 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800447 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800448 }
449
450 @Override
451 public int startOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800452 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800453 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800454 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800455 Ops ops = getOpsLocked(uid, packageName, true);
456 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800457 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
458 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800459 return AppOpsManager.MODE_IGNORED;
460 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800461 Op op = getOpLocked(ops, code, true);
462 final int switchCode = AppOpsManager.opToSwitch(code);
463 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
464 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
465 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
466 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800467 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800468 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800469 }
470 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
471 + " package " + packageName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800472 if (op.nesting == 0) {
473 op.time = System.currentTimeMillis();
Dianne Hackborn514074f2013-02-11 10:52:46 -0800474 op.rejectTime = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800475 op.duration = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800476 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800477 op.nesting++;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800478 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800479 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800480 }
481
482 @Override
483 public void finishOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800484 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800485 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800486 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800487 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800488 if (op == null) {
489 return;
490 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800491 if (op.nesting <= 1) {
492 if (op.nesting == 1) {
493 op.duration = (int)(System.currentTimeMillis() - op.time);
Dianne Hackborn514074f2013-02-11 10:52:46 -0800494 op.time += op.duration;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800495 } else {
496 Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
497 + " code " + code + " time=" + op.time + " duration=" + op.duration
498 + " nesting=" + op.nesting);
499 }
Dianne Hackborne7991752013-01-16 17:56:46 -0800500 op.nesting = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800501 } else {
502 op.nesting--;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800503 }
504 }
505 }
506
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800507 private void verifyIncomingUid(int uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800508 if (uid == Binder.getCallingUid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800509 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800510 }
511 if (Binder.getCallingPid() == Process.myPid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800512 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800513 }
514 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
515 Binder.getCallingPid(), Binder.getCallingUid(), null);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800516 }
517
Dianne Hackborn961321f2013-02-05 17:22:41 -0800518 private void verifyIncomingOp(int op) {
519 if (op >= 0 && op < AppOpsManager._NUM_OP) {
520 return;
521 }
522 throw new IllegalArgumentException("Bad operation #" + op);
523 }
524
Dianne Hackborn72e39832013-01-18 18:36:09 -0800525 private Ops getOpsLocked(int uid, String packageName, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800526 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
527 if (pkgOps == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800528 if (!edit) {
529 return null;
530 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800531 pkgOps = new HashMap<String, Ops>();
532 mUidOps.put(uid, pkgOps);
533 }
Dianne Hackborn514074f2013-02-11 10:52:46 -0800534 if (uid == 0) {
535 packageName = "root";
536 } else if (uid == Process.SHELL_UID) {
537 packageName = "com.android.shell";
538 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800539 Ops ops = pkgOps.get(packageName);
540 if (ops == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800541 if (!edit) {
542 return null;
543 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800544 // This is the first time we have seen this package name under this uid,
545 // so let's make sure it is valid.
Dianne Hackborn514074f2013-02-11 10:52:46 -0800546 if (uid != 0) {
547 final long ident = Binder.clearCallingIdentity();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800548 try {
Dianne Hackborn514074f2013-02-11 10:52:46 -0800549 int pkgUid = -1;
550 try {
551 pkgUid = mContext.getPackageManager().getPackageUid(packageName,
552 UserHandle.getUserId(uid));
553 } catch (NameNotFoundException e) {
554 }
555 if (pkgUid != uid) {
556 // Oops! The package name is not valid for the uid they are calling
557 // under. Abort.
558 Slog.w(TAG, "Bad call: specified package " + packageName
559 + " under uid " + uid + " but it is really " + pkgUid);
560 return null;
561 }
562 } finally {
563 Binder.restoreCallingIdentity(ident);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800564 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800565 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800566 ops = new Ops(packageName, uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800567 pkgOps.put(packageName, ops);
568 }
Dianne Hackborn72e39832013-01-18 18:36:09 -0800569 return ops;
570 }
571
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800572 private void scheduleWriteLocked() {
573 if (!mWriteScheduled) {
574 mWriteScheduled = true;
575 mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
576 }
577 }
578
579 private void scheduleWriteNowLocked() {
580 if (!mWriteScheduled) {
581 mWriteScheduled = true;
582 }
583 mHandler.removeCallbacks(mWriteRunner);
584 mHandler.post(mWriteRunner);
585 }
586
Dianne Hackborn72e39832013-01-18 18:36:09 -0800587 private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
588 Ops ops = getOpsLocked(uid, packageName, edit);
589 if (ops == null) {
590 return null;
591 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800592 return getOpLocked(ops, code, edit);
593 }
594
595 private Op getOpLocked(Ops ops, int code, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800596 Op op = ops.get(code);
597 if (op == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800598 if (!edit) {
599 return null;
600 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800601 op = new Op(code);
602 ops.put(code, op);
603 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800604 if (edit) {
605 scheduleWriteLocked();
Dianne Hackborn35654b62013-01-14 17:38:02 -0800606 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800607 return op;
608 }
609
Dianne Hackborn35654b62013-01-14 17:38:02 -0800610 void readState() {
611 synchronized (mFile) {
612 synchronized (this) {
613 FileInputStream stream;
614 try {
615 stream = mFile.openRead();
616 } catch (FileNotFoundException e) {
617 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
618 return;
619 }
620 boolean success = false;
621 try {
622 XmlPullParser parser = Xml.newPullParser();
623 parser.setInput(stream, null);
624 int type;
625 while ((type = parser.next()) != XmlPullParser.START_TAG
626 && type != XmlPullParser.END_DOCUMENT) {
627 ;
628 }
629
630 if (type != XmlPullParser.START_TAG) {
631 throw new IllegalStateException("no start tag found");
632 }
633
634 int outerDepth = parser.getDepth();
635 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
636 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
637 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
638 continue;
639 }
640
641 String tagName = parser.getName();
642 if (tagName.equals("pkg")) {
643 readPackage(parser);
644 } else {
645 Slog.w(TAG, "Unknown element under <app-ops>: "
646 + parser.getName());
647 XmlUtils.skipCurrentTag(parser);
648 }
649 }
650 success = true;
651 } catch (IllegalStateException e) {
652 Slog.w(TAG, "Failed parsing " + e);
653 } catch (NullPointerException e) {
654 Slog.w(TAG, "Failed parsing " + e);
655 } catch (NumberFormatException e) {
656 Slog.w(TAG, "Failed parsing " + e);
657 } catch (XmlPullParserException e) {
658 Slog.w(TAG, "Failed parsing " + e);
659 } catch (IOException e) {
660 Slog.w(TAG, "Failed parsing " + e);
661 } catch (IndexOutOfBoundsException e) {
662 Slog.w(TAG, "Failed parsing " + e);
663 } finally {
664 if (!success) {
665 mUidOps.clear();
666 }
667 try {
668 stream.close();
669 } catch (IOException e) {
670 }
671 }
672 }
673 }
674 }
675
676 void readPackage(XmlPullParser parser) throws NumberFormatException,
677 XmlPullParserException, IOException {
678 String pkgName = parser.getAttributeValue(null, "n");
679 int outerDepth = parser.getDepth();
680 int type;
681 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
682 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
683 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
684 continue;
685 }
686
687 String tagName = parser.getName();
688 if (tagName.equals("uid")) {
689 readUid(parser, pkgName);
690 } else {
691 Slog.w(TAG, "Unknown element under <pkg>: "
692 + parser.getName());
693 XmlUtils.skipCurrentTag(parser);
694 }
695 }
696 }
697
698 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
699 XmlPullParserException, IOException {
700 int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
701 int outerDepth = parser.getDepth();
702 int type;
703 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
704 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
705 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
706 continue;
707 }
708
709 String tagName = parser.getName();
710 if (tagName.equals("op")) {
711 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800712 String mode = parser.getAttributeValue(null, "m");
713 if (mode != null) {
714 op.mode = Integer.parseInt(mode);
715 }
716 String time = parser.getAttributeValue(null, "t");
717 if (time != null) {
718 op.time = Long.parseLong(time);
719 }
720 time = parser.getAttributeValue(null, "r");
721 if (time != null) {
722 op.rejectTime = Long.parseLong(time);
723 }
724 String dur = parser.getAttributeValue(null, "d");
725 if (dur != null) {
726 op.duration = Integer.parseInt(dur);
727 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800728 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
729 if (pkgOps == null) {
730 pkgOps = new HashMap<String, Ops>();
731 mUidOps.put(uid, pkgOps);
732 }
733 Ops ops = pkgOps.get(pkgName);
734 if (ops == null) {
735 ops = new Ops(pkgName, uid);
736 pkgOps.put(pkgName, ops);
737 }
738 ops.put(op.op, op);
739 } else {
740 Slog.w(TAG, "Unknown element under <pkg>: "
741 + parser.getName());
742 XmlUtils.skipCurrentTag(parser);
743 }
744 }
745 }
746
747 void writeState() {
748 synchronized (mFile) {
749 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
750
751 FileOutputStream stream;
752 try {
753 stream = mFile.startWrite();
754 } catch (IOException e) {
755 Slog.w(TAG, "Failed to write state: " + e);
756 return;
757 }
758
759 try {
760 XmlSerializer out = new FastXmlSerializer();
761 out.setOutput(stream, "utf-8");
762 out.startDocument(null, true);
763 out.startTag(null, "app-ops");
764
765 if (allOps != null) {
766 String lastPkg = null;
767 for (int i=0; i<allOps.size(); i++) {
768 AppOpsManager.PackageOps pkg = allOps.get(i);
769 if (!pkg.getPackageName().equals(lastPkg)) {
770 if (lastPkg != null) {
771 out.endTag(null, "pkg");
772 }
773 lastPkg = pkg.getPackageName();
774 out.startTag(null, "pkg");
775 out.attribute(null, "n", lastPkg);
776 }
777 out.startTag(null, "uid");
778 out.attribute(null, "n", Integer.toString(pkg.getUid()));
779 List<AppOpsManager.OpEntry> ops = pkg.getOps();
780 for (int j=0; j<ops.size(); j++) {
781 AppOpsManager.OpEntry op = ops.get(j);
782 out.startTag(null, "op");
783 out.attribute(null, "n", Integer.toString(op.getOp()));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800784 if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
785 out.attribute(null, "m", Integer.toString(op.getMode()));
786 }
787 long time = op.getTime();
788 if (time != 0) {
789 out.attribute(null, "t", Long.toString(time));
790 }
791 time = op.getRejectTime();
792 if (time != 0) {
793 out.attribute(null, "r", Long.toString(time));
794 }
795 int dur = op.getDuration();
796 if (dur != 0) {
797 out.attribute(null, "d", Integer.toString(dur));
798 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800799 out.endTag(null, "op");
800 }
801 out.endTag(null, "uid");
802 }
803 if (lastPkg != null) {
804 out.endTag(null, "pkg");
805 }
806 }
807
808 out.endTag(null, "app-ops");
809 out.endDocument();
810 mFile.finishWrite(stream);
811 } catch (IOException e) {
812 Slog.w(TAG, "Failed to write state, restoring backup.", e);
813 mFile.failWrite(stream);
814 }
815 }
816 }
817
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800818 @Override
819 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
820 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
821 != PackageManager.PERMISSION_GRANTED) {
822 pw.println("Permission Denial: can't dump ApOps service from from pid="
823 + Binder.getCallingPid()
824 + ", uid=" + Binder.getCallingUid());
825 return;
826 }
827
828 synchronized (this) {
829 pw.println("Current AppOps Service state:");
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800830 final long now = System.currentTimeMillis();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800831 for (int i=0; i<mUidOps.size(); i++) {
832 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
833 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
834 for (Ops ops : pkgOps.values()) {
835 pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
836 for (int j=0; j<ops.size(); j++) {
837 Op op = ops.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800838 pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
839 pw.print(": mode="); pw.print(op.mode);
840 if (op.time != 0) {
841 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
842 pw.print(" ago");
843 }
844 if (op.rejectTime != 0) {
845 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
846 pw.print(" ago");
847 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800848 if (op.duration == -1) {
849 pw.println(" (running)");
850 } else {
851 pw.print("; duration=");
852 TimeUtils.formatDuration(op.duration, pw);
853 pw.println();
854 }
855 }
856 }
857 }
858 }
859 }
860}