diff options
25 files changed, 970 insertions, 104 deletions
diff --git a/Android.mk b/Android.mk index 294a2fec15b8..462d6acedc91 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 000000000000..7210df40919a --- /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 74317656771f..03d1a3f3fec3 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -142,6 +142,21 @@ final class ApplicationPackageManager extends PackageManager { } @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 f895cccbe25e..8ef708c0ec1c 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 257f84ef8e94..c777250743b6 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2289,6 +2289,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 8ba19881f097..cdd91951387c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1280,6 +1280,22 @@ public abstract class PackageManager { 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 9821824502b6..abbb6a1a5c4e 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -93,6 +93,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. */ public static final int STATS_SINCE_CHARGED = 0; @@ -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<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 @@ 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<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 2c2fe8a18984..15cedf9f42a5 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 7c5a47e5baf4..54ea38542635 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 cc9615260a0c..d2052539ccb9 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 @@ public final class UserHandle implements Parcelable { } /** + * 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 000000000000..c93458797dfb --- /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 1a76461f4f26..823e19f21100 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 94e7a068c7fb..4d35a6bc3a2a 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; } @@ -849,6 +845,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<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 @@ 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); } } @@ -2677,6 +2844,11 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override + public Timer getVibratorOnTimer() { + return mVibratorOnTimer; + } + + @Override public void noteUserActivityLocked(int type) { if (mUserActivityCounters == null) { initUserActivityLocked(); @@ -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<NUM_USER_ACTIVITY_TYPES; i++) { @@ -2917,6 +3099,12 @@ public final class BatteryStatsImpl extends BatteryStats { } 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } mDischargeAmountScreenOn = 0; mDischargeAmountScreenOff = 0; - doUnplugLocked(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime); + doUnplugLocked(realtime, mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime); } else { updateKernelWakelocksLocked(); mHistoryCur.batteryLevel = (byte)level; @@ -4383,7 +4577,7 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } 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 @@ public final class BatteryStatsImpl extends BatteryStats { } 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 9822e63b0319..e357255acc08 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 cb8d0e5e8a20..6a93860654bb 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 f663e0a04085..c353ec6efe46 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 5a2f71b98c9b..0b9286ed62d5 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 000000000000..5ad4be8d3bf6 --- /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 83b94e2c25d8..9e40dc5ce823 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.Message; 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.provider.Settings; 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 class LocationManagerService extends ILocationManager.Stub { 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 class LocationManagerService extends ILocationManager.Stub { } 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { @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 a7b502ae97dd..c33eb2b5ad05 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -261,7 +261,6 @@ class ServerThread extends Thread { 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 df91decb31ed..69379f17c3fc 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.Process; 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.provider.Settings.SettingNotFoundException; 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ 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<Vibration> 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 62af91e76023..b08fc2842bfa 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 ab20208403d6..d19c7f6c7620 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 7f059f57f013..f1739d56baeb 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 2eba4e116542..5ee52de0a32d 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 { |