Merge "New "app ops" service."
diff --git a/Android.mk b/Android.mk
index 294a2fe..462d6ac 100644
--- a/Android.mk
+++ b/Android.mk
@@ -166,6 +166,7 @@
core/java/android/speech/IRecognitionService.aidl \
core/java/android/speech/tts/ITextToSpeechCallback.aidl \
core/java/android/speech/tts/ITextToSpeechService.aidl \
+ core/java/com/android/internal/app/IAppOpsService.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/IUsageStats.aidl \
core/java/com/android/internal/app/IMediaContainerService.aidl \
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
new file mode 100644
index 0000000..7210df4
--- /dev/null
+++ b/core/java/android/app/AppOpsManager.java
@@ -0,0 +1,110 @@
+/*
+ * 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 android.app;
+
+import com.android.internal.app.IAppOpsService;
+
+import android.content.Context;
+import android.os.Process;
+import android.os.RemoteException;
+
+/** @hide */
+public class AppOpsManager {
+ final Context mContext;
+ final IAppOpsService mService;
+
+ public static final int MODE_ALLOWED = 0;
+ public static final int MODE_IGNORED = 1;
+ public static final int MODE_ERRORED = 2;
+
+ public static final int OP_LOCATION = 0;
+ public static final int OP_GPS = 1;
+ public static final int OP_VIBRATE = 2;
+
+ public static String opToString(int op) {
+ switch (op) {
+ case OP_LOCATION: return "LOCATION";
+ case OP_GPS: return "GPS";
+ case OP_VIBRATE: return "VIBRATE";
+ default: return "Unknown(" + op + ")";
+ }
+ }
+
+ public AppOpsManager(Context context, IAppOpsService service) {
+ mContext = context;
+ mService = service;
+ }
+
+ public int noteOp(int op, int uid, String packageName) {
+ try {
+ int mode = mService.noteOperation(op, uid, packageName);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException("Operation not allowed");
+ }
+ return mode;
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ public int noteOpNoThrow(int op, int uid, String packageName) {
+ try {
+ return mService.noteOperation(op, uid, packageName);
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ public int noteOp(int op) {
+ return noteOp(op, Process.myUid(), mContext.getPackageName());
+ }
+
+ public int startOp(int op, int uid, String packageName) {
+ try {
+ int mode = mService.startOperation(op, uid, packageName);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException("Operation not allowed");
+ }
+ return mode;
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ public int startOpNoThrow(int op, int uid, String packageName) {
+ try {
+ return mService.startOperation(op, uid, packageName);
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ public int startOp(int op) {
+ return startOp(op, Process.myUid(), mContext.getPackageName());
+ }
+
+ public void finishOp(int op, int uid, String packageName) {
+ try {
+ mService.finishOperation(op, uid, packageName);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void finishOp(int op) {
+ finishOp(op, Process.myUid(), mContext.getPackageName());
+ }
+}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7431765..03d1a3f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -142,6 +142,21 @@
}
@Override
+ public int getPackageUid(String packageName, int userHandle)
+ throws NameNotFoundException {
+ try {
+ int uid = mPM.getPackageUid(packageName, userHandle);
+ if (uid >= 0) {
+ return uid;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(packageName);
+ }
+
+ @Override
public PermissionInfo getPermissionInfo(String name, int flags)
throws NameNotFoundException {
try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f895ccc..8ef708c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -47,11 +47,9 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.hardware.ISerialManager;
-import android.hardware.SensorManager;
import android.hardware.SerialManager;
import android.hardware.SystemSensorManager;
import android.hardware.display.DisplayManager;
-import android.hardware.input.IInputManager;
import android.hardware.input.InputManager;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbManager;
@@ -109,6 +107,8 @@
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.app.admin.DevicePolicyManager;
+
+import com.android.internal.app.IAppOpsService;
import com.android.internal.os.IDropBoxManagerService;
import java.io.File;
@@ -499,7 +499,7 @@
registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
- return new SystemVibrator();
+ return new SystemVibrator(ctx);
}});
registerService(WALLPAPER_SERVICE, WALLPAPER_FETCHER);
@@ -530,11 +530,18 @@
}});
registerService(USER_SERVICE, new ServiceFetcher() {
- public Object getService(ContextImpl ctx) {
+ public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(USER_SERVICE);
IUserManager service = IUserManager.Stub.asInterface(b);
return new UserManager(ctx, service);
}});
+
+ registerService(APP_OPS_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(APP_OPS_SERVICE);
+ IAppOpsService service = IAppOpsService.Stub.asInterface(b);
+ return new AppOpsManager(ctx, service);
+ }});
}
static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 257f84e..c777250 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2289,6 +2289,18 @@
public static final String USER_SERVICE = "user";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.app.AppOpsManager} for tracking application operations
+ * on the device.
+ *
+ * @see #getSystemService
+ * @see android.app.AppOpsManager
+ *
+ * @hide
+ */
+ public static final String APP_OPS_SERVICE = "appops";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8ba1988..cdd9195 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1280,6 +1280,22 @@
throws NameNotFoundException;
/**
+ * @hide Return the uid associated with the given package name for the
+ * given user.
+ *
+ * <p>Throws {@link NameNotFoundException} if a package with the given
+ * name can not be found on the system.
+ *
+ * @param packageName The full name (i.e. com.google.apps.contacts) of the
+ * desired package.
+ * @param userHandle The user handle identifier to look up the package under.
+ *
+ * @return Returns an integer uid who owns the given package name.
+ */
+ public abstract int getPackageUid(String packageName, int userHandle)
+ throws NameNotFoundException;
+
+ /**
* Retrieve all of the information we know about a particular permission.
*
* <p>Throws {@link NameNotFoundException} if a permission with the given
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9821824..abbb6a1 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -93,6 +93,11 @@
public static final int VIDEO_TURNED_ON = 8;
/**
+ * A constant indicating a vibrator on timer
+ */
+ public static final int VIBRATOR_ON = 9;
+
+ /**
* Include all of the data in the stats, including previously saved data.
*/
public static final int STATS_SINCE_CHARGED = 0;
@@ -131,6 +136,7 @@
private static final String APK_DATA = "apk";
private static final String PROCESS_DATA = "pr";
private static final String SENSOR_DATA = "sr";
+ private static final String VIBRATOR_DATA = "vib";
private static final String WAKELOCK_DATA = "wl";
private static final String KERNEL_WAKELOCK_DATA = "kwl";
private static final String NETWORK_DATA = "nt";
@@ -277,6 +283,7 @@
int which);
public abstract long getAudioTurnedOnTime(long batteryRealtime, int which);
public abstract long getVideoTurnedOnTime(long batteryRealtime, int which);
+ public abstract Timer getVibratorOnTimer();
/**
* Note that these must match the constants in android.os.PowerManager.
@@ -1395,6 +1402,16 @@
}
}
+ Timer vibTimer = u.getVibratorOnTimer();
+ if (vibTimer != null) {
+ // Convert from microseconds to milliseconds with rounding
+ long totalTime = (vibTimer.getTotalTimeLocked(batteryRealtime, which) + 500) / 1000;
+ int count = vibTimer.getCountLocked(which);
+ if (totalTime != 0) {
+ dumpLine(pw, uid, category, VIBRATOR_DATA, totalTime, count);
+ }
+ }
+
Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
if (processStats.size() > 0) {
for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
@@ -1919,6 +1936,26 @@
}
}
+ Timer vibTimer = u.getVibratorOnTimer();
+ if (vibTimer != null) {
+ // Convert from microseconds to milliseconds with rounding
+ long totalTime = (vibTimer.getTotalTimeLocked(
+ batteryRealtime, which) + 500) / 1000;
+ int count = vibTimer.getCountLocked(which);
+ //timer.logState();
+ if (totalTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Vibrator: ");
+ formatTimeMs(sb, totalTime);
+ sb.append("realtime (");
+ sb.append(count);
+ sb.append(" times)");
+ pw.println(sb.toString());
+ uidActivity = true;
+ }
+ }
+
Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
if (processStats.size() > 0) {
for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 2c2fe8a..15cedf9 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -20,8 +20,8 @@
interface IVibratorService
{
boolean hasVibrator();
- void vibrate(long milliseconds, IBinder token);
- void vibratePattern(in long[] pattern, int repeat, IBinder token);
+ void vibrate(String packageName, long milliseconds, IBinder token);
+ void vibratePattern(String packageName, in long[] pattern, int repeat, IBinder token);
void cancelVibrate(IBinder token);
}
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 7c5a47e..54ea385 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -16,6 +16,7 @@
package android.os;
+import android.content.Context;
import android.util.Log;
/**
@@ -26,10 +27,18 @@
public class SystemVibrator extends Vibrator {
private static final String TAG = "Vibrator";
+ private final String mPackageName;
private final IVibratorService mService;
private final Binder mToken = new Binder();
public SystemVibrator() {
+ mPackageName = null;
+ mService = IVibratorService.Stub.asInterface(
+ ServiceManager.getService("vibrator"));
+ }
+
+ public SystemVibrator(Context context) {
+ mPackageName = context.getPackageName();
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
}
@@ -54,7 +63,7 @@
return;
}
try {
- mService.vibrate(milliseconds, mToken);
+ mService.vibrate(mPackageName, milliseconds, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
@@ -71,7 +80,7 @@
// anyway
if (repeat < pattern.length) {
try {
- mService.vibratePattern(pattern, repeat, mToken);
+ mService.vibratePattern(mPackageName, pattern, repeat, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index cc96152..d205253 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -16,6 +16,8 @@
package android.os;
+import java.io.PrintWriter;
+
/**
* Representation of a user on the device.
*/
@@ -152,6 +154,50 @@
}
/**
+ * Generate a text representation of the uid, breaking out its individual
+ * components -- user, app, isolated, etc.
+ * @hide
+ */
+ public static void formatUid(StringBuilder sb, int uid) {
+ if (uid < Process.FIRST_APPLICATION_UID) {
+ sb.append(uid);
+ } else {
+ sb.append('u');
+ sb.append(getUserId(uid));
+ final int appId = getAppId(uid);
+ if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
+ sb.append('i');
+ sb.append(appId - Process.FIRST_ISOLATED_UID);
+ } else {
+ sb.append('a');
+ sb.append(appId);
+ }
+ }
+ }
+
+ /**
+ * Generate a text representation of the uid, breaking out its individual
+ * components -- user, app, isolated, etc.
+ * @hide
+ */
+ public static void formatUid(PrintWriter pw, int uid) {
+ if (uid < Process.FIRST_APPLICATION_UID) {
+ pw.print(uid);
+ } else {
+ pw.print('u');
+ pw.print(getUserId(uid));
+ final int appId = getAppId(uid);
+ if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
+ pw.print('i');
+ pw.print(appId - Process.FIRST_ISOLATED_UID);
+ } else {
+ pw.print('a');
+ pw.print(appId);
+ }
+ }
+ }
+
+ /**
* Returns the user id of the current process
* @return user id of the current process
* @hide
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
new file mode 100644
index 0000000..c934587
--- /dev/null
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.internal.app;
+
+interface IAppOpsService {
+ int noteOperation(int code, int uid, String packageName);
+ int startOperation(int code, int uid, String packageName);
+ void finishOperation(int code, int uid, String packageName);
+ int noteTimedOperation(int code, int uid, String packageName, int duration);
+ void earlyFinishOperation(int code, int uid, String packageName);
+}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1a76461..823e19f 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -37,6 +37,8 @@
void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, int type);
void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type);
+ void noteVibratorOn(int uid, long durationMillis);
+ void noteVibratorOff(int uid);
void noteStartGps(int uid);
void noteStopGps(int uid);
void noteScreenOn();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 94e7a06..4d35a6b 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,14 +16,11 @@
package com.android.internal.os;
-import static android.net.NetworkStats.IFACE_ALL;
-import static android.net.NetworkStats.UID_ALL;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
-import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.NetworkStats;
import android.os.BatteryManager;
@@ -49,7 +46,6 @@
import android.util.SparseArray;
import android.util.TimeUtils;
-import com.android.internal.R;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.util.JournaledFile;
import com.google.android.collect.Sets;
@@ -87,7 +83,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 62 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 64 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -356,8 +352,8 @@
}
public static interface Unpluggable {
- void unplug(long batteryUptime, long batteryRealtime);
- void plug(long batteryUptime, long batteryRealtime);
+ void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime);
+ void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime);
}
/**
@@ -392,12 +388,12 @@
out.writeInt(mUnpluggedCount);
}
- public void unplug(long batteryUptime, long batteryRealtime) {
+ public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
mUnpluggedCount = mPluggedCount;
mCount.set(mPluggedCount);
}
- public void plug(long batteryUptime, long batteryRealtime) {
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
mPluggedCount = mCount.get();
}
@@ -587,7 +583,7 @@
out.writeLong(mUnpluggedTime);
}
- public void unplug(long batteryUptime, long batteryRealtime) {
+ public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
if (DEBUG && mType < 0) {
Log.v(TAG, "unplug #" + mType + ": realtime=" + batteryRealtime
+ " old mUnpluggedTime=" + mUnpluggedTime
@@ -602,7 +598,7 @@
}
}
- public void plug(long batteryUptime, long batteryRealtime) {
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
if (DEBUG && mType < 0) {
Log.v(TAG, "plug #" + mType + ": realtime=" + batteryRealtime
+ " old mTotalTime=" + mTotalTime);
@@ -731,7 +727,7 @@
boolean mTrackingReportedValues;
/*
- * A sequnce counter, incremented once for each update of the stats.
+ * A sequence counter, incremented once for each update of the stats.
*/
int mUpdateVersion;
@@ -786,8 +782,8 @@
mCurrentReportedTotalTime = totalTime;
}
- public void unplug(long batteryUptime, long batteryRealtime) {
- super.unplug(batteryUptime, batteryRealtime);
+ public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ super.unplug(elapsedRealtime, batteryUptime, batteryRealtime);
if (mTrackingReportedValues) {
mUnpluggedReportedTotalTime = mCurrentReportedTotalTime;
mUnpluggedReportedCount = mCurrentReportedCount;
@@ -795,8 +791,8 @@
mInDischarge = true;
}
- public void plug(long batteryUptime, long batteryRealtime) {
- super.plug(batteryUptime, batteryRealtime);
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
mInDischarge = false;
}
@@ -849,6 +845,141 @@
}
/**
+ * A timer that increments in batches. It does not run for durations, but just jumps
+ * for a pre-determined amount.
+ */
+ public static final class BatchTimer extends Timer {
+ final Uid mUid;
+
+ /**
+ * The last time at which we updated the timer. This is in elapsed realtime microseconds.
+ */
+ long mLastAddedTime;
+
+ /**
+ * The last duration that we added to the timer. This is in microseconds.
+ */
+ long mLastAddedDuration;
+
+ /**
+ * Whether we are currently in a discharge cycle.
+ */
+ boolean mInDischarge;
+
+ BatchTimer(Uid uid, int type, ArrayList<Unpluggable> unpluggables,
+ boolean inDischarge, Parcel in) {
+ super(type, unpluggables, in);
+ mUid = uid;
+ mLastAddedTime = in.readLong();
+ mLastAddedDuration = in.readLong();
+ mInDischarge = inDischarge;
+ }
+
+ BatchTimer(Uid uid, int type, ArrayList<Unpluggable> unpluggables,
+ boolean inDischarge) {
+ super(type, unpluggables);
+ mUid = uid;
+ mInDischarge = inDischarge;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, long batteryRealtime) {
+ super.writeToParcel(out, batteryRealtime);
+ out.writeLong(mLastAddedTime);
+ out.writeLong(mLastAddedDuration);
+ }
+
+ @Override
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ recomputeLastDuration(SystemClock.elapsedRealtime() * 1000, false);
+ mInDischarge = false;
+ super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
+ }
+
+ @Override
+ public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ recomputeLastDuration(elapsedRealtime, false);
+ mInDischarge = true;
+ // If we are still within the last added duration, then re-added whatever remains.
+ if (mLastAddedTime == elapsedRealtime) {
+ mTotalTime += mLastAddedDuration;
+ }
+ super.unplug(elapsedRealtime, batteryUptime, batteryRealtime);
+ }
+
+ @Override
+ public void logState(Printer pw, String prefix) {
+ super.logState(pw, prefix);
+ pw.println(prefix + "mLastAddedTime=" + mLastAddedTime
+ + " mLastAddedDuration=" + mLastAddedDuration);
+ }
+
+ private long computeOverage(long curTime) {
+ if (mLastAddedTime > 0) {
+ return mLastTime + mLastAddedDuration - curTime;
+ }
+ return 0;
+ }
+
+ private void recomputeLastDuration(long curTime, boolean abort) {
+ final long overage = computeOverage(curTime);
+ if (overage > 0) {
+ // Aborting before the duration ran out -- roll back the remaining
+ // duration. Only do this if currently discharging; otherwise we didn't
+ // actually add the time.
+ if (mInDischarge) {
+ mTotalTime -= overage;
+ }
+ if (abort) {
+ mLastAddedTime = 0;
+ } else {
+ mLastAddedTime = curTime;
+ mLastAddedDuration -= overage;
+ }
+ }
+ }
+
+ public void addDuration(BatteryStatsImpl stats, long durationMillis) {
+ final long now = SystemClock.elapsedRealtime() * 1000;
+ recomputeLastDuration(now, true);
+ mLastAddedTime = now;
+ mLastAddedDuration = durationMillis * 1000;
+ if (mInDischarge) {
+ mTotalTime += mLastAddedDuration;
+ mCount++;
+ }
+ }
+
+ public void abortLastDuration(BatteryStatsImpl stats) {
+ final long now = SystemClock.elapsedRealtime() * 1000;
+ recomputeLastDuration(now, true);
+ }
+
+ @Override
+ protected int computeCurrentCountLocked() {
+ return mCount;
+ }
+
+ @Override
+ protected long computeRunTimeLocked(long curBatteryRealtime) {
+ final long overage = computeOverage(SystemClock.elapsedRealtime() * 1000);
+ if (overage > 0) {
+ return mTotalTime = overage;
+ }
+ return mTotalTime;
+ }
+
+ @Override
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+ final long now = SystemClock.elapsedRealtime() * 1000;
+ recomputeLastDuration(now, true);
+ boolean stillActive = mLastAddedTime == now;
+ super.reset(stats, !stillActive && detachIfReset);
+ return !stillActive;
+ }
+ }
+
+ /**
* State for keeping track of timing information.
*/
public static final class StopwatchTimer extends Timer {
@@ -902,12 +1033,12 @@
out.writeLong(mUpdateTime);
}
- public void plug(long batteryUptime, long batteryRealtime) {
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
if (mNesting > 0) {
if (DEBUG && mType < 0) {
Log.v(TAG, "old mUpdateTime=" + mUpdateTime);
}
- super.plug(batteryUptime, batteryRealtime);
+ super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
mUpdateTime = batteryRealtime;
if (DEBUG && mType < 0) {
Log.v(TAG, "new mUpdateTime=" + mUpdateTime);
@@ -1443,7 +1574,7 @@
mHistoryOverflow = false;
}
- public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
+ public void doUnplugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
NetworkStats.Entry entry = null;
// Track UID data usage
@@ -1462,7 +1593,7 @@
}
for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
- mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime);
+ mUnpluggables.get(i).unplug(elapsedRealtime, batteryUptime, batteryRealtime);
}
// Track both mobile and total overall data
@@ -1483,7 +1614,7 @@
mBluetoothPingCount = 0;
}
- public void doPlugLocked(long batteryUptime, long batteryRealtime) {
+ public void doPlugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
NetworkStats.Entry entry = null;
for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
@@ -1498,7 +1629,7 @@
}
}
for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
- mUnpluggables.get(i).plug(batteryUptime, batteryRealtime);
+ mUnpluggables.get(i).plug(elapsedRealtime, batteryUptime, batteryRealtime);
}
// Track both mobile and total overall data
@@ -2109,6 +2240,14 @@
getUidStatsLocked(uid).noteVideoTurnedOffLocked();
}
+ public void noteVibratorOnLocked(int uid, long durationMillis) {
+ getUidStatsLocked(uid).noteVibratorOnLocked(durationMillis);
+ }
+
+ public void noteVibratorOffLocked(int uid) {
+ getUidStatsLocked(uid).noteVibratorOffLocked();
+ }
+
public void noteWifiRunningLocked(WorkSource ws) {
if (!mGlobalWifiRunning) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
@@ -2402,6 +2541,8 @@
boolean mVideoTurnedOn;
StopwatchTimer mVideoTurnedOnTimer;
+ BatchTimer mVibratorOnTimer;
+
Counter[] mUserActivityCounters;
/**
@@ -2439,10 +2580,6 @@
mWifiScanTimers, mUnpluggables);
mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
mWifiMulticastTimers, mUnpluggables);
- mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
- null, mUnpluggables);
- mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
- null, mUnpluggables);
}
@Override
@@ -2587,15 +2724,19 @@
}
}
+ public StopwatchTimer createAudioTurnedOnTimerLocked() {
+ if (mAudioTurnedOnTimer == null) {
+ mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
+ null, mUnpluggables);
+ }
+ return mAudioTurnedOnTimer;
+ }
+
@Override
public void noteAudioTurnedOnLocked() {
if (!mAudioTurnedOn) {
mAudioTurnedOn = true;
- if (mAudioTurnedOnTimer == null) {
- mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
- null, mUnpluggables);
- }
- mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
+ createAudioTurnedOnTimerLocked().startRunningLocked(BatteryStatsImpl.this);
}
}
@@ -2603,19 +2744,25 @@
public void noteAudioTurnedOffLocked() {
if (mAudioTurnedOn) {
mAudioTurnedOn = false;
- mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+ if (mAudioTurnedOnTimer != null) {
+ mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+ }
}
}
+ public StopwatchTimer createVideoTurnedOnTimerLocked() {
+ if (mVideoTurnedOnTimer == null) {
+ mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
+ null, mUnpluggables);
+ }
+ return mVideoTurnedOnTimer;
+ }
+
@Override
public void noteVideoTurnedOnLocked() {
if (!mVideoTurnedOn) {
mVideoTurnedOn = true;
- if (mVideoTurnedOnTimer == null) {
- mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
- null, mUnpluggables);
- }
- mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
+ createVideoTurnedOnTimerLocked().startRunningLocked(BatteryStatsImpl.this);
}
}
@@ -2623,7 +2770,27 @@
public void noteVideoTurnedOffLocked() {
if (mVideoTurnedOn) {
mVideoTurnedOn = false;
- mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+ if (mVideoTurnedOnTimer != null) {
+ mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+ }
+ }
+ }
+
+ public BatchTimer createVibratorOnTimerLocked() {
+ if (mVibratorOnTimer == null) {
+ mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
+ mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal);
+ }
+ return mVibratorOnTimer;
+ }
+
+ public void noteVibratorOnLocked(long durationMillis) {
+ createVibratorOnTimerLocked().addDuration(BatteryStatsImpl.this, durationMillis);
+ }
+
+ public void noteVibratorOffLocked() {
+ if (mVibratorOnTimer != null) {
+ mVibratorOnTimer.abortLastDuration(BatteryStatsImpl.this);
}
}
@@ -2677,6 +2844,11 @@
}
@Override
+ public Timer getVibratorOnTimer() {
+ return mVibratorOnTimer;
+ }
+
+ @Override
public void noteUserActivityLocked(int type) {
if (mUserActivityCounters == null) {
initUserActivityLocked();
@@ -2747,6 +2919,14 @@
active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mVideoTurnedOn;
}
+ if (mVibratorOnTimer != null) {
+ if (mVibratorOnTimer.reset(BatteryStatsImpl.this, false)) {
+ mVibratorOnTimer.detach();
+ mVibratorOnTimer = null;
+ } else {
+ active = true;
+ }
+ }
mLoadedTcpBytesReceived = mLoadedTcpBytesSent = 0;
mCurrentTcpBytesReceived = mCurrentTcpBytesSent = 0;
@@ -2832,9 +3012,11 @@
}
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.detach();
+ mAudioTurnedOnTimer = null;
}
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.detach();
+ mVideoTurnedOnTimer = null;
}
if (mUserActivityCounters != null) {
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
@@ -2917,6 +3099,12 @@
} else {
out.writeInt(0);
}
+ if (mVibratorOnTimer != null) {
+ out.writeInt(1);
+ mVibratorOnTimer.writeToParcel(out, batteryRealtime);
+ } else {
+ out.writeInt(0);
+ }
if (mUserActivityCounters != null) {
out.writeInt(1);
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
@@ -3016,6 +3204,12 @@
mVideoTurnedOnTimer = null;
}
if (in.readInt() != 0) {
+ mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
+ mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal, in);
+ } else {
+ mVibratorOnTimer = null;
+ }
+ if (in.readInt() != 0) {
mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
mUserActivityCounters[i] = new Counter(mUnpluggables, in);
@@ -3256,14 +3450,14 @@
mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
}
- public void unplug(long batteryUptime, long batteryRealtime) {
+ public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
mUnpluggedUserTime = mUserTime;
mUnpluggedSystemTime = mSystemTime;
mUnpluggedStarts = mStarts;
mUnpluggedForegroundTime = mForegroundTime;
}
- public void plug(long batteryUptime, long batteryRealtime) {
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
}
void detach() {
@@ -3550,11 +3744,11 @@
mUnpluggables.add(this);
}
- public void unplug(long batteryUptime, long batteryRealtime) {
+ public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
mUnpluggedWakeups = mWakeups;
}
- public void plug(long batteryUptime, long batteryRealtime) {
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
}
void detach() {
@@ -3712,13 +3906,13 @@
mUnpluggables.add(this);
}
- public void unplug(long batteryUptime, long batteryRealtime) {
+ public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
mUnpluggedStartTime = getStartTimeToNowLocked(batteryUptime);
mUnpluggedStarts = mStarts;
mUnpluggedLaunches = mLaunches;
}
- public void plug(long batteryUptime, long batteryRealtime) {
+ public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
}
void detach() {
@@ -4367,7 +4561,7 @@
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
- doUnplugLocked(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
+ doUnplugLocked(realtime, mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
} else {
updateKernelWakelocksLocked();
mHistoryCur.batteryLevel = (byte)level;
@@ -4383,7 +4577,7 @@
mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
}
updateDischargeScreenLevelsLocked(mScreenOn, mScreenOn);
- doPlugLocked(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
+ doPlugLocked(realtime, getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
}
if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
if (mFile != null) {
@@ -5161,11 +5355,14 @@
}
u.mAudioTurnedOn = false;
if (in.readInt() != 0) {
- u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in);
+ u.createAudioTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
u.mVideoTurnedOn = false;
if (in.readInt() != 0) {
- u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in);
+ u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
+ }
+ if (in.readInt() != 0) {
+ u.createVibratorOnTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
@@ -5367,6 +5564,12 @@
} else {
out.writeInt(0);
}
+ if (u.mVibratorOnTimer != null) {
+ out.writeInt(1);
+ u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ } else {
+ out.writeInt(0);
+ }
if (u.mUserActivityCounters == null) {
out.writeInt(0);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9822e63..e357255 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1327,21 +1327,6 @@
android:label="@string/permlab_writeGservices"
android:description="@string/permdesc_writeGservices" />
- <!-- @hide Change the screen compatibility mode of applications -->
- <permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="signature"
- android:label="@string/permlab_setScreenCompatibility"
- android:description="@string/permdesc_setScreenCompatibility" />
-
- <!-- Allows an application to modify the current configuration, such
- as locale. -->
- <permission android:name="android.permission.CHANGE_CONFIGURATION"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="system|signature"
- android:label="@string/permlab_changeConfiguration"
- android:description="@string/permdesc_changeConfiguration" />
-
<!-- Allows an application to call
{@link android.app.ActivityManager#forceStopPackage}.
@hide -->
@@ -1621,6 +1606,13 @@
android:description="@string/permdesc_updateBatteryStats"
android:protectionLevel="signature|system" />
+ <!-- Allows an application to update application operation statistics. Not for
+ use by third party apps. @hide -->
+ <permission android:name="android.permission.UPDATE_APP_OPS_STATS"
+ android:label="@string/permlab_updateAppOpsStats"
+ android:description="@string/permdesc_updateAppOpsStats"
+ android:protectionLevel="signature|system" />
+
<!-- Allows an application to open windows that are for use by parts
of the system user interface. Not for use by third party apps. -->
<permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb8d0e5..6a93860 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -842,6 +842,12 @@
<string name="permdesc_updateBatteryStats">Allows the app to modify
collected battery statistics. Not for use by normal apps.</string>
+ <!-- [CHAR LIMIT=NONE] Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_updateAppOpsStats">modify app ops statistics</string>
+ <!-- [CHAR LIMIT=NONE] Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_updateAppOpsStats">Allows the app to modify
+ collected application operation statistics. Not for use by normal apps.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_backup">control system backup and restore</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index f663e0a..c353ec6 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -47,7 +47,7 @@
Location getLastLocation(in LocationRequest request, String packageName);
- boolean addGpsStatusListener(IGpsStatusListener listener);
+ boolean addGpsStatusListener(IGpsStatusListener listener, String packageName);
void removeGpsStatusListener(IGpsStatusListener listener);
boolean geocoderIsPresent();
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 5a2f71b..0b9286e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1461,7 +1461,7 @@
}
try {
GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
- result = mService.addGpsStatusListener(transport);
+ result = mService.addGpsStatusListener(transport, mContext.getPackageName());
if (result) {
mGpsStatusListeners.put(listener, transport);
}
@@ -1507,7 +1507,7 @@
}
try {
GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
- result = mService.addGpsStatusListener(transport);
+ result = mService.addGpsStatusListener(transport, mContext.getPackageName());
if (result) {
mNmeaListeners.put(listener, transport);
}
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
new file mode 100644
index 0000000..5ad4be8
--- /dev/null
+++ b/services/java/com/android/server/AppOpsService.java
@@ -0,0 +1,260 @@
+/*
+ * 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.PrintWriter;
+import java.util.HashMap;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.app.IAppOpsService;
+
+public class AppOpsService extends IAppOpsService.Stub {
+ static final String TAG = "AppOps";
+
+ Context mContext;
+ final AtomicFile mFile;
+
+ final SparseArray<HashMap<String, Ops>> mUidOps
+ = new SparseArray<HashMap<String, Ops>>();
+
+ final static class Ops extends SparseArray<Op> {
+ public final String packageName;
+
+ public Ops(String _packageName) {
+ packageName = _packageName;
+ }
+ }
+
+ final static class Op {
+ public final int op;
+ public int duration;
+ public long time;
+
+ public Op(int _op) {
+ op = _op;
+ }
+ }
+
+ public AppOpsService() {
+ mFile = new AtomicFile(new File(Environment.getSecureDataDirectory(), "appops.xml"));
+ }
+
+ public void publish(Context context) {
+ mContext = context;
+ ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
+ }
+
+ public void shutdown() {
+ Slog.w(TAG, "Writing app ops before shutdown...");
+ }
+
+ @Override
+ public int noteOperation(int code, int uid, String packageName) {
+ uid = handleIncomingUid(uid);
+ synchronized (this) {
+ Op op = getOpLocked(code, uid, packageName);
+ if (op == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ if (op.duration == -1) {
+ Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
+ + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ }
+ op.time = System.currentTimeMillis();
+ op.duration = 0;
+ }
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ @Override
+ public int startOperation(int code, int uid, String packageName) {
+ uid = handleIncomingUid(uid);
+ synchronized (this) {
+ Op op = getOpLocked(code, uid, packageName);
+ if (op == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ if (op.duration == -1) {
+ Slog.w(TAG, "Starting op not finished: uid " + uid + " pkg " + packageName
+ + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ }
+ op.time = System.currentTimeMillis();
+ op.duration = -1;
+ }
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ @Override
+ public void finishOperation(int code, int uid, String packageName) {
+ uid = handleIncomingUid(uid);
+ synchronized (this) {
+ Op op = getOpLocked(code, uid, packageName);
+ if (op == null) {
+ return;
+ }
+ if (op.duration != -1) {
+ Slog.w(TAG, "Ignoring finishing op not started: uid " + uid + " pkg " + packageName
+ + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ return;
+ }
+ op.duration = (int)(System.currentTimeMillis() - op.time);
+ }
+ }
+
+ @Override
+ public int noteTimedOperation(int code, int uid, String packageName, int duration) {
+ uid = handleIncomingUid(uid);
+ synchronized (this) {
+ Op op = getOpLocked(code, uid, packageName);
+ if (op == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ if (op.duration == -1) {
+ Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
+ + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ }
+ op.time = System.currentTimeMillis();
+ op.duration = duration;
+ }
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ @Override
+ public void earlyFinishOperation(int code, int uid, String packageName) {
+ uid = handleIncomingUid(uid);
+ synchronized (this) {
+ Op op = getOpLocked(code, uid, packageName);
+ if (op == null) {
+ return;
+ }
+ if (op.duration != -1) {
+ Slog.w(TAG, "Noting timed op not finished: uid " + uid + " pkg " + packageName
+ + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ }
+ int newDuration = (int)(System.currentTimeMillis() - op.time);
+ if (newDuration < op.duration) {
+ op.duration = newDuration;
+ }
+ }
+ }
+
+ private int handleIncomingUid(int uid) {
+ if (uid == Binder.getCallingUid()) {
+ return uid;
+ }
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return uid;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ return uid;
+ }
+
+ private Op getOpLocked(int code, int uid, String packageName) {
+ HashMap<String, Ops> pkgOps = mUidOps.get(uid);
+ if (pkgOps == null) {
+ pkgOps = new HashMap<String, Ops>();
+ mUidOps.put(uid, pkgOps);
+ }
+ Ops ops = pkgOps.get(packageName);
+ if (ops == null) {
+ // This is the first time we have seen this package name under this uid,
+ // so let's make sure it is valid.
+ // XXX for now we always allow null through until we can fix everything
+ // to provide the name.
+ if (packageName != null) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ int pkgUid = -1;
+ try {
+ pkgUid = mContext.getPackageManager().getPackageUid(packageName,
+ UserHandle.getUserId(uid));
+ } catch (NameNotFoundException e) {
+ }
+ 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);
+ pkgOps.put(packageName, ops);
+ }
+ Op op = ops.get(code);
+ if (op == null) {
+ op = new Op(code);
+ ops.put(code, op);
+ }
+ return op;
+ }
+
+ @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:");
+ 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.opToString(op.op));
+ pw.print(": time=");
+ TimeUtils.formatDuration(System.currentTimeMillis()-op.time, pw);
+ pw.print(" ago");
+ if (op.duration == -1) {
+ pw.println(" (running)");
+ } else {
+ pw.print("; duration=");
+ TimeUtils.formatDuration(op.duration, pw);
+ pw.println();
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 83b94e2..9e40dc5 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -53,6 +54,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -60,6 +62,7 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -124,6 +127,7 @@
private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
private final Context mContext;
+ private final AppOpsManager mAppOps;
// used internally for synchronization
private final Object mLock = new Object();
@@ -187,6 +191,7 @@
public LocationManagerService(Context context) {
super();
mContext = context;
+ mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
if (D) Log.d(TAG, "Constructed");
@@ -524,6 +529,10 @@
}
public boolean callLocationChangedLocked(Location location) {
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_LOCATION, mUid, mPackageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
if (mListener != null) {
try {
synchronized (this) {
@@ -1162,7 +1171,7 @@
private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
int pid, int uid, String packageName) {
if (intent == null && listener == null) {
- throw new IllegalArgumentException("need eiter listener or intent");
+ throw new IllegalArgumentException("need either listener or intent");
} else if (intent != null && listener != null) {
throw new IllegalArgumentException("cannot register both listener and intent");
} else if (intent != null) {
@@ -1185,11 +1194,14 @@
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
-
// providers may use public location API's, need to clear identity
long identity = Binder.clearCallingIdentity();
try {
+ // We don't check for MODE_IGNORED here; we will do that when we go to deliver
+ // a location.
+ mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName);
+ Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
+
synchronized (mLock) {
requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
}
@@ -1296,8 +1308,14 @@
request.getProvider());
// no need to sanitize this request, as only the provider name is used
- long identity = Binder.clearCallingIdentity();
+ final int uid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
try {
+ if (mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ return null;
+ }
+
if (mBlacklist.isBlacklisted(packageName)) {
if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
packageName);
@@ -1381,13 +1399,24 @@
@Override
- public boolean addGpsStatusListener(IGpsStatusListener listener) {
+ public boolean addGpsStatusListener(IGpsStatusListener listener, String packageName) {
if (mGpsStatusProvider == null) {
return false;
}
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
LocationManager.GPS_PROVIDER);
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
try {
mGpsStatusProvider.addGpsStatusListener(listener);
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a7b502a..c33eb2b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -261,7 +261,6 @@
ServiceManager.addService(Context.USER_SERVICE,
UserManagerService.getInstance());
-
mContentResolver = context.getContentResolver();
// The AccountManager must come before the ContentService
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index df91dec..69379f1 100644
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +31,7 @@
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Binder;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.Vibrator;
@@ -39,6 +41,9 @@
import android.util.Slog;
import android.view.InputDevice;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IBatteryStats;
+
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
@@ -54,6 +59,8 @@
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
+ private final IAppOpsService mAppOpsService;
+ private final IBatteryStats mBatteryStatsService;
private InputManager mIm;
volatile VibrateThread mThread;
@@ -64,6 +71,8 @@
private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
+ private int mCurVibUid = -1;
+
native static boolean vibratorExists();
native static void vibratorOn(long milliseconds);
native static void vibratorOff();
@@ -75,23 +84,25 @@
private final long[] mPattern;
private final int mRepeat;
private final int mUid;
+ private final String mPackageName;
- Vibration(IBinder token, long millis, int uid) {
- this(token, millis, null, 0, uid);
+ Vibration(IBinder token, long millis, int uid, String packageName) {
+ this(token, millis, null, 0, uid, packageName);
}
- Vibration(IBinder token, long[] pattern, int repeat, int uid) {
- this(token, 0, pattern, repeat, uid);
+ Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
+ this(token, 0, pattern, repeat, uid, packageName);
}
private Vibration(IBinder token, long millis, long[] pattern,
- int repeat, int uid) {
+ int repeat, int uid, String packageName) {
mToken = token;
mTimeout = millis;
mStartTime = SystemClock.uptimeMillis();
mPattern = pattern;
mRepeat = repeat;
mUid = uid;
+ mPackageName = packageName;
}
public void binderDied() {
@@ -131,6 +142,9 @@
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
+ mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
+ mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
mVibrations = new LinkedList<Vibration>();
IntentFilter filter = new IntentFilter();
@@ -164,7 +178,7 @@
return doVibratorExists();
}
- public void vibrate(long milliseconds, IBinder token) {
+ public void vibrate(String packageName, long milliseconds, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
@@ -180,12 +194,18 @@
return;
}
- Vibration vib = new Vibration(token, milliseconds, uid);
- synchronized (mVibrations) {
- removeVibrationLocked(token);
- doCancelVibrateLocked();
- mCurrentVibration = vib;
- startVibrationLocked(vib);
+ Vibration vib = new Vibration(token, milliseconds, uid, packageName);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mVibrations) {
+ removeVibrationLocked(token);
+ doCancelVibrateLocked();
+ mCurrentVibration = vib;
+ startVibrationLocked(vib);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -199,7 +219,7 @@
return true;
}
- public void vibratePattern(long[] pattern, int repeat, IBinder token) {
+ public void vibratePattern(String packageName, long[] pattern, int repeat, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
@@ -224,7 +244,7 @@
return;
}
- Vibration vib = new Vibration(token, pattern, repeat, uid);
+ Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
try {
token.linkToDeath(vib, 0);
} catch (RemoteException e) {
@@ -291,11 +311,13 @@
}
doVibratorOff();
mH.removeCallbacks(mVibrationRunnable);
+ reportFinishVibrationLocked();
}
// Lock held on mVibrations
private void startNextVibrationLocked() {
if (mVibrations.size() <= 0) {
+ reportFinishVibrationLocked();
mCurrentVibration = null;
return;
}
@@ -305,8 +327,19 @@
// Lock held on mVibrations
private void startVibrationLocked(final Vibration vib) {
+ try {
+ int mode = mAppOpsService.startOperation(AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ if (mode == AppOpsManager.MODE_ERRORED) {
+ Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
+ }
+ mH.post(mVibrationRunnable);
+ return;
+ }
+ } catch (RemoteException e) {
+ }
if (vib.mTimeout != 0) {
- doVibratorOn(vib.mTimeout);
+ doVibratorOn(vib.mTimeout, vib.mUid);
mH.postDelayed(mVibrationRunnable, vib.mTimeout);
} else {
// mThread better be null here. doCancelVibrate should always be
@@ -316,6 +349,17 @@
}
}
+ private void reportFinishVibrationLocked() {
+ if (mCurrentVibration != null) {
+ try {
+ mAppOpsService.finishOperation(AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
+ mCurrentVibration.mPackageName);
+ } catch (RemoteException e) {
+ }
+ mCurrentVibration = null;
+ }
+ }
+
// Lock held on mVibrations
private Vibration removeVibrationLocked(IBinder token) {
ListIterator<Vibration> iter = mVibrations.listIterator(0);
@@ -413,8 +457,13 @@
return vibratorExists();
}
- private void doVibratorOn(long millis) {
+ private void doVibratorOn(long millis, int uid) {
synchronized (mInputDeviceVibrators) {
+ try {
+ mBatteryStatsService.noteVibratorOn(uid, millis);
+ mCurVibUid = uid;
+ } catch (RemoteException e) {
+ }
final int vibratorCount = mInputDeviceVibrators.size();
if (vibratorCount != 0) {
for (int i = 0; i < vibratorCount; i++) {
@@ -428,6 +477,13 @@
private void doVibratorOff() {
synchronized (mInputDeviceVibrators) {
+ if (mCurVibUid >= 0) {
+ try {
+ mBatteryStatsService.noteVibratorOff(mCurVibUid);
+ } catch (RemoteException e) {
+ }
+ mCurVibUid = -1;
+ }
final int vibratorCount = mInputDeviceVibrators.size();
if (vibratorCount != 0) {
for (int i = 0; i < vibratorCount; i++) {
@@ -470,10 +526,11 @@
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
synchronized (this) {
+ final long[] pattern = mVibration.mPattern;
+ final int len = pattern.length;
+ final int repeat = mVibration.mRepeat;
+ final int uid = mVibration.mUid;
int index = 0;
- long[] pattern = mVibration.mPattern;
- int len = pattern.length;
- int repeat = mVibration.mRepeat;
long duration = 0;
while (!mDone) {
@@ -493,7 +550,7 @@
// duration is saved for delay() at top of loop
duration = pattern[index++];
if (duration > 0) {
- VibratorService.this.doVibratorOn(duration);
+ VibratorService.this.doVibratorOn(duration, uid);
}
} else {
if (repeat < 0) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 62af91e..b08fc28 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -21,6 +21,7 @@
import com.android.internal.R;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessStats;
+import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
import com.android.server.ProcessMap;
@@ -619,9 +620,14 @@
final BatteryStatsService mBatteryStatsService;
/**
- * information about component usage
+ * Information about component usage
*/
final UsageStatsService mUsageStatsService;
+
+ /**
+ * Information about and control over application operations
+ */
+ final AppOpsService mAppOpsService;
/**
* Current configuration information. HistoryRecord objects are given
@@ -1450,7 +1456,8 @@
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
-
+ m.mAppOpsService.publish(context);
+
synchronized (thr) {
thr.mReady = true;
thr.notifyAll();
@@ -1613,9 +1620,10 @@
mOnBattery = DEBUG_POWER ? true
: mBatteryStatsService.getActiveStatistics().getIsOnBattery();
mBatteryStatsService.getActiveStatistics().setCallback(this);
-
+
mUsageStatsService = new UsageStatsService(new File(
systemDir, "usagestats").toString());
+ mAppOpsService = new AppOpsService();
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
// User 0 is the first and only user that runs at boot.
@@ -7174,7 +7182,8 @@
}
}
}
-
+
+ mAppOpsService.shutdown();
mUsageStatsService.shutdown();
mBatteryStatsService.shutdown();
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index ab20208..d19c7f6 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -144,6 +144,20 @@
}
}
+ public void noteVibratorOn(int uid, long durationMillis) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteVibratorOnLocked(uid, durationMillis);
+ }
+ }
+
+ public void noteVibratorOff(int uid) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteVibratorOffLocked(uid);
+ }
+ }
+
public void noteStartGps(int uid) {
enforceCallingPermission();
synchronized (mStats) {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 7f059f5..f1739d5 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -56,6 +57,8 @@
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import android.util.NtpTrustedTime;
+
+import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.location.GpsNetInitiatedHandler;
import com.android.internal.location.ProviderProperties;
@@ -305,6 +308,7 @@
private final PendingIntent mWakeupIntent;
private final PendingIntent mTimeoutIntent;
+ private final IAppOpsService mAppOpsService;
private final IBatteryStats mBatteryStats;
// only modified on handler thread
@@ -434,6 +438,10 @@
mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ // App ops service to keep track of who is accessing the GPS
+ mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(
+ Context.APP_OPS_SERVICE));
+
// Battery statistics service to be notified when GPS turns on or off
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
@@ -863,6 +871,7 @@
}
if (newUid) {
try {
+ mAppOpsService.startOperation(AppOpsManager.OP_GPS, uid1, null);
mBatteryStats.noteStartGps(uid1);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException", e);
@@ -882,6 +891,7 @@
if (oldUid) {
try {
mBatteryStats.noteStopGps(uid1);
+ mAppOpsService.finishOperation(AppOpsManager.OP_GPS, uid1, null);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException", e);
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 2eba4e1..5ee52de 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -81,6 +81,13 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public int getPackageUid(String packageName, int userHandle)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public PermissionInfo getPermissionInfo(String name, int flags)
throws NameNotFoundException {