Merge "Fix issue #10948509: Crash in procstats when there is no data" into klp-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 51ff2f2..f0722a8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -801,7 +801,8 @@
         <activity android:name="Settings$AppOpsSummaryActivity"
                 android:label="@string/app_ops_settings"
                 android:taskAffinity=""
-                android:excludeFromRecents="true">
+                android:excludeFromRecents="true"
+                android:enabled="false">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <action android:name="android.settings.APP_OPS_SETTINGS" />
diff --git a/src/com/android/settings/applications/ProcStatsEntry.java b/src/com/android/settings/applications/ProcStatsEntry.java
index 8308784..0821ced 100644
--- a/src/com/android/settings/applications/ProcStatsEntry.java
+++ b/src/com/android/settings/applications/ProcStatsEntry.java
@@ -21,8 +21,8 @@
 import android.content.pm.PackageManager;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArrayMap;
 import android.util.Log;
-import android.util.SparseArray;
 import com.android.internal.app.ProcessStats;
 
 import java.util.ArrayList;
@@ -31,11 +31,12 @@
 
 public final class ProcStatsEntry implements Parcelable {
     private static final String TAG = "ProcStatsEntry";
+    private static boolean DEBUG = ProcessStatsUi.DEBUG;
 
     final String mPackage;
     final int mUid;
     final String mName;
-    final boolean mUnique;
+    final ArrayList<String> mPackages = new ArrayList<String>();
     final long mDuration;
     final long mAvgPss;
     final long mMaxPss;
@@ -45,33 +46,35 @@
 
     String mBestTargetPackage;
 
-    ArrayList<Service> mServices = new ArrayList<Service>(2);
+    ArrayMap<String, ArrayList<Service>> mServices = new ArrayMap<String, ArrayList<Service>>(1);
 
     public ApplicationInfo mUiTargetApp;
     public String mUiLabel;
     public String mUiBaseLabel;
     public String mUiPackage;
 
-    public ProcStatsEntry(ProcessStats.ProcessState proc,
+    public ProcStatsEntry(ProcessStats.ProcessState proc, String packageName,
             ProcessStats.ProcessDataCollection tmpTotals, boolean useUss, boolean weightWithTime) {
         ProcessStats.computeProcessData(proc, tmpTotals, 0);
         mPackage = proc.mPackage;
         mUid = proc.mUid;
         mName = proc.mName;
-        mUnique = proc.mCommonProcess == proc;
+        mPackages.add(packageName);
         mDuration = tmpTotals.totalTime;
         mAvgPss = tmpTotals.avgPss;
         mMaxPss = tmpTotals.maxPss;
         mAvgUss = tmpTotals.avgUss;
         mMaxUss = tmpTotals.maxUss;
         mWeight = (weightWithTime ? mDuration : 1) * (useUss ? mAvgUss : mAvgPss);
+        if (DEBUG) Log.d(TAG, "New proc entry " + proc.mName + ": dur=" + mDuration
+                + " avgpss=" + mAvgPss + " weight=" + mWeight);
     }
 
     public ProcStatsEntry(Parcel in) {
         mPackage = in.readString();
         mUid = in.readInt();
         mName = in.readString();
-        mUnique = in.readInt() != 0;
+        in.readStringList(mPackages);
         mDuration = in.readLong();
         mAvgPss = in.readLong();
         mMaxPss = in.readLong();
@@ -79,40 +82,118 @@
         mMaxUss = in.readLong();
         mWeight = in.readLong();
         mBestTargetPackage = in.readString();
-        in.readTypedList(mServices, Service.CREATOR);
+        final int N = in.readInt();
+        if (N > 0) {
+            mServices.ensureCapacity(N);
+            for (int i=0; i<N; i++) {
+                String key = in.readString();
+                ArrayList<Service> value = new ArrayList<Service>();
+                in.readTypedList(value, Service.CREATOR);
+                mServices.append(key, value);
+            }
+        }
     }
 
-    public void evaluateTargetPackage(ProcessStats stats, ProcessStats.ProcessDataCollection totals,
-            Comparator<ProcStatsEntry> compare, boolean useUss, boolean weightWithTime) {
+    public void addPackage(String packageName) {
+        mPackages.add(packageName);
+    }
+
+    public void evaluateTargetPackage(PackageManager pm, ProcessStats stats,
+            ProcessStats.ProcessDataCollection totals, Comparator<ProcStatsEntry> compare,
+            boolean useUss, boolean weightWithTime) {
         mBestTargetPackage = null;
-        if (mUnique) {
-            mBestTargetPackage = mPackage;
+        if (mPackages.size() == 1) {
+            if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": single pkg " + mPackages.get(0));
+            mBestTargetPackage = mPackages.get(0);
         } else {
             // See if there is one significant package that was running here.
             ArrayList<ProcStatsEntry> subProcs = new ArrayList<ProcStatsEntry>();
-            for (int ipkg=0, NPKG=stats.mPackages.getMap().size(); ipkg<NPKG; ipkg++) {
-                SparseArray<ProcessStats.PackageState> uids
-                        = stats.mPackages.getMap().valueAt(ipkg);
-                for (int iu=0, NU=uids.size(); iu<NU; iu++) {
-                    if (uids.keyAt(iu) != mUid) {
-                        continue;
-                    }
-                    ProcessStats.PackageState pkgState = uids.valueAt(iu);
-                    for (int iproc=0, NPROC=pkgState.mProcesses.size(); iproc<NPROC; iproc++) {
-                        ProcessStats.ProcessState subProc =
-                                pkgState.mProcesses.valueAt(iproc);
-                        if (subProc.mName.equals(mName)) {
-                            subProcs.add(new ProcStatsEntry(subProc, totals, useUss,
-                                    weightWithTime));
-                        }
-                    }
+            for (int ipkg=0; ipkg<mPackages.size(); ipkg++) {
+                ProcessStats.PackageState pkgState = stats.mPackages.get(mPackages.get(ipkg), mUid);
+                if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ", pkg "
+                        + mPackages.get(ipkg) + ":");
+                if (pkgState == null) {
+                    Log.w(TAG, "No package state found for " + mPackages.get(ipkg) + "/"
+                            + mUid + " in process " + mName);
+                    continue;
                 }
+                ProcessStats.ProcessState pkgProc = pkgState.mProcesses.get(mName);
+                if (pkgProc == null) {
+                    Log.w(TAG, "No process " + mName + " found in package state "
+                            + mPackages.get(ipkg) + "/" + mUid);
+                    continue;
+                }
+                subProcs.add(new ProcStatsEntry(pkgProc, pkgState.mPackageName, totals, useUss,
+                        weightWithTime));
             }
             if (subProcs.size() > 1) {
                 Collections.sort(subProcs, compare);
                 if (subProcs.get(0).mWeight > (subProcs.get(1).mWeight*3)) {
+                    if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": best pkg "
+                            + subProcs.get(0).mPackage + " weight " + subProcs.get(0).mWeight
+                            + " better than " + subProcs.get(1).mPackage
+                            + " weight " + subProcs.get(1).mWeight);
                     mBestTargetPackage = subProcs.get(0).mPackage;
+                    return;
                 }
+                // Couldn't find one that is best by weight, let's decide on best another
+                // way: the one that has the longest running service, accounts for at least
+                // half of the maximum weight, and has specified an explicit app icon.
+                long maxWeight = subProcs.get(0).mWeight;
+                long bestRunTime = -1;
+                for (int i=0; i<subProcs.size(); i++) {
+                    if (subProcs.get(i).mWeight < (maxWeight/2)) {
+                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+                                + subProcs.get(i).mPackage + " weight " + subProcs.get(i).mWeight
+                                + " too small");
+                        continue;
+                    }
+                    try {
+                        ApplicationInfo ai = pm.getApplicationInfo(subProcs.get(i).mPackage, 0);
+                        if (ai.icon == 0) {
+                            if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+                                    + subProcs.get(i).mPackage + " has no icon");
+                            continue;
+                        }
+                    } catch (PackageManager.NameNotFoundException e) {
+                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+                                + subProcs.get(i).mPackage + " failed finding app info");
+                        continue;
+                    }
+                    ArrayList<Service> subProcServices = null;
+                    for (int isp=0, NSP=mServices.size(); isp<NSP; isp++) {
+                        ArrayList<Service> subServices = mServices.valueAt(isp);
+                        if (subServices.get(0).mPackage.equals(subProcs.get(i).mPackage)) {
+                            subProcServices = subServices;
+                            break;
+                        }
+                    }
+                    long thisRunTime = 0;
+                    if (subProcServices != null) {
+                        for (int iss=0, NSS=subProcServices.size(); iss<NSS; iss++) {
+                            Service service = subProcServices.get(iss);
+                            if (service.mDuration > thisRunTime) {
+                                if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+                                        + subProcs.get(i).mPackage + " service " + service.mName
+                                        + " run time is " + service.mDuration);
+                                thisRunTime = service.mDuration;
+                                break;
+                            }
+                        }
+                    }
+                    if (thisRunTime > bestRunTime) {
+                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+                                + subProcs.get(i).mPackage + " new best run time " + thisRunTime);
+                        mBestTargetPackage = subProcs.get(i).mPackage;
+                        bestRunTime = thisRunTime;
+                    } else {
+                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+                                + subProcs.get(i).mPackage + " run time " + thisRunTime
+                                + " not as good as last " + bestRunTime);
+                    }
+                }
+            } else if (subProcs.size() == 1) {
+                mBestTargetPackage = subProcs.get(0).mPackage;
             }
         }
     }
@@ -178,7 +259,12 @@
     }
 
     public void addService(ProcessStats.ServiceState svc) {
-        mServices.add(new Service(svc));
+        ArrayList<Service> services = mServices.get(svc.mPackage);
+        if (services == null) {
+            services = new ArrayList<Service>();
+            mServices.put(svc.mPackage, services);
+        }
+        services.add(new Service(svc));
     }
 
     @Override
@@ -191,7 +277,7 @@
         dest.writeString(mPackage);
         dest.writeInt(mUid);
         dest.writeString(mName);
-        dest.writeInt(mUnique ? 1 : 0);
+        dest.writeStringList(mPackages);
         dest.writeLong(mDuration);
         dest.writeLong(mAvgPss);
         dest.writeLong(mMaxPss);
@@ -199,7 +285,12 @@
         dest.writeLong(mMaxUss);
         dest.writeLong(mWeight);
         dest.writeString(mBestTargetPackage);
-        dest.writeTypedList(mServices);
+        final int N = mServices.size();
+        dest.writeInt(N);
+        for (int i=0; i<N; i++) {
+            dest.writeString(mServices.keyAt(i));
+            dest.writeTypedList(mServices.valueAt(i));
+        }
     }
 
     public static final Parcelable.Creator<ProcStatsEntry> CREATOR
diff --git a/src/com/android/settings/applications/ProcessStatsDetail.java b/src/com/android/settings/applications/ProcessStatsDetail.java
index fad3745..a1885a7 100644
--- a/src/com/android/settings/applications/ProcessStatsDetail.java
+++ b/src/com/android/settings/applications/ProcessStatsDetail.java
@@ -168,6 +168,23 @@
         }
     }
 
+    private void addPackageHeaderItem(ViewGroup parent, String packageName) {
+        LayoutInflater inflater = getActivity().getLayoutInflater();
+        ViewGroup item = (ViewGroup) inflater.inflate(R.layout.running_processes_item,
+                null);
+        parent.addView(item);
+        final ImageView icon = (ImageView) item.findViewById(R.id.icon);
+        TextView nameView = (TextView) item.findViewById(R.id.name);
+        TextView descriptionView = (TextView) item.findViewById(R.id.description);
+        try {
+            ApplicationInfo ai = mPm.getApplicationInfo(packageName, 0);
+            icon.setImageDrawable(ai.loadIcon(mPm));
+            nameView.setText(ai.loadLabel(mPm));
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        descriptionView.setText(packageName);
+    }
+
     private void addDetailsItem(ViewGroup parent, CharSequence label, CharSequence value) {
         LayoutInflater inflater = getActivity().getLayoutInflater();
         ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
@@ -204,22 +221,31 @@
     };
 
     private void fillServicesSection() {
-        LayoutInflater inflater = getActivity().getLayoutInflater();
         if (mEntry.mServices.size() > 0) {
-            ArrayList<ProcStatsEntry.Service> services =
-                    (ArrayList<ProcStatsEntry.Service>)mEntry.mServices.clone();
-            Collections.sort(services, sServiceCompare);
-            for (int i=0; i<services.size(); i++) {
-                ProcStatsEntry.Service service = services.get(i);
-                String label = service.mName;
-                int tail = label.lastIndexOf('.');
-                if (tail >= 0 && tail < (label.length()-1)) {
-                    label = label.substring(tail+1);
+            boolean addPackageSections = false;
+            if (mEntry.mServices.size() > 1
+                    || !mEntry.mServices.valueAt(0).get(0).mPackage.equals(mEntry.mPackage)) {
+                addPackageSections = true;
+            }
+            for (int ip=0; ip<mEntry.mServices.size(); ip++) {
+                ArrayList<ProcStatsEntry.Service> services =
+                        (ArrayList<ProcStatsEntry.Service>)mEntry.mServices.valueAt(ip).clone();
+                Collections.sort(services, sServiceCompare);
+                if (addPackageSections) {
+                    addPackageHeaderItem(mServicesParent, services.get(0).mPackage);
                 }
-                long duration = service.mDuration;
-                final double percentOfTime = (((double)duration) / mTotalTime) * 100;
-                addDetailsItem(mServicesParent, label, getActivity().getResources().getString(
-                        R.string.percentage, (int) Math.ceil(percentOfTime)));
+                for (int is=0; is<services.size(); is++) {
+                    ProcStatsEntry.Service service = services.get(is);
+                    String label = service.mName;
+                    int tail = label.lastIndexOf('.');
+                    if (tail >= 0 && tail < (label.length()-1)) {
+                        label = label.substring(tail+1);
+                    }
+                    long duration = service.mDuration;
+                    final double percentOfTime = (((double)duration) / mTotalTime) * 100;
+                    addDetailsItem(mServicesParent, label, getActivity().getResources().getString(
+                            R.string.percentage, (int) Math.ceil(percentOfTime)));
+                }
             }
         }
     }
diff --git a/src/com/android/settings/applications/ProcessStatsPreference.java b/src/com/android/settings/applications/ProcessStatsPreference.java
index 2dea96a..bf2676d 100644
--- a/src/com/android/settings/applications/ProcessStatsPreference.java
+++ b/src/com/android/settings/applications/ProcessStatsPreference.java
@@ -45,7 +45,7 @@
     public void setPercent(double percentOfWeight, double percentOfTime) {
         mProgress = (int) Math.ceil(percentOfWeight);
         mProgressText = getContext().getResources().getString(
-                R.string.percentage, (int) Math.ceil(percentOfTime));
+                R.string.percentage, (int) Math.round(percentOfTime));
         notifyChanged();
     }
 
diff --git a/src/com/android/settings/applications/ProcessStatsUi.java b/src/com/android/settings/applications/ProcessStatsUi.java
index b7ffcef..0e27577 100644
--- a/src/com/android/settings/applications/ProcessStatsUi.java
+++ b/src/com/android/settings/applications/ProcessStatsUi.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
-import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -30,7 +29,6 @@
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -39,6 +37,7 @@
 import android.view.MenuItem;
 import android.view.SubMenu;
 import com.android.internal.app.IProcessStats;
+import com.android.internal.app.ProcessMap;
 import com.android.internal.app.ProcessStats;
 import com.android.settings.R;
 import com.android.settings.fuelgauge.Utils;
@@ -50,8 +49,8 @@
 import java.util.Comparator;
 
 public class ProcessStatsUi extends PreferenceFragment {
-    private static final String TAG = "ProcessStatsUi";
-    private static final boolean DEBUG = false;
+    static final String TAG = "ProcessStatsUi";
+    static final boolean DEBUG = false;
 
     private static final String KEY_APP_LIST = "app_list";
     private static final String KEY_MEM_STATUS = "mem_status";
@@ -76,6 +75,10 @@
                 return 1;
             } else if (lhs.mWeight > rhs.mWeight) {
                 return -1;
+            } else if (lhs.mDuration < rhs.mDuration) {
+                return 1;
+            } else if (lhs.mDuration > rhs.mDuration) {
+                return -1;
             }
             return 0;
         }
@@ -112,7 +115,7 @@
     // batches of 3 hours so we want to allow the time we use to be slightly
     // smaller than the actual time selected instead of bumping up to 3 hours
     // beyond it.
-    private static final long DURATION_QUANTUM = 3*60*60*1000;
+    private static final long DURATION_QUANTUM = ProcessStats.COMMIT_PERIOD;
     private static long[] sDurations = new long[] {
         3*60*60*1000 - DURATION_QUANTUM/2, 6*60*60*1000 - DURATION_QUANTUM/2,
         12*60*60*1000 - DURATION_QUANTUM/2, 24*60*60*1000 - DURATION_QUANTUM/2
@@ -319,6 +322,12 @@
             ProcessStats.STATE_CACHED_EMPTY
     };
 
+    private String makeDuration(long time) {
+        StringBuilder sb = new StringBuilder(32);
+        TimeUtils.formatDuration(time, sb);
+        return sb.toString();
+    }
+
     private void refreshStats() {
         updateMenus();
 
@@ -378,6 +387,7 @@
 
         mTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations,
                 mStats.mMemFactor, mStats.mStartTime, now);
+        if (DEBUG) Log.d(TAG, "Total time of stats: " + makeDuration(mTotalTime));
 
         LinearColorPreference colors = new LinearColorPreference(getActivity());
         colors.setOrder(-1);
@@ -396,7 +406,7 @@
                         + memTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]) / (float)mTotalTime,
                 memTimes[ProcessStats.ADJ_MEM_FACTOR_NORMAL] / (float)mTotalTime);
 
-        ArrayList<ProcStatsEntry> procs = new ArrayList<ProcStatsEntry>();
+        ArrayList<ProcStatsEntry> entries = new ArrayList<ProcStatsEntry>();
 
         /*
         ArrayList<ProcessStats.ProcessState> rawProcs = mStats.collectProcessesLocked(
@@ -407,50 +417,43 @@
         }
         */
 
-        ArrayMap<String, ProcStatsEntry> processes = new ArrayMap<String, ProcStatsEntry>(
-                mStats.mProcesses.getMap().size());
-        for (int ip=0, N=mStats.mProcesses.getMap().size(); ip<N; ip++) {
-            SparseArray<ProcessStats.ProcessState> uids = mStats.mProcesses.getMap().valueAt(ip);
-            for (int iu=0; iu<uids.size(); iu++) {
-                ProcStatsEntry ent = new ProcStatsEntry(uids.valueAt(iu), totals, mUseUss,
-                        mStatsType == MENU_TYPE_BACKGROUND);
-                procs.add(ent);
-                processes.put(ent.mName, ent);
+        if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES");
+
+        final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>();
+        for (int ipkg=0, N=mStats.mPackages.getMap().size(); ipkg<N; ipkg++) {
+            final SparseArray<ProcessStats.PackageState> pkgUids
+                    = mStats.mPackages.getMap().valueAt(ipkg);
+            for (int iu=0; iu<pkgUids.size(); iu++) {
+                final ProcessStats.PackageState st = pkgUids.valueAt(iu);
+                for (int iproc=0; iproc<st.mProcesses.size(); iproc++) {
+                    final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc);
+                    final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName,
+                            pkgProc.mUid);
+                    if (proc == null) {
+                        Log.w(TAG, "No process found for pkg " + st.mPackageName
+                                + "/" + st.mUid + " proc name " + pkgProc.mName);
+                        continue;
+                    }
+                    ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid);
+                    if (ent == null) {
+                        ent = new ProcStatsEntry(proc, st.mPackageName, totals, mUseUss,
+                                mStatsType == MENU_TYPE_BACKGROUND);
+                        if (ent.mDuration > 0) {
+                            if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/"
+                                    + proc.mUid + ": time=" + makeDuration(ent.mDuration) + " ("
+                                    + ((((double)ent.mDuration) / mTotalTime) * 100) + "%)"
+                                    + " pss=" + ent.mAvgPss);
+                            entriesMap.put(proc.mName, proc.mUid, ent);
+                            entries.add(ent);
+                        }
+                    }  else {
+                        ent.addPackage(st.mPackageName);
+                    }
+                }
             }
         }
 
-        Collections.sort(procs, sEntryCompare);
-        while (procs.size() > MAX_ITEMS_TO_LIST) {
-            procs.remove(procs.size()-1);
-        }
-
-        long maxWeight = 0;
-        for (int i=0, N=(procs != null ? procs.size() : 0); i<N; i++) {
-            ProcStatsEntry proc = procs.get(i);
-            if (maxWeight < proc.mWeight) {
-                maxWeight = proc.mWeight;
-            }
-        }
-        mMaxWeight = maxWeight;
-
-        for (int i=0, N=(procs != null ? procs.size() : 0); i<N; i++) {
-            ProcStatsEntry proc = procs.get(i);
-            final double percentOfWeight = (((double)proc.mWeight) / maxWeight) * 100;
-            final double percentOfTime = (((double)proc.mDuration) / mTotalTime) * 100;
-            if (percentOfWeight < 1) break;
-            ProcessStatsPreference pref = new ProcessStatsPreference(getActivity(), null, proc);
-            proc.evaluateTargetPackage(mStats, totals, sEntryCompare, mUseUss,
-                    mStatsType == MENU_TYPE_BACKGROUND);
-            proc.retrieveUiData(pm);
-            pref.setTitle(proc.mUiLabel);
-            if (proc.mUiTargetApp != null) {
-                pref.setIcon(proc.mUiTargetApp.loadIcon(pm));
-            }
-            pref.setOrder(i);
-            pref.setPercent(percentOfWeight, percentOfTime);
-            mAppListGroup.addPreference(pref);
-            if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) break;
-        }
+        if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES");
 
         // Add in service info.
         if (mStatsType == MENU_TYPE_BACKGROUND) {
@@ -461,13 +464,85 @@
                     for (int is=0, NS=ps.mServices.size(); is<NS; is++) {
                         ProcessStats.ServiceState ss = ps.mServices.valueAt(is);
                         if (ss.mProcessName != null) {
-                            ProcStatsEntry ent = processes.get(ss.mProcessName);
-                            ent.addService(ss);
+                            ProcStatsEntry ent = entriesMap.get(ss.mProcessName, uids.keyAt(iu));
+                            if (ent != null) {
+                                if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName
+                                        + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc "
+                                        + ss.mProcessName);
+                                ent.addService(ss);
+                            } else {
+                                Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu)
+                                        + " for service " + ss.mName);
+                            }
                         }
                     }
                 }
             }
         }
+
+        /*
+        SparseArray<ArrayMap<String, ProcStatsEntry>> processes
+                = new SparseArray<ArrayMap<String, ProcStatsEntry>>();
+        for (int ip=0, N=mStats.mProcesses.getMap().size(); ip<N; ip++) {
+            SparseArray<ProcessStats.ProcessState> uids = mStats.mProcesses.getMap().valueAt(ip);
+            for (int iu=0; iu<uids.size(); iu++) {
+                ProcessStats.ProcessState st = uids.valueAt(iu);
+                ProcStatsEntry ent = new ProcStatsEntry(st, totals, mUseUss,
+                        mStatsType == MENU_TYPE_BACKGROUND);
+                if (ent.mDuration > 0) {
+                    if (DEBUG) Log.d(TAG, "Adding proc " + st.mName + "/" + st.mUid + ": time="
+                            + makeDuration(ent.mDuration) + " ("
+                            + ((((double)ent.mDuration) / mTotalTime) * 100) + "%)");
+                    procs.add(ent);
+                    ArrayMap<String, ProcStatsEntry> uidProcs = processes.get(ent.mUid);
+                    if (uidProcs == null) {
+                        uidProcs = new ArrayMap<String, ProcStatsEntry>();
+                        processes.put(ent.mUid, uidProcs);
+                    }
+                    uidProcs.put(ent.mName, ent);
+                }
+            }
+        }
+        */
+
+        Collections.sort(entries, sEntryCompare);
+
+        long maxWeight = 1;
+        for (int i=0, N=(entries != null ? entries.size() : 0); i<N; i++) {
+            ProcStatsEntry proc = entries.get(i);
+            if (maxWeight < proc.mWeight) {
+                maxWeight = proc.mWeight;
+            }
+        }
+        mMaxWeight = maxWeight;
+
+        if (DEBUG) Log.d(TAG, "-------------------- BUILDING UI");
+
+        for (int i=0, N=(entries != null ? entries.size() : 0); i<N; i++) {
+            ProcStatsEntry proc = entries.get(i);
+            final double percentOfWeight = (((double)proc.mWeight) / maxWeight) * 100;
+            final double percentOfTime = (((double)proc.mDuration) / mTotalTime) * 100;
+            if (percentOfWeight < 1 && percentOfTime < 33) {
+                if (DEBUG) Log.d(TAG, "Skipping " + proc.mName + " weight=" + percentOfWeight
+                        + " time=" + percentOfTime);
+                continue;
+            }
+            ProcessStatsPreference pref = new ProcessStatsPreference(getActivity(), null, proc);
+            proc.evaluateTargetPackage(pm, mStats, totals, sEntryCompare, mUseUss,
+                    mStatsType == MENU_TYPE_BACKGROUND);
+            proc.retrieveUiData(pm);
+            pref.setTitle(proc.mUiLabel);
+            if (proc.mUiTargetApp != null) {
+                pref.setIcon(proc.mUiTargetApp.loadIcon(pm));
+            }
+            pref.setOrder(i);
+            pref.setPercent(percentOfWeight, percentOfTime);
+            mAppListGroup.addPreference(pref);
+            if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) {
+                if (DEBUG) Log.d(TAG, "Done with UI, hit item limit!");
+                break;
+            }
+        }
     }
 
     private void load() {