OSDN Git Service

Some improvements to the manage apps code:
authorDianne Hackborn <hackbod@google.com>
Mon, 20 Sep 2010 18:40:46 +0000 (11:40 -0700)
committerDianne Hackborn <hackbod@google.com>
Tue, 21 Sep 2010 00:06:17 +0000 (17:06 -0700)
- 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

src/com/android/settings/applications/ApplicationsState.java
src/com/android/settings/applications/InstalledAppDetails.java
src/com/android/settings/applications/ManageApplications.java
src/com/android/settings/fuelgauge/BatteryHistoryChart.java
src/com/android/settings/fuelgauge/PowerUsageSummary.java

index 1f94d0a..8924b29 100644 (file)
@@ -42,6 +42,7 @@ public class ApplicationsState {
     public static interface Callbacks {
         public void onRunningStateChanged(boolean running);
         public void onPackageListChanged();
+        public void onRebuildComplete(ArrayList<AppEntry> apps);
         public void onPackageIconChanged();
         public void onPackageSizeChanged(String packageName);
         public void onAllSizesComputed();
@@ -154,6 +155,14 @@ public class ApplicationsState {
     long mCurId = 1;
     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.
      */
@@ -209,8 +218,9 @@ public class ApplicationsState {
     }
 
     class MainHandler extends Handler {
-        static final int MSG_PACKAGE_LIST_CHANGED = 1;
-        static final int MSG_PACKAGE_ICON_CHANGED = 2;
+        static final int MSG_REBUILD_COMPLETE = 1;
+        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_ALL_SIZES_COMPUTED = 5;
         static final int MSG_RUNNING_STATE_CHANGED = 6;
@@ -218,6 +228,11 @@ public class ApplicationsState {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case MSG_REBUILD_COMPLETE: {
+                    if (mCurCallbacks != null) {
+                        mCurCallbacks.onRebuildComplete((ArrayList<AppEntry>)msg.obj);
+                    }
+                } break;
                 case MSG_PACKAGE_LIST_CHANGED: {
                     if (mCurCallbacks != null) {
                         mCurCallbacks.onPackageListChanged();
@@ -305,20 +320,89 @@ public class ApplicationsState {
 
     // Creates a new list of app entries with the given filter and 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) {
-            if (DEBUG) Log.i(TAG, "Rebuilding...");
-            for (int i=0; i<mApplications.size(); i++) {
-                ApplicationInfo info = mApplications.get(i);
-                if (filter == null || filter.filterApp(info)) {
+            apps = new ArrayList<ApplicationInfo>(mApplications);
+        }
+
+        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);
                     if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
                     filteredApps.add(entry);
                 }
             }
         }
+
         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) {
@@ -447,9 +531,10 @@ public class ApplicationsState {
     final HandlerThread mThread;
     final BackgroundHandler mBackgroundHandler;
     class BackgroundHandler extends Handler {
-        static final int MSG_LOAD_ENTRIES = 1;
-        static final int MSG_LOAD_ICONS = 2;
-        static final int MSG_LOAD_SIZES = 3;
+        static final int MSG_REBUILD_LIST = 1;
+        static final int MSG_LOAD_ENTRIES = 2;
+        static final int MSG_LOAD_ICONS = 3;
+        static final int MSG_LOAD_SIZES = 4;
 
         boolean mRunning;
 
@@ -498,7 +583,12 @@ public class ApplicationsState {
 
         @Override
         public void handleMessage(Message msg) {
+            // Always try rebuilding list first thing, if needed.
+            handleRebuildList();
+
             switch (msg.what) {
+                case MSG_REBUILD_LIST: {
+                } break;
                 case MSG_LOAD_ENTRIES: {
                     int numDone = 0;
                     synchronized (mEntriesMap) {
index af03549..1583ef1 100644 (file)
@@ -18,6 +18,7 @@ package com.android.settings.applications;
 
 import com.android.internal.content.PackageHelper;
 import com.android.settings.R;
+import com.android.settings.applications.ApplicationsState.AppEntry;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -385,6 +386,10 @@ public class InstalledAppDetails extends Activity
     }
 
     @Override
+    public void onRebuildComplete(ArrayList<AppEntry> apps) {
+    }
+
+    @Override
     public void onPackageSizeChanged(String packageName) {
         if (packageName.equals(mAppEntry.info.packageName)) {
             refreshSizeInfo();
index 34e0e8e..f8463c3 100644 (file)
@@ -23,6 +23,7 @@ import android.app.TabActivity;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -47,6 +48,7 @@ import android.widget.AdapterView.OnItemClickListener;
 
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.List;
 
 /**
  * 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 boolean mResumed;
         private int mLastFilterMode=-1, mLastSortMode=-1;
+        private boolean mWaitingForData;
         CharSequence mCurFilterPrefix;
 
         private Filter mFilter = new Filter() {
@@ -184,7 +187,7 @@ public class ManageApplications extends TabActivity implements
                 mState.resume(this);
                 mLastFilterMode = filter;
                 mLastSortMode = sort;
-                rebuild();
+                rebuild(true);
             } else {
                 rebuild(filter, sort);
             }
@@ -203,10 +206,10 @@ public class ManageApplications extends TabActivity implements
             }
             mLastFilterMode = filter;
             mLastSortMode = sort;
-            rebuild();
+            rebuild(true);
         }
         
-        public void rebuild() {
+        public void rebuild(boolean eraseold) {
             if (DEBUG) Log.i(TAG, "Rebuilding app list...");
             ApplicationsState.AppFilter filterObj;
             Comparator<AppEntry> comparatorObj;
@@ -229,9 +232,28 @@ public class ManageApplications extends TabActivity implements
                     comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
                     break;
             }
-            mBaseEntries = mState.rebuild(filterObj, comparatorObj);
-            mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
+            ArrayList<ApplicationsState.AppEntry> entries
+                    = 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();
+
+            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,
@@ -260,8 +282,18 @@ public class ManageApplications extends TabActivity implements
         }
 
         @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
         public void onPackageListChanged() {
-            rebuild();
+            rebuild(false);
         }
 
         @Override
@@ -284,7 +316,7 @@ public class ManageApplications extends TabActivity implements
                         // user viewed, and are sorting by size...  they may
                         // have cleared data, so we immediately want to resort
                         // the list with the new size to reflect it to the user.
-                        rebuild();
+                        rebuild(false);
                     }
                     return;
                 }
@@ -294,7 +326,7 @@ public class ManageApplications extends TabActivity implements
         @Override
         public void onAllSizesComputed() {
             if (mLastSortMode == SORT_ORDER_SIZE) {
-                rebuild();
+                rebuild(false);
             }
         }
         
@@ -566,6 +598,7 @@ public class ManageApplications extends TabActivity implements
             if (mCurView != which) {
                 mRunningProcessesView.setVisibility(View.GONE);
                 mListContainer.setVisibility(View.VISIBLE);
+                mLoadingContainer.setVisibility(View.GONE);
             }
             if (mActivityResumed) {
                 mApplicationsAdapter.resume(mFilterApps, mSortOrder);
index fd36a9d..a74780d 100644 (file)
@@ -35,6 +35,69 @@ import android.util.TypedValue;
 import android.view.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 SERIF = 2;
     static final int MONOSPACE = 3;
@@ -42,7 +105,7 @@ public class BatteryHistoryChart extends View {
     static final int BATTERY_WARN = 29;
     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.
     static final int NUM_PHONE_SIGNALS = 7;
 
@@ -55,11 +118,7 @@ public class BatteryHistoryChart extends View {
     final Paint mGpsOnPaint = new Paint();
     final Paint mWifiRunningPaint = new Paint();
     final Paint mWakeLockPaint = new Paint();
-    final Paint[] mPhoneSignalPaints = new Paint[NUM_PHONE_SIGNALS];
-    final int[] mPhoneSignalColors = new int[] {
-            0x00000000, 0xffa00000, 0xffa0a000, 0xff808020,
-            0xff808040, 0xff808060, 0xff008000
-    };
+    final ChartData mPhoneSignalChart = new ChartData();
     final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
     
     final Path mBatLevelPath = new Path();
@@ -103,14 +162,11 @@ public class BatteryHistoryChart extends View {
     int mLevelOffset;
     int mLevelTop;
     int mLevelBottom;
-    static final int PHONE_SIGNAL_X_MASK = 0x0000ffff;
-    static final int PHONE_SIGNAL_BIN_MASK = 0xffff0000;
-    static final int PHONE_SIGNAL_BIN_SHIFT = 16;
-    int mNumPhoneSignalTicks;
-    int[] mPhoneSignalTicks;
+    static final int PHONE_SIGNAL_X_MASK = CHART_DATA_X_MASK;
+    static final int PHONE_SIGNAL_BIN_MASK = CHART_DATA_BIN_MASK;
+    static final int PHONE_SIGNAL_BIN_SHIFT = CHART_DATA_BIN_SHIFT;
     
     int mNumHist;
-    BatteryStats.HistoryItem mHistFirst;
     long mHistStart;
     long mHistEnd;
     int mBatLow;
@@ -131,19 +187,14 @@ public class BatteryHistoryChart extends View {
         mBatteryCriticalPaint.setStyle(Paint.Style.STROKE);
         mChargingPaint.setARGB(255, 0, 128, 0);
         mChargingPaint.setStyle(Paint.Style.STROKE);
-        mScreenOnPaint.setARGB(255, 0, 0, 255);
         mScreenOnPaint.setStyle(Paint.Style.STROKE);
-        mGpsOnPaint.setARGB(255, 0, 0, 255);
         mGpsOnPaint.setStyle(Paint.Style.STROKE);
-        mWifiRunningPaint.setARGB(255, 0, 0, 255);
         mWifiRunningPaint.setStyle(Paint.Style.STROKE);
-        mWakeLockPaint.setARGB(255, 0, 0, 255);
         mWakeLockPaint.setStyle(Paint.Style.STROKE);
-        for (int i=0; i<NUM_PHONE_SIGNALS; i++) {
-            mPhoneSignalPaints[i] = new Paint();
-            mPhoneSignalPaints[i].setColor(mPhoneSignalColors[i]);
-            mPhoneSignalPaints[i].setStyle(Paint.Style.FILL);
-        }
+        mPhoneSignalChart.setColors(new int[] {
+                0x00000000, 0xffa00000, 0xffa0a000, 0xff808020,
+                0xff808040, 0xff808060, 0xff008000
+        });
         
         mTextPaint.density = getResources().getDisplayMetrics().density;
         mTextPaint.setCompatibilityScaling(
@@ -296,29 +347,30 @@ public class BatteryHistoryChart extends View {
         mWakeLockLabel = getContext().getString(R.string.battery_stats_wake_lock_label);
         mPhoneSignalLabel = getContext().getString(R.string.battery_stats_phone_signal_label);
         
-        BatteryStats.HistoryItem rec = stats.getHistory();
-        mHistFirst = null;
         int pos = 0;
         int lastInteresting = 0;
         byte lastLevel = -1;
         mBatLow = 0;
         mBatHigh = 100;
         int aggrStates = 0;
-        while (rec != null) {
-            pos++;
-            if (rec.cmd == HistoryItem.CMD_UPDATE) {
-                if (mHistFirst == null) {
-                    mHistFirst = rec;
-                    mHistStart = rec.time;
-                }
-                if (rec.batteryLevel != lastLevel || pos == 1) {
-                    lastLevel = rec.batteryLevel;
-                    lastInteresting = pos;
-                    mHistEnd = rec.time;
+        boolean first = true;
+        if (stats.startIteratingHistoryLocked()) {
+            final HistoryItem rec = new HistoryItem();
+            while (stats.getNextHistoryLocked(rec)) {
+                pos++;
+                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;
                 }
-                aggrStates |= rec.states;
             }
-            rec = rec.next;
         }
         mNumHist = lastInteresting;
         mHaveGps = (aggrStates&HistoryItem.STATE_GPS_ON_FLAG) != 0;
@@ -337,15 +389,9 @@ public class BatteryHistoryChart extends View {
         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,
             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 (lastX >= 0 && lastX < w) {
                 if (lastPath != null) {
@@ -373,9 +419,7 @@ public class BatteryHistoryChart extends View {
         if (lastWakeLock) {
             mWakeLockPath.lineTo(w, h-mWakeLockOffset);
         }
-        if (lastPhoneSignal != 0) {
-            addPhoneSignalTick(w, 0);
-        }
+        mPhoneSignalChart.finish(w);
     }
     
     @Override
@@ -389,10 +433,18 @@ public class BatteryHistoryChart extends View {
             mLargeMode = true;
             mLineWidth = textHeight/2;
             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 {
             mLargeMode = false;
             mLineWidth = mThinLineWidth;
             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;
         mTextPaint.setStrokeWidth(mThinLineWidth);
@@ -414,14 +466,14 @@ public class BatteryHistoryChart extends View {
             mGpsOnOffset = mWifiRunningOffset + (mHaveWifi ? barOffset : 0);
             mPhoneSignalOffset = mGpsOnOffset + (mHaveGps ? barOffset : 0);
             mLevelOffset = mPhoneSignalOffset + barOffset + mLineWidth;
-            mPhoneSignalTicks = new int[w+2];
+            mPhoneSignalChart.init(w);
         } else {
             mScreenOnOffset = mGpsOnOffset = mWifiRunningOffset
                     = mWakeLockOffset = mLineWidth;
             mChargingOffset = mLineWidth*2;
             mPhoneSignalOffset = 0;
             mLevelOffset = mLineWidth*3;
-            mPhoneSignalTicks = null;
+            mPhoneSignalChart.init(0);
         }
 
         mBatLevelPath.reset();
@@ -443,146 +495,142 @@ public class BatteryHistoryChart extends View {
         final int levelh = h - mLevelOffset - mLevelTop;
         mLevelBottom = mLevelTop + levelh;
         
-        BatteryStats.HistoryItem rec = mHistFirst;
         int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1;
         int i = 0;
         Path curLevelPath = null;
         Path lastLinePath = null;
         boolean lastCharging = false, lastScreenOn = false, lastGpsOn = false;
         boolean lastWifiRunning = false, lastWakeLock = false;
-        int lastPhoneSignalBin = 0;
         final int N = mNumHist;
-        while (rec != null && i < N) {
-            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) {
-                    // We have moved by at least a pixel.
-                    if (lastY != y) {
-                        // Don't plot changes within a pixel.
-                        Path path;
-                        byte value = rec.batteryLevel;
-                        if (value <= BATTERY_CRITICAL) path = mBatCriticalPath;
-                        else if (value <= BATTERY_WARN) path = mBatWarnPath;
-                        else path = mBatGoodPath;
-                        
-                        if (path != lastLinePath) {
-                            if (lastLinePath != null) {
-                                lastLinePath.lineTo(x, y);
+        if (mStats.startIteratingHistoryLocked()) {
+            final HistoryItem rec = new HistoryItem();
+            while (mStats.getNextHistoryLocked(rec) && i < N) {
+                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) {
+                        // We have moved by at least a pixel.
+                        if (lastY != y) {
+                            // Don't plot changes within a pixel.
+                            Path path;
+                            byte value = rec.batteryLevel;
+                            if (value <= BATTERY_CRITICAL) path = mBatCriticalPath;
+                            else if (value <= BATTERY_WARN) path = mBatWarnPath;
+                            else path = mBatGoodPath;
+
+                            if (path != lastLinePath) {
+                                if (lastLinePath != null) {
+                                    lastLinePath.lineTo(x, y);
+                                }
+                                path.moveTo(x, y);
+                                lastLinePath = path;
+                            } else {
+                                path.lineTo(x, y);
+                            }
+
+                            if (curLevelPath == null) {
+                                curLevelPath = mBatLevelPath;
+                                curLevelPath.moveTo(x, y);
+                                startX = x;
+                            } else {
+                                curLevelPath.lineTo(x, y);
                             }
-                            path.moveTo(x, y);
-                            lastLinePath = path;
-                        } else {
-                            path.lineTo(x, y);
+                            lastX = x;
+                            lastY = y;
                         }
-                        
-                        if (curLevelPath == null) {
-                            curLevelPath = mBatLevelPath;
-                            curLevelPath.moveTo(x, y);
-                            startX = x;
-                        } else {
-                            curLevelPath.lineTo(x, y);
+
+                        final boolean charging =
+                            (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
+                        if (charging != lastCharging) {
+                            if (charging) {
+                                mChargingPath.moveTo(x, h-mChargingOffset);
+                            } else {
+                                mChargingPath.lineTo(x, h-mChargingOffset);
+                            }
+                            lastCharging = charging;
                         }
-                        lastX = x;
-                        lastY = y;
-                    }
 
-                    final boolean charging =
-                        (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
-                    if (charging != lastCharging) {
-                        if (charging) {
-                            mChargingPath.moveTo(x, h-mChargingOffset);
-                        } else {
-                            mChargingPath.lineTo(x, h-mChargingOffset);
+                        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;
                         }
-                        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);
+                        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;
                         }
-                        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);
+                        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;
                         }
-                        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);
+                        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;
                         }
-                        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);
+                        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);
                         }
-                        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) {
+                    if (curLevelPath != null) {
+                        finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
+                                lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,
+                                lastWakeLock, lastLinePath);
+                        lastX = lastY = -1;
+                        curLevelPath = null;
+                        lastLinePath = null;
+                        lastCharging = lastScreenOn = lastGpsOn = lastWakeLock = false;
                     }
                 }
                 
-            } else if (rec.cmd != BatteryStats.HistoryItem.CMD_OVERFLOW) {
-                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;
-                }
+                i++;
             }
-            
-            rec = rec.next;
-            i++;
         }
         
         finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX,
                 lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,
-                lastWakeLock, lastPhoneSignalBin, lastLinePath);
+                lastWakeLock, lastLinePath);
     }
     
     @Override
@@ -611,19 +659,8 @@ public class BatteryHistoryChart extends View {
         if (!mBatCriticalPath.isEmpty()) {
             canvas.drawPath(mBatCriticalPath, mBatteryCriticalPaint);
         }
-        int lastBin=0, lastX=0;
         int top = height-mPhoneSignalOffset - (mLineWidth/2);
-        int bottom = 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;
-        }
+        mPhoneSignalChart.draw(canvas, top, mLineWidth);
         if (!mScreenOnPath.isEmpty()) {
             canvas.drawPath(mScreenOnPath, mScreenOnPaint);
         }
index ac9401d..e0d614d 100644 (file)
@@ -18,7 +18,6 @@ package com.android.settings.fuelgauge;
 
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.drawable.Drawable;
 import android.hardware.SensorManager;
 import android.os.BatteryStats;
 import android.os.Bundle;
@@ -70,6 +69,7 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
     BatteryStatsImpl mStats;
     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 PreferenceGroup mAppListGroup;
 
@@ -82,6 +82,7 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
     private double mMaxPower = 1;
     private double mTotalPower;
     private double mWifiPower;
+    private double mBluetoothPower;
     private PowerProfile mPowerProfile;
 
     // How much the apps together have left WIFI running.
@@ -228,6 +229,25 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
                     sipper.tcpBytesReceived,
                 };
             } 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:
             {
                 types = new int[] {
@@ -295,11 +315,13 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
         mMaxPower = 0;
         mTotalPower = 0;
         mWifiPower = 0;
+        mBluetoothPower = 0;
         mAppWifiRunning = 0;
 
         mAppListGroup.removeAll();
         mUsageList.clear();
         mWifiSippers.clear();
+        mBluetoothSippers.clear();
         processAppUsage();
         processMiscUsage();
 
@@ -400,11 +422,15 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
                     }
                     cpuTime += tmpCpuTime;
                     power += processPower;
-                    if (highestDrain < processPower) {
+                    if (packageWithHighestDrain == null
+                            || packageWithHighestDrain.startsWith("*")) {
+                        highestDrain = processPower;
+                        packageWithHighestDrain = ent.getKey();
+                    } else if (highestDrain < processPower
+                            && !ent.getKey().startsWith("*")) {
                         highestDrain = processPower;
                         packageWithHighestDrain = ent.getKey();
                     }
-
                 }
                 if (DEBUG) Log.i(TAG, "Max drain of " + highestDrain 
                         + " by " + packageWithHighestDrain);
@@ -486,12 +512,16 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
                 app.tcpBytesSent = tcpBytesSent;
                 if (u.getUid() == Process.WIFI_UID) {
                     mWifiSippers.add(app);
+                } else if (u.getUid() == Process.BLUETOOTH_GID) {
+                    mBluetoothSippers.add(app);
                 } else {
                     mUsageList.add(app);
                 }
             }
             if (u.getUid() == Process.WIFI_UID) {
                 mWifiPower += power;
+            } else if (u.getUid() == Process.BLUETOOTH_GID) {
+                mBluetoothPower += power;
             } else {
                 if (power > mMaxPower) mMaxPower = 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) {
         long onTimeMs = mStats.getWifiOnTime(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);
         BatterySipper bs = addEntry(getString(R.string.power_wifi), DrainType.WIFI, runningTimeMs,
                 R.drawable.ic_settings_wifi, wifiPower + mWifiPower);
-        for (int i=0; i<mWifiSippers.size(); i++) {
-            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;
-        }
+        aggregateSippers(bs, mWifiSippers, "WIFI");
     }
 
     private void addIdleUsage(long uSecNow) {
@@ -592,9 +626,9 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
         int btPingCount = mStats.getBluetoothPingCount();
         btPower += (btPingCount
                 * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) / 1000;
-
-        addEntry(getString(R.string.power_bluetooth), DrainType.BLUETOOTH, btOnTimeMs,
-                R.drawable.ic_settings_bluetooth, btPower);
+        BatterySipper bs = addEntry(getString(R.string.power_bluetooth), DrainType.BLUETOOTH,
+                btOnTimeMs, R.drawable.ic_settings_bluetooth, btPower + mBluetoothPower);
+        aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
     }
 
     private double getAverageDataCost() {