Some improvements to the manage apps code:

- Battery stats now aggregates bluetooth usage.
- Battery stats now uses new history iterator API.
- Battery stats chart is refectored to have the start of a
  common facility for building tick charts.
- Manage apps will now asynchronously wait for the apps list
  if it is taking >.25ms to build.

Change-Id: I568dd74beedf9a0a5a4c88ab567510cee9af8299
This commit is contained in:
Dianne Hackborn
2010-09-20 11:40:46 -07:00
parent cb818619c6
commit 19df79af26
5 changed files with 415 additions and 216 deletions

View File

@@ -42,6 +42,7 @@ public class ApplicationsState {
public static interface Callbacks { public static interface Callbacks {
public void onRunningStateChanged(boolean running); public void onRunningStateChanged(boolean running);
public void onPackageListChanged(); public void onPackageListChanged();
public void onRebuildComplete(ArrayList<AppEntry> apps);
public void onPackageIconChanged(); public void onPackageIconChanged();
public void onPackageSizeChanged(String packageName); public void onPackageSizeChanged(String packageName);
public void onAllSizesComputed(); public void onAllSizesComputed();
@@ -154,6 +155,14 @@ public class ApplicationsState {
long mCurId = 1; long mCurId = 1;
String mCurComputingSizePkg; String mCurComputingSizePkg;
// Rebuilding of app list. Synchronized on mRebuildSync.
final Object mRebuildSync = new Object();
boolean mRebuildRequested;
boolean mRebuildAsync;
AppFilter mRebuildFilter;
Comparator<AppEntry> mRebuildComparator;
ArrayList<AppEntry> mRebuildResult;
/** /**
* Receives notifications when applications are added/removed. * Receives notifications when applications are added/removed.
*/ */
@@ -209,8 +218,9 @@ public class ApplicationsState {
} }
class MainHandler extends Handler { class MainHandler extends Handler {
static final int MSG_PACKAGE_LIST_CHANGED = 1; static final int MSG_REBUILD_COMPLETE = 1;
static final int MSG_PACKAGE_ICON_CHANGED = 2; static final int MSG_PACKAGE_LIST_CHANGED = 2;
static final int MSG_PACKAGE_ICON_CHANGED = 3;
static final int MSG_PACKAGE_SIZE_CHANGED = 4; static final int MSG_PACKAGE_SIZE_CHANGED = 4;
static final int MSG_ALL_SIZES_COMPUTED = 5; static final int MSG_ALL_SIZES_COMPUTED = 5;
static final int MSG_RUNNING_STATE_CHANGED = 6; static final int MSG_RUNNING_STATE_CHANGED = 6;
@@ -218,6 +228,11 @@ public class ApplicationsState {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
case MSG_REBUILD_COMPLETE: {
if (mCurCallbacks != null) {
mCurCallbacks.onRebuildComplete((ArrayList<AppEntry>)msg.obj);
}
} break;
case MSG_PACKAGE_LIST_CHANGED: { case MSG_PACKAGE_LIST_CHANGED: {
if (mCurCallbacks != null) { if (mCurCallbacks != null) {
mCurCallbacks.onPackageListChanged(); mCurCallbacks.onPackageListChanged();
@@ -305,20 +320,89 @@ public class ApplicationsState {
// Creates a new list of app entries with the given filter and comparator. // Creates a new list of app entries with the given filter and comparator.
ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) { ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>(); synchronized (mRebuildSync) {
mRebuildRequested = true;
mRebuildAsync = false;
mRebuildFilter = filter;
mRebuildComparator = comparator;
mRebuildResult = null;
if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_REBUILD_LIST);
}
// We will wait for .25s for the list to be built.
long waitend = SystemClock.uptimeMillis()+250;
while (mRebuildResult == null) {
long now = SystemClock.uptimeMillis();
if (now >= waitend) {
break;
}
try {
mRebuildSync.wait(waitend - now);
} catch (InterruptedException e) {
}
}
mRebuildAsync = true;
return mRebuildResult;
}
}
void handleRebuildList() {
AppFilter filter;
Comparator<AppEntry> comparator;
synchronized (mRebuildSync) {
if (!mRebuildRequested) {
return;
}
filter = mRebuildFilter;
comparator = mRebuildComparator;
mRebuildRequested = false;
mRebuildFilter = null;
mRebuildComparator = null;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
List<ApplicationInfo> apps;
synchronized (mEntriesMap) { synchronized (mEntriesMap) {
if (DEBUG) Log.i(TAG, "Rebuilding..."); apps = new ArrayList<ApplicationInfo>(mApplications);
for (int i=0; i<mApplications.size(); i++) { }
ApplicationInfo info = mApplications.get(i);
if (filter == null || filter.filterApp(info)) { ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
if (DEBUG) Log.i(TAG, "Rebuilding...");
for (int i=0; i<apps.size(); i++) {
ApplicationInfo info = apps.get(i);
if (filter == null || filter.filterApp(info)) {
synchronized (mEntriesMap) {
AppEntry entry = getEntryLocked(info); AppEntry entry = getEntryLocked(info);
if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry); if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
filteredApps.add(entry); filteredApps.add(entry);
} }
} }
} }
Collections.sort(filteredApps, comparator); Collections.sort(filteredApps, comparator);
return filteredApps;
synchronized (mRebuildSync) {
if (!mRebuildRequested) {
if (!mRebuildAsync) {
mRebuildResult = filteredApps;
mRebuildSync.notifyAll();
} else {
if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE)) {
Message msg = mMainHandler.obtainMessage(
MainHandler.MSG_REBUILD_COMPLETE, filteredApps);
mMainHandler.sendMessage(msg);
}
}
}
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} }
AppEntry getEntry(String packageName) { AppEntry getEntry(String packageName) {
@@ -447,9 +531,10 @@ public class ApplicationsState {
final HandlerThread mThread; final HandlerThread mThread;
final BackgroundHandler mBackgroundHandler; final BackgroundHandler mBackgroundHandler;
class BackgroundHandler extends Handler { class BackgroundHandler extends Handler {
static final int MSG_LOAD_ENTRIES = 1; static final int MSG_REBUILD_LIST = 1;
static final int MSG_LOAD_ICONS = 2; static final int MSG_LOAD_ENTRIES = 2;
static final int MSG_LOAD_SIZES = 3; static final int MSG_LOAD_ICONS = 3;
static final int MSG_LOAD_SIZES = 4;
boolean mRunning; boolean mRunning;
@@ -498,7 +583,12 @@ public class ApplicationsState {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
// Always try rebuilding list first thing, if needed.
handleRebuildList();
switch (msg.what) { switch (msg.what) {
case MSG_REBUILD_LIST: {
} break;
case MSG_LOAD_ENTRIES: { case MSG_LOAD_ENTRIES: {
int numDone = 0; int numDone = 0;
synchronized (mEntriesMap) { synchronized (mEntriesMap) {

View File

@@ -18,6 +18,7 @@ package com.android.settings.applications;
import com.android.internal.content.PackageHelper; import com.android.internal.content.PackageHelper;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.applications.ApplicationsState.AppEntry;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
@@ -384,6 +385,10 @@ public class InstalledAppDetails extends Activity
refreshUi(); refreshUi();
} }
@Override
public void onRebuildComplete(ArrayList<AppEntry> apps) {
}
@Override @Override
public void onPackageSizeChanged(String packageName) { public void onPackageSizeChanged(String packageName) {
if (packageName.equals(mAppEntry.info.packageName)) { if (packageName.equals(mAppEntry.info.packageName)) {

View File

@@ -23,6 +23,7 @@ import android.app.TabActivity;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
@@ -47,6 +48,7 @@ import android.widget.AdapterView.OnItemClickListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
/** /**
* Activity to pick an application that will be used to display installation information and * Activity to pick an application that will be used to display installation information and
@@ -152,6 +154,7 @@ public class ManageApplications extends TabActivity implements
private ArrayList<ApplicationsState.AppEntry> mEntries; private ArrayList<ApplicationsState.AppEntry> mEntries;
private boolean mResumed; private boolean mResumed;
private int mLastFilterMode=-1, mLastSortMode=-1; private int mLastFilterMode=-1, mLastSortMode=-1;
private boolean mWaitingForData;
CharSequence mCurFilterPrefix; CharSequence mCurFilterPrefix;
private Filter mFilter = new Filter() { private Filter mFilter = new Filter() {
@@ -184,7 +187,7 @@ public class ManageApplications extends TabActivity implements
mState.resume(this); mState.resume(this);
mLastFilterMode = filter; mLastFilterMode = filter;
mLastSortMode = sort; mLastSortMode = sort;
rebuild(); rebuild(true);
} else { } else {
rebuild(filter, sort); rebuild(filter, sort);
} }
@@ -203,10 +206,10 @@ public class ManageApplications extends TabActivity implements
} }
mLastFilterMode = filter; mLastFilterMode = filter;
mLastSortMode = sort; mLastSortMode = sort;
rebuild(); rebuild(true);
} }
public void rebuild() { public void rebuild(boolean eraseold) {
if (DEBUG) Log.i(TAG, "Rebuilding app list..."); if (DEBUG) Log.i(TAG, "Rebuilding app list...");
ApplicationsState.AppFilter filterObj; ApplicationsState.AppFilter filterObj;
Comparator<AppEntry> comparatorObj; Comparator<AppEntry> comparatorObj;
@@ -229,9 +232,28 @@ public class ManageApplications extends TabActivity implements
comparatorObj = ApplicationsState.ALPHA_COMPARATOR; comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
break; break;
} }
mBaseEntries = mState.rebuild(filterObj, comparatorObj); ArrayList<ApplicationsState.AppEntry> entries
mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries); = mState.rebuild(filterObj, comparatorObj);
if (entries == null && !eraseold) {
// Don't have new list yet, but can continue using the old one.
return;
}
mBaseEntries = entries;
if (mBaseEntries != null) {
mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
} else {
mEntries = null;
}
notifyDataSetChanged(); notifyDataSetChanged();
if (entries == null) {
mWaitingForData = true;
mListContainer.setVisibility(View.INVISIBLE);
mLoadingContainer.setVisibility(View.VISIBLE);
} else {
mListContainer.setVisibility(View.VISIBLE);
mLoadingContainer.setVisibility(View.GONE);
}
} }
ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix, ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
@@ -259,9 +281,19 @@ public class ManageApplications extends TabActivity implements
setProgressBarIndeterminateVisibility(running); setProgressBarIndeterminateVisibility(running);
} }
@Override
public void onRebuildComplete(ArrayList<AppEntry> apps) {
mListContainer.setVisibility(View.VISIBLE);
mLoadingContainer.setVisibility(View.GONE);
mWaitingForData = false;
mBaseEntries = apps;
mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
notifyDataSetChanged();
}
@Override @Override
public void onPackageListChanged() { public void onPackageListChanged() {
rebuild(); rebuild(false);
} }
@Override @Override
@@ -284,7 +316,7 @@ public class ManageApplications extends TabActivity implements
// user viewed, and are sorting by size... they may // user viewed, and are sorting by size... they may
// have cleared data, so we immediately want to resort // have cleared data, so we immediately want to resort
// the list with the new size to reflect it to the user. // the list with the new size to reflect it to the user.
rebuild(); rebuild(false);
} }
return; return;
} }
@@ -294,7 +326,7 @@ public class ManageApplications extends TabActivity implements
@Override @Override
public void onAllSizesComputed() { public void onAllSizesComputed() {
if (mLastSortMode == SORT_ORDER_SIZE) { if (mLastSortMode == SORT_ORDER_SIZE) {
rebuild(); rebuild(false);
} }
} }
@@ -566,6 +598,7 @@ public class ManageApplications extends TabActivity implements
if (mCurView != which) { if (mCurView != which) {
mRunningProcessesView.setVisibility(View.GONE); mRunningProcessesView.setVisibility(View.GONE);
mListContainer.setVisibility(View.VISIBLE); mListContainer.setVisibility(View.VISIBLE);
mLoadingContainer.setVisibility(View.GONE);
} }
if (mActivityResumed) { if (mActivityResumed) {
mApplicationsAdapter.resume(mFilterApps, mSortOrder); mApplicationsAdapter.resume(mFilterApps, mSortOrder);

View File

@@ -35,6 +35,69 @@ import android.util.TypedValue;
import android.view.View; import android.view.View;
public class BatteryHistoryChart extends View { public class BatteryHistoryChart extends View {
static final int CHART_DATA_X_MASK = 0x0000ffff;
static final int CHART_DATA_BIN_MASK = 0xffff0000;
static final int CHART_DATA_BIN_SHIFT = 16;
static class ChartData {
int[] mColors;
Paint[] mPaints;
int mNumTicks;
int[] mTicks;
int mLastBin;
void setColors(int[] colors) {
mColors = colors;
mPaints = new Paint[colors.length];
for (int i=0; i<colors.length; i++) {
mPaints[i] = new Paint();
mPaints[i].setColor(colors[i]);
mPaints[i].setStyle(Paint.Style.FILL);
}
}
void init(int width) {
if (width > 0) {
mTicks = new int[width+2];
} else {
mTicks = null;
}
mNumTicks = 0;
mLastBin = 0;
}
void addTick(int x, int bin) {
if (bin != mLastBin) {
mTicks[mNumTicks] = x | bin << CHART_DATA_BIN_SHIFT;
mNumTicks++;
mLastBin = bin;
}
}
void finish(int width) {
if (mLastBin != 0) {
addTick(width, 0);
}
}
void draw(Canvas canvas, int top, int height) {
int lastBin=0, lastX=0;
int bottom = top + height;
for (int i=0; i<mNumTicks; i++) {
int tick = mTicks[i];
int x = tick&CHART_DATA_X_MASK;
int bin = (tick&CHART_DATA_BIN_MASK) >> CHART_DATA_BIN_SHIFT;
if (lastBin != 0) {
canvas.drawRect(lastX, top, x, bottom, mPaints[lastBin]);
}
lastBin = bin;
lastX = x;
}
}
}
static final int SANS = 1; static final int SANS = 1;
static final int SERIF = 2; static final int SERIF = 2;
static final int MONOSPACE = 3; static final int MONOSPACE = 3;
@@ -42,7 +105,7 @@ public class BatteryHistoryChart extends View {
static final int BATTERY_WARN = 29; static final int BATTERY_WARN = 29;
static final int BATTERY_CRITICAL = 14; static final int BATTERY_CRITICAL = 14;
// First value if for phone off; sirst value is "scanning"; following values // First value if for phone off; first value is "scanning"; following values
// are battery stats signal strength buckets. // are battery stats signal strength buckets.
static final int NUM_PHONE_SIGNALS = 7; static final int NUM_PHONE_SIGNALS = 7;
@@ -55,11 +118,7 @@ public class BatteryHistoryChart extends View {
final Paint mGpsOnPaint = new Paint(); final Paint mGpsOnPaint = new Paint();
final Paint mWifiRunningPaint = new Paint(); final Paint mWifiRunningPaint = new Paint();
final Paint mWakeLockPaint = new Paint(); final Paint mWakeLockPaint = new Paint();
final Paint[] mPhoneSignalPaints = new Paint[NUM_PHONE_SIGNALS]; final ChartData mPhoneSignalChart = new ChartData();
final int[] mPhoneSignalColors = new int[] {
0x00000000, 0xffa00000, 0xffa0a000, 0xff808020,
0xff808040, 0xff808060, 0xff008000
};
final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
final Path mBatLevelPath = new Path(); final Path mBatLevelPath = new Path();
@@ -103,14 +162,11 @@ public class BatteryHistoryChart extends View {
int mLevelOffset; int mLevelOffset;
int mLevelTop; int mLevelTop;
int mLevelBottom; int mLevelBottom;
static final int PHONE_SIGNAL_X_MASK = 0x0000ffff; static final int PHONE_SIGNAL_X_MASK = CHART_DATA_X_MASK;
static final int PHONE_SIGNAL_BIN_MASK = 0xffff0000; static final int PHONE_SIGNAL_BIN_MASK = CHART_DATA_BIN_MASK;
static final int PHONE_SIGNAL_BIN_SHIFT = 16; static final int PHONE_SIGNAL_BIN_SHIFT = CHART_DATA_BIN_SHIFT;
int mNumPhoneSignalTicks;
int[] mPhoneSignalTicks;
int mNumHist; int mNumHist;
BatteryStats.HistoryItem mHistFirst;
long mHistStart; long mHistStart;
long mHistEnd; long mHistEnd;
int mBatLow; int mBatLow;
@@ -131,19 +187,14 @@ public class BatteryHistoryChart extends View {
mBatteryCriticalPaint.setStyle(Paint.Style.STROKE); mBatteryCriticalPaint.setStyle(Paint.Style.STROKE);
mChargingPaint.setARGB(255, 0, 128, 0); mChargingPaint.setARGB(255, 0, 128, 0);
mChargingPaint.setStyle(Paint.Style.STROKE); mChargingPaint.setStyle(Paint.Style.STROKE);
mScreenOnPaint.setARGB(255, 0, 0, 255);
mScreenOnPaint.setStyle(Paint.Style.STROKE); mScreenOnPaint.setStyle(Paint.Style.STROKE);
mGpsOnPaint.setARGB(255, 0, 0, 255);
mGpsOnPaint.setStyle(Paint.Style.STROKE); mGpsOnPaint.setStyle(Paint.Style.STROKE);
mWifiRunningPaint.setARGB(255, 0, 0, 255);
mWifiRunningPaint.setStyle(Paint.Style.STROKE); mWifiRunningPaint.setStyle(Paint.Style.STROKE);
mWakeLockPaint.setARGB(255, 0, 0, 255);
mWakeLockPaint.setStyle(Paint.Style.STROKE); mWakeLockPaint.setStyle(Paint.Style.STROKE);
for (int i=0; i<NUM_PHONE_SIGNALS; i++) { mPhoneSignalChart.setColors(new int[] {
mPhoneSignalPaints[i] = new Paint(); 0x00000000, 0xffa00000, 0xffa0a000, 0xff808020,
mPhoneSignalPaints[i].setColor(mPhoneSignalColors[i]); 0xff808040, 0xff808060, 0xff008000
mPhoneSignalPaints[i].setStyle(Paint.Style.FILL); });
}
mTextPaint.density = getResources().getDisplayMetrics().density; mTextPaint.density = getResources().getDisplayMetrics().density;
mTextPaint.setCompatibilityScaling( mTextPaint.setCompatibilityScaling(
@@ -296,29 +347,30 @@ public class BatteryHistoryChart extends View {
mWakeLockLabel = getContext().getString(R.string.battery_stats_wake_lock_label); mWakeLockLabel = getContext().getString(R.string.battery_stats_wake_lock_label);
mPhoneSignalLabel = getContext().getString(R.string.battery_stats_phone_signal_label); mPhoneSignalLabel = getContext().getString(R.string.battery_stats_phone_signal_label);
BatteryStats.HistoryItem rec = stats.getHistory();
mHistFirst = null;
int pos = 0; int pos = 0;
int lastInteresting = 0; int lastInteresting = 0;
byte lastLevel = -1; byte lastLevel = -1;
mBatLow = 0; mBatLow = 0;
mBatHigh = 100; mBatHigh = 100;
int aggrStates = 0; int aggrStates = 0;
while (rec != null) { boolean first = true;
pos++; if (stats.startIteratingHistoryLocked()) {
if (rec.cmd == HistoryItem.CMD_UPDATE) { final HistoryItem rec = new HistoryItem();
if (mHistFirst == null) { while (stats.getNextHistoryLocked(rec)) {
mHistFirst = rec; pos++;
mHistStart = rec.time; if (rec.cmd == HistoryItem.CMD_UPDATE) {
if (first) {
first = false;
mHistStart = rec.time;
}
if (rec.batteryLevel != lastLevel || pos == 1) {
lastLevel = rec.batteryLevel;
lastInteresting = pos;
mHistEnd = rec.time;
}
aggrStates |= rec.states;
} }
if (rec.batteryLevel != lastLevel || pos == 1) {
lastLevel = rec.batteryLevel;
lastInteresting = pos;
mHistEnd = rec.time;
}
aggrStates |= rec.states;
} }
rec = rec.next;
} }
mNumHist = lastInteresting; mNumHist = lastInteresting;
mHaveGps = (aggrStates&HistoryItem.STATE_GPS_ON_FLAG) != 0; mHaveGps = (aggrStates&HistoryItem.STATE_GPS_ON_FLAG) != 0;
@@ -337,15 +389,9 @@ public class BatteryHistoryChart extends View {
mTextDescent = (int)mTextPaint.descent(); mTextDescent = (int)mTextPaint.descent();
} }
void addPhoneSignalTick(int x, int bin) {
mPhoneSignalTicks[mNumPhoneSignalTicks]
= x | bin << PHONE_SIGNAL_BIN_SHIFT;
mNumPhoneSignalTicks++;
}
void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath, void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath,
int lastX, boolean lastCharging, boolean lastScreenOn, boolean lastGpsOn, int lastX, boolean lastCharging, boolean lastScreenOn, boolean lastGpsOn,
boolean lastWifiRunning, boolean lastWakeLock, int lastPhoneSignal, Path lastPath) { boolean lastWifiRunning, boolean lastWakeLock, Path lastPath) {
if (curLevelPath != null) { if (curLevelPath != null) {
if (lastX >= 0 && lastX < w) { if (lastX >= 0 && lastX < w) {
if (lastPath != null) { if (lastPath != null) {
@@ -373,9 +419,7 @@ public class BatteryHistoryChart extends View {
if (lastWakeLock) { if (lastWakeLock) {
mWakeLockPath.lineTo(w, h-mWakeLockOffset); mWakeLockPath.lineTo(w, h-mWakeLockOffset);
} }
if (lastPhoneSignal != 0) { mPhoneSignalChart.finish(w);
addPhoneSignalTick(w, 0);
}
} }
@Override @Override
@@ -389,10 +433,18 @@ public class BatteryHistoryChart extends View {
mLargeMode = true; mLargeMode = true;
mLineWidth = textHeight/2; mLineWidth = textHeight/2;
mLevelTop = textHeight + mLineWidth; mLevelTop = textHeight + mLineWidth;
mScreenOnPaint.setARGB(255, 32, 64, 255);
mGpsOnPaint.setARGB(255, 32, 64, 255);
mWifiRunningPaint.setARGB(255, 32, 64, 255);
mWakeLockPaint.setARGB(255, 32, 64, 255);
} else { } else {
mLargeMode = false; mLargeMode = false;
mLineWidth = mThinLineWidth; mLineWidth = mThinLineWidth;
mLevelTop = 0; mLevelTop = 0;
mScreenOnPaint.setARGB(255, 0, 0, 255);
mGpsOnPaint.setARGB(255, 0, 0, 255);
mWifiRunningPaint.setARGB(255, 0, 0, 255);
mWakeLockPaint.setARGB(255, 0, 0, 255);
} }
if (mLineWidth <= 0) mLineWidth = 1; if (mLineWidth <= 0) mLineWidth = 1;
mTextPaint.setStrokeWidth(mThinLineWidth); mTextPaint.setStrokeWidth(mThinLineWidth);
@@ -414,14 +466,14 @@ public class BatteryHistoryChart extends View {
mGpsOnOffset = mWifiRunningOffset + (mHaveWifi ? barOffset : 0); mGpsOnOffset = mWifiRunningOffset + (mHaveWifi ? barOffset : 0);
mPhoneSignalOffset = mGpsOnOffset + (mHaveGps ? barOffset : 0); mPhoneSignalOffset = mGpsOnOffset + (mHaveGps ? barOffset : 0);
mLevelOffset = mPhoneSignalOffset + barOffset + mLineWidth; mLevelOffset = mPhoneSignalOffset + barOffset + mLineWidth;
mPhoneSignalTicks = new int[w+2]; mPhoneSignalChart.init(w);
} else { } else {
mScreenOnOffset = mGpsOnOffset = mWifiRunningOffset mScreenOnOffset = mGpsOnOffset = mWifiRunningOffset
= mWakeLockOffset = mLineWidth; = mWakeLockOffset = mLineWidth;
mChargingOffset = mLineWidth*2; mChargingOffset = mLineWidth*2;
mPhoneSignalOffset = 0; mPhoneSignalOffset = 0;
mLevelOffset = mLineWidth*3; mLevelOffset = mLineWidth*3;
mPhoneSignalTicks = null; mPhoneSignalChart.init(0);
} }
mBatLevelPath.reset(); mBatLevelPath.reset();
@@ -443,146 +495,142 @@ public class BatteryHistoryChart extends View {
final int levelh = h - mLevelOffset - mLevelTop; final int levelh = h - mLevelOffset - mLevelTop;
mLevelBottom = mLevelTop + levelh; mLevelBottom = mLevelTop + levelh;
BatteryStats.HistoryItem rec = mHistFirst;
int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1; int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1;
int i = 0; int i = 0;
Path curLevelPath = null; Path curLevelPath = null;
Path lastLinePath = null; Path lastLinePath = null;
boolean lastCharging = false, lastScreenOn = false, lastGpsOn = false; boolean lastCharging = false, lastScreenOn = false, lastGpsOn = false;
boolean lastWifiRunning = false, lastWakeLock = false; boolean lastWifiRunning = false, lastWakeLock = false;
int lastPhoneSignalBin = 0;
final int N = mNumHist; final int N = mNumHist;
while (rec != null && i < N) { if (mStats.startIteratingHistoryLocked()) {
if (rec.cmd == BatteryStats.HistoryItem.CMD_UPDATE) { final HistoryItem rec = new HistoryItem();
x = (int)(((rec.time-timeStart)*w)/timeChange); while (mStats.getNextHistoryLocked(rec) && i < N) {
y = mLevelTop + levelh - ((rec.batteryLevel-batLow)*(levelh-1))/batChange; if (rec.cmd == BatteryStats.HistoryItem.CMD_UPDATE) {
x = (int)(((rec.time-timeStart)*w)/timeChange);
y = mLevelTop + levelh - ((rec.batteryLevel-batLow)*(levelh-1))/batChange;
if (lastX != x) { if (lastX != x) {
// We have moved by at least a pixel. // We have moved by at least a pixel.
if (lastY != y) { if (lastY != y) {
// Don't plot changes within a pixel. // Don't plot changes within a pixel.
Path path; Path path;
byte value = rec.batteryLevel; byte value = rec.batteryLevel;
if (value <= BATTERY_CRITICAL) path = mBatCriticalPath; if (value <= BATTERY_CRITICAL) path = mBatCriticalPath;
else if (value <= BATTERY_WARN) path = mBatWarnPath; else if (value <= BATTERY_WARN) path = mBatWarnPath;
else path = mBatGoodPath; else path = mBatGoodPath;
if (path != lastLinePath) { if (path != lastLinePath) {
if (lastLinePath != null) { if (lastLinePath != null) {
lastLinePath.lineTo(x, y); lastLinePath.lineTo(x, y);
}
path.moveTo(x, y);
lastLinePath = path;
} else {
path.lineTo(x, y);
} }
path.moveTo(x, y);
lastLinePath = path; if (curLevelPath == null) {
} else { curLevelPath = mBatLevelPath;
path.lineTo(x, y); curLevelPath.moveTo(x, y);
startX = x;
} else {
curLevelPath.lineTo(x, y);
}
lastX = x;
lastY = y;
} }
if (curLevelPath == null) { final boolean charging =
curLevelPath = mBatLevelPath; (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
curLevelPath.moveTo(x, y); if (charging != lastCharging) {
startX = x; if (charging) {
} else { mChargingPath.moveTo(x, h-mChargingOffset);
curLevelPath.lineTo(x, y); } else {
mChargingPath.lineTo(x, h-mChargingOffset);
}
lastCharging = charging;
}
final boolean screenOn =
(rec.states&HistoryItem.STATE_SCREEN_ON_FLAG) != 0;
if (screenOn != lastScreenOn) {
if (screenOn) {
mScreenOnPath.moveTo(x, h-mScreenOnOffset);
} else {
mScreenOnPath.lineTo(x, h-mScreenOnOffset);
}
lastScreenOn = screenOn;
}
final boolean gpsOn =
(rec.states&HistoryItem.STATE_GPS_ON_FLAG) != 0;
if (gpsOn != lastGpsOn) {
if (gpsOn) {
mGpsOnPath.moveTo(x, h-mGpsOnOffset);
} else {
mGpsOnPath.lineTo(x, h-mGpsOnOffset);
}
lastGpsOn = gpsOn;
}
final boolean wifiRunning =
(rec.states&HistoryItem.STATE_WIFI_RUNNING_FLAG) != 0;
if (wifiRunning != lastWifiRunning) {
if (wifiRunning) {
mWifiRunningPath.moveTo(x, h-mWifiRunningOffset);
} else {
mWifiRunningPath.lineTo(x, h-mWifiRunningOffset);
}
lastWifiRunning = wifiRunning;
}
final boolean wakeLock =
(rec.states&HistoryItem.STATE_WAKE_LOCK_FLAG) != 0;
if (wakeLock != lastWakeLock) {
if (wakeLock) {
mWakeLockPath.moveTo(x, h-mWakeLockOffset);
} else {
mWakeLockPath.lineTo(x, h-mWakeLockOffset);
}
lastWakeLock = wakeLock;
}
if (mLargeMode) {
int bin;
if (((rec.states&HistoryItem.STATE_PHONE_STATE_MASK)
>> HistoryItem.STATE_PHONE_STATE_SHIFT)
== ServiceState.STATE_POWER_OFF) {
bin = 0;
} else if ((rec.states&HistoryItem.STATE_PHONE_SCANNING_FLAG) != 0) {
bin = 1;
} else {
bin = (rec.states&HistoryItem.STATE_SIGNAL_STRENGTH_MASK)
>> HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT;
bin += 2;
}
mPhoneSignalChart.addTick(x, bin);
} }
lastX = x;
lastY = y;
} }
final boolean charging = } else if (rec.cmd != BatteryStats.HistoryItem.CMD_OVERFLOW) {
(rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0; if (curLevelPath != null) {
if (charging != lastCharging) { finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
if (charging) { lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,
mChargingPath.moveTo(x, h-mChargingOffset); lastWakeLock, lastLinePath);
} else { lastX = lastY = -1;
mChargingPath.lineTo(x, h-mChargingOffset); curLevelPath = null;
} lastLinePath = null;
lastCharging = charging; lastCharging = lastScreenOn = lastGpsOn = lastWakeLock = false;
}
final boolean screenOn =
(rec.states&HistoryItem.STATE_SCREEN_ON_FLAG) != 0;
if (screenOn != lastScreenOn) {
if (screenOn) {
mScreenOnPath.moveTo(x, h-mScreenOnOffset);
} else {
mScreenOnPath.lineTo(x, h-mScreenOnOffset);
}
lastScreenOn = screenOn;
}
final boolean gpsOn =
(rec.states&HistoryItem.STATE_GPS_ON_FLAG) != 0;
if (gpsOn != lastGpsOn) {
if (gpsOn) {
mGpsOnPath.moveTo(x, h-mGpsOnOffset);
} else {
mGpsOnPath.lineTo(x, h-mGpsOnOffset);
}
lastGpsOn = gpsOn;
}
final boolean wifiRunning =
(rec.states&HistoryItem.STATE_WIFI_RUNNING_FLAG) != 0;
if (wifiRunning != lastWifiRunning) {
if (wifiRunning) {
mWifiRunningPath.moveTo(x, h-mWifiRunningOffset);
} else {
mWifiRunningPath.lineTo(x, h-mWifiRunningOffset);
}
lastWifiRunning = wifiRunning;
}
final boolean wakeLock =
(rec.states&HistoryItem.STATE_WAKE_LOCK_FLAG) != 0;
if (wakeLock != lastWakeLock) {
if (wakeLock) {
mWakeLockPath.moveTo(x, h-mWakeLockOffset);
} else {
mWakeLockPath.lineTo(x, h-mWakeLockOffset);
}
lastWakeLock = wakeLock;
}
if (mLargeMode) {
int bin;
if (((rec.states&HistoryItem.STATE_PHONE_STATE_MASK)
>> HistoryItem.STATE_PHONE_STATE_SHIFT)
== ServiceState.STATE_POWER_OFF) {
bin = 0;
} else if ((rec.states&HistoryItem.STATE_PHONE_SCANNING_FLAG) != 0) {
bin = 1;
} else {
bin = (rec.states&HistoryItem.STATE_SIGNAL_STRENGTH_MASK)
>> HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT;
bin += 2;
}
if (bin != lastPhoneSignalBin) {
addPhoneSignalTick(x, bin);
lastPhoneSignalBin = bin;
}
} }
} }
} else if (rec.cmd != BatteryStats.HistoryItem.CMD_OVERFLOW) { i++;
if (curLevelPath != null) {
finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,
lastWakeLock, lastPhoneSignalBin, lastLinePath);
lastX = lastY = -1;
curLevelPath = null;
lastLinePath = null;
lastCharging = lastScreenOn = lastGpsOn = lastWakeLock = false;
lastPhoneSignalBin = 0;
}
} }
rec = rec.next;
i++;
} }
finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX, finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX,
lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning, lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,
lastWakeLock, lastPhoneSignalBin, lastLinePath); lastWakeLock, lastLinePath);
} }
@Override @Override
@@ -611,19 +659,8 @@ public class BatteryHistoryChart extends View {
if (!mBatCriticalPath.isEmpty()) { if (!mBatCriticalPath.isEmpty()) {
canvas.drawPath(mBatCriticalPath, mBatteryCriticalPaint); canvas.drawPath(mBatCriticalPath, mBatteryCriticalPaint);
} }
int lastBin=0, lastX=0;
int top = height-mPhoneSignalOffset - (mLineWidth/2); int top = height-mPhoneSignalOffset - (mLineWidth/2);
int bottom = top + mLineWidth; mPhoneSignalChart.draw(canvas, top, mLineWidth);
for (int i=0; i<mNumPhoneSignalTicks; i++) {
int tick = mPhoneSignalTicks[i];
int x = tick&PHONE_SIGNAL_X_MASK;
int bin = (tick&PHONE_SIGNAL_BIN_MASK) >> PHONE_SIGNAL_BIN_SHIFT;
if (lastBin != 0) {
canvas.drawRect(lastX, top, x, bottom, mPhoneSignalPaints[lastBin]);
}
lastBin = bin;
lastX = x;
}
if (!mScreenOnPath.isEmpty()) { if (!mScreenOnPath.isEmpty()) {
canvas.drawPath(mScreenOnPath, mScreenOnPaint); canvas.drawPath(mScreenOnPath, mScreenOnPaint);
} }

View File

@@ -18,7 +18,6 @@ package com.android.settings.fuelgauge;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.hardware.SensorManager; import android.hardware.SensorManager;
import android.os.BatteryStats; import android.os.BatteryStats;
import android.os.Bundle; import android.os.Bundle;
@@ -70,6 +69,7 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
BatteryStatsImpl mStats; BatteryStatsImpl mStats;
private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>(); private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>(); private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>();
private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>();
private PreferenceGroup mAppListGroup; private PreferenceGroup mAppListGroup;
@@ -82,6 +82,7 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
private double mMaxPower = 1; private double mMaxPower = 1;
private double mTotalPower; private double mTotalPower;
private double mWifiPower; private double mWifiPower;
private double mBluetoothPower;
private PowerProfile mPowerProfile; private PowerProfile mPowerProfile;
// How much the apps together have left WIFI running. // How much the apps together have left WIFI running.
@@ -228,6 +229,25 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
sipper.tcpBytesReceived, sipper.tcpBytesReceived,
}; };
} break; } 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_send,
R.string.usage_type_data_recv,
};
values = new double[] {
sipper.usageTime,
sipper.cpuTime,
sipper.cpuFgTime,
sipper.wakeLockTime,
sipper.tcpBytesSent,
sipper.tcpBytesReceived,
};
} break;
default: default:
{ {
types = new int[] { types = new int[] {
@@ -295,11 +315,13 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
mMaxPower = 0; mMaxPower = 0;
mTotalPower = 0; mTotalPower = 0;
mWifiPower = 0; mWifiPower = 0;
mBluetoothPower = 0;
mAppWifiRunning = 0; mAppWifiRunning = 0;
mAppListGroup.removeAll(); mAppListGroup.removeAll();
mUsageList.clear(); mUsageList.clear();
mWifiSippers.clear(); mWifiSippers.clear();
mBluetoothSippers.clear();
processAppUsage(); processAppUsage();
processMiscUsage(); processMiscUsage();
@@ -400,11 +422,15 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
} }
cpuTime += tmpCpuTime; cpuTime += tmpCpuTime;
power += processPower; power += processPower;
if (highestDrain < processPower) { if (packageWithHighestDrain == null
|| packageWithHighestDrain.startsWith("*")) {
highestDrain = processPower;
packageWithHighestDrain = ent.getKey();
} else if (highestDrain < processPower
&& !ent.getKey().startsWith("*")) {
highestDrain = processPower; highestDrain = processPower;
packageWithHighestDrain = ent.getKey(); packageWithHighestDrain = ent.getKey();
} }
} }
if (DEBUG) Log.i(TAG, "Max drain of " + highestDrain if (DEBUG) Log.i(TAG, "Max drain of " + highestDrain
+ " by " + packageWithHighestDrain); + " by " + packageWithHighestDrain);
@@ -486,12 +512,16 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
app.tcpBytesSent = tcpBytesSent; app.tcpBytesSent = tcpBytesSent;
if (u.getUid() == Process.WIFI_UID) { if (u.getUid() == Process.WIFI_UID) {
mWifiSippers.add(app); mWifiSippers.add(app);
} else if (u.getUid() == Process.BLUETOOTH_GID) {
mBluetoothSippers.add(app);
} else { } else {
mUsageList.add(app); mUsageList.add(app);
} }
} }
if (u.getUid() == Process.WIFI_UID) { if (u.getUid() == Process.WIFI_UID) {
mWifiPower += power; mWifiPower += power;
} else if (u.getUid() == Process.BLUETOOTH_GID) {
mBluetoothPower += power;
} else { } else {
if (power > mMaxPower) mMaxPower = power; if (power > mMaxPower) mMaxPower = power;
mTotalPower += power; mTotalPower += power;
@@ -551,6 +581,20 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
} }
} }
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.tcpBytesReceived += wbs.tcpBytesReceived;
bs.tcpBytesSent += wbs.tcpBytesSent;
}
}
private void addWiFiUsage(long uSecNow) { private void addWiFiUsage(long uSecNow) {
long onTimeMs = mStats.getWifiOnTime(uSecNow, mStatsType) / 1000; long onTimeMs = mStats.getWifiOnTime(uSecNow, mStatsType) / 1000;
long runningTimeMs = mStats.getGlobalWifiRunningTime(uSecNow, mStatsType) / 1000; long runningTimeMs = mStats.getGlobalWifiRunningTime(uSecNow, mStatsType) / 1000;
@@ -564,17 +608,7 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
if (DEBUG) Log.i(TAG, "WIFI power=" + wifiPower + " from procs=" + mWifiPower); if (DEBUG) Log.i(TAG, "WIFI power=" + wifiPower + " from procs=" + mWifiPower);
BatterySipper bs = addEntry(getString(R.string.power_wifi), DrainType.WIFI, runningTimeMs, BatterySipper bs = addEntry(getString(R.string.power_wifi), DrainType.WIFI, runningTimeMs,
R.drawable.ic_settings_wifi, wifiPower + mWifiPower); R.drawable.ic_settings_wifi, wifiPower + mWifiPower);
for (int i=0; i<mWifiSippers.size(); i++) { aggregateSippers(bs, mWifiSippers, "WIFI");
BatterySipper wbs = mWifiSippers.get(i);
if (DEBUG) Log.i(TAG, "WIFI 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.tcpBytesReceived += wbs.tcpBytesReceived;
bs.tcpBytesSent += wbs.tcpBytesSent;
}
} }
private void addIdleUsage(long uSecNow) { private void addIdleUsage(long uSecNow) {
@@ -592,9 +626,9 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
int btPingCount = mStats.getBluetoothPingCount(); int btPingCount = mStats.getBluetoothPingCount();
btPower += (btPingCount btPower += (btPingCount
* mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) / 1000; * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) / 1000;
BatterySipper bs = addEntry(getString(R.string.power_bluetooth), DrainType.BLUETOOTH,
addEntry(getString(R.string.power_bluetooth), DrainType.BLUETOOTH, btOnTimeMs, btOnTimeMs, R.drawable.ic_settings_bluetooth, btPower + mBluetoothPower);
R.drawable.ic_settings_bluetooth, btPower); aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
} }
private double getAverageDataCost() { private double getAverageDataCost() {