diff options
author | Dianne Hackborn <hackbod@google.com> | 2014-01-16 01:39:21 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-01-16 01:39:22 +0000 |
commit | c1405900609f9cc42280a6eee0cf2f44dd6ca9f7 (patch) | |
tree | fdaa757fd3348a1635384bff0aa286e11b3b4f67 | |
parent | df062746f9aae9df9494072ba7197acbafdb141b (diff) | |
parent | 7c80af410b09fa14b6fdb5ecec14b6b96562de4c (diff) | |
download | packages_apps_Settings-c1405900609f9cc42280a6eee0cf2f44dd6ca9f7.zip packages_apps_Settings-c1405900609f9cc42280a6eee0cf2f44dd6ca9f7.tar.gz packages_apps_Settings-c1405900609f9cc42280a6eee0cf2f44dd6ca9f7.tar.bz2 |
Merge "Switch to BatteryStatsHelper implementation in the framework."
6 files changed, 518 insertions, 1192 deletions
diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java new file mode 100644 index 0000000..4d1fb2f --- /dev/null +++ b/src/com/android/settings/fuelgauge/BatteryEntry.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2014 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.settings.fuelgauge; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.graphics.drawable.Drawable; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.UserManager; +import com.android.internal.os.BatterySipper; +import com.android.settings.R; +import com.android.settings.users.UserUtils; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Wraps the power usage data of a BatterySipper with information about package name + * and icon image. + */ +public class BatteryEntry { + public static final int MSG_UPDATE_NAME_ICON = 1; + public static final int MSG_REPORT_FULLY_DRAWN = 2; + + static final HashMap<String,UidToDetail> sUidCache = new HashMap<String,UidToDetail>(); + + static final ArrayList<BatteryEntry> mRequestQueue = new ArrayList<BatteryEntry>(); + static Handler mHandler; + + static private class NameAndIconLoader extends Thread { + private boolean mAbort = false; + + public NameAndIconLoader() { + super("BatteryUsage Icon Loader"); + } + + public void abort() { + mAbort = true; + } + + @Override + public void run() { + while (true) { + BatteryEntry be; + synchronized (mRequestQueue) { + if (mRequestQueue.isEmpty() || mAbort) { + mHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN); + mRequestQueue.clear(); + return; + } + be = mRequestQueue.remove(0); + } + be.loadNameAndIcon(); + } + } + } + + private static NameAndIconLoader mRequestThread; + + public static void startRequestQueue() { + if (mHandler != null) { + synchronized (mRequestQueue) { + if (!mRequestQueue.isEmpty()) { + if (mRequestThread != null) { + mRequestThread.abort(); + } + mRequestThread = new NameAndIconLoader(); + mRequestThread.setPriority(Thread.MIN_PRIORITY); + mRequestThread.start(); + mRequestQueue.notify(); + } + } + } + } + + public static void stopRequestQueue() { + synchronized (mRequestQueue) { + if (mRequestThread != null) { + mRequestThread.abort(); + mRequestThread = null; + mHandler = null; + } + } + } + + public static void clearUidCache() { + sUidCache.clear(); + } + + public final Context context; + public final BatterySipper sipper; + + public String name; + public Drawable icon; + public int iconId; // For passing to the detail screen. + public String defaultPackageName; + + static class UidToDetail { + String name; + String packageName; + Drawable icon; + } + + public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper) { + mHandler = handler; + this.context = context; + this.sipper = sipper; + switch (sipper.drainType) { + case IDLE: + name = context.getResources().getString(R.string.power_idle); + iconId = R.drawable.ic_settings_phone_idle; + break; + case CELL: + name = context.getResources().getString(R.string.power_cell); + iconId = R.drawable.ic_settings_cell_standby; + break; + case PHONE: + name = context.getResources().getString(R.string.power_phone); + iconId = R.drawable.ic_settings_voice_calls; + break; + case WIFI: + name = context.getResources().getString(R.string.power_wifi); + iconId = R.drawable.ic_settings_wifi; + break; + case BLUETOOTH: + name = context.getResources().getString(R.string.power_bluetooth); + iconId = R.drawable.ic_settings_bluetooth; + break; + case SCREEN: + name = context.getResources().getString(R.string.power_screen); + iconId = R.drawable.ic_settings_display; + break; + case APP: + name = sipper.packageWithHighestDrain; + break; + case USER: { + UserInfo info = um.getUserInfo(sipper.userId); + if (info != null) { + icon = UserUtils.getUserIcon(context, um, info, context.getResources()); + name = info != null ? info.name : null; + if (name == null) { + name = Integer.toString(info.id); + } + name = context.getResources().getString( + R.string.running_process_item_user_label, name); + } else { + icon = null; + name = context.getResources().getString( + R.string.running_process_item_removed_user_label); + } + } break; + case UNACCOUNTED: + name = context.getResources().getString(R.string.power_unaccounted); + iconId = R.drawable.ic_power_system; + break; + case OVERCOUNTED: + name = context.getResources().getString(R.string.power_overcounted); + iconId = R.drawable.ic_power_system; + break; + } + if (iconId > 0) { + icon = context.getResources().getDrawable(iconId); + } + if ((name == null || iconId == 0) && this.sipper.uidObj != null) { + getQuickNameIconForUid(this.sipper.uidObj); + } + } + + public Drawable getIcon() { + return icon; + } + + /** + * Gets the application name + */ + public String getLabel() { + return name; + } + + void getQuickNameIconForUid(BatteryStats.Uid uidObj) { + final int uid = uidObj.getUid(); + final String uidString = Integer.toString(uid); + if (sUidCache.containsKey(uidString)) { + UidToDetail utd = sUidCache.get(uidString); + defaultPackageName = utd.packageName; + name = utd.name; + icon = utd.icon; + return; + } + PackageManager pm = context.getPackageManager(); + String[] packages = pm.getPackagesForUid(uid); + icon = pm.getDefaultActivityIcon(); + if (packages == null) { + //name = Integer.toString(uid); + if (uid == 0) { + name = context.getResources().getString(R.string.process_kernel_label); + } else if ("mediaserver".equals(name)) { + name = context.getResources().getString(R.string.process_mediaserver_label); + } + iconId = R.drawable.ic_power_system; + icon = context.getResources().getDrawable(iconId); + return; + } else { + //name = packages[0]; + } + if (mHandler != null) { + synchronized (mRequestQueue) { + mRequestQueue.add(this); + } + } + } + + /** + * Loads the app label and icon image and stores into the cache. + */ + public void loadNameAndIcon() { + // Bail out if the current sipper is not an App sipper. + if (sipper.uidObj == null) { + return; + } + PackageManager pm = context.getPackageManager(); + final int uid = sipper.uidObj.getUid(); + final Drawable defaultActivityIcon = pm.getDefaultActivityIcon(); + sipper.mPackages = pm.getPackagesForUid(uid); + if (sipper.mPackages == null) { + name = Integer.toString(uid); + return; + } + + String[] packageLabels = new String[sipper.mPackages.length]; + System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length); + + int preferredIndex = -1; + // Convert package names to user-facing labels where possible + for (int i = 0; i < packageLabels.length; i++) { + // Check if package matches preferred package + if (packageLabels[i].equals(name)) preferredIndex = i; + try { + ApplicationInfo ai = pm.getApplicationInfo(packageLabels[i], 0); + CharSequence label = ai.loadLabel(pm); + if (label != null) { + packageLabels[i] = label.toString(); + } + if (ai.icon != 0) { + defaultPackageName = sipper.mPackages[i]; + icon = ai.loadIcon(pm); + break; + } + } catch (PackageManager.NameNotFoundException e) { + } + } + if (icon == null) icon = defaultActivityIcon; + + if (packageLabels.length == 1) { + name = packageLabels[0]; + } else { + // Look for an official name for this UID. + for (String pkgName : sipper.mPackages) { + try { + final PackageInfo pi = pm.getPackageInfo(pkgName, 0); + if (pi.sharedUserLabel != 0) { + final CharSequence nm = pm.getText(pkgName, + pi.sharedUserLabel, pi.applicationInfo); + if (nm != null) { + name = nm.toString(); + if (pi.applicationInfo.icon != 0) { + defaultPackageName = pkgName; + icon = pi.applicationInfo.loadIcon(pm); + } + break; + } + } + } catch (PackageManager.NameNotFoundException e) { + } + } + } + final String uidString = Integer.toString(sipper.uidObj.getUid()); + UidToDetail utd = new UidToDetail(); + utd.name = name; + utd.icon = icon; + utd.packageName = defaultPackageName; + sUidCache.put(uidString, utd); + if (mHandler != null) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this)); + } + } +} diff --git a/src/com/android/settings/fuelgauge/BatterySipper.java b/src/com/android/settings/fuelgauge/BatterySipper.java deleted file mode 100644 index d1f885c..0000000 --- a/src/com/android/settings/fuelgauge/BatterySipper.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2009 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.settings.fuelgauge; - -import com.android.settings.R; -import com.android.settings.fuelgauge.PowerUsageDetail.DrainType; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.BatteryStats.Uid; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Contains information about package name, icon image, power usage about an - * application or a system service. - */ -public class BatterySipper implements Comparable<BatterySipper> { - final Context mContext; - /* Cache cleared when PowerUsageSummary is destroyed */ - static final HashMap<String,UidToDetail> sUidCache = new HashMap<String,UidToDetail>(); - final ArrayList<BatterySipper> mRequestQueue; - final Handler mHandler; - String name; - Drawable icon; - int iconId; // For passing to the detail screen. - Uid uidObj; - double value; - double[] values; - DrainType drainType; - long usageTime; - long cpuTime; - long gpsTime; - long wifiRunningTime; - long cpuFgTime; - long wakeLockTime; - long mobileRxPackets; - long mobileTxPackets; - long wifiRxPackets; - long wifiTxPackets; - long mobileRxBytes; - long mobileTxBytes; - long wifiRxBytes; - long wifiTxBytes; - double percent; - double noCoveragePercent; - String defaultPackageName; - String[] mPackages; - - static class UidToDetail { - String name; - String packageName; - Drawable icon; - } - - BatterySipper(Context context, ArrayList<BatterySipper> requestQueue, - Handler handler, String label, DrainType drainType, - int iconId, Uid uid, double[] values) { - mContext = context; - mRequestQueue = requestQueue; - mHandler = handler; - this.values = values; - name = label; - this.drainType = drainType; - if (iconId > 0) { - icon = mContext.getResources().getDrawable(iconId); - } - if (values != null) value = values[0]; - if ((label == null || iconId == 0) && uid != null) { - getQuickNameIconForUid(uid); - } - uidObj = uid; - } - - double getSortValue() { - return value; - } - - double[] getValues() { - return values; - } - - public Drawable getIcon() { - return icon; - } - - /** - * Gets the application name - */ - public String getLabel() { - return name; - } - - @Override - public int compareTo(BatterySipper other) { - // Return the flipped value because we want the items in descending order - return Double.compare(other.getSortValue(), getSortValue()); - } - - /** - * Gets a list of packages associated with the current user - */ - public String[] getPackages() { - return mPackages; - } - - public int getUid() { - // Bail out if the current sipper is not an App sipper. - if (uidObj == null) { - return 0; - } - return uidObj.getUid(); - } - - void getQuickNameIconForUid(Uid uidObj) { - final int uid = uidObj.getUid(); - final String uidString = Integer.toString(uid); - if (sUidCache.containsKey(uidString)) { - UidToDetail utd = sUidCache.get(uidString); - defaultPackageName = utd.packageName; - name = utd.name; - icon = utd.icon; - return; - } - PackageManager pm = mContext.getPackageManager(); - String[] packages = pm.getPackagesForUid(uid); - icon = pm.getDefaultActivityIcon(); - if (packages == null) { - //name = Integer.toString(uid); - if (uid == 0) { - name = mContext.getResources().getString(R.string.process_kernel_label); - } else if ("mediaserver".equals(name)) { - name = mContext.getResources().getString(R.string.process_mediaserver_label); - } - iconId = R.drawable.ic_power_system; - icon = mContext.getResources().getDrawable(iconId); - return; - } else { - //name = packages[0]; - } - if (mHandler != null) { - synchronized (mRequestQueue) { - mRequestQueue.add(this); - } - } - } - - public static void clearUidCache() { - sUidCache.clear(); - } - - /** - * Loads the app label and icon image and stores into the cache. - */ - public void loadNameAndIcon() { - // Bail out if the current sipper is not an App sipper. - if (uidObj == null) { - return; - } - PackageManager pm = mContext.getPackageManager(); - final int uid = uidObj.getUid(); - final Drawable defaultActivityIcon = pm.getDefaultActivityIcon(); - mPackages = pm.getPackagesForUid(uid); - if (mPackages == null) { - name = Integer.toString(uid); - return; - } - - String[] packageLabels = new String[mPackages.length]; - System.arraycopy(mPackages, 0, packageLabels, 0, mPackages.length); - - int preferredIndex = -1; - // Convert package names to user-facing labels where possible - for (int i = 0; i < packageLabels.length; i++) { - // Check if package matches preferred package - if (packageLabels[i].equals(name)) preferredIndex = i; - try { - ApplicationInfo ai = pm.getApplicationInfo(packageLabels[i], 0); - CharSequence label = ai.loadLabel(pm); - if (label != null) { - packageLabels[i] = label.toString(); - } - if (ai.icon != 0) { - defaultPackageName = mPackages[i]; - icon = ai.loadIcon(pm); - break; - } - } catch (NameNotFoundException e) { - } - } - if (icon == null) icon = defaultActivityIcon; - - if (packageLabels.length == 1) { - name = packageLabels[0]; - } else { - // Look for an official name for this UID. - for (String pkgName : mPackages) { - try { - final PackageInfo pi = pm.getPackageInfo(pkgName, 0); - if (pi.sharedUserLabel != 0) { - final CharSequence nm = pm.getText(pkgName, - pi.sharedUserLabel, pi.applicationInfo); - if (nm != null) { - name = nm.toString(); - if (pi.applicationInfo.icon != 0) { - defaultPackageName = pkgName; - icon = pi.applicationInfo.loadIcon(pm); - } - break; - } - } - } catch (PackageManager.NameNotFoundException e) { - } - } - } - final String uidString = Integer.toString(uidObj.getUid()); - UidToDetail utd = new UidToDetail(); - utd.name = name; - utd.icon = icon; - utd.packageName = defaultPackageName; - sUidCache.put(uidString, utd); - if (mHandler != null) { - mHandler.sendMessage( - mHandler.obtainMessage(BatteryStatsHelper.MSG_UPDATE_NAME_ICON, this)); - } - } -} diff --git a/src/com/android/settings/fuelgauge/BatteryStatsHelper.java b/src/com/android/settings/fuelgauge/BatteryStatsHelper.java deleted file mode 100644 index a02917e..0000000 --- a/src/com/android/settings/fuelgauge/BatteryStatsHelper.java +++ /dev/null @@ -1,907 +0,0 @@ -/* - * Copyright (C) 2009 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.settings.fuelgauge; - -import static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA; -import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA; -import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA; -import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA; - -import android.app.Activity; -import android.content.Context; -import android.content.pm.UserInfo; -import android.graphics.drawable.Drawable; -import android.hardware.Sensor; -import android.hardware.SensorManager; -import android.os.BatteryStats; -import android.os.BatteryStats.Uid; -import android.os.Bundle; -import android.os.Handler; -import android.os.Parcel; -import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.UserHandle; -import android.os.UserManager; -import android.preference.PreferenceActivity; -import android.telephony.SignalStrength; -import android.util.Log; -import android.util.SparseArray; - -import com.android.internal.app.IBatteryStats; -import com.android.internal.os.BatteryStatsImpl; -import com.android.internal.os.PowerProfile; -import com.android.internal.util.FastPrintWriter; -import com.android.settings.R; -import com.android.settings.fuelgauge.PowerUsageDetail.DrainType; -import com.android.settings.users.UserUtils; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * A helper class for retrieving the power usage information for all applications and services. - * - * The caller must initialize this class as soon as activity object is ready to use (for example, in - * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy(). - */ -public class BatteryStatsHelper { - - private static final boolean DEBUG = false; - - private static final String TAG = BatteryStatsHelper.class.getSimpleName(); - - private static BatteryStatsImpl sStatsXfer; - private IBatteryStats mBatteryInfo; - private UserManager mUm; - private BatteryStatsImpl mStats; - private PowerProfile mPowerProfile; - - private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>(); - private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>(); - private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>(); - private final SparseArray<List<BatterySipper>> mUserSippers - = new SparseArray<List<BatterySipper>>(); - private final SparseArray<Double> mUserPower = new SparseArray<Double>(); - - private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; - - private long mStatsPeriod = 0; - private double mMaxPower = 1; - private double mTotalPower; - private double mTotalPowermAh; - private double mWifiPower; - private double mBluetoothPower; - private double mMinDrainedPower; - private double mMaxDrainedPower; - - // How much the apps together have left WIFI running. - private long mAppWifiRunning; - - /** Queue for fetching name and icon for an application */ - private ArrayList<BatterySipper> mRequestQueue = new ArrayList<BatterySipper>(); - - private Activity mActivity; - private Handler mHandler; - - private class NameAndIconLoader extends Thread { - private boolean mAbort = false; - - public NameAndIconLoader() { - super("BatteryUsage Icon Loader"); - } - - public void abort() { - mAbort = true; - } - - @Override - public void run() { - while (true) { - BatterySipper bs; - synchronized (mRequestQueue) { - if (mRequestQueue.isEmpty() || mAbort) { - mHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN); - return; - } - bs = mRequestQueue.remove(0); - } - bs.loadNameAndIcon(); - } - } - } - - private NameAndIconLoader mRequestThread; - - public BatteryStatsHelper(Activity activity, Handler handler) { - mActivity = activity; - mHandler = handler; - } - - /** Clears the current stats and forces recreating for future use. */ - public void clearStats() { - mStats = null; - } - - public BatteryStatsImpl getStats() { - if (mStats == null) { - load(); - } - return mStats; - } - - public PowerProfile getPowerProfile() { - return mPowerProfile; - } - - public void create(Bundle icicle) { - if (icicle != null) { - mStats = sStatsXfer; - } - mBatteryInfo = IBatteryStats.Stub.asInterface( - ServiceManager.getService(BatteryStats.SERVICE_NAME)); - mUm = (UserManager) mActivity.getSystemService(Context.USER_SERVICE); - mPowerProfile = new PowerProfile(mActivity); - } - - public void pause() { - if (mRequestThread != null) { - mRequestThread.abort(); - } - } - - public void destroy() { - if (mActivity.isChangingConfigurations()) { - sStatsXfer = mStats; - } else { - BatterySipper.sUidCache.clear(); - } - } - - public void startBatteryDetailPage( - PreferenceActivity caller, BatterySipper sipper, boolean showLocationButton) { - // Initialize mStats if necessary. - getStats(); - - Bundle args = new Bundle(); - args.putString(PowerUsageDetail.EXTRA_TITLE, sipper.name); - args.putInt(PowerUsageDetail.EXTRA_PERCENT, (int) - Math.ceil(sipper.getSortValue() * 100 / mTotalPower)); - args.putInt(PowerUsageDetail.EXTRA_GAUGE, (int) - Math.ceil(sipper.getSortValue() * 100 / mMaxPower)); - args.putLong(PowerUsageDetail.EXTRA_USAGE_DURATION, mStatsPeriod); - args.putString(PowerUsageDetail.EXTRA_ICON_PACKAGE, sipper.defaultPackageName); - args.putInt(PowerUsageDetail.EXTRA_ICON_ID, sipper.iconId); - args.putDouble(PowerUsageDetail.EXTRA_NO_COVERAGE, sipper.noCoveragePercent); - if (sipper.uidObj != null) { - args.putInt(PowerUsageDetail.EXTRA_UID, sipper.uidObj.getUid()); - } - args.putSerializable(PowerUsageDetail.EXTRA_DRAIN_TYPE, sipper.drainType); - args.putBoolean(PowerUsageDetail.EXTRA_SHOW_LOCATION_BUTTON, showLocationButton); - - int[] types; - double[] values; - switch (sipper.drainType) { - case APP: - case USER: - { - Uid uid = sipper.uidObj; - types = new int[] { - R.string.usage_type_cpu, - R.string.usage_type_cpu_foreground, - R.string.usage_type_wake_lock, - R.string.usage_type_gps, - R.string.usage_type_wifi_running, - R.string.usage_type_data_recv, - R.string.usage_type_data_send, - R.string.usage_type_data_wifi_recv, - R.string.usage_type_data_wifi_send, - R.string.usage_type_audio, - R.string.usage_type_video, - }; - values = new double[] { - sipper.cpuTime, - sipper.cpuFgTime, - sipper.wakeLockTime, - sipper.gpsTime, - sipper.wifiRunningTime, - sipper.mobileRxPackets, - sipper.mobileTxPackets, - sipper.wifiRxPackets, - sipper.wifiTxPackets, - 0, - 0 - }; - - if (sipper.drainType == DrainType.APP) { - Writer result = new StringWriter(); - PrintWriter printWriter = new FastPrintWriter(result, false, 1024); - mStats.dumpLocked(printWriter, "", mStatsType, uid.getUid()); - printWriter.flush(); - args.putString(PowerUsageDetail.EXTRA_REPORT_DETAILS, result.toString()); - - result = new StringWriter(); - printWriter = new FastPrintWriter(result, false, 1024); - mStats.dumpCheckinLocked(printWriter, mStatsType, uid.getUid()); - printWriter.flush(); - args.putString(PowerUsageDetail.EXTRA_REPORT_CHECKIN_DETAILS, - result.toString()); - } - } - break; - case CELL: - { - types = new int[] { - R.string.usage_type_on_time, - R.string.usage_type_no_coverage - }; - values = new double[] { - sipper.usageTime, - sipper.noCoveragePercent - }; - } - break; - case WIFI: - { - types = new int[] { - R.string.usage_type_wifi_running, - R.string.usage_type_cpu, - R.string.usage_type_cpu_foreground, - R.string.usage_type_wake_lock, - R.string.usage_type_data_recv, - R.string.usage_type_data_send, - R.string.usage_type_data_wifi_recv, - R.string.usage_type_data_wifi_send, - }; - values = new double[] { - sipper.usageTime, - sipper.cpuTime, - sipper.cpuFgTime, - sipper.wakeLockTime, - sipper.mobileRxPackets, - sipper.mobileTxPackets, - sipper.wifiRxPackets, - sipper.wifiTxPackets, - }; - } break; - case BLUETOOTH: - { - types = new int[] { - R.string.usage_type_on_time, - R.string.usage_type_cpu, - R.string.usage_type_cpu_foreground, - R.string.usage_type_wake_lock, - R.string.usage_type_data_recv, - R.string.usage_type_data_send, - R.string.usage_type_data_wifi_recv, - R.string.usage_type_data_wifi_send, - }; - values = new double[] { - sipper.usageTime, - sipper.cpuTime, - sipper.cpuFgTime, - sipper.wakeLockTime, - sipper.mobileRxPackets, - sipper.mobileTxPackets, - sipper.wifiRxPackets, - sipper.wifiTxPackets, - }; - } break; - case UNACCOUNTED: - case OVERCOUNTED: - { - types = new int[] { - R.string.usage_type_total_battery_capacity, - R.string.usage_type_computed_power, - R.string.usage_type_min_actual_power, - R.string.usage_type_max_actual_power, - }; - values = new double[] { - mPowerProfile.getBatteryCapacity(), - mTotalPowermAh, - mMinDrainedPower, - mMaxDrainedPower, - }; - } break; - default: - { - types = new int[] { - R.string.usage_type_on_time - }; - values = new double[] { - sipper.usageTime - }; - } - } - args.putIntArray(PowerUsageDetail.EXTRA_DETAIL_TYPES, types); - args.putDoubleArray(PowerUsageDetail.EXTRA_DETAIL_VALUES, values); - caller.startPreferencePanel(PowerUsageDetail.class.getName(), args, - R.string.details_title, null, null, 0); - } - - /** - * Refreshes the power usage list. - * @param includeZeroConsumption whether includes those applications which have consumed very - * little power up till now. - */ - public void refreshStats(boolean includeZeroConsumption) { - // Initialize mStats if necessary. - getStats(); - - mMaxPower = 0; - mTotalPower = 0; - mWifiPower = 0; - mBluetoothPower = 0; - mAppWifiRunning = 0; - - mUsageList.clear(); - mWifiSippers.clear(); - mBluetoothSippers.clear(); - mUserSippers.clear(); - mUserPower.clear(); - - mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() - * mPowerProfile.getBatteryCapacity()) / 100; - mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() - * mPowerProfile.getBatteryCapacity()) / 100; - - processAppUsage(includeZeroConsumption); - processMiscUsage(); - - // We have been computing totals in seconds, convert to hours. - mTotalPowermAh = mTotalPower / 3600; - - if (true || mStats.getLowDischargeAmountSinceCharge() > 10) { - if (mMinDrainedPower > mTotalPowermAh) { - double amount = mMinDrainedPower - mTotalPowermAh; - if (mMaxPower < amount) { - mMaxPower = amount; - } - addEntryNoTotal(mActivity.getString(R.string.power_unaccounted), - DrainType.UNACCOUNTED, 0, R.drawable.ic_power_system, amount * 3600); - } else if (mMaxDrainedPower < mTotalPowermAh) { - double amount = mTotalPowermAh - mMaxDrainedPower; - if (mMaxPower < amount) { - mMaxPower = amount; - } - addEntryNoTotal(mActivity.getString(R.string.power_overcounted), - DrainType.OVERCOUNTED, 0, R.drawable.ic_power_system, amount * 3600); - } - } - - Collections.sort(mUsageList); - - if (mHandler != null) { - synchronized (mRequestQueue) { - if (!mRequestQueue.isEmpty()) { - if (mRequestThread != null) { - mRequestThread.abort(); - } - mRequestThread = new NameAndIconLoader(); - mRequestThread.setPriority(Thread.MIN_PRIORITY); - mRequestThread.start(); - mRequestQueue.notify(); - } - } - } - } - - private void processAppUsage(boolean includeZeroConsumption) { - SensorManager sensorManager = (SensorManager) mActivity.getSystemService( - Context.SENSOR_SERVICE); - final int which = mStatsType; - final int speedSteps = mPowerProfile.getNumSpeedSteps(); - final double[] powerCpuNormal = new double[speedSteps]; - final long[] cpuSpeedStepTimes = new long[speedSteps]; - for (int p = 0; p < speedSteps; p++) { - powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); - } - final double mobilePowerPerPacket = getMobilePowerPerPacket(); - final double wifiPowerPerPacket = getWifiPowerPerPacket(); - long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which); - long appWakelockTime = 0; - BatterySipper osApp = null; - mStatsPeriod = uSecTime; - SparseArray<? extends Uid> uidStats = mStats.getUidStats(); - final int NU = uidStats.size(); - for (int iu = 0; iu < NU; iu++) { - Uid u = uidStats.valueAt(iu); - double p; // in mAs - double power = 0; // in mAs - double highestDrain = 0; - String packageWithHighestDrain = null; - //mUsageList.add(new AppUsage(u.getUid(), new double[] {power})); - Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); - long cpuTime = 0; - long cpuFgTime = 0; - long wakelockTime = 0; - long gpsTime = 0; - if (DEBUG) Log.i(TAG, "UID " + u.getUid()); - if (processStats.size() > 0) { - // Process CPU time - for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent - : processStats.entrySet()) { - Uid.Proc ps = ent.getValue(); - final long userTime = ps.getUserTime(which); - final long systemTime = ps.getSystemTime(which); - final long foregroundTime = ps.getForegroundTime(which); - cpuFgTime += foregroundTime * 10; // convert to millis - final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis - int totalTimeAtSpeeds = 0; - // Get the total first - for (int step = 0; step < speedSteps; step++) { - cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which); - totalTimeAtSpeeds += cpuSpeedStepTimes[step]; - } - if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1; - // Then compute the ratio of time spent at each speed - double processPower = 0; - for (int step = 0; step < speedSteps; step++) { - double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds; - processPower += ratio * tmpCpuTime * powerCpuNormal[step]; - } - cpuTime += tmpCpuTime; - if (DEBUG && processPower != 0) { - Log.i(TAG, String.format("process %s, cpu power=%.2f", - ent.getKey(), processPower / 1000)); - } - power += processPower; - if (packageWithHighestDrain == null - || packageWithHighestDrain.startsWith("*")) { - highestDrain = processPower; - packageWithHighestDrain = ent.getKey(); - } else if (highestDrain < processPower - && !ent.getKey().startsWith("*")) { - highestDrain = processPower; - packageWithHighestDrain = ent.getKey(); - } - } - } - if (cpuFgTime > cpuTime) { - if (DEBUG && cpuFgTime > cpuTime + 10000) { - Log.i(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); - } - cpuTime = cpuFgTime; // Statistics may not have been gathered yet. - } - power /= 1000; - if (DEBUG && power != 0) Log.i(TAG, String.format("total cpu power=%.2f", power)); - - // Process wake lock usage - Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats(); - for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry - : wakelockStats.entrySet()) { - Uid.Wakelock wakelock = wakelockEntry.getValue(); - // Only care about partial wake locks since full wake locks - // are canceled when the user turns the screen off. - BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL); - if (timer != null) { - wakelockTime += timer.getTotalTimeLocked(uSecTime, which); - } - } - wakelockTime /= 1000; // convert to millis - appWakelockTime += wakelockTime; - - // Add cost of holding a wake lock - p = (wakelockTime - * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / 1000; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("wakelock power=%.2f", p)); - - // Add cost of mobile traffic - final long mobileRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType); - final long mobileTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType); - final long mobileRxB = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType); - final long mobileTxB = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType); - p = (mobileRx + mobileTx) * mobilePowerPerPacket; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("mobile power=%.2f", p)); - - // Add cost of wifi traffic - final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType); - final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType); - final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType); - final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType); - p = (wifiRx + wifiTx) * wifiPowerPerPacket; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("wifi power=%.2f", p)); - - // Add cost of keeping WIFI running. - long wifiRunningTimeMs = u.getWifiRunningTime(uSecTime, which) / 1000; - mAppWifiRunning += wifiRunningTimeMs; - p = (wifiRunningTimeMs - * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("wifi running power=%.2f", p)); - - // Add cost of WIFI scans - long wifiScanTimeMs = u.getWifiScanTime(uSecTime, which) / 1000; - p = (wifiScanTimeMs - * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / 1000; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("wifi scanning power=%.2f", p)); - for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { - long batchScanTimeMs = u.getWifiBatchedScanTime(bin, uSecTime, which) / 1000; - p = (batchScanTimeMs - * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin)); - power += p; - if (DEBUG && p != 0) { - Log.i(TAG, String.format("wifi batched scanning lvl %d = %.2f", bin, p)); - } - } - - // Process Sensor usage - Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats(); - for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry - : sensorStats.entrySet()) { - Uid.Sensor sensor = sensorEntry.getValue(); - int sensorHandle = sensor.getHandle(); - BatteryStats.Timer timer = sensor.getSensorTime(); - long sensorTime = timer.getTotalTimeLocked(uSecTime, which) / 1000; - double multiplier = 0; - switch (sensorHandle) { - case Uid.Sensor.GPS: - multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON); - gpsTime = sensorTime; - break; - default: - List<Sensor> sensorList = sensorManager.getSensorList( - android.hardware.Sensor.TYPE_ALL); - for (android.hardware.Sensor s : sensorList) { - if (s.getHandle() == sensorHandle) { - multiplier = s.getPower(); - break; - } - } - } - p = (multiplier * sensorTime) / 1000; - power += p; - if (DEBUG && p != 0) { - Log.i(TAG, String.format("sensor %s power=%.2f", sensor.toString(), p)); - } - } - - if (DEBUG) Log.i(TAG, String.format("UID %d total power=%.2f", u.getUid(), power)); - - // Add the app to the list if it is consuming power - boolean isOtherUser = false; - final int userId = UserHandle.getUserId(u.getUid()); - if (power != 0 || includeZeroConsumption || u.getUid() == 0) { - BatterySipper app = new BatterySipper(mActivity, mRequestQueue, mHandler, - packageWithHighestDrain, DrainType.APP, 0, u, - new double[] {power}); - app.cpuTime = cpuTime; - app.gpsTime = gpsTime; - app.wifiRunningTime = wifiRunningTimeMs; - app.cpuFgTime = cpuFgTime; - app.wakeLockTime = wakelockTime; - app.mobileRxPackets = mobileRx; - app.mobileTxPackets = mobileTx; - app.wifiRxPackets = wifiRx; - app.wifiTxPackets = wifiTx; - app.mobileRxBytes = mobileRxB; - app.mobileTxBytes = mobileTxB; - app.wifiRxBytes = wifiRxB; - app.wifiTxBytes = wifiTxB; - if (u.getUid() == Process.WIFI_UID) { - mWifiSippers.add(app); - } else if (u.getUid() == Process.BLUETOOTH_UID) { - mBluetoothSippers.add(app); - } else if (userId != UserHandle.myUserId() - && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) { - isOtherUser = true; - List<BatterySipper> list = mUserSippers.get(userId); - if (list == null) { - list = new ArrayList<BatterySipper>(); - mUserSippers.put(userId, list); - } - list.add(app); - } else { - mUsageList.add(app); - } - if (u.getUid() == 0) { - osApp = app; - } - } - if (power != 0 || includeZeroConsumption) { - if (u.getUid() == Process.WIFI_UID) { - mWifiPower += power; - } else if (u.getUid() == Process.BLUETOOTH_UID) { - mBluetoothPower += power; - } else if (isOtherUser) { - Double userPower = mUserPower.get(userId); - if (userPower == null) { - userPower = power; - } else { - userPower += power; - } - mUserPower.put(userId, userPower); - } else { - if (power > mMaxPower) mMaxPower = power; - mTotalPower += power; - } - } - } - - // The device has probably been awake for longer than the screen on - // time and application wake lock time would account for. Assign - // this remainder to the OS, if possible. - if (osApp != null) { - long wakeTimeMillis = mStats.computeBatteryUptime( - SystemClock.uptimeMillis() * 1000, which) / 1000; - wakeTimeMillis -= appWakelockTime + (mStats.getScreenOnTime( - SystemClock.elapsedRealtime(), which) / 1000); - if (wakeTimeMillis > 0) { - double power = (wakeTimeMillis - * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / 1000; - if (DEBUG) Log.i(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + power); - osApp.wakeLockTime += wakeTimeMillis; - osApp.value += power; - osApp.values[0] += power; - if (osApp.value > mMaxPower) mMaxPower = osApp.value; - mTotalPower += power; - } - } - } - - private void addPhoneUsage(long uSecNow) { - long phoneOnTimeMs = mStats.getPhoneOnTime(uSecNow, mStatsType) / 1000; - double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) - * phoneOnTimeMs / 1000; - addEntry(mActivity.getString(R.string.power_phone), DrainType.PHONE, phoneOnTimeMs, - R.drawable.ic_settings_voice_calls, phoneOnPower); - } - - private void addScreenUsage(long uSecNow) { - double power = 0; - long screenOnTimeMs = mStats.getScreenOnTime(uSecNow, mStatsType) / 1000; - power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); - final double screenFullPower = - mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); - for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { - double screenBinPower = screenFullPower * (i + 0.5f) - / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; - long brightnessTime = mStats.getScreenBrightnessTime(i, uSecNow, mStatsType) / 1000; - power += screenBinPower * brightnessTime; - if (DEBUG) { - Log.i(TAG, "Screen bin power = " + (int) screenBinPower + ", time = " - + brightnessTime); - } - } - power /= 1000; // To seconds - addEntry(mActivity.getString(R.string.power_screen), DrainType.SCREEN, screenOnTimeMs, - R.drawable.ic_settings_display, power); - } - - private void addRadioUsage(long uSecNow) { - double power = 0; - final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; - long signalTimeMs = 0; - for (int i = 0; i < BINS; i++) { - long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, uSecNow, mStatsType) / 1000; - power += strengthTimeMs / 1000 - * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i); - signalTimeMs += strengthTimeMs; - } - long scanningTimeMs = mStats.getPhoneSignalScanningTime(uSecNow, mStatsType) / 1000; - power += scanningTimeMs / 1000 * mPowerProfile.getAveragePower( - PowerProfile.POWER_RADIO_SCANNING); - BatterySipper bs = - addEntry(mActivity.getString(R.string.power_cell), DrainType.CELL, - signalTimeMs, R.drawable.ic_settings_cell_standby, power); - if (signalTimeMs != 0) { - bs.noCoveragePercent = mStats.getPhoneSignalStrengthTime(0, uSecNow, mStatsType) - / 1000 * 100.0 / signalTimeMs; - } - } - - private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) { - for (int i=0; i<from.size(); i++) { - BatterySipper wbs = from.get(i); - if (DEBUG) Log.i(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime); - bs.cpuTime += wbs.cpuTime; - bs.gpsTime += wbs.gpsTime; - bs.wifiRunningTime += wbs.wifiRunningTime; - bs.cpuFgTime += wbs.cpuFgTime; - bs.wakeLockTime += wbs.wakeLockTime; - bs.mobileRxPackets += wbs.mobileRxPackets; - bs.mobileTxPackets += wbs.mobileTxPackets; - bs.wifiRxPackets += wbs.wifiRxPackets; - bs.wifiTxPackets += wbs.wifiTxPackets; - bs.mobileRxBytes += wbs.mobileRxBytes; - bs.mobileTxBytes += wbs.mobileTxBytes; - bs.wifiRxBytes += wbs.wifiRxBytes; - bs.wifiTxBytes += wbs.wifiTxBytes; - } - } - - private void addWiFiUsage(long uSecNow) { - long onTimeMs = mStats.getWifiOnTime(uSecNow, mStatsType) / 1000; - long runningTimeMs = mStats.getGlobalWifiRunningTime(uSecNow, mStatsType) / 1000; - if (DEBUG) Log.i(TAG, "WIFI runningTime=" + runningTimeMs - + " app runningTime=" + mAppWifiRunning); - runningTimeMs -= mAppWifiRunning; - if (runningTimeMs < 0) runningTimeMs = 0; - double wifiPower = (onTimeMs * 0 /* TODO */ - * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON) - + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000; - if (DEBUG) Log.i(TAG, "WIFI power=" + wifiPower + " from procs=" + mWifiPower); - BatterySipper bs = addEntry(mActivity.getString(R.string.power_wifi), DrainType.WIFI, - runningTimeMs, R.drawable.ic_settings_wifi, wifiPower + mWifiPower); - aggregateSippers(bs, mWifiSippers, "WIFI"); - } - - private void addIdleUsage(long uSecNow) { - long idleTimeMs = (uSecNow - mStats.getScreenOnTime(uSecNow, mStatsType)) / 1000; - double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) - / 1000; - addEntry(mActivity.getString(R.string.power_idle), DrainType.IDLE, idleTimeMs, - R.drawable.ic_settings_phone_idle, idlePower); - } - - private void addBluetoothUsage(long uSecNow) { - long btOnTimeMs = mStats.getBluetoothOnTime(uSecNow, mStatsType) / 1000; - double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON) - / 1000; - int btPingCount = mStats.getBluetoothPingCount(); - btPower += (btPingCount - * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) / 1000; - BatterySipper bs = addEntry(mActivity.getString(R.string.power_bluetooth), - DrainType.BLUETOOTH, btOnTimeMs, R.drawable.ic_settings_bluetooth, - btPower + mBluetoothPower); - aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); - } - - private void addUserUsage() { - for (int i=0; i<mUserSippers.size(); i++) { - final int userId = mUserSippers.keyAt(i); - final List<BatterySipper> sippers = mUserSippers.valueAt(i); - UserInfo info = mUm.getUserInfo(userId); - Drawable icon; - String name; - if (info != null) { - icon = UserUtils.getUserIcon(mActivity, mUm, info, mActivity.getResources()); - name = info != null ? info.name : null; - if (name == null) { - name = Integer.toString(info.id); - } - name = mActivity.getResources().getString( - R.string.running_process_item_user_label, name); - } else { - icon = null; - name = mActivity.getResources().getString( - R.string.running_process_item_removed_user_label); - } - Double userPower = mUserPower.get(userId); - double power = (userPower != null) ? userPower : 0.0; - BatterySipper bs = addEntry(name, DrainType.USER, 0, 0, power); - bs.icon = icon; - aggregateSippers(bs, sippers, "User"); - } - } - - /** - * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio. - */ - private double getMobilePowerPerPacket() { - final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system - final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) - / 3600; - - final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType); - final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType); - final long mobileData = mobileRx + mobileTx; - - final long radioDataUptimeMs = mStats.getRadioDataUptime() / 1000; - final double mobilePps = radioDataUptimeMs != 0 - ? mobileData / (double)radioDataUptimeMs - : (((double)MOBILE_BPS) / 8 / 2048); - - return MOBILE_POWER / mobilePps; - } - - /** - * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio. - */ - private double getWifiPowerPerPacket() { - final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system - final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) - / 3600; - return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048); - } - - private void processMiscUsage() { - final int which = mStatsType; - long uSecTime = SystemClock.elapsedRealtime() * 1000; - final long uSecNow = mStats.computeBatteryRealtime(uSecTime, which); - final long timeSinceUnplugged = uSecNow; - if (DEBUG) { - Log.i(TAG, "Uptime since last unplugged = " + (timeSinceUnplugged / 1000)); - } - - addUserUsage(); - addPhoneUsage(uSecNow); - addScreenUsage(uSecNow); - addWiFiUsage(uSecNow); - addBluetoothUsage(uSecNow); - addIdleUsage(uSecNow); // Not including cellular idle power - // Don't compute radio usage if it's a wifi-only device - if (!com.android.settings.Utils.isWifiOnly(mActivity)) { - addRadioUsage(uSecNow); - } - } - - private BatterySipper addEntry(String label, DrainType drainType, long time, int iconId, - double power) { - mTotalPower += power; - return addEntryNoTotal(label, drainType, time, iconId, power); - } - - private BatterySipper addEntryNoTotal(String label, DrainType drainType, long time, int iconId, - double power) { - if (power > mMaxPower) mMaxPower = power; - mTotalPower += power; - BatterySipper bs = new BatterySipper(mActivity, mRequestQueue, mHandler, - label, drainType, iconId, null, new double[] {power}); - bs.usageTime = time; - bs.iconId = iconId; - mUsageList.add(bs); - return bs; - } - - public List<BatterySipper> getUsageList() { - return mUsageList; - } - - static final int MSG_UPDATE_NAME_ICON = 1; - static final int MSG_REPORT_FULLY_DRAWN = 2; - - public double getMaxPower() { - return mMaxPower; - } - - public double getTotalPower() { - return mTotalPower; - } - - public double getMinDrainedPower() { - return mMinDrainedPower; - } - - public double getMaxDrainedPower() { - return mMaxDrainedPower; - } - - private void load() { - try { - byte[] data = mBatteryInfo.getStatistics(); - Parcel parcel = Parcel.obtain(); - parcel.unmarshall(data, 0, data.length); - parcel.setDataPosition(0); - mStats = com.android.internal.os.BatteryStatsImpl.CREATOR - .createFromParcel(parcel); - mStats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException:", e); - } - } -} diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java index c8bfa21..e10b74f 100644 --- a/src/com/android/settings/fuelgauge/PowerGaugePreference.java +++ b/src/com/android/settings/fuelgauge/PowerGaugePreference.java @@ -24,6 +24,7 @@ import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; +import com.android.internal.os.BatterySipper; import com.android.settings.R; /** @@ -31,11 +32,11 @@ import com.android.settings.R; * the left for the subsystem/app type. */ public class PowerGaugePreference extends Preference { - private BatterySipper mInfo; + private BatteryEntry mInfo; private int mProgress; private CharSequence mProgressText; - public PowerGaugePreference(Context context, Drawable icon, BatterySipper info) { + public PowerGaugePreference(Context context, Drawable icon, BatteryEntry info) { super(context); setLayoutResource(R.layout.app_percentage_item); setIcon(icon != null ? icon : new ColorDrawable(0)); @@ -49,7 +50,7 @@ public class PowerGaugePreference extends Preference { notifyChanged(); } - BatterySipper getInfo() { + BatteryEntry getInfo() { return mInfo; } diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java index 79b7c42..6a3a31b 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java @@ -34,13 +34,13 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.BatteryStats; import android.os.Bundle; import android.os.Process; import android.os.UserHandle; import android.preference.PreferenceActivity; import android.provider.Settings; import android.text.TextUtils; -import android.text.format.Formatter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -49,6 +49,9 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; +import com.android.internal.util.FastPrintWriter; import com.android.settings.DisplaySettings; import com.android.settings.R; import com.android.settings.WirelessSettings; @@ -57,20 +60,11 @@ import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.location.LocationSettings; import com.android.settings.wifi.WifiSettings; -public class PowerUsageDetail extends Fragment implements Button.OnClickListener { +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; - enum DrainType { - IDLE, - CELL, - PHONE, - WIFI, - BLUETOOTH, - SCREEN, - APP, - USER, - UNACCOUNTED, - OVERCOUNTED - } +public class PowerUsageDetail extends Fragment implements Button.OnClickListener { // Note: Must match the sequence of the DrainType private static int[] sDrainTypeDesciptions = new int[] { @@ -86,6 +80,170 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener R.string.battery_desc_overcounted, }; + public static void startBatteryDetailPage( + PreferenceActivity caller, BatteryStatsHelper helper, BatteryEntry entry, + boolean showLocationButton) { + // Initialize mStats if necessary. + helper.getStats(); + + Bundle args = new Bundle(); + args.putString(PowerUsageDetail.EXTRA_TITLE, entry.name); + args.putInt(PowerUsageDetail.EXTRA_PERCENT, (int) + Math.ceil(entry.sipper.value * 100 / helper.getTotalPower())); + args.putInt(PowerUsageDetail.EXTRA_GAUGE, (int) + Math.ceil(entry.sipper.value * 100 / helper.getMaxPower())); + args.putLong(PowerUsageDetail.EXTRA_USAGE_DURATION, helper.getStatsPeriod()); + args.putString(PowerUsageDetail.EXTRA_ICON_PACKAGE, entry.defaultPackageName); + args.putInt(PowerUsageDetail.EXTRA_ICON_ID, entry.iconId); + args.putDouble(PowerUsageDetail.EXTRA_NO_COVERAGE, entry.sipper.noCoveragePercent); + if (entry.sipper.uidObj != null) { + args.putInt(PowerUsageDetail.EXTRA_UID, entry.sipper.uidObj.getUid()); + } + args.putSerializable(PowerUsageDetail.EXTRA_DRAIN_TYPE, entry.sipper.drainType); + args.putBoolean(PowerUsageDetail.EXTRA_SHOW_LOCATION_BUTTON, showLocationButton); + + int[] types; + double[] values; + switch (entry.sipper.drainType) { + case APP: + case USER: + { + BatteryStats.Uid uid = entry.sipper.uidObj; + types = new int[] { + R.string.usage_type_cpu, + R.string.usage_type_cpu_foreground, + R.string.usage_type_wake_lock, + R.string.usage_type_gps, + R.string.usage_type_wifi_running, + R.string.usage_type_data_recv, + R.string.usage_type_data_send, + R.string.usage_type_data_wifi_recv, + R.string.usage_type_data_wifi_send, + R.string.usage_type_audio, + R.string.usage_type_video, + }; + values = new double[] { + entry.sipper.cpuTime, + entry.sipper.cpuFgTime, + entry.sipper.wakeLockTime, + entry.sipper.gpsTime, + entry.sipper.wifiRunningTime, + entry.sipper.mobileRxPackets, + entry.sipper.mobileTxPackets, + entry.sipper.wifiRxPackets, + entry.sipper.wifiTxPackets, + 0, + 0 + }; + + if (entry.sipper.drainType == BatterySipper.DrainType.APP) { + Writer result = new StringWriter(); + PrintWriter printWriter = new FastPrintWriter(result, false, 1024); + helper.getStats().dumpLocked(caller, printWriter, "", helper.getStatsType(), + uid.getUid()); + printWriter.flush(); + args.putString(PowerUsageDetail.EXTRA_REPORT_DETAILS, result.toString()); + + result = new StringWriter(); + printWriter = new FastPrintWriter(result, false, 1024); + helper.getStats().dumpCheckinLocked(caller, printWriter, helper.getStatsType(), + uid.getUid()); + printWriter.flush(); + args.putString(PowerUsageDetail.EXTRA_REPORT_CHECKIN_DETAILS, + result.toString()); + } + } + break; + case CELL: + { + types = new int[] { + R.string.usage_type_on_time, + R.string.usage_type_no_coverage + }; + values = new double[] { + entry.sipper.usageTime, + entry.sipper.noCoveragePercent + }; + } + break; + case WIFI: + { + types = new int[] { + R.string.usage_type_wifi_running, + R.string.usage_type_cpu, + R.string.usage_type_cpu_foreground, + R.string.usage_type_wake_lock, + R.string.usage_type_data_recv, + R.string.usage_type_data_send, + R.string.usage_type_data_wifi_recv, + R.string.usage_type_data_wifi_send, + }; + values = new double[] { + entry.sipper.usageTime, + entry.sipper.cpuTime, + entry.sipper.cpuFgTime, + entry.sipper.wakeLockTime, + entry.sipper.mobileRxPackets, + entry.sipper.mobileTxPackets, + entry.sipper.wifiRxPackets, + entry.sipper.wifiTxPackets, + }; + } break; + case BLUETOOTH: + { + types = new int[] { + R.string.usage_type_on_time, + R.string.usage_type_cpu, + R.string.usage_type_cpu_foreground, + R.string.usage_type_wake_lock, + R.string.usage_type_data_recv, + R.string.usage_type_data_send, + R.string.usage_type_data_wifi_recv, + R.string.usage_type_data_wifi_send, + }; + values = new double[] { + entry.sipper.usageTime, + entry.sipper.cpuTime, + entry.sipper.cpuFgTime, + entry.sipper.wakeLockTime, + entry.sipper.mobileRxPackets, + entry.sipper.mobileTxPackets, + entry.sipper.wifiRxPackets, + entry.sipper.wifiTxPackets, + }; + } break; + case UNACCOUNTED: + case OVERCOUNTED: + { + types = new int[] { + R.string.usage_type_total_battery_capacity, + R.string.usage_type_computed_power, + R.string.usage_type_min_actual_power, + R.string.usage_type_max_actual_power, + }; + values = new double[] { + helper.getPowerProfile().getBatteryCapacity(), + helper.getTotalPower(), + helper.getMinDrainedPower(), + helper.getMaxDrainedPower(), + }; + } break; + default: + { + types = new int[] { + R.string.usage_type_on_time + }; + values = new double[] { + entry.sipper.usageTime + }; + } + } + args.putIntArray(PowerUsageDetail.EXTRA_DETAIL_TYPES, types); + args.putDoubleArray(PowerUsageDetail.EXTRA_DETAIL_VALUES, values); + caller.startPreferencePanel(PowerUsageDetail.class.getName(), args, + R.string.details_title, null, null, 0); + } + public static final int ACTION_DISPLAY_SETTINGS = 1; public static final int ACTION_WIFI_SETTINGS = 2; public static final int ACTION_BLUETOOTH_SETTINGS = 3; @@ -129,7 +287,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener private ViewGroup mDetailsParent; private ViewGroup mControlsParent; private long mStartTime; - private DrainType mDrainType; + private BatterySipper.DrainType mDrainType; private Drawable mAppIcon; private double mNoCoverage; // Percentage of time that there was no coverage @@ -179,7 +337,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener final int gaugeValue = args.getInt(EXTRA_GAUGE, 1); mUsageSince = args.getInt(EXTRA_USAGE_SINCE, USAGE_SINCE_UNPLUGGED); mUid = args.getInt(EXTRA_UID, 0); - mDrainType = (DrainType) args.getSerializable(EXTRA_DRAIN_TYPE); + mDrainType = (BatterySipper.DrainType) args.getSerializable(EXTRA_DRAIN_TYPE); mNoCoverage = args.getDouble(EXTRA_NO_COVERAGE, 0); String iconPackage = args.getString(EXTRA_ICON_PACKAGE); int iconId = args.getInt(EXTRA_ICON_ID, 0); diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 2c78375..06aff2e 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -26,6 +26,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Parcel; +import android.os.UserHandle; +import android.os.UserManager; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; @@ -36,6 +38,8 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.PowerProfile; import com.android.settings.HelpUtils; import com.android.settings.R; @@ -59,6 +63,8 @@ public class PowerUsageSummary extends PreferenceFragment { private static final int MENU_STATS_REFRESH = Menu.FIRST + 1; private static final int MENU_HELP = Menu.FIRST + 2; + private UserManager mUm; + private PreferenceGroup mAppListGroup; private String mBatteryLevel; private String mBatteryStatus; @@ -87,7 +93,8 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void onAttach(Activity activity) { super.onAttach(activity); - mStatsHelper = new BatteryStatsHelper(activity, mHandler); + mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE); + mStatsHelper = new BatteryStatsHelper(activity); } @Override @@ -111,8 +118,8 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void onPause() { - mStatsHelper.pause(); - mHandler.removeMessages(BatteryStatsHelper.MSG_UPDATE_NAME_ICON); + BatteryEntry.stopRequestQueue(); + mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON); getActivity().unregisterReceiver(mBatteryInfoReceiver); super.onPause(); } @@ -120,7 +127,10 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void onDestroy() { super.onDestroy(); - mStatsHelper.destroy(); + if (getActivity().isChangingConfigurations()) { + mStatsHelper.storeState(); + BatteryEntry.clearUidCache(); + } } @Override @@ -140,8 +150,9 @@ public class PowerUsageSummary extends PreferenceFragment { return false; } PowerGaugePreference pgp = (PowerGaugePreference) preference; - BatterySipper sipper = pgp.getInfo(); - mStatsHelper.startBatteryDetailPage((PreferenceActivity) getActivity(), sipper, true); + BatteryEntry entry = pgp.getInfo(); + PowerUsageDetail.startBatteryDetailPage((PreferenceActivity) getActivity(), mStatsHelper, + entry, true); return super.onPreferenceTreeClick(preferenceScreen, preference); } @@ -227,20 +238,22 @@ public class PowerUsageSummary extends PreferenceFragment { addNotAvailableMessage(); return; } - mStatsHelper.refreshStats(false); + mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); List<BatterySipper> usageList = mStatsHelper.getUsageList(); - for (BatterySipper sipper : usageList) { - if (sipper.getSortValue() < MIN_POWER_THRESHOLD) continue; + for (int i=0; i<usageList.size(); i++) { + BatterySipper sipper = usageList.get(i); + if ((sipper.value*60*60) < MIN_POWER_THRESHOLD) continue; final double percentOfTotal = - ((sipper.getSortValue() / mStatsHelper.getTotalPower()) * 100); + ((sipper.value / mStatsHelper.getTotalPower()) * 100); if (percentOfTotal < 1) continue; + BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper); PowerGaugePreference pref = - new PowerGaugePreference(getActivity(), sipper.getIcon(), sipper); + new PowerGaugePreference(getActivity(), entry.getIcon(), entry); final double percentOfMax = - (sipper.getSortValue() * 100) / mStatsHelper.getMaxPower(); + (sipper.value * 100) / mStatsHelper.getMaxPower(); sipper.percent = percentOfTotal; - pref.setTitle(sipper.name); - pref.setOrder(Integer.MAX_VALUE - (int) sipper.getSortValue()); // Invert the order + pref.setTitle(entry.getLabel()); + pref.setOrder(i+1); pref.setPercent(percentOfMax, percentOfTotal); if (sipper.uidObj != null) { pref.setKey(Integer.toString(sipper.uidObj.getUid())); @@ -248,6 +261,8 @@ public class PowerUsageSummary extends PreferenceFragment { mAppListGroup.addPreference(pref); if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) break; } + + BatteryEntry.startRequestQueue(); } Handler mHandler = new Handler() { @@ -255,17 +270,17 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void handleMessage(Message msg) { switch (msg.what) { - case BatteryStatsHelper.MSG_UPDATE_NAME_ICON: - BatterySipper bs = (BatterySipper) msg.obj; + case BatteryEntry.MSG_UPDATE_NAME_ICON: + BatteryEntry entry = (BatteryEntry) msg.obj; PowerGaugePreference pgp = (PowerGaugePreference) findPreference( - Integer.toString(bs.uidObj.getUid())); + Integer.toString(entry.sipper.uidObj.getUid())); if (pgp != null) { - pgp.setIcon(bs.icon); - pgp.setTitle(bs.name); + pgp.setIcon(entry.icon); + pgp.setTitle(entry.name); } break; - case BatteryStatsHelper.MSG_REPORT_FULLY_DRAWN: + case BatteryEntry.MSG_REPORT_FULLY_DRAWN: Activity activity = getActivity(); if (activity != null) { activity.reportFullyDrawn(); |