diff --git a/Android.mk b/Android.mk index 294a2fec15b88..462d6acedc91f 100644 --- a/Android.mk +++ b/Android.mk @@ -166,6 +166,7 @@ LOCAL_SRC_FILES += \ 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 0000000000000..7210df40919a1 --- /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 74317656771fa..03d1a3f3fec34 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -141,6 +141,21 @@ final class ApplicationPackageManager extends PackageManager { throw new NameNotFoundException(packageName); } + @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 { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f895cccbe25e8..8ef708c0ec1cd 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -47,11 +47,9 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory; 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.view.textservice.TextServicesManager; 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { }}); 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 257f84ef8e942..c777250743b6f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2288,6 +2288,18 @@ public abstract class Context { */ 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 8ba19881f0970..cdd91951387c2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1279,6 +1279,22 @@ public abstract class PackageManager { public abstract int[] getPackageGids(String packageName) throws NameNotFoundException; + /** + * @hide Return the uid associated with the given package name for the + * given user. + * + *

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. * diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 9821824502b68..abbb6a1a5c4e7 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -92,6 +92,11 @@ public abstract class BatteryStats implements Parcelable { */ 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. */ @@ -131,6 +136,7 @@ public abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { } } + 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 processStats = u.getProcessStats(); if (processStats.size() > 0) { for (Map.Entry ent @@ -1919,6 +1936,26 @@ public abstract class BatteryStats implements Parcelable { } } + 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 processStats = u.getProcessStats(); if (processStats.size() > 0) { for (Map.Entry ent diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index 2c2fe8a189849..15cedf9f42a54 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -20,8 +20,8 @@ package android.os; 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 7c5a47e5baf47..54ea385426352 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 @@ import android.util.Log; 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 @@ public class SystemVibrator extends Vibrator { 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 @@ public class SystemVibrator extends Vibrator { // 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 cc9615260a0c6..d2052539ccb9c 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. */ @@ -151,6 +153,50 @@ public final class UserHandle implements Parcelable { - Process.FIRST_APPLICATION_UID; } + /** + * 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 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 0000000000000..c93458797dfb1 --- /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 1a76461f4f26a..823e19f21100b 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -37,6 +37,8 @@ interface IBatteryStats { 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 94e7a068c7fb9..4d35a6bc3a2a4 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.Slog; 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { } 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { } } - 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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; } @@ -848,6 +844,141 @@ public final class BatteryStatsImpl extends BatteryStats { } } + /** + * 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 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 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. */ @@ -902,12 +1033,12 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } } 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mVideoTurnedOn; StopwatchTimer mVideoTurnedOnTimer; + BatchTimer mVibratorOnTimer; + Counter[] mUserActivityCounters; /** @@ -2439,10 +2580,6 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { } } + 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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); } } @@ -2676,6 +2843,11 @@ public final class BatteryStatsImpl extends BatteryStats { return mVideoTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which); } + @Override + public Timer getVibratorOnTimer() { + return mVibratorOnTimer; + } + @Override public void noteUserActivityLocked(int type) { if (mUserActivityCounters == null) { @@ -2747,6 +2919,14 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } if (mAudioTurnedOnTimer != null) { mAudioTurnedOnTimer.detach(); + mAudioTurnedOnTimer = null; } if (mVideoTurnedOnTimer != null) { mVideoTurnedOnTimer.detach(); + mVideoTurnedOnTimer = null; } if (mUserActivityCounters != null) { for (int i=0; i - - - - - - @@ -1621,6 +1606,13 @@ android:description="@string/permdesc_updateBatteryStats" android:protectionLevel="signature|system" /> + + + Allows the app to modify collected battery statistics. Not for use by normal apps. + + modify app ops statistics + + Allows the app to modify + collected application operation statistics. Not for use by normal apps. + control system backup and restore diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index f663e0a040855..c353ec6efe46a 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -47,7 +47,7 @@ interface ILocationManager 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 5a2f71b98c9bf..0b9286ed62d59 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1461,7 +1461,7 @@ public class LocationManager { } 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 @@ public class LocationManager { } 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 0000000000000..5ad4be8d3bf69 --- /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> mUidOps + = new SparseArray>(); + + final static class Ops extends SparseArray { + 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 pkgOps = mUidOps.get(uid); + if (pkgOps == null) { + pkgOps = new HashMap(); + 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 pkgOps = mUidOps.valueAt(i); + for (Ops ops : pkgOps.values()) { + pw.print(" Package "); pw.print(ops.packageName); pw.println(":"); + for (int j=0; j(); IntentFilter filter = new IntentFilter(); @@ -164,7 +178,7 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub } doVibratorOff(); mH.removeCallbacks(mVibrationRunnable); + reportFinishVibrationLocked(); } // Lock held on mVibrations private void startNextVibrationLocked() { if (mVibrations.size() <= 0) { + reportFinishVibrationLocked(); mCurrentVibration = null; return; } @@ -305,8 +327,19 @@ public class VibratorService extends IVibratorService.Stub // 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 @@ public class VibratorService extends IVibratorService.Stub } } + 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 iter = mVibrations.listIterator(0); @@ -413,8 +457,13 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub // 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 62af91e760231..b08fc2842bfab 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative m.mBatteryStatsService.publish(context); m.mUsageStatsService.publish(context); - + m.mAppOpsService.publish(context); + synchronized (thr) { thr.mReady = true; thr.notifyAll(); @@ -1613,9 +1620,10 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - + + 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 ab20208403d62..d19c7f6c76205 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -144,6 +144,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } + 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 7f059f57f0133..f1739d56baeb0 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.TelephonyManager; 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 @@ public class GpsLocationProvider implements LocationProviderInterface { 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 @@ public class GpsLocationProvider implements LocationProviderInterface { 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 @@ public class GpsLocationProvider implements LocationProviderInterface { } 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 @@ public class GpsLocationProvider implements LocationProviderInterface { 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 2eba4e1165427..5ee52de0a32db 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -81,6 +81,13 @@ public class MockPackageManager extends PackageManager { 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 {