blob: 748b3cbbac1898c1cd34c3dec1b4fb1e99090bb5 [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 Hackborn35654b62013-01-14 17:38:02 -080028import java.util.List;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080029
30import android.app.AppOpsManager;
31import android.content.Context;
32import android.content.pm.PackageManager;
33import android.content.pm.PackageManager.NameNotFoundException;
Dianne Hackborn35654b62013-01-14 17:38:02 -080034import android.os.AsyncTask;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080035import android.os.Binder;
Dianne Hackborn35654b62013-01-14 17:38:02 -080036import android.os.Handler;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080037import android.os.Process;
38import android.os.ServiceManager;
39import android.os.UserHandle;
40import android.util.AtomicFile;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080041import android.util.Log;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080042import android.util.Slog;
43import android.util.SparseArray;
44import android.util.TimeUtils;
Dianne Hackborn35654b62013-01-14 17:38:02 -080045import android.util.Xml;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080046
47import com.android.internal.app.IAppOpsService;
Dianne Hackborn35654b62013-01-14 17:38:02 -080048import com.android.internal.util.FastXmlSerializer;
49import com.android.internal.util.XmlUtils;
50
51import org.xmlpull.v1.XmlPullParser;
52import org.xmlpull.v1.XmlPullParserException;
53import org.xmlpull.v1.XmlSerializer;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080054
55public class AppOpsService extends IAppOpsService.Stub {
56 static final String TAG = "AppOps";
Dianne Hackborn35654b62013-01-14 17:38:02 -080057 static final boolean DEBUG = false;
58
59 // Write at most every 30 minutes.
60 static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080061
62 Context mContext;
63 final AtomicFile mFile;
Dianne Hackborn35654b62013-01-14 17:38:02 -080064 final Handler mHandler;
65
66 boolean mWriteScheduled;
67 final Runnable mWriteRunner = new Runnable() {
68 public void run() {
69 synchronized (AppOpsService.this) {
70 mWriteScheduled = false;
71 AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
72 @Override protected Void doInBackground(Void... params) {
73 writeState();
74 return null;
75 }
76 };
77 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
78 }
79 }
80 };
Dianne Hackborna06de0f2012-12-11 16:34:47 -080081
82 final SparseArray<HashMap<String, Ops>> mUidOps
83 = new SparseArray<HashMap<String, Ops>>();
84
85 final static class Ops extends SparseArray<Op> {
86 public final String packageName;
Dianne Hackborn35654b62013-01-14 17:38:02 -080087 public final int uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080088
Dianne Hackborn35654b62013-01-14 17:38:02 -080089 public Ops(String _packageName, int _uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -080090 packageName = _packageName;
Dianne Hackborn35654b62013-01-14 17:38:02 -080091 uid = _uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080092 }
93 }
94
95 final static class Op {
96 public final int op;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080097 public int mode;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080098 public int duration;
99 public long time;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800100 public long rejectTime;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800101 public int nesting;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800102
103 public Op(int _op) {
104 op = _op;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800105 mode = AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800106 }
107 }
108
Dianne Hackborn35654b62013-01-14 17:38:02 -0800109 public AppOpsService(File storagePath) {
110 mFile = new AtomicFile(storagePath);
111 mHandler = new Handler();
112 readState();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800113 }
114
115 public void publish(Context context) {
116 mContext = context;
117 ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
118 }
119
120 public void shutdown() {
121 Slog.w(TAG, "Writing app ops before shutdown...");
Dianne Hackborn35654b62013-01-14 17:38:02 -0800122 boolean doWrite = false;
123 synchronized (this) {
124 if (mWriteScheduled) {
125 mWriteScheduled = false;
126 doWrite = true;
127 }
128 }
129 if (doWrite) {
130 writeState();
131 }
132 }
133
Dianne Hackborn72e39832013-01-18 18:36:09 -0800134 private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
135 ArrayList<AppOpsManager.OpEntry> resOps = null;
136 if (ops == null) {
137 resOps = new ArrayList<AppOpsManager.OpEntry>();
138 for (int j=0; j<pkgOps.size(); j++) {
139 Op curOp = pkgOps.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800140 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
141 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800142 }
143 } else {
144 for (int j=0; j<ops.length; j++) {
145 Op curOp = pkgOps.get(ops[j]);
146 if (curOp != null) {
147 if (resOps == null) {
148 resOps = new ArrayList<AppOpsManager.OpEntry>();
149 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800150 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
151 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800152 }
153 }
154 }
155 return resOps;
156 }
157
Dianne Hackborn35654b62013-01-14 17:38:02 -0800158 @Override
159 public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
160 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
161 Binder.getCallingPid(), Binder.getCallingUid(), null);
162 ArrayList<AppOpsManager.PackageOps> res = null;
163 synchronized (this) {
164 for (int i=0; i<mUidOps.size(); i++) {
165 HashMap<String, Ops> packages = mUidOps.valueAt(i);
166 for (Ops pkgOps : packages.values()) {
Dianne Hackborn72e39832013-01-18 18:36:09 -0800167 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800168 if (resOps != null) {
169 if (res == null) {
170 res = new ArrayList<AppOpsManager.PackageOps>();
171 }
172 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
173 pkgOps.packageName, pkgOps.uid, resOps);
174 res.add(resPackage);
175 }
176 }
177 }
178 }
179 return res;
180 }
181
182 @Override
Dianne Hackborn72e39832013-01-18 18:36:09 -0800183 public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
184 int[] ops) {
185 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
186 Binder.getCallingPid(), Binder.getCallingUid(), null);
187 synchronized (this) {
188 Ops pkgOps = getOpsLocked(uid, packageName, false);
189 if (pkgOps == null) {
190 return null;
191 }
192 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
193 if (resOps == null) {
194 return null;
195 }
196 ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
197 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
198 pkgOps.packageName, pkgOps.uid, resOps);
199 res.add(resPackage);
200 return res;
201 }
202 }
203
204 @Override
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800205 public void setMode(int code, int uid, String packageName, int mode) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800206 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800207 verifyIncomingOp(code);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800208 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800209 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, true);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800210 if (op != null) {
211 if (op.mode != mode) {
212 op.mode = mode;
213 scheduleWriteNowLocked();
214 }
215 }
216 }
217 }
218
219 @Override
Dianne Hackborn35654b62013-01-14 17:38:02 -0800220 public int checkOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800221 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800222 verifyIncomingOp(code);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800223 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800224 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800225 if (op == null) {
226 return AppOpsManager.MODE_ALLOWED;
227 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800228 return op.mode;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800229 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800230 }
231
232 @Override
233 public int noteOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800234 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800235 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800236 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800237 Ops ops = getOpsLocked(uid, packageName, true);
238 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800239 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
240 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800241 return AppOpsManager.MODE_IGNORED;
242 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800243 Op op = getOpLocked(ops, code, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800244 if (op.duration == -1) {
245 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
246 + " code " + code + " time=" + op.time + " duration=" + op.duration);
247 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800248 op.duration = 0;
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800249 final int switchCode = AppOpsManager.opToSwitch(code);
250 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
251 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
252 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
253 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800254 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800255 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800256 }
257 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
258 + " package " + packageName);
259 op.time = System.currentTimeMillis();
260 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800261 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800262 }
263
264 @Override
265 public int startOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800266 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800267 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800268 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800269 Ops ops = getOpsLocked(uid, packageName, true);
270 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800271 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
272 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800273 return AppOpsManager.MODE_IGNORED;
274 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800275 Op op = getOpLocked(ops, code, true);
276 final int switchCode = AppOpsManager.opToSwitch(code);
277 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
278 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
279 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
280 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800281 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800282 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800283 }
284 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
285 + " package " + packageName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800286 if (op.nesting == 0) {
287 op.time = System.currentTimeMillis();
288 op.duration = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800289 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800290 op.nesting++;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800291 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800292 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800293 }
294
295 @Override
296 public void finishOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800297 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800298 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800299 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800300 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800301 if (op == null) {
302 return;
303 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800304 if (op.nesting <= 1) {
305 if (op.nesting == 1) {
306 op.duration = (int)(System.currentTimeMillis() - op.time);
307 } else {
308 Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
309 + " code " + code + " time=" + op.time + " duration=" + op.duration
310 + " nesting=" + op.nesting);
311 }
Dianne Hackborne7991752013-01-16 17:56:46 -0800312 op.nesting = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800313 } else {
314 op.nesting--;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800315 }
316 }
317 }
318
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800319 private void verifyIncomingUid(int uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800320 if (uid == Binder.getCallingUid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800321 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800322 }
323 if (Binder.getCallingPid() == Process.myPid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800324 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800325 }
326 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
327 Binder.getCallingPid(), Binder.getCallingUid(), null);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800328 }
329
Dianne Hackborn961321f2013-02-05 17:22:41 -0800330 private void verifyIncomingOp(int op) {
331 if (op >= 0 && op < AppOpsManager._NUM_OP) {
332 return;
333 }
334 throw new IllegalArgumentException("Bad operation #" + op);
335 }
336
Dianne Hackborn72e39832013-01-18 18:36:09 -0800337 private Ops getOpsLocked(int uid, String packageName, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800338 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
339 if (pkgOps == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800340 if (!edit) {
341 return null;
342 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800343 pkgOps = new HashMap<String, Ops>();
344 mUidOps.put(uid, pkgOps);
345 }
346 Ops ops = pkgOps.get(packageName);
347 if (ops == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800348 if (!edit) {
349 return null;
350 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800351 // This is the first time we have seen this package name under this uid,
352 // so let's make sure it is valid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800353 final long ident = Binder.clearCallingIdentity();
354 try {
355 int pkgUid = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800356 try {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800357 pkgUid = mContext.getPackageManager().getPackageUid(packageName,
358 UserHandle.getUserId(uid));
359 } catch (NameNotFoundException e) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800360 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800361 if (pkgUid != uid) {
362 // Oops! The package name is not valid for the uid they are calling
363 // under. Abort.
364 Slog.w(TAG, "Bad call: specified package " + packageName
365 + " under uid " + uid + " but it is really " + pkgUid);
366 return null;
367 }
368 } finally {
369 Binder.restoreCallingIdentity(ident);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800370 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800371 ops = new Ops(packageName, uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800372 pkgOps.put(packageName, ops);
373 }
Dianne Hackborn72e39832013-01-18 18:36:09 -0800374 return ops;
375 }
376
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800377 private void scheduleWriteLocked() {
378 if (!mWriteScheduled) {
379 mWriteScheduled = true;
380 mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
381 }
382 }
383
384 private void scheduleWriteNowLocked() {
385 if (!mWriteScheduled) {
386 mWriteScheduled = true;
387 }
388 mHandler.removeCallbacks(mWriteRunner);
389 mHandler.post(mWriteRunner);
390 }
391
Dianne Hackborn72e39832013-01-18 18:36:09 -0800392 private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
393 Ops ops = getOpsLocked(uid, packageName, edit);
394 if (ops == null) {
395 return null;
396 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800397 return getOpLocked(ops, code, edit);
398 }
399
400 private Op getOpLocked(Ops ops, int code, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800401 Op op = ops.get(code);
402 if (op == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800403 if (!edit) {
404 return null;
405 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800406 op = new Op(code);
407 ops.put(code, op);
408 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800409 if (edit) {
410 scheduleWriteLocked();
Dianne Hackborn35654b62013-01-14 17:38:02 -0800411 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800412 return op;
413 }
414
Dianne Hackborn35654b62013-01-14 17:38:02 -0800415 void readState() {
416 synchronized (mFile) {
417 synchronized (this) {
418 FileInputStream stream;
419 try {
420 stream = mFile.openRead();
421 } catch (FileNotFoundException e) {
422 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
423 return;
424 }
425 boolean success = false;
426 try {
427 XmlPullParser parser = Xml.newPullParser();
428 parser.setInput(stream, null);
429 int type;
430 while ((type = parser.next()) != XmlPullParser.START_TAG
431 && type != XmlPullParser.END_DOCUMENT) {
432 ;
433 }
434
435 if (type != XmlPullParser.START_TAG) {
436 throw new IllegalStateException("no start tag found");
437 }
438
439 int outerDepth = parser.getDepth();
440 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
441 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
442 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
443 continue;
444 }
445
446 String tagName = parser.getName();
447 if (tagName.equals("pkg")) {
448 readPackage(parser);
449 } else {
450 Slog.w(TAG, "Unknown element under <app-ops>: "
451 + parser.getName());
452 XmlUtils.skipCurrentTag(parser);
453 }
454 }
455 success = true;
456 } catch (IllegalStateException e) {
457 Slog.w(TAG, "Failed parsing " + e);
458 } catch (NullPointerException e) {
459 Slog.w(TAG, "Failed parsing " + e);
460 } catch (NumberFormatException e) {
461 Slog.w(TAG, "Failed parsing " + e);
462 } catch (XmlPullParserException e) {
463 Slog.w(TAG, "Failed parsing " + e);
464 } catch (IOException e) {
465 Slog.w(TAG, "Failed parsing " + e);
466 } catch (IndexOutOfBoundsException e) {
467 Slog.w(TAG, "Failed parsing " + e);
468 } finally {
469 if (!success) {
470 mUidOps.clear();
471 }
472 try {
473 stream.close();
474 } catch (IOException e) {
475 }
476 }
477 }
478 }
479 }
480
481 void readPackage(XmlPullParser parser) throws NumberFormatException,
482 XmlPullParserException, IOException {
483 String pkgName = parser.getAttributeValue(null, "n");
484 int outerDepth = parser.getDepth();
485 int type;
486 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
487 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
488 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
489 continue;
490 }
491
492 String tagName = parser.getName();
493 if (tagName.equals("uid")) {
494 readUid(parser, pkgName);
495 } else {
496 Slog.w(TAG, "Unknown element under <pkg>: "
497 + parser.getName());
498 XmlUtils.skipCurrentTag(parser);
499 }
500 }
501 }
502
503 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
504 XmlPullParserException, IOException {
505 int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
506 int outerDepth = parser.getDepth();
507 int type;
508 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
509 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
510 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
511 continue;
512 }
513
514 String tagName = parser.getName();
515 if (tagName.equals("op")) {
516 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800517 String mode = parser.getAttributeValue(null, "m");
518 if (mode != null) {
519 op.mode = Integer.parseInt(mode);
520 }
521 String time = parser.getAttributeValue(null, "t");
522 if (time != null) {
523 op.time = Long.parseLong(time);
524 }
525 time = parser.getAttributeValue(null, "r");
526 if (time != null) {
527 op.rejectTime = Long.parseLong(time);
528 }
529 String dur = parser.getAttributeValue(null, "d");
530 if (dur != null) {
531 op.duration = Integer.parseInt(dur);
532 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800533 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
534 if (pkgOps == null) {
535 pkgOps = new HashMap<String, Ops>();
536 mUidOps.put(uid, pkgOps);
537 }
538 Ops ops = pkgOps.get(pkgName);
539 if (ops == null) {
540 ops = new Ops(pkgName, uid);
541 pkgOps.put(pkgName, ops);
542 }
543 ops.put(op.op, op);
544 } else {
545 Slog.w(TAG, "Unknown element under <pkg>: "
546 + parser.getName());
547 XmlUtils.skipCurrentTag(parser);
548 }
549 }
550 }
551
552 void writeState() {
553 synchronized (mFile) {
554 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
555
556 FileOutputStream stream;
557 try {
558 stream = mFile.startWrite();
559 } catch (IOException e) {
560 Slog.w(TAG, "Failed to write state: " + e);
561 return;
562 }
563
564 try {
565 XmlSerializer out = new FastXmlSerializer();
566 out.setOutput(stream, "utf-8");
567 out.startDocument(null, true);
568 out.startTag(null, "app-ops");
569
570 if (allOps != null) {
571 String lastPkg = null;
572 for (int i=0; i<allOps.size(); i++) {
573 AppOpsManager.PackageOps pkg = allOps.get(i);
574 if (!pkg.getPackageName().equals(lastPkg)) {
575 if (lastPkg != null) {
576 out.endTag(null, "pkg");
577 }
578 lastPkg = pkg.getPackageName();
579 out.startTag(null, "pkg");
580 out.attribute(null, "n", lastPkg);
581 }
582 out.startTag(null, "uid");
583 out.attribute(null, "n", Integer.toString(pkg.getUid()));
584 List<AppOpsManager.OpEntry> ops = pkg.getOps();
585 for (int j=0; j<ops.size(); j++) {
586 AppOpsManager.OpEntry op = ops.get(j);
587 out.startTag(null, "op");
588 out.attribute(null, "n", Integer.toString(op.getOp()));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800589 if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
590 out.attribute(null, "m", Integer.toString(op.getMode()));
591 }
592 long time = op.getTime();
593 if (time != 0) {
594 out.attribute(null, "t", Long.toString(time));
595 }
596 time = op.getRejectTime();
597 if (time != 0) {
598 out.attribute(null, "r", Long.toString(time));
599 }
600 int dur = op.getDuration();
601 if (dur != 0) {
602 out.attribute(null, "d", Integer.toString(dur));
603 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800604 out.endTag(null, "op");
605 }
606 out.endTag(null, "uid");
607 }
608 if (lastPkg != null) {
609 out.endTag(null, "pkg");
610 }
611 }
612
613 out.endTag(null, "app-ops");
614 out.endDocument();
615 mFile.finishWrite(stream);
616 } catch (IOException e) {
617 Slog.w(TAG, "Failed to write state, restoring backup.", e);
618 mFile.failWrite(stream);
619 }
620 }
621 }
622
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800623 @Override
624 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
625 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
626 != PackageManager.PERMISSION_GRANTED) {
627 pw.println("Permission Denial: can't dump ApOps service from from pid="
628 + Binder.getCallingPid()
629 + ", uid=" + Binder.getCallingUid());
630 return;
631 }
632
633 synchronized (this) {
634 pw.println("Current AppOps Service state:");
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800635 final long now = System.currentTimeMillis();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800636 for (int i=0; i<mUidOps.size(); i++) {
637 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
638 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
639 for (Ops ops : pkgOps.values()) {
640 pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
641 for (int j=0; j<ops.size(); j++) {
642 Op op = ops.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800643 pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
644 pw.print(": mode="); pw.print(op.mode);
645 if (op.time != 0) {
646 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
647 pw.print(" ago");
648 }
649 if (op.rejectTime != 0) {
650 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
651 pw.print(" ago");
652 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800653 if (op.duration == -1) {
654 pw.println(" (running)");
655 } else {
656 pw.print("; duration=");
657 TimeUtils.formatDuration(op.duration, pw);
658 pw.println();
659 }
660 }
661 }
662 }
663 }
664 }
665}