OSDN Git Service

Allow user to see and stop heavy-weight processes.
authorDianne Hackborn <hackbod@google.com>
Thu, 24 Jun 2010 18:29:50 +0000 (11:29 -0700)
committerDianne Hackborn <hackbod@google.com>
Thu, 24 Jun 2010 19:38:33 +0000 (12:38 -0700)
Change-Id: If5caed3972ab03a54fbf8c459cdfc136e4bdc020

res/values/strings.xml
src/com/android/settings/applications/RunningProcessesView.java
src/com/android/settings/applications/RunningServiceDetails.java
src/com/android/settings/applications/RunningState.java
src/com/android/settings/fuelgauge/BatteryHistoryChart.java

index 03d2136..4a5907d 100644 (file)
@@ -1788,6 +1788,9 @@ found in the list of installed applications.</string>
     <!-- Running service details, default description for services that are started. -->
     <string name="service_stop_description">This service was started by its
         application.  Stopping it may cause the application to fail.</string>
+    <!-- Running service details, description for running heavy-weight process. -->
+    <string name="heavy_weight_stop_description">This application can not safely
+        be stopped.  Doing so may lose some of your current work.</string>
     <!-- Running service details, default description for services that are managed. -->
     <string name="service_manage_description"><xliff:g id="client_name">%1$s</xliff:g>:
         currently in use.  Touch Settings to control it.</string>
index 9fbae6d..d63cc88 100644 (file)
@@ -130,8 +130,16 @@ public class RunningProcessesView extends FrameLayout
                     uptimeView.setText(DateUtils.formatElapsedTime(builder,
                             (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
                 } else {
-                    uptimeView.setText(context.getResources().getText(
-                            R.string.service_restarting));
+                    boolean isService = false;
+                    if (mItem instanceof RunningState.MergedItem) {
+                        isService = ((RunningState.MergedItem)mItem).mServices.size() > 0;
+                    }
+                    if (isService) {
+                        uptimeView.setText(context.getResources().getText(
+                                R.string.service_restarting));
+                    } else {
+                        uptimeView.setText("");
+                    }
                 }
             }
         }
index f33625e..f469608 100644 (file)
@@ -55,6 +55,8 @@ public class RunningServiceDetails extends Activity {
     RunningProcessesView.ActiveItem mSnippetActiveItem;
     RunningProcessesView.ViewHolder mSnippetViewHolder;
     
+    int mNumServices, mNumProcesses;
+    
     TextView mServicesHeader;
     TextView mProcessesHeader;
     final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
@@ -79,7 +81,7 @@ public class RunningServiceDetails extends Activity {
                 } catch (ActivityNotFoundException e) {
                     Log.w(TAG, e);
                 }
-            } else {
+            } else if (mActiveItem.mItem instanceof RunningState.ServiceItem) {
                 RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
                 stopService(new Intent().setComponent(si.mRunningService.service));
                 if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
@@ -91,6 +93,10 @@ public class RunningServiceDetails extends Activity {
                         mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
                     }
                 }
+            } else {
+                // Heavy-weight process.  We'll do a force-stop on it.
+                mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
+                finish();
             }
         }
     }
@@ -163,6 +169,129 @@ public class RunningServiceDetails extends Activity {
         return false;
     }
     
+    void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi) {
+        if (mNumServices == 0) {
+            mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
+                    mAllDetails, false);
+            mServicesHeader.setText(R.string.runningservicedetails_services_title);
+            mAllDetails.addView(mServicesHeader);
+        }
+        mNumServices++;
+        
+        RunningState.BaseItem bi = si != null ? si : mi;
+        
+        ActiveDetail detail = new ActiveDetail();
+        View root = mInflater.inflate(R.layout.running_service_details_service,
+                mAllDetails, false);
+        mAllDetails.addView(root);
+        detail.mRootView = root;
+        detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
+        detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder);
+        
+        if (si != null && si.mRunningService.clientLabel != 0) {
+            detail.mManageIntent = mAm.getRunningServiceControlPanel(
+                    si.mRunningService.service);
+        }
+        
+        TextView description = (TextView)root.findViewById(R.id.comp_description);
+        if (si != null && si.mServiceInfo.descriptionRes != 0) {
+            description.setText(getPackageManager().getText(
+                    si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
+                    si.mServiceInfo.applicationInfo));
+        } else {
+            if (detail.mManageIntent != null) {
+                try {
+                    Resources clientr = getPackageManager().getResourcesForApplication(
+                            si.mRunningService.clientPackage);
+                    String label = clientr.getString(si.mRunningService.clientLabel);
+                    description.setText(getString(R.string.service_manage_description,
+                            label));
+                } catch (PackageManager.NameNotFoundException e) {
+                }
+            } else {
+                description.setText(getText(si != null
+                        ? R.string.service_stop_description
+                        : R.string.heavy_weight_stop_description));
+            }
+        }
+        
+        View button = root.findViewById(R.id.right_button);
+        button.setOnClickListener(detail);
+        ((TextView)button).setText(getText(detail.mManageIntent != null
+                ? R.string.service_manage : R.string.service_stop));
+        root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
+        
+        mActiveDetails.add(detail);
+    }
+    
+    void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) {
+        if (mNumProcesses == 0) {
+            mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
+                    mAllDetails, false);
+            mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
+            mAllDetails.addView(mProcessesHeader);
+        }
+        mNumProcesses++;
+        
+        ActiveDetail detail = new ActiveDetail();
+        View root = mInflater.inflate(R.layout.running_service_details_process,
+                mAllDetails, false);
+        mAllDetails.addView(root);
+        detail.mRootView = root;
+        detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
+        detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
+        
+        TextView description = (TextView)root.findViewById(R.id.comp_description);
+        if (isMain) {
+            description.setText(R.string.main_running_process_description);
+        } else {
+            int textid = 0;
+            CharSequence label = null;
+            ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
+            final ComponentName comp = rpi.importanceReasonComponent;
+            //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
+            //        + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
+            switch (rpi.importanceReasonCode) {
+                case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
+                    textid = R.string.process_provider_in_use_description;
+                    List<ProviderInfo> providers = null;
+                    if (comp != null) {
+                        providers = getPackageManager()
+                                .queryContentProviders(comp.getPackageName(),
+                                        rpi.uid, 0);
+                    }
+                    if (providers != null) {
+                        for (int j=0; j<providers.size(); j++) {
+                            ProviderInfo prov = providers.get(j);
+                            if (comp.getClassName().equals(prov.name)) {
+                                label = RunningState.makeLabel(getPackageManager(),
+                                        prov.name, prov);
+                                break;
+                            }
+                        }
+                    }
+                    break;
+                case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
+                    textid = R.string.process_service_in_use_description;
+                    if (rpi.importanceReasonComponent != null) {
+                        try {
+                            ServiceInfo serv = getPackageManager().getServiceInfo(
+                                    rpi.importanceReasonComponent, 0);
+                            label = RunningState.makeLabel(getPackageManager(),
+                                    serv.name, serv);
+                        } catch (NameNotFoundException e) {
+                        }
+                    }
+                    break;
+            }
+            if (textid != 0 && label != null) {
+                description.setText(getString(textid, label));
+            }
+        }
+        
+        mActiveDetails.add(detail);
+    }
+    
     void addDetailViews() {
         for (int i=mActiveDetails.size()-1; i>=0; i--) {
             mAllDetails.removeView(mActiveDetails.get(i).mRootView);
@@ -179,58 +308,20 @@ public class RunningServiceDetails extends Activity {
             mProcessesHeader = null;
         }
         
+        mNumServices = mNumProcesses = 0;
+        
         if (mMergedItem != null) {
             for (int i=0; i<mMergedItem.mServices.size(); i++) {
-                if (i == 0) {
-                    mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
-                            mAllDetails, false);
-                    mServicesHeader.setText(R.string.runningservicedetails_services_title);
-                    mAllDetails.addView(mServicesHeader);
-                }
-                RunningState.ServiceItem si = mMergedItem.mServices.get(i);
-                ActiveDetail detail = new ActiveDetail();
-                View root = mInflater.inflate(R.layout.running_service_details_service,
-                        mAllDetails, false);
-                mAllDetails.addView(root);
-                detail.mRootView = root;
-                detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
-                detail.mActiveItem = detail.mViewHolder.bind(mState, si, mBuilder);
-                
-                if (si.mRunningService.clientLabel != 0) {
-                    detail.mManageIntent = mAm.getRunningServiceControlPanel(
-                            si.mRunningService.service);
-                }
-                
-                TextView description = (TextView)root.findViewById(R.id.comp_description);
-                if (si.mServiceInfo.descriptionRes != 0) {
-                    description.setText(getPackageManager().getText(
-                            si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
-                            si.mServiceInfo.applicationInfo));
-                } else {
-                    if (detail.mManageIntent != null) {
-                        try {
-                            Resources clientr = getPackageManager().getResourcesForApplication(
-                                    si.mRunningService.clientPackage);
-                            String label = clientr.getString(si.mRunningService.clientLabel);
-                            description.setText(getString(R.string.service_manage_description,
-                                    label));
-                        } catch (PackageManager.NameNotFoundException e) {
-                        }
-                    } else {
-                        description.setText(getText(R.string.service_stop_description));
-                    }
-                }
-                
-                View button = root.findViewById(R.id.right_button);
-                button.setOnClickListener(detail);
-                ((TextView)button).setText(getText(detail.mManageIntent != null
-                        ? R.string.service_manage : R.string.service_stop));
-                root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
-                
-                mActiveDetails.add(detail);
+                addServiceDetailsView(mMergedItem.mServices.get(i), mMergedItem);
+            }
+            
+            if (mMergedItem.mServices.size() <= 0) {
+                // This item does not have any services, so it must be
+                // a heavy-weight process...  we will put a fake service
+                // entry for it, to allow the user to "stop" it.
+                addServiceDetailsView(null, mMergedItem);
             }
             
-            boolean didProcess = false;
             for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
                 RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
                         : mMergedItem.mOtherProcesses.get(i);
@@ -238,70 +329,7 @@ public class RunningServiceDetails extends Activity {
                     continue;
                 }
                 
-                if (!didProcess) {
-                    mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
-                            mAllDetails, false);
-                    mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
-                    mAllDetails.addView(mProcessesHeader);
-                    didProcess = true;
-                }
-                ActiveDetail detail = new ActiveDetail();
-                View root = mInflater.inflate(R.layout.running_service_details_process,
-                        mAllDetails, false);
-                mAllDetails.addView(root);
-                detail.mRootView = root;
-                detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
-                detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
-                
-                TextView description = (TextView)root.findViewById(R.id.comp_description);
-                if (i < 0) {
-                    description.setText(R.string.main_running_process_description);
-                } else {
-                    int textid = 0;
-                    CharSequence label = null;
-                    ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
-                    final ComponentName comp = rpi.importanceReasonComponent;
-                    //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
-                    //        + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
-                    switch (rpi.importanceReasonCode) {
-                        case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
-                            textid = R.string.process_provider_in_use_description;
-                            List<ProviderInfo> providers = null;
-                            if (comp != null) {
-                                providers = getPackageManager()
-                                        .queryContentProviders(comp.getPackageName(),
-                                                rpi.uid, 0);
-                            }
-                            if (providers != null) {
-                                for (int j=0; j<providers.size(); j++) {
-                                    ProviderInfo prov = providers.get(i);
-                                    if (comp.getClassName().equals(prov.name)) {
-                                        label = RunningState.makeLabel(getPackageManager(),
-                                                prov.name, prov);
-                                        break;
-                                    }
-                                }
-                            }
-                            break;
-                        case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
-                            textid = R.string.process_service_in_use_description;
-                            if (rpi.importanceReasonComponent != null) {
-                                try {
-                                    ServiceInfo serv = getPackageManager().getServiceInfo(
-                                            rpi.importanceReasonComponent, 0);
-                                    label = RunningState.makeLabel(getPackageManager(),
-                                            serv.name, serv);
-                                } catch (NameNotFoundException e) {
-                                }
-                            }
-                            break;
-                    }
-                    if (textid != 0 && label != null) {
-                        description.setText(getString(textid, label));
-                    }
-                }
-                
-                mActiveDetails.add(detail);
+                addProcessDetailsView(pi, i < 0);
             }
         }
     }
index 2b51421..b3f56e3 100644 (file)
@@ -47,18 +47,34 @@ import java.util.List;
  */
 public class RunningState {
 
-    final SparseArray<HashMap<String, ProcessItem>> mProcesses
+    // Processes that are hosting a service we are interested in, organized
+    // by uid and name.  Note that this mapping does not change even across
+    // service restarts, and during a restart there will still be a process
+    // entry.
+    final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName
             = new SparseArray<HashMap<String, ProcessItem>>();
-    final SparseArray<ProcessItem> mActiveProcesses
+    
+    // Processes that are hosting a service we are interested in, organized
+    // by their pid.  These disappear and re-appear as services are restarted.
+    final SparseArray<ProcessItem> mServiceProcessesByPid
             = new SparseArray<ProcessItem>();
+    
+    // Used to sort the interesting processes.
     final ServiceProcessComparator mServiceProcessComparator
             = new ServiceProcessComparator();
     
-    // Temporary for finding process dependencies.
+    // Additional heavy-weight processes to be shown to the user, even if
+    // there is no service running in them.
+    final ArrayList<ProcessItem> mHeavyProcesses = new ArrayList<ProcessItem>();
+    
+    // All currently running processes, for finding dependencies etc.
     final SparseArray<ProcessItem> mRunningProcesses
             = new SparseArray<ProcessItem>();
     
+    // The processes associated with services, in sorted order.
     final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
+    
+    // All processes, used for retrieving memory information.
     final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
     
     int mSequence = 0;
@@ -128,6 +144,8 @@ public class RunningState {
         int mRunningSeq;
         ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
         
+        MergedItem mMergedItem;
+        
         // Purely for sorting.
         boolean mIsSystem;
         boolean mIsStarted;
@@ -435,10 +453,10 @@ public class RunningState {
                 continue;
             }
             
-            HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
+            HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
             if (procs == null) {
                 procs = new HashMap<String, ProcessItem>();
-                mProcesses.put(si.uid, procs);
+                mServiceProcessesByName.put(si.uid, procs);
             }
             ProcessItem proc = procs.get(si.process);
             if (proc == null) {
@@ -453,10 +471,10 @@ public class RunningState {
                     changed = true;
                     if (proc.mPid != pid) {
                         if (proc.mPid != 0) {
-                            mActiveProcesses.remove(proc.mPid);
+                            mServiceProcessesByPid.remove(proc.mPid);
                         }
                         if (pid != 0) {
-                            mActiveProcesses.put(pid, proc);
+                            mServiceProcessesByPid.put(pid, proc);
                         }
                         proc.mPid = pid;
                     }
@@ -474,19 +492,30 @@ public class RunningState {
         final int NP = processes != null ? processes.size() : 0;
         for (int i=0; i<NP; i++) {
             ActivityManager.RunningAppProcessInfo pi = processes.get(i);
-            ProcessItem proc = mActiveProcesses.get(pi.pid);
+            ProcessItem proc = mServiceProcessesByPid.get(pi.pid);
             if (proc == null) {
                 // This process is not one that is a direct container
                 // of a service, so look for it in the secondary
                 // running list.
                 proc = mRunningProcesses.get(pi.pid);
                 if (proc == null) {
+                    changed = true;
                     proc = new ProcessItem(context, pi.uid, pi.processName);
                     proc.mPid = pi.pid;
                     mRunningProcesses.put(pi.pid, proc);
                 }
                 proc.mDependentProcesses.clear();
             }
+            
+            if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_HEAVY_WEIGHT) != 0) {
+                if (!mHeavyProcesses.contains(proc)) {
+                    changed = true;
+                    mHeavyProcesses.add(proc);
+                }
+                proc.mCurSeq = mSequence;
+                proc.ensureLabel(pm);
+            }
+            
             proc.mRunningSeq = mSequence;
             proc.mRunningProcessInfo = pi;
         }
@@ -499,7 +528,7 @@ public class RunningState {
             if (proc.mRunningSeq == mSequence) {
                 int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
                 if (clientPid != 0) {
-                    ProcessItem client = mActiveProcesses.get(clientPid);
+                    ProcessItem client = mServiceProcessesByPid.get(clientPid);
                     if (client == null) {
                         client = mRunningProcesses.get(clientPid);
                     }
@@ -508,29 +537,42 @@ public class RunningState {
                     }
                 } else {
                     // In this pass the process doesn't have a client.
-                    // Clear to make sure if it later gets the same one
-                    // that we will detect the change.
+                    // Clear to make sure that, if it later gets the same one,
+                    // we will detect the change.
                     proc.mClient = null;
                 }
             } else {
+                changed = true;
                 mRunningProcesses.remove(mRunningProcesses.keyAt(i));
             }
         }
         
+        // Remove any old heavy processes.
+        int NHP = mHeavyProcesses.size();
+        for (int i=0; i<NHP; i++) {
+            ProcessItem proc = mHeavyProcesses.get(i);
+            if (mRunningProcesses.get(proc.mPid) == null) {
+                changed = true;
+                mHeavyProcesses.remove(i);
+                i--;
+                NHP--;
+            }
+        }
+        
         // Follow the tree from all primary service processes to all
         // processes they are dependent on, marking these processes as
         // still being active and determining if anything has changed.
-        final int NAP = mActiveProcesses.size();
+        final int NAP = mServiceProcessesByPid.size();
         for (int i=0; i<NAP; i++) {
-            ProcessItem proc = mActiveProcesses.valueAt(i);
+            ProcessItem proc = mServiceProcessesByPid.valueAt(i);
             if (proc.mCurSeq == mSequence) {
                 changed |= proc.buildDependencyChain(context, pm, mSequence);
             }
         }
         
         // Look for services and their primary processes that no longer exist...
-        for (int i=0; i<mProcesses.size(); i++) {
-            HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
+        for (int i=0; i<mServiceProcessesByName.size(); i++) {
+            HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i);
             Iterator<ProcessItem> pit = procs.values().iterator();
             while (pit.hasNext()) {
                 ProcessItem pi = pit.next();
@@ -545,10 +587,10 @@ public class RunningState {
                     changed = true;
                     pit.remove();
                     if (procs.size() == 0) {
-                        mProcesses.remove(mProcesses.keyAt(i));
+                        mServiceProcessesByName.remove(mServiceProcessesByName.keyAt(i));
                     }
                     if (pi.mPid != 0) {
-                        mActiveProcesses.remove(pi.mPid);
+                        mServiceProcessesByPid.remove(pi.mPid);
                     }
                     continue;
                 }
@@ -566,8 +608,8 @@ public class RunningState {
         if (changed) {
             // First determine an order for the services.
             ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
-            for (int i=0; i<mProcesses.size(); i++) {
-                for (ProcessItem pi : mProcesses.valueAt(i).values()) {
+            for (int i=0; i<mServiceProcessesByName.size(); i++) {
+                for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) {
                     pi.mIsSystem = false;
                     pi.mIsStarted = true;
                     pi.mActiveSince = Long.MAX_VALUE;
@@ -643,6 +685,23 @@ public class RunningState {
                 mergedItem.update(context);
                 newMergedItems.add(mergedItem);
             }
+            
+            // Finally, heavy-weight processes need to be shown and will
+            // go at the top.
+            NHP = mHeavyProcesses.size();
+            for (int i=0; i<NHP; i++) {
+                ProcessItem proc = mHeavyProcesses.get(i);
+                if (proc.mClient == null && proc.mServices.size() <= 0) {
+                    if (proc.mMergedItem == null) {
+                        proc.mMergedItem = new MergedItem();
+                        proc.mMergedItem.mProcess = proc;
+                    }
+                    proc.mMergedItem.update(context);
+                    newMergedItems.add(0, proc.mMergedItem);
+                    mProcessItems.add(proc);
+                }
+            }
+            
             synchronized (mLock) {
                 mItems = newItems;
                 mMergedItems = newMergedItems;
index 43905dc..dba7809 100644 (file)
@@ -288,9 +288,9 @@ public class BatteryHistoryChart extends View {
     }
 
     void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath,
-            int lastBatX, boolean lastCharging, boolean lastScreenOn, Path lastPath) {
+            int lastX, boolean lastCharging, boolean lastScreenOn, Path lastPath) {
         if (curLevelPath != null) {
-            if (lastBatX >= 0) {
+            if (lastX >= 0 && lastX < w) {
                 if (lastPath != null) {
                     lastPath.lineTo(w, y);
                 }
@@ -301,10 +301,10 @@ public class BatteryHistoryChart extends View {
             curLevelPath.close();
         }
         
-        if (lastCharging) {
+        if (lastCharging && lastX < w) {
             mChargingPath.lineTo(w, h-mChargingOffset);
         }
-        if (lastScreenOn) {
+        if (lastScreenOn && lastX < w) {
             mScreenOnPath.lineTo(w, h-mScreenOnOffset);
         }
     }
@@ -329,7 +329,7 @@ public class BatteryHistoryChart extends View {
         final int levelh = h - mLevelOffset;
         
         BatteryStats.HistoryItem rec = mHistFirst;
-        int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1, lastBatX = -1;
+        int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1;
         int i = 0;
         Path curLevelPath = null;
         Path lastLinePath = null;
@@ -342,11 +342,8 @@ public class BatteryHistoryChart extends View {
                 
                 if (lastX != x) {
                     // We have moved by at least a pixel.
-                    if (lastY == y) {
-                        // Battery level is still the same; don't plot,
-                        // but remember it.
-                        lastBatX = x;
-                    } else {
+                    if (lastY != y) {
+                        // Don't plot changes within a pixel.
                         Path path;
                         byte value = rec.batteryLevel;
                         if (value <= BATTERY_CRITICAL) path = mBatCriticalPath;
@@ -372,7 +369,6 @@ public class BatteryHistoryChart extends View {
                         }
                         lastX = x;
                         lastY = y;
-                        lastBatX = -1;
                         
                         final boolean charging =
                             (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
@@ -399,9 +395,9 @@ public class BatteryHistoryChart extends View {
                 }
                 
             } else if (curLevelPath != null) {
-                finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastBatX,
+                finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
                         lastCharging, lastScreenOn, lastLinePath);
-                lastX = lastY = lastBatX = -1;
+                lastX = lastY = -1;
                 curLevelPath = null;
                 lastLinePath = null;
                 lastCharging = lastScreenOn = false;
@@ -411,7 +407,7 @@ public class BatteryHistoryChart extends View {
             i++;
         }
         
-        finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastBatX,
+        finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX,
                 lastCharging, lastScreenOn, lastLinePath);
     }