blob: 5ad4be8d3bf6987afee4eb812694fc01b02aa58f [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;
21import java.io.PrintWriter;
22import java.util.HashMap;
23
24import android.app.AppOpsManager;
25import android.content.Context;
26import android.content.pm.PackageManager;
27import android.content.pm.PackageManager.NameNotFoundException;
28import android.os.Binder;
29import android.os.Environment;
30import android.os.Process;
31import android.os.ServiceManager;
32import android.os.UserHandle;
33import android.util.AtomicFile;
34import android.util.Slog;
35import android.util.SparseArray;
36import android.util.TimeUtils;
37
38import com.android.internal.app.IAppOpsService;
39
40public class AppOpsService extends IAppOpsService.Stub {
41 static final String TAG = "AppOps";
42
43 Context mContext;
44 final AtomicFile mFile;
45
46 final SparseArray<HashMap<String, Ops>> mUidOps
47 = new SparseArray<HashMap<String, Ops>>();
48
49 final static class Ops extends SparseArray<Op> {
50 public final String packageName;
51
52 public Ops(String _packageName) {
53 packageName = _packageName;
54 }
55 }
56
57 final static class Op {
58 public final int op;
59 public int duration;
60 public long time;
61
62 public Op(int _op) {
63 op = _op;
64 }
65 }
66
67 public AppOpsService() {
68 mFile = new AtomicFile(new File(Environment.getSecureDataDirectory(), "appops.xml"));
69 }
70
71 public void publish(Context context) {
72 mContext = context;
73 ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
74 }
75
76 public void shutdown() {
77 Slog.w(TAG, "Writing app ops before shutdown...");
78 }
79
80 @Override
81 public int noteOperation(int code, int uid, String packageName) {
82 uid = handleIncomingUid(uid);
83 synchronized (this) {
84 Op op = getOpLocked(code, uid, packageName);
85 if (op == null) {
86 return AppOpsManager.MODE_IGNORED;
87 }
88 if (op.duration == -1) {
89 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
90 + " code " + code + " time=" + op.time + " duration=" + op.duration);
91 }
92 op.time = System.currentTimeMillis();
93 op.duration = 0;
94 }
95 return AppOpsManager.MODE_ALLOWED;
96 }
97
98 @Override
99 public int startOperation(int code, int uid, String packageName) {
100 uid = handleIncomingUid(uid);
101 synchronized (this) {
102 Op op = getOpLocked(code, uid, packageName);
103 if (op == null) {
104 return AppOpsManager.MODE_IGNORED;
105 }
106 if (op.duration == -1) {
107 Slog.w(TAG, "Starting op not finished: uid " + uid + " pkg " + packageName
108 + " code " + code + " time=" + op.time + " duration=" + op.duration);
109 }
110 op.time = System.currentTimeMillis();
111 op.duration = -1;
112 }
113 return AppOpsManager.MODE_ALLOWED;
114 }
115
116 @Override
117 public void finishOperation(int code, int uid, String packageName) {
118 uid = handleIncomingUid(uid);
119 synchronized (this) {
120 Op op = getOpLocked(code, uid, packageName);
121 if (op == null) {
122 return;
123 }
124 if (op.duration != -1) {
125 Slog.w(TAG, "Ignoring finishing op not started: uid " + uid + " pkg " + packageName
126 + " code " + code + " time=" + op.time + " duration=" + op.duration);
127 return;
128 }
129 op.duration = (int)(System.currentTimeMillis() - op.time);
130 }
131 }
132
133 @Override
134 public int noteTimedOperation(int code, int uid, String packageName, int duration) {
135 uid = handleIncomingUid(uid);
136 synchronized (this) {
137 Op op = getOpLocked(code, uid, packageName);
138 if (op == null) {
139 return AppOpsManager.MODE_IGNORED;
140 }
141 if (op.duration == -1) {
142 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
143 + " code " + code + " time=" + op.time + " duration=" + op.duration);
144 }
145 op.time = System.currentTimeMillis();
146 op.duration = duration;
147 }
148 return AppOpsManager.MODE_ALLOWED;
149 }
150
151 @Override
152 public void earlyFinishOperation(int code, int uid, String packageName) {
153 uid = handleIncomingUid(uid);
154 synchronized (this) {
155 Op op = getOpLocked(code, uid, packageName);
156 if (op == null) {
157 return;
158 }
159 if (op.duration != -1) {
160 Slog.w(TAG, "Noting timed op not finished: uid " + uid + " pkg " + packageName
161 + " code " + code + " time=" + op.time + " duration=" + op.duration);
162 }
163 int newDuration = (int)(System.currentTimeMillis() - op.time);
164 if (newDuration < op.duration) {
165 op.duration = newDuration;
166 }
167 }
168 }
169
170 private int handleIncomingUid(int uid) {
171 if (uid == Binder.getCallingUid()) {
172 return uid;
173 }
174 if (Binder.getCallingPid() == Process.myPid()) {
175 return uid;
176 }
177 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
178 Binder.getCallingPid(), Binder.getCallingUid(), null);
179 return uid;
180 }
181
182 private Op getOpLocked(int code, int uid, String packageName) {
183 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
184 if (pkgOps == null) {
185 pkgOps = new HashMap<String, Ops>();
186 mUidOps.put(uid, pkgOps);
187 }
188 Ops ops = pkgOps.get(packageName);
189 if (ops == null) {
190 // This is the first time we have seen this package name under this uid,
191 // so let's make sure it is valid.
192 // XXX for now we always allow null through until we can fix everything
193 // to provide the name.
194 if (packageName != null) {
195 final long ident = Binder.clearCallingIdentity();
196 try {
197 int pkgUid = -1;
198 try {
199 pkgUid = mContext.getPackageManager().getPackageUid(packageName,
200 UserHandle.getUserId(uid));
201 } catch (NameNotFoundException e) {
202 }
203 if (pkgUid != uid) {
204 // Oops! The package name is not valid for the uid they are calling
205 // under. Abort.
206 Slog.w(TAG, "Bad call: specified package " + packageName
207 + " under uid " + uid + " but it is really " + pkgUid);
208 return null;
209 }
210 } finally {
211 Binder.restoreCallingIdentity(ident);
212 }
213 }
214 ops = new Ops(packageName);
215 pkgOps.put(packageName, ops);
216 }
217 Op op = ops.get(code);
218 if (op == null) {
219 op = new Op(code);
220 ops.put(code, op);
221 }
222 return op;
223 }
224
225 @Override
226 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
227 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
228 != PackageManager.PERMISSION_GRANTED) {
229 pw.println("Permission Denial: can't dump ApOps service from from pid="
230 + Binder.getCallingPid()
231 + ", uid=" + Binder.getCallingUid());
232 return;
233 }
234
235 synchronized (this) {
236 pw.println("Current AppOps Service state:");
237 for (int i=0; i<mUidOps.size(); i++) {
238 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
239 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
240 for (Ops ops : pkgOps.values()) {
241 pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
242 for (int j=0; j<ops.size(); j++) {
243 Op op = ops.valueAt(j);
244 pw.print(" "); pw.print(AppOpsManager.opToString(op.op));
245 pw.print(": time=");
246 TimeUtils.formatDuration(System.currentTimeMillis()-op.time, pw);
247 pw.print(" ago");
248 if (op.duration == -1) {
249 pw.println(" (running)");
250 } else {
251 pw.print("; duration=");
252 TimeUtils.formatDuration(op.duration, pw);
253 pw.println();
254 }
255 }
256 }
257 }
258 }
259 }
260}