OSDN Git Service

Top level screen for App Fuel Gauge.
authorAmith Yamasani <yamasani@google.com>
Fri, 22 May 2009 22:18:46 +0000 (15:18 -0700)
committerAmith Yamasani <yamasani@google.com>
Sat, 23 May 2009 02:41:32 +0000 (19:41 -0700)
Shows sorted list of power usage (with some debug information) by the top 10
apps and subsystems. Doesn't yet take into account certain subsystems such
as data network usage, audio/video DSP usage, Bluetooth and lights other than
screen backlight.

Screen, Idle, Wifi and Voice usage and CPU time are accounted for.

Also need to add detail screens for each item and suggested actions.

AndroidManifest.xml
res/drawable/app_gauge.9.png [new file with mode: 0644]
res/layout/preference_powergauge.xml [new file with mode: 0644]
res/values/strings.xml
res/xml/application_settings.xml
res/xml/power_usage_summary.xml [new file with mode: 0644]
src/com/android/settings/fuelgauge/PowerGaugePreference.java [new file with mode: 0644]
src/com/android/settings/fuelgauge/PowerUsageSummary.java [new file with mode: 0644]

index 8891552..60d9616 100644 (file)
             </intent-filter>
         </activity>
 
+        <activity android:name=".fuelgauge.PowerUsageSummary" android:label="@string/power_usage_summary_title">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 
 </manifest>
diff --git a/res/drawable/app_gauge.9.png b/res/drawable/app_gauge.9.png
new file mode 100644 (file)
index 0000000..4d4db24
Binary files /dev/null and b/res/drawable/app_gauge.9.png differ
diff --git a/res/layout/preference_powergauge.xml b/res/layout/preference_powergauge.xml
new file mode 100644 (file)
index 0000000..551659e
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical"
+    android:paddingLeft="16dip"
+    android:id="@+android:id/widget_frame"
+    android:paddingRight="?android:attr/scrollbarSize">
+
+    <ImageView
+        android:id="@+id/appIcon"
+        android:layout_width="48dip"
+        android:layout_height="wrap_content"
+        android:layout_marginRight="6dip"
+        android:layout_gravity="center" />
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginRight="6dip"
+        android:layout_marginTop="6dip"
+        android:layout_marginBottom="6dip"
+        android:layout_weight="1">
+
+        <TextView android:id="@+android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:visibility="gone"
+            android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        <ImageView
+            android:id="@+id/appGauge"
+            android:layout_height="wrap_content"
+            android:layout_width="fill_parent"
+            android:layout_marginRight="6dip"
+            android:layout_gravity="center_vertical" />
+
+        <TextView android:id="@+android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/appGauge"
+            android:layout_alignLeft="@id/appGauge"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:maxLines="2" />
+
+    </RelativeLayout>
+
+</LinearLayout>
index 5b04e51..4879db2 100644 (file)
@@ -1574,6 +1574,7 @@ found in the list of installed applications.</string>
     <string name="usage_time_label">Usage time</string>
 
     <!-- Accessibility settings -->
+    <skip/>
 
     <!-- Settings title for accessibility settings -->
     <string name="accessibility_settings_title">Accessibility</string>
@@ -1599,6 +1600,26 @@ found in the list of installed applications.</string>
          selects to disable accessibility. This avoids accidental disabling. -->
     <string name="accessibility_service_disable_warning">Disable accessibility?</string>
 
+    <!-- App Fuel Gauge strings -->
+    <skip/>
+
+    <!-- Activity title for App Fuel Gauge summary -->
+    <string name="power_usage_summary_title">Power usage summary</string>
+    <!-- CPU awake time title -->
+    <string name="awake">Device awake time</string>
+    <!-- Wifi on time -->
+    <string name="wifi_on_time">WiFi on time</string>
+
+    <!-- Label for power consumed by the screen -->
+    <string name="power_screen">Screen on</string>
+    <!-- Label for power consumed by WiFi -->
+    <string name="power_wifi">WiFi</string>
+    <!-- Label for power consumed by Cell idle -->
+    <string name="power_cell">Cell</string>
+    <!-- Label for power consumed by Calling -->
+    <string name="power_phone">Voice</string>
+    <!-- Label for power consumed when Idle -->
+    <string name="power_idle">Standby</string>
 </resources>
 
 
index 8d0a7cb..4da2036 100644 (file)
     </PreferenceScreen>
         
     <PreferenceScreen
+            android:key="power_usage"
+            android:title="@string/power_usage_summary_title">
+        <intent android:action="android.intent.action.MAIN"
+                android:targetPackage="com.android.settings"
+                android:targetClass="com.android.settings.fuelgauge.PowerUsageSummary" />
+    </PreferenceScreen>
+
+    <PreferenceScreen
             android:title="@string/manageapplications_settings_title"
             android:summary="@string/manageapplications_settings_summary">
         <intent android:action="android.intent.action.MAIN"
diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml
new file mode 100644 (file)
index 0000000..0c35905
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/power_usage_summary_title">
+
+    <PreferenceCategory
+            android:key="app_list"
+            android:title="Application usage"/>
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
new file mode 100644 (file)
index 0000000..5e89535
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.android.settings.fuelgauge;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.preference.Preference;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.settings.R;
+
+/**
+ * Custom preference for displaying power consumption as a bar and an icon on the left for the
+ * subsystem/app type.
+ *
+ */
+public class PowerGaugePreference extends Preference {
+
+    private Drawable mIcon;
+    private GaugeDrawable mGauge;
+    private double mValue;
+
+    public PowerGaugePreference(Context context, Drawable icon) {
+        super(context);
+        setLayoutResource(R.layout.preference_powergauge);
+        mIcon = icon;
+        mGauge = new GaugeDrawable();
+        mGauge.bar = context.getResources().getDrawable(R.drawable.app_gauge);
+    }
+
+    /**
+     * Sets the width of the gauge in percentage (0 - 100)
+     * @param percent
+     */
+    void setGaugeValue(double percent) {
+        mValue = percent;
+        mGauge.percent = mValue;
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        super.onBindView(view);
+
+        ImageView appIcon = (ImageView) view.findViewById(R.id.appIcon);
+        if (mIcon == null) {
+            mIcon = getContext().getResources().getDrawable(android.R.drawable.sym_def_app_icon);
+        }
+        appIcon.setImageDrawable(mIcon);
+
+        ImageView appGauge = (ImageView) view.findViewById(R.id.appGauge);
+        appGauge.setImageDrawable(mGauge);
+    }
+
+    static class GaugeDrawable extends Drawable {
+        Drawable bar;
+        double percent;
+        int lastWidth = -1;
+
+        @Override
+        public void draw(Canvas canvas) {
+            if (lastWidth == -1) {
+                lastWidth = getBarWidth();
+                bar.setBounds(0, 0, lastWidth, bar.getIntrinsicHeight());
+            }
+            bar.draw(canvas);
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.TRANSLUCENT;
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            // Ignore
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {
+            // Ignore
+        }
+
+        private int getBarWidth() {
+            int width = (int) ((this.getBounds().width() * percent) / 100);
+            int intrinsicWidth = bar.getIntrinsicWidth();
+            return Math.max(width, intrinsicWidth);
+        }
+
+        @Override
+        public int getIntrinsicHeight() {
+            return bar.getIntrinsicHeight();
+        }
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
new file mode 100644 (file)
index 0000000..3abd858
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.android.settings.fuelgauge;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.BatteryStats.Uid;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.PowerProfile;
+import com.android.settings.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Displays a list of apps and subsystems that consume power, ordered by how much power was
+ * consumed since the last time it was unplugged.
+ */
+public class PowerUsageSummary extends PreferenceActivity {
+
+    private static final boolean DEBUG = true;
+
+    private static final String TAG = "PowerUsageSummary";
+    private static final String PREF_APP_LIST = "app_list";
+
+    IBatteryStats mBatteryInfo;
+    BatteryStatsImpl mStats;
+    private List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
+
+    private PreferenceGroup mAppListGroup;
+
+    private int mStatsType = BatteryStats.STATS_UNPLUGGED;
+
+    private static final int MIN_POWER_THRESHOLD = 5;
+    private static final int MAX_ITEMS_TO_LIST = 10;
+
+    private double mMaxPower = 1;
+    private double mTotalPower;
+
+    private boolean mScaleByMax = true;
+
+    private PowerProfile mPowerProfile;
+
+    private static final long BATTERY_SIZE = 1200;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        addPreferencesFromResource(R.xml.power_usage_summary);
+        mBatteryInfo = IBatteryStats.Stub.asInterface(
+                ServiceManager.getService("batteryinfo"));
+        mAppListGroup = (PreferenceGroup) findPreference(PREF_APP_LIST);
+        mPowerProfile = new PowerProfile(this, "power_profile_default");
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        updateAppsList();
+    }
+
+    private void updateAppsList() {
+        if (mStats == null) {
+            load();
+        }
+
+        mAppListGroup.removeAll();
+        mUsageList.clear();
+        processCpuUsage();
+        processMiscUsage();
+
+        mAppListGroup.setOrderingAsAdded(false);
+
+        Collections.sort(mUsageList);
+        for (BatterySipper g : mUsageList) {
+            if (g.getSortValue() < MIN_POWER_THRESHOLD) continue;
+            double percent =  ((g.getSortValue() / mTotalPower) * 100 / BATTERY_SIZE);
+            PowerGaugePreference pref = new PowerGaugePreference(this, g.getIcon());
+            double scaleByMax = (g.getSortValue() * 100) / mTotalPower;
+            pref.setSummary(g.getLabel() + "  ( " + (int) g.getSortValue() + "mA, "
+                    + String.format("%3.2f", scaleByMax) + "% )");
+            pref.setOrder(Integer.MAX_VALUE - (int) g.getSortValue()); // Invert the order
+            pref.setGaugeValue(mScaleByMax ? scaleByMax : percent);
+            mAppListGroup.addPreference(pref);
+            if (mAppListGroup.getPreferenceCount() > MAX_ITEMS_TO_LIST) break;
+        }
+    }
+
+    private void processCpuUsage() {
+        final int which = mStatsType;
+        final double powerCpuNormal = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_NORMAL);
+        long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime(), which) * 1000;
+        SparseArray<? extends Uid> uidStats = mStats.getUidStats();
+        final int NU = uidStats.size();
+        if (DEBUG) Log.i(TAG, "uidStats size = " + NU);
+        for (int iu = 0; iu < NU; iu++) {
+            Uid u = uidStats.valueAt(iu);
+            double power = 0;
+            //mUsageList.add(new AppUsage(u.getUid(), new double[] {power}));
+            Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
+            if (processStats.size() > 0) {
+                for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
+                        : processStats.entrySet()) {
+
+                    Uid.Proc ps = ent.getValue();
+                    long userTime = ps.getUserTime(which);
+                    long systemTime = ps.getSystemTime(which);
+                    //long starts = ps.getStarts(which);
+                    power += (userTime + systemTime) * 10 /* convert to milliseconds */
+                            * powerCpuNormal;
+
+                }
+            }
+            power /= 1000;
+
+            Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
+            for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry
+                    : sensorStats.entrySet()) {
+                Uid.Sensor sensor = sensorEntry.getValue();
+                int sensorType = sensor.getHandle();
+                BatteryStats.Timer timer = sensor.getSensorTime();
+                long sensorTime = timer.getTotalTimeLocked(uSecTime, which);
+                double multiplier = 0;
+                switch (sensorType) {
+                    case Uid.Sensor.GPS:
+                        multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON);
+                        break;
+                }
+                power += multiplier * sensorTime;
+            }
+            if (power != 0) {
+                BatterySipper app = new BatterySipper(null, 0, u.getUid(), new double[] {power});
+                mUsageList.add(app);
+            }
+            if (power > mMaxPower) mMaxPower = power;
+            mTotalPower += power;
+            if (DEBUG) Log.i(TAG, "Added power = " + power);
+        }
+    }
+
+    private double getPhoneOnPower(long uSecNow) {
+        return mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
+                * mStats.getPhoneOnTime(uSecNow, mStatsType) / 1000 / 1000;
+    }
+
+    private double getScreenOnPower(long uSecNow) {
+        double power = 0;
+        power += mStats.getScreenOnTime(uSecNow, mStatsType)
+                * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON) / 1000; // millis
+        final double screenFullPower =
+                mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
+        for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+            double screenBinPower = screenFullPower * i / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
+            long brightnessTime = mStats.getScreenBrightnessTime(i, uSecNow, mStatsType) / 1000;
+            power += screenBinPower * brightnessTime;
+            if (DEBUG) {
+                Log.i(TAG, "Screen bin power = " + (int) screenBinPower + ", time = "
+                        + brightnessTime);
+            }
+        }
+        return power / 1000;
+    }
+
+    private double getRadioPower(long uSecNow, int which) {
+        double power = 0;
+        final int BINS = BatteryStats.NUM_SIGNAL_STRENGTH_BINS;
+        for (int i = 0; i < BINS; i++) {
+            power += mStats.getPhoneSignalStrengthTime(i, uSecNow, which) / 1000 / 1000 *
+                mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON)
+                * ((BINS - i) / (double) BINS);
+        }
+        return power;
+    }
+
+    private void processMiscUsage() {
+        final int which = mStatsType;
+        long uSecTime = SystemClock.elapsedRealtime() * 1000;
+        final long uSecNow = mStats.computeBatteryRealtime(uSecTime, which);
+        final long timeSinceUnplugged = uSecNow;
+        if (DEBUG) {
+            Log.i(TAG, "Uptime since last unplugged = " + (timeSinceUnplugged / 1000));
+        }
+
+        double phoneOnPower = getPhoneOnPower(uSecNow);
+        addEntry(getString(R.string.power_phone), android.R.drawable.ic_menu_call, phoneOnPower);
+
+        double screenOnPower = getScreenOnPower(uSecNow);
+        addEntry(getString(R.string.power_screen), android.R.drawable.ic_menu_view, screenOnPower);
+
+        double wifiPower = (mStats.getWifiOnTime(uSecNow, which) * 0 /* TODO */
+                * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)
+            + mStats.getWifiRunningTime(uSecNow, which)
+                * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000 / 1000;
+        addEntry(getString(R.string.power_wifi), R.drawable.ic_wifi_signal_4, wifiPower);
+
+        double idlePower = ((timeSinceUnplugged -  mStats.getScreenOnTime(uSecNow, mStatsType))
+                * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) / 1000 / 1000;
+        addEntry(getString(R.string.power_idle), android.R.drawable.ic_lock_power_off, idlePower);
+
+        double radioPower = getRadioPower(uSecNow, which);
+        addEntry(getString(R.string.power_cell),
+                android.R.drawable.ic_menu_sort_by_size, radioPower);
+    }
+
+    private void addEntry(String label, int iconId, double power) {
+        if (power > mMaxPower) mMaxPower = power;
+        mTotalPower += power;
+        BatterySipper bs = new BatterySipper(label, iconId, 0, new double[] {power});
+        mUsageList.add(bs);
+    }
+
+    private void load() {
+        try {
+            byte[] data = mBatteryInfo.getStatistics();
+            Parcel parcel = Parcel.obtain();
+            parcel.unmarshall(data, 0, data.length);
+            parcel.setDataPosition(0);
+            mStats = com.android.internal.os.BatteryStatsImpl.CREATOR
+                    .createFromParcel(parcel);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException:", e);
+        }
+    }
+
+    class BatterySipper implements Comparable<BatterySipper> {
+        String mLabel;
+        Drawable mIcon;
+        int mUid;
+        double mValue;
+        double[] mValues;
+
+        BatterySipper(String label, int iconId, int uid, double[] values) {
+            mValues = values;
+            mLabel = label;
+            if (iconId > 0) {
+                mIcon = getResources().getDrawable(iconId);
+            }
+            if (mValues != null) mValue = mValues[0];
+            //if (uid > 0 && (mLabel == null || mIcon == null) // TODO:
+            if ((label == null || iconId == 0) && uid > 0) {
+                getNameForUid(uid);
+            }
+        }
+
+        double getSortValue() {
+            return mValue;
+        }
+
+        double[] getValues() {
+            return mValues;
+        }
+
+        Drawable getIcon() {
+            return mIcon;
+        }
+
+        public int compareTo(BatterySipper other) {
+            // Return the flipped value because we want the items in descending order
+            return (int) (other.getSortValue() - getSortValue());
+        }
+
+        String getLabel() {
+            return mLabel;
+        }
+
+        /**
+         * Sets mLabel and mIcon
+         * @param uid Uid of the application
+         */
+        void getNameForUid(int uid) {
+            // TODO: Do this on a separate thread
+            PackageManager pm = getPackageManager();
+            String[] packages = pm.getPackagesForUid(uid);
+            if (packages == null) {
+                mLabel = Integer.toString(uid);
+                return;
+            }
+
+            String[] packageNames = new String[packages.length];
+            System.arraycopy(packages, 0, packageNames, 0, packages.length);
+
+            // Convert package names to user-facing labels where possible
+            for (int i = 0; i < packageNames.length; i++) {
+                //packageNames[i] = PowerUsageSummary.getLabel(packageNames[i], pm);
+                try {
+                    ApplicationInfo ai = pm.getApplicationInfo(packageNames[i], 0);
+                    CharSequence label = ai.loadLabel(pm);
+                    if (label != null) {
+                        packageNames[i] = label.toString();
+                    }
+                    if (mIcon == null) {
+                        mIcon = ai.loadIcon(pm);
+                    }
+                } catch (NameNotFoundException e) {
+                }
+            }
+
+            if (packageNames.length == 1) {
+                mLabel = packageNames[0];
+            } else {
+                // Look for an official name for this UID.
+                for (String name : packages) {
+                    try {
+                        PackageInfo pi = pm.getPackageInfo(name, 0);
+                        if (pi.sharedUserLabel != 0) {
+                            CharSequence nm = pm.getText(name,
+                                    pi.sharedUserLabel, pi.applicationInfo);
+                            if (nm != null) {
+                                mLabel = nm.toString();
+                                break;
+                            }
+                        }
+                    } catch (PackageManager.NameNotFoundException e) {
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file