David Braun f5d831915d Implement new method for handling SMS/MMS on the platform
Multi project change:
The changes in this project add the new (hidden) default sms application
setting to Settings.Secure and updates AppOps to support the concept
of an op defaulting to something other than allowed. OP_WRITE_SMS is set
to default to MODE_IGNORED.

Bug: 10449618
Change-Id: I37619784ac70c27cf9fbcbfcac1b263398bc4e01
2013-09-16 13:43:51 -07:00

1083 lines
41 KiB
Java

/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.Xml;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
public class AppOpsService extends IAppOpsService.Stub {
static final String TAG = "AppOps";
static final boolean DEBUG = false;
// Write at most every 30 minutes.
static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
Context mContext;
final AtomicFile mFile;
final Handler mHandler;
boolean mWriteScheduled;
final Runnable mWriteRunner = new Runnable() {
public void run() {
synchronized (AppOpsService.this) {
mWriteScheduled = false;
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
writeState();
return null;
}
};
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
}
}
};
final SparseArray<HashMap<String, Ops>> mUidOps
= new SparseArray<HashMap<String, Ops>>();
public final static class Ops extends SparseArray<Op> {
public final String packageName;
public final int uid;
public Ops(String _packageName, int _uid) {
packageName = _packageName;
uid = _uid;
}
}
public final static class Op {
public final int uid;
public final String packageName;
public final int op;
public int mode;
public int duration;
public long time;
public long rejectTime;
public int nesting;
public Op(int _uid, String _packageName, int _op) {
uid = _uid;
packageName = _packageName;
op = _op;
mode = AppOpsManager.opToDefaultMode(op);
}
}
final SparseArray<ArrayList<Callback>> mOpModeWatchers
= new SparseArray<ArrayList<Callback>>();
final ArrayMap<String, ArrayList<Callback>> mPackageModeWatchers
= new ArrayMap<String, ArrayList<Callback>>();
final ArrayMap<IBinder, Callback> mModeWatchers
= new ArrayMap<IBinder, Callback>();
public final class Callback implements DeathRecipient {
final IAppOpsCallback mCallback;
public Callback(IAppOpsCallback callback) {
mCallback = callback;
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
}
}
public void unlinkToDeath() {
mCallback.asBinder().unlinkToDeath(this, 0);
}
@Override
public void binderDied() {
stopWatchingMode(mCallback);
}
}
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
public final class ClientState extends Binder implements DeathRecipient {
final IBinder mAppToken;
final int mPid;
final ArrayList<Op> mStartedOps;
public ClientState(IBinder appToken) {
mAppToken = appToken;
mPid = Binder.getCallingPid();
if (appToken instanceof Binder) {
// For local clients, there is no reason to track them.
mStartedOps = null;
} else {
mStartedOps = new ArrayList<Op>();
try {
mAppToken.linkToDeath(this, 0);
} catch (RemoteException e) {
}
}
}
@Override
public String toString() {
return "ClientState{" +
"mAppToken=" + mAppToken +
", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
'}';
}
@Override
public void binderDied() {
synchronized (AppOpsService.this) {
for (int i=mStartedOps.size()-1; i>=0; i--) {
finishOperationLocked(mStartedOps.get(i));
}
mClients.remove(mAppToken);
}
}
}
public AppOpsService(File storagePath) {
mFile = new AtomicFile(storagePath);
mHandler = new Handler();
readState();
}
public void publish(Context context) {
mContext = context;
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
}
public void systemReady() {
synchronized (this) {
boolean changed = false;
for (int i=0; i<mUidOps.size(); i++) {
HashMap<String, Ops> pkgs = mUidOps.valueAt(i);
Iterator<Ops> it = pkgs.values().iterator();
while (it.hasNext()) {
Ops ops = it.next();
int curUid;
try {
curUid = mContext.getPackageManager().getPackageUid(ops.packageName,
UserHandle.getUserId(ops.uid));
} catch (NameNotFoundException e) {
curUid = -1;
}
if (curUid != ops.uid) {
Slog.i(TAG, "Pruning old package " + ops.packageName
+ "/" + ops.uid + ": new uid=" + curUid);
it.remove();
changed = true;
}
}
if (pkgs.size() <= 0) {
mUidOps.removeAt(i);
}
}
if (changed) {
scheduleWriteLocked();
}
}
}
public void packageRemoved(int uid, String packageName) {
synchronized (this) {
HashMap<String, Ops> pkgs = mUidOps.get(uid);
if (pkgs != null) {
if (pkgs.remove(packageName) != null) {
if (pkgs.size() <= 0) {
mUidOps.remove(uid);
}
scheduleWriteLocked();
}
}
}
}
public void uidRemoved(int uid) {
synchronized (this) {
if (mUidOps.indexOfKey(uid) >= 0) {
mUidOps.remove(uid);
scheduleWriteLocked();
}
}
}
public void shutdown() {
Slog.w(TAG, "Writing app ops before shutdown...");
boolean doWrite = false;
synchronized (this) {
if (mWriteScheduled) {
mWriteScheduled = false;
doWrite = true;
}
}
if (doWrite) {
writeState();
}
}
private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
ArrayList<AppOpsManager.OpEntry> resOps = null;
if (ops == null) {
resOps = new ArrayList<AppOpsManager.OpEntry>();
for (int j=0; j<pkgOps.size(); j++) {
Op curOp = pkgOps.valueAt(j);
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
curOp.rejectTime, curOp.duration));
}
} else {
for (int j=0; j<ops.length; j++) {
Op curOp = pkgOps.get(ops[j]);
if (curOp != null) {
if (resOps == null) {
resOps = new ArrayList<AppOpsManager.OpEntry>();
}
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
curOp.rejectTime, curOp.duration));
}
}
}
return resOps;
}
@Override
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
ArrayList<AppOpsManager.PackageOps> res = null;
synchronized (this) {
for (int i=0; i<mUidOps.size(); i++) {
HashMap<String, Ops> packages = mUidOps.valueAt(i);
for (Ops pkgOps : packages.values()) {
ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
if (resOps != null) {
if (res == null) {
res = new ArrayList<AppOpsManager.PackageOps>();
}
AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
pkgOps.packageName, pkgOps.uid, resOps);
res.add(resPackage);
}
}
}
}
return res;
}
@Override
public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
int[] ops) {
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
synchronized (this) {
Ops pkgOps = getOpsLocked(uid, packageName, false);
if (pkgOps == null) {
return null;
}
ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
if (resOps == null) {
return null;
}
ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
pkgOps.packageName, pkgOps.uid, resOps);
res.add(resPackage);
return res;
}
}
private void pruneOp(Op op, int uid, String packageName) {
if (op.time == 0 && op.rejectTime == 0) {
Ops ops = getOpsLocked(uid, packageName, false);
if (ops != null) {
ops.remove(op.op);
if (ops.size() <= 0) {
HashMap<String, Ops> pkgOps = mUidOps.get(uid);
if (pkgOps != null) {
pkgOps.remove(ops.packageName);
if (pkgOps.size() <= 0) {
mUidOps.remove(uid);
}
}
}
}
}
}
@Override
public void setMode(int code, int uid, String packageName, int mode) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
ArrayList<Callback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
synchronized (this) {
Op op = getOpLocked(code, uid, packageName, true);
if (op != null) {
if (op.mode != mode) {
op.mode = mode;
ArrayList<Callback> cbs = mOpModeWatchers.get(code);
if (cbs != null) {
if (repCbs == null) {
repCbs = new ArrayList<Callback>();
}
repCbs.addAll(cbs);
}
cbs = mPackageModeWatchers.get(packageName);
if (cbs != null) {
if (repCbs == null) {
repCbs = new ArrayList<Callback>();
}
repCbs.addAll(cbs);
}
if (mode == AppOpsManager.opToDefaultMode(op.op)) {
// If going into the default mode, prune this op
// if there is nothing else interesting in it.
pruneOp(op, uid, packageName);
}
scheduleWriteNowLocked();
}
}
}
if (repCbs != null) {
for (int i=0; i<repCbs.size(); i++) {
try {
repCbs.get(i).mCallback.opChanged(code, packageName);
} catch (RemoteException e) {
}
}
}
}
private static HashMap<Callback, ArrayList<Pair<String, Integer>>> addCallbacks(
HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks,
String packageName, int op, ArrayList<Callback> cbs) {
if (cbs == null) {
return callbacks;
}
if (callbacks == null) {
callbacks = new HashMap<Callback, ArrayList<Pair<String, Integer>>>();
}
for (int i=0; i<cbs.size(); i++) {
Callback cb = cbs.get(i);
ArrayList<Pair<String, Integer>> reports = callbacks.get(cb);
if (reports == null) {
reports = new ArrayList<Pair<String, Integer>>();
callbacks.put(cb, reports);
}
reports.add(new Pair<String, Integer>(packageName, op));
}
return callbacks;
}
@Override
public void resetAllModes() {
mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
synchronized (this) {
boolean changed = false;
for (int i=mUidOps.size()-1; i>=0; i--) {
HashMap<String, Ops> packages = mUidOps.valueAt(i);
Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Ops> ent = it.next();
String packageName = ent.getKey();
Ops pkgOps = ent.getValue();
for (int j=pkgOps.size()-1; j>=0; j--) {
Op curOp = pkgOps.valueAt(j);
if (curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
changed = true;
callbacks = addCallbacks(callbacks, packageName, curOp.op,
mOpModeWatchers.get(curOp.op));
callbacks = addCallbacks(callbacks, packageName, curOp.op,
mPackageModeWatchers.get(packageName));
if (curOp.time == 0 && curOp.rejectTime == 0) {
pkgOps.removeAt(j);
}
}
}
if (pkgOps.size() == 0) {
it.remove();
}
}
if (packages.size() == 0) {
mUidOps.removeAt(i);
}
}
if (changed) {
scheduleWriteNowLocked();
}
}
if (callbacks != null) {
for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
Callback cb = ent.getKey();
ArrayList<Pair<String, Integer>> reports = ent.getValue();
for (int i=0; i<reports.size(); i++) {
Pair<String, Integer> rep = reports.get(i);
try {
cb.mCallback.opChanged(rep.second, rep.first);
} catch (RemoteException e) {
}
}
}
}
}
@Override
public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
synchronized (this) {
op = AppOpsManager.opToSwitch(op);
Callback cb = mModeWatchers.get(callback.asBinder());
if (cb == null) {
cb = new Callback(callback);
mModeWatchers.put(callback.asBinder(), cb);
}
if (op != AppOpsManager.OP_NONE) {
ArrayList<Callback> cbs = mOpModeWatchers.get(op);
if (cbs == null) {
cbs = new ArrayList<Callback>();
mOpModeWatchers.put(op, cbs);
}
cbs.add(cb);
}
if (packageName != null) {
ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
if (cbs == null) {
cbs = new ArrayList<Callback>();
mPackageModeWatchers.put(packageName, cbs);
}
cbs.add(cb);
}
}
}
@Override
public void stopWatchingMode(IAppOpsCallback callback) {
synchronized (this) {
Callback cb = mModeWatchers.remove(callback.asBinder());
if (cb != null) {
cb.unlinkToDeath();
for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
cbs.remove(cb);
if (cbs.size() <= 0) {
mOpModeWatchers.removeAt(i);
}
}
for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
cbs.remove(cb);
if (cbs.size() <= 0) {
mPackageModeWatchers.removeAt(i);
}
}
}
}
}
@Override
public IBinder getToken(IBinder clientToken) {
synchronized (this) {
ClientState cs = mClients.get(clientToken);
if (cs == null) {
cs = new ClientState(clientToken);
mClients.put(clientToken, cs);
}
return cs;
}
}
@Override
public int checkOperation(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
synchronized (this) {
Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
return op.mode;
}
}
@Override
public int checkPackage(int uid, String packageName) {
synchronized (this) {
if (getOpsLocked(uid, packageName, true) != null) {
return AppOpsManager.MODE_ALLOWED;
} else {
return AppOpsManager.MODE_ERRORED;
}
}
}
@Override
public int noteOperation(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
return AppOpsManager.MODE_ERRORED;
}
Op op = getOpLocked(ops, code, true);
if (op.duration == -1) {
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
+ " code " + code + " time=" + op.time + " duration=" + op.duration);
}
op.duration = 0;
final int switchCode = AppOpsManager.opToSwitch(code);
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package " + packageName);
op.rejectTime = System.currentTimeMillis();
return switchOp.mode;
}
if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+ " package " + packageName);
op.time = System.currentTimeMillis();
op.rejectTime = 0;
return AppOpsManager.MODE_ALLOWED;
}
}
@Override
public int startOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
ClientState client = (ClientState)token;
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
return AppOpsManager.MODE_ERRORED;
}
Op op = getOpLocked(ops, code, true);
final int switchCode = AppOpsManager.opToSwitch(code);
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package " + packageName);
op.rejectTime = System.currentTimeMillis();
return switchOp.mode;
}
if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+ " package " + packageName);
if (op.nesting == 0) {
op.time = System.currentTimeMillis();
op.rejectTime = 0;
op.duration = -1;
}
op.nesting++;
if (client.mStartedOps != null) {
client.mStartedOps.add(op);
}
return AppOpsManager.MODE_ALLOWED;
}
}
@Override
public void finishOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
ClientState client = (ClientState)token;
synchronized (this) {
Op op = getOpLocked(code, uid, packageName, true);
if (op == null) {
return;
}
if (client.mStartedOps != null) {
if (!client.mStartedOps.remove(op)) {
throw new IllegalStateException("Operation not started: uid" + op.uid
+ " pkg=" + op.packageName + " op=" + op.op);
}
}
finishOperationLocked(op);
}
}
void finishOperationLocked(Op op) {
if (op.nesting <= 1) {
if (op.nesting == 1) {
op.duration = (int)(System.currentTimeMillis() - op.time);
op.time += op.duration;
} else {
Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
+ op.packageName + " code " + op.op + " time=" + op.time
+ " duration=" + op.duration + " nesting=" + op.nesting);
}
op.nesting = 0;
} else {
op.nesting--;
}
}
private void verifyIncomingUid(int uid) {
if (uid == Binder.getCallingUid()) {
return;
}
if (Binder.getCallingPid() == Process.myPid()) {
return;
}
mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
private void verifyIncomingOp(int op) {
if (op >= 0 && op < AppOpsManager._NUM_OP) {
return;
}
throw new IllegalArgumentException("Bad operation #" + op);
}
private Ops getOpsLocked(int uid, String packageName, boolean edit) {
HashMap<String, Ops> pkgOps = mUidOps.get(uid);
if (pkgOps == null) {
if (!edit) {
return null;
}
pkgOps = new HashMap<String, Ops>();
mUidOps.put(uid, pkgOps);
}
if (uid == 0) {
packageName = "root";
} else if (uid == Process.SHELL_UID) {
packageName = "com.android.shell";
}
Ops ops = pkgOps.get(packageName);
if (ops == null) {
if (!edit) {
return null;
}
// This is the first time we have seen this package name under this uid,
// so let's make sure it is valid.
if (uid != 0) {
final long ident = Binder.clearCallingIdentity();
try {
int pkgUid = -1;
try {
pkgUid = mContext.getPackageManager().getPackageUid(packageName,
UserHandle.getUserId(uid));
} catch (NameNotFoundException e) {
if ("media".equals(packageName)) {
pkgUid = Process.MEDIA_UID;
}
}
if (pkgUid != uid) {
// Oops! The package name is not valid for the uid they are calling
// under. Abort.
Slog.w(TAG, "Bad call: specified package " + packageName
+ " under uid " + uid + " but it is really " + pkgUid);
return null;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
ops = new Ops(packageName, uid);
pkgOps.put(packageName, ops);
}
return ops;
}
private void scheduleWriteLocked() {
if (!mWriteScheduled) {
mWriteScheduled = true;
mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
}
}
private void scheduleWriteNowLocked() {
if (!mWriteScheduled) {
mWriteScheduled = true;
}
mHandler.removeCallbacks(mWriteRunner);
mHandler.post(mWriteRunner);
}
private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
Ops ops = getOpsLocked(uid, packageName, edit);
if (ops == null) {
return null;
}
return getOpLocked(ops, code, edit);
}
private Op getOpLocked(Ops ops, int code, boolean edit) {
Op op = ops.get(code);
if (op == null) {
if (!edit) {
return null;
}
op = new Op(ops.uid, ops.packageName, code);
ops.put(code, op);
}
if (edit) {
scheduleWriteLocked();
}
return op;
}
void readState() {
synchronized (mFile) {
synchronized (this) {
FileInputStream stream;
try {
stream = mFile.openRead();
} catch (FileNotFoundException e) {
Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
return;
}
boolean success = false;
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(stream, null);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
if (type != XmlPullParser.START_TAG) {
throw new IllegalStateException("no start tag found");
}
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("pkg")) {
readPackage(parser);
} else {
Slog.w(TAG, "Unknown element under <app-ops>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
success = true;
} catch (IllegalStateException e) {
Slog.w(TAG, "Failed parsing " + e);
} catch (NullPointerException e) {
Slog.w(TAG, "Failed parsing " + e);
} catch (NumberFormatException e) {
Slog.w(TAG, "Failed parsing " + e);
} catch (XmlPullParserException e) {
Slog.w(TAG, "Failed parsing " + e);
} catch (IOException e) {
Slog.w(TAG, "Failed parsing " + e);
} catch (IndexOutOfBoundsException e) {
Slog.w(TAG, "Failed parsing " + e);
} finally {
if (!success) {
mUidOps.clear();
}
try {
stream.close();
} catch (IOException e) {
}
}
}
}
}
void readPackage(XmlPullParser parser) throws NumberFormatException,
XmlPullParserException, IOException {
String pkgName = parser.getAttributeValue(null, "n");
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("uid")) {
readUid(parser, pkgName);
} else {
Slog.w(TAG, "Unknown element under <pkg>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
}
void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
XmlPullParserException, IOException {
int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("op")) {
Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
String mode = parser.getAttributeValue(null, "m");
if (mode != null) {
op.mode = Integer.parseInt(mode);
}
String time = parser.getAttributeValue(null, "t");
if (time != null) {
op.time = Long.parseLong(time);
}
time = parser.getAttributeValue(null, "r");
if (time != null) {
op.rejectTime = Long.parseLong(time);
}
String dur = parser.getAttributeValue(null, "d");
if (dur != null) {
op.duration = Integer.parseInt(dur);
}
HashMap<String, Ops> pkgOps = mUidOps.get(uid);
if (pkgOps == null) {
pkgOps = new HashMap<String, Ops>();
mUidOps.put(uid, pkgOps);
}
Ops ops = pkgOps.get(pkgName);
if (ops == null) {
ops = new Ops(pkgName, uid);
pkgOps.put(pkgName, ops);
}
ops.put(op.op, op);
} else {
Slog.w(TAG, "Unknown element under <pkg>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
}
void writeState() {
synchronized (mFile) {
List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
FileOutputStream stream;
try {
stream = mFile.startWrite();
} catch (IOException e) {
Slog.w(TAG, "Failed to write state: " + e);
return;
}
try {
XmlSerializer out = new FastXmlSerializer();
out.setOutput(stream, "utf-8");
out.startDocument(null, true);
out.startTag(null, "app-ops");
if (allOps != null) {
String lastPkg = null;
for (int i=0; i<allOps.size(); i++) {
AppOpsManager.PackageOps pkg = allOps.get(i);
if (!pkg.getPackageName().equals(lastPkg)) {
if (lastPkg != null) {
out.endTag(null, "pkg");
}
lastPkg = pkg.getPackageName();
out.startTag(null, "pkg");
out.attribute(null, "n", lastPkg);
}
out.startTag(null, "uid");
out.attribute(null, "n", Integer.toString(pkg.getUid()));
List<AppOpsManager.OpEntry> ops = pkg.getOps();
for (int j=0; j<ops.size(); j++) {
AppOpsManager.OpEntry op = ops.get(j);
out.startTag(null, "op");
out.attribute(null, "n", Integer.toString(op.getOp()));
if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
out.attribute(null, "m", Integer.toString(op.getMode()));
}
long time = op.getTime();
if (time != 0) {
out.attribute(null, "t", Long.toString(time));
}
time = op.getRejectTime();
if (time != 0) {
out.attribute(null, "r", Long.toString(time));
}
int dur = op.getDuration();
if (dur != 0) {
out.attribute(null, "d", Integer.toString(dur));
}
out.endTag(null, "op");
}
out.endTag(null, "uid");
}
if (lastPkg != null) {
out.endTag(null, "pkg");
}
}
out.endTag(null, "app-ops");
out.endDocument();
mFile.finishWrite(stream);
} catch (IOException e) {
Slog.w(TAG, "Failed to write state, restoring backup.", e);
mFile.failWrite(stream);
}
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump ApOps service from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
}
synchronized (this) {
pw.println("Current AppOps Service state:");
final long now = System.currentTimeMillis();
boolean needSep = false;
if (mOpModeWatchers.size() > 0) {
needSep = true;
pw.println(" Op mode watchers:");
for (int i=0; i<mOpModeWatchers.size(); i++) {
pw.print(" Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
pw.println(":");
ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i);
for (int j=0; j<callbacks.size(); j++) {
pw.print(" #"); pw.print(j); pw.print(": ");
pw.println(callbacks.get(j));
}
}
}
if (mPackageModeWatchers.size() > 0) {
needSep = true;
pw.println(" Package mode watchers:");
for (int i=0; i<mPackageModeWatchers.size(); i++) {
pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
pw.println(":");
ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i);
for (int j=0; j<callbacks.size(); j++) {
pw.print(" #"); pw.print(j); pw.print(": ");
pw.println(callbacks.get(j));
}
}
}
if (mModeWatchers.size() > 0) {
needSep = true;
pw.println(" All mode watchers:");
for (int i=0; i<mModeWatchers.size(); i++) {
pw.print(" "); pw.print(mModeWatchers.keyAt(i));
pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
}
}
if (mClients.size() > 0) {
needSep = true;
pw.println(" Clients:");
for (int i=0; i<mClients.size(); i++) {
pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":");
ClientState cs = mClients.valueAt(i);
pw.print(" "); pw.println(cs);
if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
pw.println(" Started ops:");
for (int j=0; j<cs.mStartedOps.size(); j++) {
Op op = cs.mStartedOps.get(j);
pw.print(" "); pw.print("uid="); pw.print(op.uid);
pw.print(" pkg="); pw.print(op.packageName);
pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
}
}
}
}
if (needSep) {
pw.println();
}
for (int i=0; i<mUidOps.size(); i++) {
pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
for (Ops ops : pkgOps.values()) {
pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
for (int j=0; j<ops.size(); j++) {
Op op = ops.valueAt(j);
pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
pw.print(": mode="); pw.print(op.mode);
if (op.time != 0) {
pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
pw.print(" ago");
}
if (op.rejectTime != 0) {
pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
pw.print(" ago");
}
if (op.duration == -1) {
pw.println(" (running)");
} else {
pw.print("; duration=");
TimeUtils.formatDuration(op.duration, pw);
pw.println();
}
}
}
}
}
}
}