blob: 335917e20f007963ea3c148c9507206524a636e4 [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 Hackborn5e45ee62013-01-24 19:13:44 -0800207 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800208 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, true);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800209 if (op != null) {
210 if (op.mode != mode) {
211 op.mode = mode;
212 scheduleWriteNowLocked();
213 }
214 }
215 }
216 }
217
218 @Override
Dianne Hackborn35654b62013-01-14 17:38:02 -0800219 public int checkOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800220 verifyIncomingUid(uid);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800221 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800222 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800223 if (op == null) {
224 return AppOpsManager.MODE_ALLOWED;
225 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800226 return op.mode;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800227 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800228 }
229
230 @Override
231 public int noteOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800232 verifyIncomingUid(uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800233 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800234 Ops ops = getOpsLocked(uid, packageName, true);
235 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800236 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
237 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800238 return AppOpsManager.MODE_IGNORED;
239 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800240 Op op = getOpLocked(ops, code, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800241 if (op.duration == -1) {
242 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
243 + " code " + code + " time=" + op.time + " duration=" + op.duration);
244 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800245 op.duration = 0;
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800246 final int switchCode = AppOpsManager.opToSwitch(code);
247 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
248 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
249 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
250 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800251 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800252 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800253 }
254 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
255 + " package " + packageName);
256 op.time = System.currentTimeMillis();
257 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800258 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800259 }
260
261 @Override
262 public int startOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800263 verifyIncomingUid(uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800264 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800265 Ops ops = getOpsLocked(uid, packageName, true);
266 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800267 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
268 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800269 return AppOpsManager.MODE_IGNORED;
270 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800271 Op op = getOpLocked(ops, code, true);
272 final int switchCode = AppOpsManager.opToSwitch(code);
273 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
274 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
275 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
276 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800277 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800278 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800279 }
280 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
281 + " package " + packageName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800282 if (op.nesting == 0) {
283 op.time = System.currentTimeMillis();
284 op.duration = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800285 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800286 op.nesting++;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800287 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800288 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800289 }
290
291 @Override
292 public void finishOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800293 verifyIncomingUid(uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800294 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800295 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800296 if (op == null) {
297 return;
298 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800299 if (op.nesting <= 1) {
300 if (op.nesting == 1) {
301 op.duration = (int)(System.currentTimeMillis() - op.time);
302 } else {
303 Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
304 + " code " + code + " time=" + op.time + " duration=" + op.duration
305 + " nesting=" + op.nesting);
306 }
Dianne Hackborne7991752013-01-16 17:56:46 -0800307 op.nesting = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800308 } else {
309 op.nesting--;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800310 }
311 }
312 }
313
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800314 private void verifyIncomingUid(int uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800315 if (uid == Binder.getCallingUid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800316 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800317 }
318 if (Binder.getCallingPid() == Process.myPid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800319 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800320 }
321 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
322 Binder.getCallingPid(), Binder.getCallingUid(), null);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800323 }
324
Dianne Hackborn72e39832013-01-18 18:36:09 -0800325 private Ops getOpsLocked(int uid, String packageName, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800326 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
327 if (pkgOps == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800328 if (!edit) {
329 return null;
330 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800331 pkgOps = new HashMap<String, Ops>();
332 mUidOps.put(uid, pkgOps);
333 }
334 Ops ops = pkgOps.get(packageName);
335 if (ops == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800336 if (!edit) {
337 return null;
338 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800339 // This is the first time we have seen this package name under this uid,
340 // so let's make sure it is valid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800341 final long ident = Binder.clearCallingIdentity();
342 try {
343 int pkgUid = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800344 try {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800345 pkgUid = mContext.getPackageManager().getPackageUid(packageName,
346 UserHandle.getUserId(uid));
347 } catch (NameNotFoundException e) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800348 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800349 if (pkgUid != uid) {
350 // Oops! The package name is not valid for the uid they are calling
351 // under. Abort.
352 Slog.w(TAG, "Bad call: specified package " + packageName
353 + " under uid " + uid + " but it is really " + pkgUid);
354 return null;
355 }
356 } finally {
357 Binder.restoreCallingIdentity(ident);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800358 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800359 ops = new Ops(packageName, uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800360 pkgOps.put(packageName, ops);
361 }
Dianne Hackborn72e39832013-01-18 18:36:09 -0800362 return ops;
363 }
364
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800365 private void scheduleWriteLocked() {
366 if (!mWriteScheduled) {
367 mWriteScheduled = true;
368 mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
369 }
370 }
371
372 private void scheduleWriteNowLocked() {
373 if (!mWriteScheduled) {
374 mWriteScheduled = true;
375 }
376 mHandler.removeCallbacks(mWriteRunner);
377 mHandler.post(mWriteRunner);
378 }
379
Dianne Hackborn72e39832013-01-18 18:36:09 -0800380 private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
381 Ops ops = getOpsLocked(uid, packageName, edit);
382 if (ops == null) {
383 return null;
384 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800385 return getOpLocked(ops, code, edit);
386 }
387
388 private Op getOpLocked(Ops ops, int code, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800389 Op op = ops.get(code);
390 if (op == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800391 if (!edit) {
392 return null;
393 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800394 op = new Op(code);
395 ops.put(code, op);
396 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800397 if (edit) {
398 scheduleWriteLocked();
Dianne Hackborn35654b62013-01-14 17:38:02 -0800399 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800400 return op;
401 }
402
Dianne Hackborn35654b62013-01-14 17:38:02 -0800403 void readState() {
404 synchronized (mFile) {
405 synchronized (this) {
406 FileInputStream stream;
407 try {
408 stream = mFile.openRead();
409 } catch (FileNotFoundException e) {
410 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
411 return;
412 }
413 boolean success = false;
414 try {
415 XmlPullParser parser = Xml.newPullParser();
416 parser.setInput(stream, null);
417 int type;
418 while ((type = parser.next()) != XmlPullParser.START_TAG
419 && type != XmlPullParser.END_DOCUMENT) {
420 ;
421 }
422
423 if (type != XmlPullParser.START_TAG) {
424 throw new IllegalStateException("no start tag found");
425 }
426
427 int outerDepth = parser.getDepth();
428 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
429 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
430 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
431 continue;
432 }
433
434 String tagName = parser.getName();
435 if (tagName.equals("pkg")) {
436 readPackage(parser);
437 } else {
438 Slog.w(TAG, "Unknown element under <app-ops>: "
439 + parser.getName());
440 XmlUtils.skipCurrentTag(parser);
441 }
442 }
443 success = true;
444 } catch (IllegalStateException e) {
445 Slog.w(TAG, "Failed parsing " + e);
446 } catch (NullPointerException e) {
447 Slog.w(TAG, "Failed parsing " + e);
448 } catch (NumberFormatException e) {
449 Slog.w(TAG, "Failed parsing " + e);
450 } catch (XmlPullParserException e) {
451 Slog.w(TAG, "Failed parsing " + e);
452 } catch (IOException e) {
453 Slog.w(TAG, "Failed parsing " + e);
454 } catch (IndexOutOfBoundsException e) {
455 Slog.w(TAG, "Failed parsing " + e);
456 } finally {
457 if (!success) {
458 mUidOps.clear();
459 }
460 try {
461 stream.close();
462 } catch (IOException e) {
463 }
464 }
465 }
466 }
467 }
468
469 void readPackage(XmlPullParser parser) throws NumberFormatException,
470 XmlPullParserException, IOException {
471 String pkgName = parser.getAttributeValue(null, "n");
472 int outerDepth = parser.getDepth();
473 int type;
474 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
475 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
476 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
477 continue;
478 }
479
480 String tagName = parser.getName();
481 if (tagName.equals("uid")) {
482 readUid(parser, pkgName);
483 } else {
484 Slog.w(TAG, "Unknown element under <pkg>: "
485 + parser.getName());
486 XmlUtils.skipCurrentTag(parser);
487 }
488 }
489 }
490
491 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
492 XmlPullParserException, IOException {
493 int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
494 int outerDepth = parser.getDepth();
495 int type;
496 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
497 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
498 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
499 continue;
500 }
501
502 String tagName = parser.getName();
503 if (tagName.equals("op")) {
504 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800505 String mode = parser.getAttributeValue(null, "m");
506 if (mode != null) {
507 op.mode = Integer.parseInt(mode);
508 }
509 String time = parser.getAttributeValue(null, "t");
510 if (time != null) {
511 op.time = Long.parseLong(time);
512 }
513 time = parser.getAttributeValue(null, "r");
514 if (time != null) {
515 op.rejectTime = Long.parseLong(time);
516 }
517 String dur = parser.getAttributeValue(null, "d");
518 if (dur != null) {
519 op.duration = Integer.parseInt(dur);
520 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800521 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
522 if (pkgOps == null) {
523 pkgOps = new HashMap<String, Ops>();
524 mUidOps.put(uid, pkgOps);
525 }
526 Ops ops = pkgOps.get(pkgName);
527 if (ops == null) {
528 ops = new Ops(pkgName, uid);
529 pkgOps.put(pkgName, ops);
530 }
531 ops.put(op.op, op);
532 } else {
533 Slog.w(TAG, "Unknown element under <pkg>: "
534 + parser.getName());
535 XmlUtils.skipCurrentTag(parser);
536 }
537 }
538 }
539
540 void writeState() {
541 synchronized (mFile) {
542 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
543
544 FileOutputStream stream;
545 try {
546 stream = mFile.startWrite();
547 } catch (IOException e) {
548 Slog.w(TAG, "Failed to write state: " + e);
549 return;
550 }
551
552 try {
553 XmlSerializer out = new FastXmlSerializer();
554 out.setOutput(stream, "utf-8");
555 out.startDocument(null, true);
556 out.startTag(null, "app-ops");
557
558 if (allOps != null) {
559 String lastPkg = null;
560 for (int i=0; i<allOps.size(); i++) {
561 AppOpsManager.PackageOps pkg = allOps.get(i);
562 if (!pkg.getPackageName().equals(lastPkg)) {
563 if (lastPkg != null) {
564 out.endTag(null, "pkg");
565 }
566 lastPkg = pkg.getPackageName();
567 out.startTag(null, "pkg");
568 out.attribute(null, "n", lastPkg);
569 }
570 out.startTag(null, "uid");
571 out.attribute(null, "n", Integer.toString(pkg.getUid()));
572 List<AppOpsManager.OpEntry> ops = pkg.getOps();
573 for (int j=0; j<ops.size(); j++) {
574 AppOpsManager.OpEntry op = ops.get(j);
575 out.startTag(null, "op");
576 out.attribute(null, "n", Integer.toString(op.getOp()));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800577 if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
578 out.attribute(null, "m", Integer.toString(op.getMode()));
579 }
580 long time = op.getTime();
581 if (time != 0) {
582 out.attribute(null, "t", Long.toString(time));
583 }
584 time = op.getRejectTime();
585 if (time != 0) {
586 out.attribute(null, "r", Long.toString(time));
587 }
588 int dur = op.getDuration();
589 if (dur != 0) {
590 out.attribute(null, "d", Integer.toString(dur));
591 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800592 out.endTag(null, "op");
593 }
594 out.endTag(null, "uid");
595 }
596 if (lastPkg != null) {
597 out.endTag(null, "pkg");
598 }
599 }
600
601 out.endTag(null, "app-ops");
602 out.endDocument();
603 mFile.finishWrite(stream);
604 } catch (IOException e) {
605 Slog.w(TAG, "Failed to write state, restoring backup.", e);
606 mFile.failWrite(stream);
607 }
608 }
609 }
610
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800611 @Override
612 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
613 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
614 != PackageManager.PERMISSION_GRANTED) {
615 pw.println("Permission Denial: can't dump ApOps service from from pid="
616 + Binder.getCallingPid()
617 + ", uid=" + Binder.getCallingUid());
618 return;
619 }
620
621 synchronized (this) {
622 pw.println("Current AppOps Service state:");
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800623 final long now = System.currentTimeMillis();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800624 for (int i=0; i<mUidOps.size(); i++) {
625 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
626 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
627 for (Ops ops : pkgOps.values()) {
628 pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
629 for (int j=0; j<ops.size(); j++) {
630 Op op = ops.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800631 pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
632 pw.print(": mode="); pw.print(op.mode);
633 if (op.time != 0) {
634 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
635 pw.print(" ago");
636 }
637 if (op.rejectTime != 0) {
638 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
639 pw.print(" ago");
640 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800641 if (op.duration == -1) {
642 pw.println(" (running)");
643 } else {
644 pw.print("; duration=");
645 TimeUtils.formatDuration(op.duration, pw);
646 pw.println();
647 }
648 }
649 }
650 }
651 }
652 }
653}