blob: e94d03ccdc887b62fe562ec05f9ecb7fed7665aa [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
152 public void shutdown() {
153 Slog.w(TAG, "Writing app ops before shutdown...");
Dianne Hackborn35654b62013-01-14 17:38:02 -0800154 boolean doWrite = false;
155 synchronized (this) {
156 if (mWriteScheduled) {
157 mWriteScheduled = false;
158 doWrite = true;
159 }
160 }
161 if (doWrite) {
162 writeState();
163 }
164 }
165
Dianne Hackborn72e39832013-01-18 18:36:09 -0800166 private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
167 ArrayList<AppOpsManager.OpEntry> resOps = null;
168 if (ops == null) {
169 resOps = new ArrayList<AppOpsManager.OpEntry>();
170 for (int j=0; j<pkgOps.size(); j++) {
171 Op curOp = pkgOps.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800172 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
173 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800174 }
175 } else {
176 for (int j=0; j<ops.length; j++) {
177 Op curOp = pkgOps.get(ops[j]);
178 if (curOp != null) {
179 if (resOps == null) {
180 resOps = new ArrayList<AppOpsManager.OpEntry>();
181 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800182 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
183 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800184 }
185 }
186 }
187 return resOps;
188 }
189
Dianne Hackborn35654b62013-01-14 17:38:02 -0800190 @Override
191 public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
192 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
193 Binder.getCallingPid(), Binder.getCallingUid(), null);
194 ArrayList<AppOpsManager.PackageOps> res = null;
195 synchronized (this) {
196 for (int i=0; i<mUidOps.size(); i++) {
197 HashMap<String, Ops> packages = mUidOps.valueAt(i);
198 for (Ops pkgOps : packages.values()) {
Dianne Hackborn72e39832013-01-18 18:36:09 -0800199 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800200 if (resOps != null) {
201 if (res == null) {
202 res = new ArrayList<AppOpsManager.PackageOps>();
203 }
204 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
205 pkgOps.packageName, pkgOps.uid, resOps);
206 res.add(resPackage);
207 }
208 }
209 }
210 }
211 return res;
212 }
213
214 @Override
Dianne Hackborn72e39832013-01-18 18:36:09 -0800215 public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
216 int[] ops) {
217 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
218 Binder.getCallingPid(), Binder.getCallingUid(), null);
219 synchronized (this) {
220 Ops pkgOps = getOpsLocked(uid, packageName, false);
221 if (pkgOps == null) {
222 return null;
223 }
224 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
225 if (resOps == null) {
226 return null;
227 }
228 ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
229 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
230 pkgOps.packageName, pkgOps.uid, resOps);
231 res.add(resPackage);
232 return res;
233 }
234 }
235
236 @Override
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800237 public void setMode(int code, int uid, String packageName, int mode) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800238 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800239 verifyIncomingOp(code);
Dianne Hackbornc2293022013-02-06 23:14:49 -0800240 ArrayList<Callback> repCbs = null;
241 code = AppOpsManager.opToSwitch(code);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800242 synchronized (this) {
Dianne Hackbornc2293022013-02-06 23:14:49 -0800243 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800244 if (op != null) {
245 if (op.mode != mode) {
246 op.mode = mode;
Dianne Hackbornc2293022013-02-06 23:14:49 -0800247 ArrayList<Callback> cbs = mOpModeWatchers.get(code);
248 if (cbs != null) {
249 if (repCbs == null) {
250 repCbs = new ArrayList<Callback>();
251 }
252 repCbs.addAll(cbs);
253 }
254 cbs = mPackageModeWatchers.get(packageName);
255 if (cbs != null) {
256 if (repCbs == null) {
257 repCbs = new ArrayList<Callback>();
258 }
259 repCbs.addAll(cbs);
260 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800261 scheduleWriteNowLocked();
262 }
263 }
264 }
Dianne Hackbornc2293022013-02-06 23:14:49 -0800265 if (repCbs != null) {
266 for (int i=0; i<repCbs.size(); i++) {
267 try {
268 repCbs.get(i).mCallback.opChanged(code, packageName);
269 } catch (RemoteException e) {
270 }
271 }
272 }
273 }
274
275 @Override
276 public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
277 synchronized (this) {
278 op = AppOpsManager.opToSwitch(op);
279 Callback cb = mModeWatchers.get(callback.asBinder());
280 if (cb == null) {
281 cb = new Callback(callback);
282 mModeWatchers.put(callback.asBinder(), cb);
283 }
284 if (op != AppOpsManager.OP_NONE) {
285 ArrayList<Callback> cbs = mOpModeWatchers.get(op);
286 if (cbs == null) {
287 cbs = new ArrayList<Callback>();
288 mOpModeWatchers.put(op, cbs);
289 }
290 cbs.add(cb);
291 }
292 if (packageName != null) {
293 ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
294 if (cbs == null) {
295 cbs = new ArrayList<Callback>();
296 mPackageModeWatchers.put(packageName, cbs);
297 }
298 cbs.add(cb);
299 }
300 }
301 }
302
303 @Override
304 public void stopWatchingMode(IAppOpsCallback callback) {
305 synchronized (this) {
306 Callback cb = mModeWatchers.remove(callback.asBinder());
307 if (cb != null) {
308 cb.unlinkToDeath();
309 for (int i=0; i<mOpModeWatchers.size(); i++) {
310 ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
311 cbs.remove(cb);
312 if (cbs.size() <= 0) {
313 mOpModeWatchers.removeAt(i);
314 }
315 }
316 if (mPackageModeWatchers.size() > 0) {
317 Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
318 while (it.hasNext()) {
319 ArrayList<Callback> cbs = it.next();
320 cbs.remove(cb);
321 if (cbs.size() <= 0) {
322 it.remove();
323 }
324 }
325 }
326 }
327 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800328 }
329
330 @Override
Dianne Hackborn35654b62013-01-14 17:38:02 -0800331 public int checkOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800332 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800333 verifyIncomingOp(code);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800334 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800335 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800336 if (op == null) {
337 return AppOpsManager.MODE_ALLOWED;
338 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800339 return op.mode;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800340 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800341 }
342
343 @Override
344 public int noteOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800345 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800346 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800347 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800348 Ops ops = getOpsLocked(uid, packageName, true);
349 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800350 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
351 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800352 return AppOpsManager.MODE_IGNORED;
353 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800354 Op op = getOpLocked(ops, code, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800355 if (op.duration == -1) {
356 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
357 + " code " + code + " time=" + op.time + " duration=" + op.duration);
358 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800359 op.duration = 0;
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800360 final int switchCode = AppOpsManager.opToSwitch(code);
361 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
362 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
363 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
364 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800365 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800366 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800367 }
368 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
369 + " package " + packageName);
370 op.time = System.currentTimeMillis();
371 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800372 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800373 }
374
375 @Override
376 public int startOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800377 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800378 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800379 synchronized (this) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800380 Ops ops = getOpsLocked(uid, packageName, true);
381 if (ops == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800382 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
383 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800384 return AppOpsManager.MODE_IGNORED;
385 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800386 Op op = getOpLocked(ops, code, true);
387 final int switchCode = AppOpsManager.opToSwitch(code);
388 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
389 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
390 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
391 + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800392 op.rejectTime = System.currentTimeMillis();
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800393 return switchOp.mode;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800394 }
395 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
396 + " package " + packageName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800397 if (op.nesting == 0) {
398 op.time = System.currentTimeMillis();
399 op.duration = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800400 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800401 op.nesting++;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800402 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800403 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800404 }
405
406 @Override
407 public void finishOperation(int code, int uid, String packageName) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800408 verifyIncomingUid(uid);
Dianne Hackborn961321f2013-02-05 17:22:41 -0800409 verifyIncomingOp(code);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800410 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800411 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800412 if (op == null) {
413 return;
414 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800415 if (op.nesting <= 1) {
416 if (op.nesting == 1) {
417 op.duration = (int)(System.currentTimeMillis() - op.time);
418 } else {
419 Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
420 + " code " + code + " time=" + op.time + " duration=" + op.duration
421 + " nesting=" + op.nesting);
422 }
Dianne Hackborne7991752013-01-16 17:56:46 -0800423 op.nesting = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800424 } else {
425 op.nesting--;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800426 }
427 }
428 }
429
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800430 private void verifyIncomingUid(int uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800431 if (uid == Binder.getCallingUid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800432 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800433 }
434 if (Binder.getCallingPid() == Process.myPid()) {
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800435 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800436 }
437 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
438 Binder.getCallingPid(), Binder.getCallingUid(), null);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800439 }
440
Dianne Hackborn961321f2013-02-05 17:22:41 -0800441 private void verifyIncomingOp(int op) {
442 if (op >= 0 && op < AppOpsManager._NUM_OP) {
443 return;
444 }
445 throw new IllegalArgumentException("Bad operation #" + op);
446 }
447
Dianne Hackborn72e39832013-01-18 18:36:09 -0800448 private Ops getOpsLocked(int uid, String packageName, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800449 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
450 if (pkgOps == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800451 if (!edit) {
452 return null;
453 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800454 pkgOps = new HashMap<String, Ops>();
455 mUidOps.put(uid, pkgOps);
456 }
457 Ops ops = pkgOps.get(packageName);
458 if (ops == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800459 if (!edit) {
460 return null;
461 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800462 // This is the first time we have seen this package name under this uid,
463 // so let's make sure it is valid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800464 final long ident = Binder.clearCallingIdentity();
465 try {
466 int pkgUid = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800467 try {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800468 pkgUid = mContext.getPackageManager().getPackageUid(packageName,
469 UserHandle.getUserId(uid));
470 } catch (NameNotFoundException e) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800471 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800472 if (pkgUid != uid) {
473 // Oops! The package name is not valid for the uid they are calling
474 // under. Abort.
475 Slog.w(TAG, "Bad call: specified package " + packageName
476 + " under uid " + uid + " but it is really " + pkgUid);
477 return null;
478 }
479 } finally {
480 Binder.restoreCallingIdentity(ident);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800481 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800482 ops = new Ops(packageName, uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800483 pkgOps.put(packageName, ops);
484 }
Dianne Hackborn72e39832013-01-18 18:36:09 -0800485 return ops;
486 }
487
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800488 private void scheduleWriteLocked() {
489 if (!mWriteScheduled) {
490 mWriteScheduled = true;
491 mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
492 }
493 }
494
495 private void scheduleWriteNowLocked() {
496 if (!mWriteScheduled) {
497 mWriteScheduled = true;
498 }
499 mHandler.removeCallbacks(mWriteRunner);
500 mHandler.post(mWriteRunner);
501 }
502
Dianne Hackborn72e39832013-01-18 18:36:09 -0800503 private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
504 Ops ops = getOpsLocked(uid, packageName, edit);
505 if (ops == null) {
506 return null;
507 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800508 return getOpLocked(ops, code, edit);
509 }
510
511 private Op getOpLocked(Ops ops, int code, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800512 Op op = ops.get(code);
513 if (op == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800514 if (!edit) {
515 return null;
516 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800517 op = new Op(code);
518 ops.put(code, op);
519 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800520 if (edit) {
521 scheduleWriteLocked();
Dianne Hackborn35654b62013-01-14 17:38:02 -0800522 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800523 return op;
524 }
525
Dianne Hackborn35654b62013-01-14 17:38:02 -0800526 void readState() {
527 synchronized (mFile) {
528 synchronized (this) {
529 FileInputStream stream;
530 try {
531 stream = mFile.openRead();
532 } catch (FileNotFoundException e) {
533 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
534 return;
535 }
536 boolean success = false;
537 try {
538 XmlPullParser parser = Xml.newPullParser();
539 parser.setInput(stream, null);
540 int type;
541 while ((type = parser.next()) != XmlPullParser.START_TAG
542 && type != XmlPullParser.END_DOCUMENT) {
543 ;
544 }
545
546 if (type != XmlPullParser.START_TAG) {
547 throw new IllegalStateException("no start tag found");
548 }
549
550 int outerDepth = parser.getDepth();
551 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
552 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
553 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
554 continue;
555 }
556
557 String tagName = parser.getName();
558 if (tagName.equals("pkg")) {
559 readPackage(parser);
560 } else {
561 Slog.w(TAG, "Unknown element under <app-ops>: "
562 + parser.getName());
563 XmlUtils.skipCurrentTag(parser);
564 }
565 }
566 success = true;
567 } catch (IllegalStateException e) {
568 Slog.w(TAG, "Failed parsing " + e);
569 } catch (NullPointerException e) {
570 Slog.w(TAG, "Failed parsing " + e);
571 } catch (NumberFormatException e) {
572 Slog.w(TAG, "Failed parsing " + e);
573 } catch (XmlPullParserException e) {
574 Slog.w(TAG, "Failed parsing " + e);
575 } catch (IOException e) {
576 Slog.w(TAG, "Failed parsing " + e);
577 } catch (IndexOutOfBoundsException e) {
578 Slog.w(TAG, "Failed parsing " + e);
579 } finally {
580 if (!success) {
581 mUidOps.clear();
582 }
583 try {
584 stream.close();
585 } catch (IOException e) {
586 }
587 }
588 }
589 }
590 }
591
592 void readPackage(XmlPullParser parser) throws NumberFormatException,
593 XmlPullParserException, IOException {
594 String pkgName = parser.getAttributeValue(null, "n");
595 int outerDepth = parser.getDepth();
596 int type;
597 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
598 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
599 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
600 continue;
601 }
602
603 String tagName = parser.getName();
604 if (tagName.equals("uid")) {
605 readUid(parser, pkgName);
606 } else {
607 Slog.w(TAG, "Unknown element under <pkg>: "
608 + parser.getName());
609 XmlUtils.skipCurrentTag(parser);
610 }
611 }
612 }
613
614 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
615 XmlPullParserException, IOException {
616 int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
617 int outerDepth = parser.getDepth();
618 int type;
619 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
620 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
621 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
622 continue;
623 }
624
625 String tagName = parser.getName();
626 if (tagName.equals("op")) {
627 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800628 String mode = parser.getAttributeValue(null, "m");
629 if (mode != null) {
630 op.mode = Integer.parseInt(mode);
631 }
632 String time = parser.getAttributeValue(null, "t");
633 if (time != null) {
634 op.time = Long.parseLong(time);
635 }
636 time = parser.getAttributeValue(null, "r");
637 if (time != null) {
638 op.rejectTime = Long.parseLong(time);
639 }
640 String dur = parser.getAttributeValue(null, "d");
641 if (dur != null) {
642 op.duration = Integer.parseInt(dur);
643 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800644 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
645 if (pkgOps == null) {
646 pkgOps = new HashMap<String, Ops>();
647 mUidOps.put(uid, pkgOps);
648 }
649 Ops ops = pkgOps.get(pkgName);
650 if (ops == null) {
651 ops = new Ops(pkgName, uid);
652 pkgOps.put(pkgName, ops);
653 }
654 ops.put(op.op, op);
655 } else {
656 Slog.w(TAG, "Unknown element under <pkg>: "
657 + parser.getName());
658 XmlUtils.skipCurrentTag(parser);
659 }
660 }
661 }
662
663 void writeState() {
664 synchronized (mFile) {
665 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
666
667 FileOutputStream stream;
668 try {
669 stream = mFile.startWrite();
670 } catch (IOException e) {
671 Slog.w(TAG, "Failed to write state: " + e);
672 return;
673 }
674
675 try {
676 XmlSerializer out = new FastXmlSerializer();
677 out.setOutput(stream, "utf-8");
678 out.startDocument(null, true);
679 out.startTag(null, "app-ops");
680
681 if (allOps != null) {
682 String lastPkg = null;
683 for (int i=0; i<allOps.size(); i++) {
684 AppOpsManager.PackageOps pkg = allOps.get(i);
685 if (!pkg.getPackageName().equals(lastPkg)) {
686 if (lastPkg != null) {
687 out.endTag(null, "pkg");
688 }
689 lastPkg = pkg.getPackageName();
690 out.startTag(null, "pkg");
691 out.attribute(null, "n", lastPkg);
692 }
693 out.startTag(null, "uid");
694 out.attribute(null, "n", Integer.toString(pkg.getUid()));
695 List<AppOpsManager.OpEntry> ops = pkg.getOps();
696 for (int j=0; j<ops.size(); j++) {
697 AppOpsManager.OpEntry op = ops.get(j);
698 out.startTag(null, "op");
699 out.attribute(null, "n", Integer.toString(op.getOp()));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800700 if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
701 out.attribute(null, "m", Integer.toString(op.getMode()));
702 }
703 long time = op.getTime();
704 if (time != 0) {
705 out.attribute(null, "t", Long.toString(time));
706 }
707 time = op.getRejectTime();
708 if (time != 0) {
709 out.attribute(null, "r", Long.toString(time));
710 }
711 int dur = op.getDuration();
712 if (dur != 0) {
713 out.attribute(null, "d", Integer.toString(dur));
714 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800715 out.endTag(null, "op");
716 }
717 out.endTag(null, "uid");
718 }
719 if (lastPkg != null) {
720 out.endTag(null, "pkg");
721 }
722 }
723
724 out.endTag(null, "app-ops");
725 out.endDocument();
726 mFile.finishWrite(stream);
727 } catch (IOException e) {
728 Slog.w(TAG, "Failed to write state, restoring backup.", e);
729 mFile.failWrite(stream);
730 }
731 }
732 }
733
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800734 @Override
735 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
736 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
737 != PackageManager.PERMISSION_GRANTED) {
738 pw.println("Permission Denial: can't dump ApOps service from from pid="
739 + Binder.getCallingPid()
740 + ", uid=" + Binder.getCallingUid());
741 return;
742 }
743
744 synchronized (this) {
745 pw.println("Current AppOps Service state:");
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800746 final long now = System.currentTimeMillis();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800747 for (int i=0; i<mUidOps.size(); i++) {
748 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
749 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
750 for (Ops ops : pkgOps.values()) {
751 pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
752 for (int j=0; j<ops.size(); j++) {
753 Op op = ops.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800754 pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
755 pw.print(": mode="); pw.print(op.mode);
756 if (op.time != 0) {
757 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
758 pw.print(" ago");
759 }
760 if (op.rejectTime != 0) {
761 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
762 pw.print(" ago");
763 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800764 if (op.duration == -1) {
765 pw.println(" (running)");
766 } else {
767 pw.print("; duration=");
768 TimeUtils.formatDuration(op.duration, pw);
769 pw.println();
770 }
771 }
772 }
773 }
774 }
775 }
776}