Merge "Missing return statement if a print job is no longer present." into klp-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 51ff2f2..ab31b8c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -15,6 +15,7 @@
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
     <uses-permission android:name="android.permission.NFC" />
     <uses-permission android:name="android.permission.HARDWARE_TEST" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
@@ -801,7 +802,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/res/drawable-hdpi/ic_qs_certificate_info.png b/res/drawable-hdpi/ic_qs_certificate_info.png
deleted file mode 100644
index 1fdaaf9..0000000
--- a/res/drawable-hdpi/ic_qs_certificate_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_qs_certificate_info.png b/res/drawable-mdpi/ic_qs_certificate_info.png
deleted file mode 100644
index 3b49472..0000000
--- a/res/drawable-mdpi/ic_qs_certificate_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_qs_certificate_info.png b/res/drawable-xhdpi/ic_qs_certificate_info.png
deleted file mode 100644
index b3de2ce..0000000
--- a/res/drawable-xhdpi/ic_qs_certificate_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_qs_certificate_info.png b/res/drawable-xxhdpi/ic_qs_certificate_info.png
deleted file mode 100644
index 5d6f6c7..0000000
--- a/res/drawable-xxhdpi/ic_qs_certificate_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_settings_about.png b/res/drawable-xxhdpi/ic_settings_about.png
new file mode 100644
index 0000000..924d962
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_settings_about.png
Binary files differ
diff --git a/res/layout/empty_print_state.xml b/res/layout/empty_print_state.xml
index 135b3dd..e97bb85 100644
--- a/res/layout/empty_print_state.xml
+++ b/res/layout/empty_print_state.xml
@@ -15,7 +15,7 @@
 -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/empty_printers_list_service_disabled"
+    android:id="@+id/empty_print_state"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:visibility="gone">
@@ -28,11 +28,12 @@
         android:orientation="vertical">
 
         <ImageView
+            android:id="@+id/icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginBottom="12dip"
             android:src="@drawable/ic_grayedout_printer"
-            android:contentDescription="@string/print_service_disabled">
+            android:contentDescription="@null">
         </ImageView>
 
         <TextView
diff --git a/res/menu/nfc_payment_settings.xml b/res/menu/nfc_payment_settings.xml
deleted file mode 100644
index f35374e..0000000
--- a/res/menu/nfc_payment_settings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/nfc_payment_menu_item_add_service"
-        android:title="@string/nfc_payment_menu_item_add_service"
-        android:showAsAction="ifRoom">
-    </item>
-</menu>
diff --git a/res/menu/print_settings.xml b/res/menu/print_settings.xml
deleted file mode 100644
index 9df1181..0000000
--- a/res/menu/print_settings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/print_menu_item_add_service"
-        android:title="@string/print_menu_item_add_service"
-        android:showAsAction="ifRoom">
-    </item>
-</menu>
diff --git a/res/values/donottranslate.xml b/res/values/donottranslate.xml
index f290f38..e6bd9a6 100644
--- a/res/values/donottranslate.xml
+++ b/res/values/donottranslate.xml
@@ -33,8 +33,4 @@
         <item>@string/input_method_selector_always_show_value</item>
         <item>@string/input_method_selector_always_hide_value</item>
     </string-array>
-    <!-- Default query string to search for a print service. -->
-    <string name="download_print_service_query">market://search?q=print service</string>
-    <!-- Default query string to search for a NFC payment service. -->
-    <string name="download_nfc_payment_service_query">market://search?q=nfc payment</string>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3c45bbc..9c5d039 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2408,16 +2408,16 @@
     <string name="location_mode_high_accuracy_title">High accuracy</string>
     <!-- [CHAR LIMIT=30] Location settings screen, battery saving location mode -->
     <string name="location_mode_battery_saving_title">Battery saving</string>
-    <!-- [CHAR LIMIT=30] Location settings screen, device sensors only location mode -->
-    <string name="location_mode_sensors_only_title">Device sensors</string>
+    <!-- [CHAR LIMIT=30] Location settings screen, device only location mode -->
+    <string name="location_mode_sensors_only_title">Device only</string>
     <!-- [CHAR LIMIT=30] Location settings screen, location off mode -->
     <string name="location_mode_location_off_title">Location off</string>
     <!-- [CHAR LIMIT=30] Location settings screen, sub category for recent location requests -->
     <string name="location_category_recent_location_requests">Recent location requests</string>
     <!-- Location settings screen, displayed when there's no recent app accessing location -->
     <string name="location_no_recent_apps">No apps have requested location recently</string>
-    <!-- [CHAR LIMIT=30] Location settings screen, sub category for app settings -->
-    <string name="location_category_app_settings">App settings</string>
+    <!-- [CHAR LIMIT=30] Location settings screen, sub category for location services -->
+    <string name="location_category_location_services">Location services</string>
     <!-- [CHAR LIMIT=30] Location settings screen, recent location requests high battery use-->
     <string name="location_high_battery_use">High battery use</string>
     <!-- [CHAR LIMIT=30] Location settings screen, recent location requests low battery use-->
@@ -3430,6 +3430,9 @@
     <!-- Title for the prompt shown as a placeholder if no print serivices are installed. [CHAR LIMIT=50] -->
     <string name="print_no_services_installed">No services installed</string>
 
+    <!-- Title for the prompt shown as a placeholder if no printers are found while searching. [CHAR LIMIT=50] -->
+    <string name="print_no_printers_found">No printers found</string>
+
     <!-- Title for print menu item to launch a settings activity. [CHAR LIMIT=25] -->
     <string name="print_menu_item_settings">Settings</string>
 
@@ -4656,12 +4659,12 @@
     <!-- Shows up when there is a user SSL CA Cert installed on the
          device.  Indicates to the user that SSL traffic can be intercepted.  [CHAR LIMIT=NONE] -->
     <string name="ssl_ca_cert_warning">Network may be monitored</string>
-    <!-- Button to close the SSL CA cert warning dialog box.  [CHAR LIMIT=NONE] -->
+    <!-- Button to close the SSL CA cert warning dialog box, meaning the user is done reading.  [CHAR LIMIT=NONE] -->
     <string name="done_button">Done</string>
     <!-- Title of Dialog warning users of SSL monitoring. [CHAR LIMIT=NONE] -->
     <string name="ssl_ca_cert_dialog_title">Network monitoring</string>
     <!-- Text of message to show to users whose administrator has installed a SSL CA Cert.  [CHAR LIMIT=NONE] -->
-    <string name="ssl_ca_cert_info_message">This device is managed by: <xliff:g id="managing_domain">%s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity, including emails, apps, and secure websites.\n\nFor more information, contact your administrator.</string>
+    <string name="ssl_ca_cert_info_message">This device is managed by:\n<xliff:g id="managing_domain">%s</xliff:g>\n\nYour administrator is capable of monitoring your network activity, including emails, apps, and secure websites.\n\nFor more information, contact your administrator.</string>
     <!-- Text of warning to show to users that have a SSL CA Cert installed.  [CHAR LIMIT=NONE] -->
     <string name="ssl_ca_cert_warning_message">A third party is capable of monitoring your network\nactivity, including emails, apps, and secure websites.\n\nA trusted credential installed on your device is making this possible.</string>
     <!-- Label on button that will take the user to the Trusted Credentials settings page.  [CHAR LIMIT=NONE]-->
diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml
index 5be6bd3..83b80b1 100644
--- a/res/xml/location_settings.xml
+++ b/res/xml/location_settings.xml
@@ -26,7 +26,7 @@
             android:title="@string/location_category_recent_location_requests" />
 
         <PreferenceCategory
-            android:key="app_settings"
-            android:title="@string/location_category_app_settings" />
+            android:key="location_services"
+            android:title="@string/location_category_location_services" />
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java
index 94c793d..7057f1a 100644
--- a/src/com/android/settings/CryptKeeper.java
+++ b/src/com/android/settings/CryptKeeper.java
@@ -239,6 +239,7 @@
             | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
             | StatusBarManager.DISABLE_SYSTEM_INFO
             | StatusBarManager.DISABLE_HOME
+            | StatusBarManager.DISABLE_SEARCH
             | StatusBarManager.DISABLE_RECENT;
 
     /** @return whether or not this Activity was started for debugging the UI only. */
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index f365403..e002264 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -20,6 +20,7 @@
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
@@ -244,15 +245,25 @@
         // Enable or disable keyguard widget checkbox based on DPM state
         mEnableKeyguardWidgets = (CheckBoxPreference) root.findPreference(KEY_ENABLE_WIDGETS);
         if (mEnableKeyguardWidgets != null) {
-            final boolean disabled = (0 != (mDPM.getKeyguardDisabledFeatures(null)
-                    & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL));
-            if (disabled) {
-                mEnableKeyguardWidgets.setSummary(
-                        R.string.security_enable_widgets_disabled_summary);
+            if (ActivityManager.isLowRamDeviceStatic()) {
+                // Widgets take a lot of RAM, so disable them on low-memory devices
+                PreferenceGroup securityCategory
+                        = (PreferenceGroup) root.findPreference(KEY_SECURITY_CATEGORY);
+                if (securityCategory != null) {
+                    securityCategory.removePreference(root.findPreference(KEY_ENABLE_WIDGETS));
+                    mEnableKeyguardWidgets = null;
+                }
             } else {
-                mEnableKeyguardWidgets.setSummary("");
+                final boolean disabled = (0 != (mDPM.getKeyguardDisabledFeatures(null)
+                        & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL));
+                if (disabled) {
+                    mEnableKeyguardWidgets.setSummary(
+                            R.string.security_enable_widgets_disabled_summary);
+                } else {
+                    mEnableKeyguardWidgets.setSummary("");
+                }
+                mEnableKeyguardWidgets.setEnabled(!disabled);
             }
-            mEnableKeyguardWidgets.setEnabled(!disabled);
         }
 
         // Show password
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index ac67a38..87d34c6 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -32,6 +32,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
+import android.nfc.NfcAdapter;
 import android.os.Bundle;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
@@ -574,8 +575,15 @@
                     target.remove(i);
                 }
             } else if (id == R.id.nfc_payment_settings) {
-                if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
+                if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
                     target.remove(i);
+                } else {
+                    // Only show if NFC is on and we have the HCE feature
+                    NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
+                    if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
+                            PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+                        target.remove(i);
+                    }
                 }
             } else if (id == R.id.development_settings) {
                 if (!showDev) {
@@ -871,7 +879,7 @@
                             holder.divider_.setVisibility(View.VISIBLE);
                             boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
                             if (isManaged) {
-                                holder.button_.setImageResource(R.drawable.ic_qs_certificate_info);
+                                holder.button_.setImageResource(R.drawable.ic_settings_about);
                             } else {
                                 holder.button_.setImageResource(
                                         android.R.drawable.stat_notify_error);
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() {
diff --git a/src/com/android/settings/location/LocationMode.java b/src/com/android/settings/location/LocationMode.java
index 63ba6ea..233b010 100644
--- a/src/com/android/settings/location/LocationMode.java
+++ b/src/com/android/settings/location/LocationMode.java
@@ -45,12 +45,6 @@
     private RadioButtonPreference mSensorsOnly;
 
     @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-        createPreferenceHierarchy();
-    }
-
-    @Override
     public void onResume() {
         super.onResume();
         createPreferenceHierarchy();
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index add82c6..06a6650 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -37,7 +37,6 @@
 import android.widget.Switch;
 
 import com.android.settings.R;
-import com.android.settings.fuelgauge.BatteryStatsHelper;
 
 import java.util.Collections;
 import java.util.Comparator;
@@ -56,13 +55,12 @@
     /** Key for preference category "Recent location requests" */
     private static final String KEY_RECENT_LOCATION_REQUESTS = "recent_location_requests";
     /** Key for preference category "Location services" */
-    private static final String KEY_APP_SETTINGS = "app_settings";
+    private static final String KEY_LOCATION_SERVICES = "location_services";
 
     private Switch mSwitch;
     private boolean mValidListener;
     private Preference mLocationMode;
     private PreferenceCategory mCategoryRecentLocationRequests;
-    private BatteryStatsHelper mStatsHelper;
     /** Receives UPDATE_INTENT  */
     private BroadcastReceiver mReceiver;
 
@@ -71,24 +69,6 @@
     }
 
     @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-        createPreferenceHierarchy();
-    }
-
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        mStatsHelper = new BatteryStatsHelper(activity, null);
-    }
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        mStatsHelper.create(icicle);
-    }
-
-    @Override
     public void onResume() {
         super.onResume();
         mSwitch = new Switch(getActivity());
@@ -107,13 +87,6 @@
         super.onPause();
         mValidListener = false;
         mSwitch.setOnCheckedChangeListener(null);
-        mStatsHelper.pause();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mStatsHelper.destroy();
     }
 
     private void addPreferencesSorted(List<Preference> prefs, PreferenceGroup container) {
@@ -153,7 +126,7 @@
 
         mCategoryRecentLocationRequests =
                 (PreferenceCategory) root.findPreference(KEY_RECENT_LOCATION_REQUESTS);
-        RecentLocationApps recentApps = new RecentLocationApps(activity, mStatsHelper);
+        RecentLocationApps recentApps = new RecentLocationApps(activity);
         List<Preference> recentLocationRequests = recentApps.getAppList();
         if (recentLocationRequests.size() > 0) {
             addPreferencesSorted(recentLocationRequests, mCategoryRecentLocationRequests);
@@ -166,7 +139,7 @@
             mCategoryRecentLocationRequests.addPreference(banner);
         }
 
-        addAppSettings(activity, root);
+        addLocationServices(activity, root);
 
         // Only show the master switch when we're not in multi-pane mode, and not being used as
         // Setup Wizard.
@@ -198,11 +171,11 @@
      * up-to-date after mode changes even if an affected app doesn't send the setting changed
      * broadcast.
      */
-    private void addAppSettings(Context context, PreferenceScreen root) {
-        PreferenceCategory categoryAppSettings =
-                (PreferenceCategory) root.findPreference(KEY_APP_SETTINGS);
+    private void addLocationServices(Context context, PreferenceScreen root) {
+        PreferenceCategory categoryLocationServices =
+                (PreferenceCategory) root.findPreference(KEY_LOCATION_SERVICES);
         final SettingsInjector injector = new SettingsInjector(context);
-        List<Preference> appSettings = injector.getInjectedSettings();
+        List<Preference> locationServices = injector.getInjectedSettings();
 
         mReceiver = new BroadcastReceiver() {
             @Override
@@ -219,11 +192,11 @@
         filter.addAction(LocationManager.MODE_CHANGED_ACTION);
         context.registerReceiver(mReceiver, filter);
 
-        if (appSettings.size() > 0) {
-            addPreferencesSorted(appSettings, categoryAppSettings);
+        if (locationServices.size() > 0) {
+            addPreferencesSorted(locationServices, categoryLocationServices);
         } else {
             // If there's no item to display, remove the whole category.
-            root.removePreference(categoryAppSettings);
+            root.removePreference(categoryLocationServices);
         }
     }
 
diff --git a/src/com/android/settings/location/RecentLocationApps.java b/src/com/android/settings/location/RecentLocationApps.java
index 9488db7..5708434 100644
--- a/src/com/android/settings/location/RecentLocationApps.java
+++ b/src/com/android/settings/location/RecentLocationApps.java
@@ -20,7 +20,6 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -32,11 +31,8 @@
 
 import com.android.settings.R;
 import com.android.settings.applications.InstalledAppDetails;
-import com.android.settings.fuelgauge.BatterySipper;
-import com.android.settings.fuelgauge.BatteryStatsHelper;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -45,51 +41,15 @@
 public class RecentLocationApps {
     private static final String TAG = RecentLocationApps.class.getSimpleName();
     private static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
-    private static final String GOOGLE_SERVICES_SHARED_UID = "com.google.uid.shared";
-    private static final String GCORE_PACKAGE_NAME = "com.google.android.gms";
 
     private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
 
     private final PreferenceActivity mActivity;
-    private final BatteryStatsHelper mStatsHelper;
     private final PackageManager mPackageManager;
-    private final Drawable mGCoreIcon;
 
-    // Stores all the packages that requested location within the designated interval
-    // key - package name of the app
-    // value - whether the app has requested high power location
-
-    public RecentLocationApps(PreferenceActivity activity, BatteryStatsHelper sipperUtil) {
+    public RecentLocationApps(PreferenceActivity activity) {
         mActivity = activity;
         mPackageManager = activity.getPackageManager();
-        mStatsHelper = sipperUtil;
-        Drawable icon = null;
-        try {
-            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
-                    GCORE_PACKAGE_NAME, PackageManager.GET_META_DATA);
-            icon = mPackageManager.getApplicationIcon(appInfo);
-        } catch (PackageManager.NameNotFoundException e) {
-            if (Log.isLoggable(TAG, Log.INFO)) {
-                Log.i(TAG, "GCore not installed");
-            }
-            icon = null;
-        }
-        mGCoreIcon = icon;
-    }
-
-    private class UidEntryClickedListener
-            implements Preference.OnPreferenceClickListener {
-        private BatterySipper mSipper;
-
-        public UidEntryClickedListener(BatterySipper sipper) {
-            mSipper = sipper;
-        }
-
-        @Override
-        public boolean onPreferenceClick(Preference preference) {
-            mStatsHelper.startBatteryDetailPage(mActivity, mSipper, false);
-            return true;
-        }
     }
 
     private class PackageEntryClickedListener
@@ -129,49 +89,10 @@
     }
 
     /**
-     * Stores a BatterySipper object and records whether the sipper has been used.
-     */
-    private static final class BatterySipperWrapper {
-        private BatterySipper mSipper;
-        private boolean mUsed;
-
-        public BatterySipperWrapper(BatterySipper sipper) {
-            mSipper = sipper;
-            mUsed = false;
-        }
-
-        public BatterySipper batterySipper() {
-            return mSipper;
-        }
-
-        public boolean used() {
-            return mUsed;
-        }
-
-        public void setUsed() {
-            mUsed = true;
-        }
-    }
-
-    /**
      * Fills a list of applications which queried location recently within
      * specified time.
      */
     public List<Preference> getAppList() {
-        // Retrieve Uid-based battery blaming info and generate a package to BatterySipper HashMap
-        // for later faster looking up.
-        mStatsHelper.refreshStats(true);
-        List<BatterySipper> usageList = mStatsHelper.getUsageList();
-        // Key: package Uid. Value: BatterySipperWrapper.
-        HashMap<Integer, BatterySipperWrapper> sipperMap =
-                new HashMap<Integer, BatterySipperWrapper>(usageList.size());
-        for (BatterySipper sipper: usageList) {
-            int uid = sipper.getUid();
-            if (uid != 0) {
-                sipperMap.put(uid, new BatterySipperWrapper(sipper));
-            }
-        }
-
         // Retrieve a location usage list from AppOps
         AppOpsManager aoManager =
                 (AppOpsManager) mActivity.getSystemService(Context.APP_OPS_SERVICE);
@@ -191,8 +112,7 @@
             boolean isAndroidOs = (uid == Process.SYSTEM_UID)
                     && ANDROID_SYSTEM_PACKAGE_NAME.equals(ops.getPackageName());
             if (!isAndroidOs && ActivityManager.getCurrentUser() == UserHandle.getUserId(uid)) {
-                BatterySipperWrapper wrapper = sipperMap.get(uid);
-                Preference pref = getPreferenceFromOps(now, ops, wrapper);
+                Preference pref = getPreferenceFromOps(now, ops);
                 if (pref != null) {
                     prefs.add(pref);
                 }
@@ -203,56 +123,14 @@
     }
 
     /**
-     * Retrieves the icon for given BatterySipper object.
-     *
-     * The icons on location blaming page are actually Uid-based rather than package based. For
-     * those packages that share the same Uid, BatteryStatsHelper picks the one with the most CPU
-     * usage. Both "Contact Sync" and GCore belong to "Google Services" and they share the same Uid.
-     * As a result, sometimes Contact icon may be chosen to represent "Google Services" by
-     * BatteryStatsHelper.
-     *
-     * In order to avoid displaying Contact icon for "Google Services", we hack this method to
-     * always return Puzzle icon for all packages that share the Uid of "Google Services".
-     */
-    private Drawable getIcon(BatterySipper sipper, AppOpsManager.PackageOps ops) {
-        Drawable icon = null;
-        if (mGCoreIcon != null) {
-            try {
-                PackageInfo info = mPackageManager.getPackageInfo(
-                        ops.getPackageName(), PackageManager.GET_META_DATA);
-                if (info != null && GOOGLE_SERVICES_SHARED_UID.equals(info.sharedUserId)) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "shareUserId matches GCore, force using puzzle icon");
-                    }
-                    icon = mGCoreIcon;
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, e.toString());
-                }
-            }
-        }
-        if (icon == null) {
-            icon = sipper.getIcon();
-        }
-        return icon;
-    }
-
-    /**
      * Creates a Preference entry for the given PackageOps.
      *
      * This method examines the time interval of the PackageOps first. If the PackageOps is older
      * than the designated interval, this method ignores the PackageOps object and returns null.
-     *
-     * When the PackageOps is fresh enough, if the package has a corresponding battery blaming entry
-     * in the Uid-based battery sipper list, this method returns a Preference pointing to the Uid
-     * battery blaming page. If the package doesn't have a battery sipper entry (typically shouldn't
-     * happen), this method returns a Preference pointing to the App Info page for that package.
+     * When the PackageOps is fresh enough, this method returns a Preference pointing to the App
+     * Info page for that package.
      */
-    private Preference getPreferenceFromOps(
-            long now,
-            AppOpsManager.PackageOps ops,
-            BatterySipperWrapper wrapper) {
+    private Preference getPreferenceFromOps(long now, AppOpsManager.PackageOps ops) {
         String packageName = ops.getPackageName();
         List<AppOpsManager.OpEntry> entries = ops.getOps();
         boolean highBattery = false;
@@ -284,48 +162,27 @@
         // The package is fresh enough, continue.
 
         Preference pref = null;
-        if (wrapper != null) {
-            // Contains sipper. Link to Battery Blaming page.
-
-            // We're listing by UID rather than package. Check whether the entry has been used
-            // before to prevent the same UID from showing up twice.
-            if (!wrapper.used()) {
-                BatterySipper sipper = wrapper.batterySipper();
-                sipper.loadNameAndIcon();
-                pref = createRecentLocationEntry(
-                        getIcon(sipper, ops),
-                        sipper.getLabel(),
-                        highBattery,
-                        new UidEntryClickedListener(sipper));
-                wrapper.setUsed();
-            }
-        } else {
-            // No corresponding sipper. Link to App Info page.
-
-            // This is grouped by package rather than UID, but that's OK because this branch
-            // shouldn't happen in practice.
-            try {
-                ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
-                        packageName, PackageManager.GET_META_DATA);
-                // Multiple users can install the same package. Each user gets a different Uid for
-                // the same package.
-                //
-                // Here we retrieve the Uid with package name, that will be the Uid for that package
-                // associated with the current active user. If the Uid differs from the Uid in ops,
-                // that means this entry belongs to another inactive user and we should ignore that.
-                if (appInfo.uid == ops.getUid()) {
-                    pref = createRecentLocationEntry(
-                            mPackageManager.getApplicationIcon(appInfo),
-                            mPackageManager.getApplicationLabel(appInfo),
-                            highBattery,
-                            new PackageEntryClickedListener(packageName));
-                } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "package " + packageName + " with Uid " + ops.getUid() +
-                            " belongs to another inactive account, ignored.");
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.wtf(TAG, "Package not found: " + packageName, e);
-            }
+        try {
+          ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
+              packageName, PackageManager.GET_META_DATA);
+          // Multiple users can install the same package. Each user gets a different Uid for
+          // the same package.
+          //
+          // Here we retrieve the Uid with package name, that will be the Uid for that package
+          // associated with the current active user. If the Uid differs from the Uid in ops,
+          // that means this entry belongs to another inactive user and we should ignore that.
+          if (appInfo.uid == ops.getUid()) {
+            pref = createRecentLocationEntry(
+                mPackageManager.getApplicationIcon(appInfo),
+                mPackageManager.getApplicationLabel(appInfo),
+                highBattery,
+                new PackageEntryClickedListener(packageName));
+          } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "package " + packageName + " with Uid " + ops.getUid() +
+                " belongs to another inactive account, ignored.");
+          }
+        } catch (PackageManager.NameNotFoundException e) {
+          Log.wtf(TAG, "Package not found: " + packageName, e);
         }
 
         return pref;
diff --git a/src/com/android/settings/nfc/PaymentSettings.java b/src/com/android/settings/nfc/PaymentSettings.java
index 06697a4..7548c50 100644
--- a/src/com/android/settings/nfc/PaymentSettings.java
+++ b/src/com/android/settings/nfc/PaymentSettings.java
@@ -25,6 +25,7 @@
 import android.preference.Preference;
 import android.preference.PreferenceManager;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -152,10 +153,13 @@
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
-        inflater.inflate(R.menu.nfc_payment_settings, menu);
-        MenuItem menuItem = menu.findItem(R.id.nfc_payment_menu_item_add_service);
-        menuItem.setIntent(new Intent(Intent.ACTION_VIEW,
-                Uri.parse(getString(R.string.download_nfc_payment_service_query))));
+        String searchUri = Settings.Secure.getString(getContentResolver(),
+                Settings.Secure.PAYMENT_SERVICE_SEARCH_URI);
+        if (!TextUtils.isEmpty(searchUri)) {
+            MenuItem menuItem = menu.add(R.string.nfc_payment_menu_item_add_service);
+            menuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            menuItem.setIntent(new Intent(Intent.ACTION_VIEW,Uri.parse(searchUri)));
+        }
     }
 
     private final Handler mHandler = new Handler() {
diff --git a/src/com/android/settings/print/PrintServiceSettingsFragment.java b/src/com/android/settings/print/PrintServiceSettingsFragment.java
index 9db2dec..044d86e 100644
--- a/src/com/android/settings/print/PrintServiceSettingsFragment.java
+++ b/src/com/android/settings/print/PrintServiceSettingsFragment.java
@@ -95,6 +95,7 @@
         @Override
         public void onChanged() {
             invalidateOptionsMenuIfNeeded();
+            updateEmptyView();
         }
 
         @Override
@@ -227,14 +228,15 @@
         ViewGroup contentRoot = (ViewGroup) listView.getParent();
         View emptyView = listView.getEmptyView();
         if (!mToggleSwitch.isChecked()) {
-            if (emptyView != null
-                    && emptyView.getId() != R.id.empty_printers_list_service_disabled) {
+            if (emptyView != null && emptyView.getId() != R.id.empty_print_state) {
                 contentRoot.removeView(emptyView);
                 emptyView = null;
             }
             if (emptyView == null) {
                 emptyView = getActivity().getLayoutInflater().inflate(
                         R.layout.empty_print_state, contentRoot, false);
+                ImageView iconView = (ImageView) emptyView.findViewById(R.id.icon);
+                iconView.setContentDescription(getString(R.string.print_service_disabled));
                 TextView textView = (TextView) emptyView.findViewById(R.id.message);
                 textView.setText(R.string.print_service_disabled);
                 contentRoot.addView(emptyView);
@@ -252,6 +254,21 @@
                 contentRoot.addView(emptyView);
                 listView.setEmptyView(emptyView);
             }
+        } else if (mPrintersAdapter.getCount() <= 0) {
+            if (emptyView != null && emptyView.getId() != R.id.empty_print_state) {
+                contentRoot.removeView(emptyView);
+                emptyView = null;
+            }
+            if (emptyView == null) {
+                emptyView = getActivity().getLayoutInflater().inflate(
+                        R.layout.empty_print_state, contentRoot, false);
+                ImageView iconView = (ImageView) emptyView.findViewById(R.id.icon);
+                iconView.setContentDescription(getString(R.string.print_no_printers_found));
+                TextView textView = (TextView) emptyView.findViewById(R.id.message);
+                textView.setText(R.string.print_no_printers_found);
+                contentRoot.addView(emptyView);
+                listView.setEmptyView(emptyView);
+            }
         }
     }
 
diff --git a/src/com/android/settings/print/PrintSettingsFragment.java b/src/com/android/settings/print/PrintSettingsFragment.java
index 84865c3..8d080ce 100644
--- a/src/com/android/settings/print/PrintSettingsFragment.java
+++ b/src/com/android/settings/print/PrintSettingsFragment.java
@@ -150,10 +150,13 @@
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
-        inflater.inflate(R.menu.print_settings, menu);
-        MenuItem menuItem = menu.findItem(R.id.print_menu_item_add_service);
-        menuItem.setIntent(new Intent(Intent.ACTION_VIEW,
-                Uri.parse(getString(R.string.download_print_service_query))));
+        String searchUri = Settings.Secure.getString(getContentResolver(),
+                Settings.Secure.PRINT_SERVICE_SEARCH_URI);
+        if (!TextUtils.isEmpty(searchUri)) {
+            MenuItem menuItem = menu.add(R.string.print_menu_item_add_service);
+            menuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            menuItem.setIntent(new Intent(Intent.ACTION_VIEW,Uri.parse(searchUri)));
+        }
     }
 
     @Override