Merge "Add non-app power consumers to the PowerStatsViewer"
diff --git a/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
index 378d035..28ea05f 100644
--- a/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
+++ b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
@@ -35,8 +35,7 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".AppPickerActivity"
-                  android:label="Power Stats - Select an App"/>
-
+        <activity android:name=".PowerConsumerPickerActivity"
+                  android:label="Select a power consumer"/>
     </application>
 </manifest>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_info_layout.xml
similarity index 98%
rename from core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml
rename to core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_info_layout.xml
index fe6fe2d..fbd0ebd 100644
--- a/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_info_layout.xml
@@ -52,7 +52,7 @@
             android:textAppearance="?android:attr/textAppearanceListItem"/>
 
         <TextView
-            android:id="@+id/uid"
+            android:id="@+id/details"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:ellipsize="marquee"
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_activity_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_activity_layout.xml
new file mode 100644
index 0000000..ecc89f0
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_activity_layout.xml
@@ -0,0 +1,33 @@
+<!--
+  ~ Copyright (C) 2020 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="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <com.google.android.material.tabs.TabLayout
+        android:id="@+id/tab_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+    <androidx.viewpager.widget.ViewPager
+        android:id="@+id/pager"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+</LinearLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_layout.xml
similarity index 96%
rename from core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml
rename to core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_layout.xml
index 6f28999..bea38c1 100644
--- a/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_layout.xml
@@ -20,7 +20,7 @@
     android:layout_height="match_parent">
 
     <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/app_list_view"
+        android:id="@+id/list_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="gone"/>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
index 9949418..238e238 100644
--- a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
@@ -42,7 +42,7 @@
                 android:paddingStart="10dp"
                 android:paddingEnd="10dp">
 
-                <include layout="@layout/app_info_layout"/>
+                <include layout="@layout/power_consumer_info_layout"/>
 
             </LinearLayout>
         </androidx.cardview.widget.CardView>
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java
deleted file mode 100644
index 8526561..0000000
--- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2020 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.frameworks.core.powerstatsviewer;
-
-import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Process;
-
-import com.android.internal.os.BatterySipper;
-
-class AppInfoHelper {
-
-    private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
-
-    public static class AppInfo {
-        public int uid;
-        public CharSequence label;
-        public double powerMah;
-        public ApplicationInfo iconInfo;
-        public CharSequence packages;
-    }
-
-    @Nullable
-    public static AppInfo makeApplicationInfo(PackageManager packageManager, int uid,
-            @Nullable BatterySipper sipper) {
-        if (sipper != null && sipper.drainType != BatterySipper.DrainType.APP) {
-            return null;
-        }
-
-        String packageWithHighestDrain = null;
-
-        AppInfo info = new AppInfo();
-        info.uid = uid;
-        if (sipper != null) {
-            sipper.sumPower();
-            info.powerMah = sipper.totalSmearedPowerMah;
-            packageWithHighestDrain = sipper.packageWithHighestDrain;
-        }
-        if (info.uid == Process.ROOT_UID) {
-            info.label = "<root>";
-        } else {
-            String[] packages = packageManager.getPackagesForUid(info.uid);
-            String primaryPackageName = null;
-            if (info.uid == Process.SYSTEM_UID) {
-                primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
-            } else if (packages != null) {
-                for (String name : packages) {
-                    primaryPackageName = name;
-                    if (name.equals(packageWithHighestDrain)) {
-                        break;
-                    }
-                }
-            }
-
-            if (primaryPackageName != null) {
-                try {
-                    ApplicationInfo applicationInfo =
-                            packageManager.getApplicationInfo(primaryPackageName, 0);
-                    info.label = applicationInfo.loadLabel(packageManager);
-                    info.iconInfo = applicationInfo;
-                } catch (PackageManager.NameNotFoundException e) {
-                    info.label = primaryPackageName;
-                }
-            } else if (packageWithHighestDrain != null) {
-                info.label = packageWithHighestDrain;
-            }
-
-            if (packages != null && packages.length > 0) {
-                StringBuilder sb = new StringBuilder();
-                if (primaryPackageName != null) {
-                    sb.append(primaryPackageName);
-                }
-                for (String packageName : packages) {
-                    if (packageName.equals(primaryPackageName)) {
-                        continue;
-                    }
-
-                    if (sb.length() != 0) {
-                        sb.append(", ");
-                    }
-                    sb.append(packageName);
-                }
-
-                info.packages = sb;
-            }
-        }
-
-        // Default the app icon to System Server. This includes root, dex2oat and other UIDs.
-        if (info.iconInfo == null) {
-            try {
-                info.iconInfo =
-                        packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
-            } catch (PackageManager.NameNotFoundException nameNotFoundException) {
-                // Won't happen
-            }
-        }
-        return info;
-    }
-}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java
deleted file mode 100644
index b4fc73c..0000000
--- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2008 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.frameworks.core.powerstatsviewer;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.BatteryStats;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.UserManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.activity.ComponentActivity;
-import androidx.activity.result.contract.ActivityResultContract;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.loader.app.LoaderManager;
-import androidx.loader.content.Loader;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.frameworks.core.powerstatsviewer.AppInfoHelper.AppInfo;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
-import com.android.settingslib.utils.AsyncLoaderCompat;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Picker, showing a sorted list of applications consuming power.  Returns the selected
- * application UID or Process.INVALID_UID.
- */
-public class AppPickerActivity extends ComponentActivity {
-    private static final String TAG = "AppPicker";
-
-    public static final ActivityResultContract<Void, Integer> CONTRACT =
-            new ActivityResultContract<Void, Integer>() {
-                @NonNull
-                @Override
-                public Intent createIntent(@NonNull Context context, Void aVoid) {
-                    return new Intent(context, AppPickerActivity.class);
-                }
-
-                @Override
-                public Integer parseResult(int resultCode, @Nullable Intent intent) {
-                    if (resultCode != RESULT_OK || intent == null) {
-                        return Process.INVALID_UID;
-                    }
-                    return intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
-                }
-            };
-
-    private AppListAdapter mAppListAdapter;
-    private RecyclerView mAppList;
-    private View mLoadingView;
-
-    private interface OnAppSelectedListener {
-        void onAppSelected(int uid);
-    }
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        getActionBar().setDisplayHomeAsUpEnabled(true);
-
-        setContentView(R.layout.app_picker_layout);
-
-        mLoadingView = findViewById(R.id.loading_view);
-
-        mAppList = findViewById(R.id.app_list_view);
-        mAppList.setLayoutManager(new LinearLayoutManager(this));
-        mAppListAdapter = new AppListAdapter(AppPickerActivity.this::setSelectedUid);
-        mAppList.setAdapter(mAppListAdapter);
-
-        LoaderManager.getInstance(this).initLoader(0, null,
-                new AppListLoaderCallbacks());
-    }
-
-    protected void setSelectedUid(int uid) {
-        Intent intent = new Intent();
-        intent.putExtra(Intent.EXTRA_UID, uid);
-        setResult(RESULT_OK, intent);
-        finish();
-    }
-
-    @Override
-    public boolean onNavigateUp() {
-        onBackPressed();
-        return true;
-    }
-
-    private static class AppListLoader extends AsyncLoaderCompat<List<AppInfo>> {
-        private final BatteryStatsHelper mStatsHelper;
-        private final UserManager mUserManager;
-        private final PackageManager mPackageManager;
-
-        AppListLoader(Context context) {
-            super(context);
-            mUserManager = context.getSystemService(UserManager.class);
-            mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */);
-            mStatsHelper.create((Bundle) null);
-            mStatsHelper.clearStats();
-            mPackageManager = context.getPackageManager();
-        }
-
-        @Override
-        public List<AppInfo> loadInBackground() {
-            List<AppInfo> applicationList = new ArrayList<>();
-
-            mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
-                    mUserManager.getUserProfiles());
-
-            final List<BatterySipper> usageList = mStatsHelper.getUsageList();
-            for (BatterySipper sipper : usageList) {
-                AppInfo info =
-                        AppInfoHelper.makeApplicationInfo(mPackageManager, sipper.getUid(), sipper);
-                if (info != null) {
-                    applicationList.add(info);
-                }
-            }
-
-            applicationList.sort(
-                    Comparator.comparing((AppInfo a) -> a.powerMah).reversed());
-            return applicationList;
-        }
-
-        @Override
-        protected void onDiscardResult(List<AppInfo> result) {
-        }
-    }
-
-    private class AppListLoaderCallbacks implements
-            LoaderManager.LoaderCallbacks<List<AppInfo>> {
-
-        @NonNull
-        @Override
-        public Loader<List<AppInfo>> onCreateLoader(int id, Bundle args) {
-            return new AppListLoader(AppPickerActivity.this);
-        }
-
-        @Override
-        public void onLoadFinished(@NonNull Loader<List<AppInfo>> loader,
-                List<AppInfo> applicationList) {
-            mAppListAdapter.setApplicationList(applicationList);
-            mAppList.setVisibility(View.VISIBLE);
-            mLoadingView.setVisibility(View.GONE);
-        }
-
-        @Override
-        public void onLoaderReset(@NonNull Loader<List<AppInfo>> loader) {
-        }
-    }
-
-    public class AppListAdapter extends RecyclerView.Adapter<AppViewHolder> {
-        private final OnAppSelectedListener mListener;
-        private List<AppInfo> mApplicationList;
-
-        public AppListAdapter(OnAppSelectedListener listener) {
-            mListener = listener;
-        }
-
-        void setApplicationList(List<AppInfo> applicationList) {
-            mApplicationList = applicationList;
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public int getItemCount() {
-            return mApplicationList.size();
-        }
-
-        @NonNull
-        @Override
-        public AppViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
-            LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
-            View view = layoutInflater.inflate(R.layout.app_info_layout, viewGroup, false);
-            return new AppViewHolder(view, mListener);
-        }
-
-        @Override
-        public void onBindViewHolder(@NonNull AppViewHolder appViewHolder, int position) {
-            AppInfo item = mApplicationList.get(position);
-            appViewHolder.uid = item.uid;
-            appViewHolder.titleView.setText(item.label);
-            appViewHolder.uidView.setText(
-                    String.format(Locale.getDefault(), "UID: %d", item.uid));
-            appViewHolder.powerView.setText(
-                    String.format(Locale.getDefault(), "%.1f mAh", item.powerMah));
-            appViewHolder.iconView.setImageDrawable(
-                    item.iconInfo.loadIcon(getPackageManager()));
-            if (item.packages != null) {
-                appViewHolder.packagesView.setText(item.packages);
-                appViewHolder.packagesView.setVisibility(View.VISIBLE);
-            } else {
-                appViewHolder.packagesView.setVisibility(View.GONE);
-            }
-        }
-    }
-
-    // View Holder used when displaying apps
-    public static class AppViewHolder extends RecyclerView.ViewHolder
-            implements View.OnClickListener {
-        private final OnAppSelectedListener mListener;
-
-        public int uid;
-        public TextView titleView;
-        public TextView uidView;
-        public ImageView iconView;
-        public TextView packagesView;
-        public TextView powerView;
-
-        AppViewHolder(View view, OnAppSelectedListener listener) {
-            super(view);
-            mListener = listener;
-            view.setOnClickListener(this);
-            titleView = view.findViewById(android.R.id.title);
-            uidView = view.findViewById(R.id.uid);
-            iconView = view.findViewById(android.R.id.icon);
-            packagesView = view.findViewById(R.id.packages);
-            powerView = view.findViewById(R.id.power_mah);
-            powerView.setVisibility(View.VISIBLE);
-        }
-
-        @Override
-        public void onClick(View v) {
-            mListener.onAppSelected(uid);
-        }
-    }
-}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerInfoHelper.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerInfoHelper.java
new file mode 100644
index 0000000..6fec240
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerInfoHelper.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 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.frameworks.core.powerstatsviewer;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.os.BatterySipper;
+
+import java.util.Locale;
+
+class PowerConsumerInfoHelper {
+
+    private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
+
+    public static class PowerConsumerInfo {
+        public String id;
+        public CharSequence label;
+        public double powerMah;
+        public ApplicationInfo iconInfo;
+        public CharSequence packages;
+        public CharSequence details;
+    }
+
+    @NonNull
+    public static PowerConsumerInfo makePowerConsumerInfo(PackageManager packageManager,
+            @NonNull BatterySipper sipper) {
+        PowerConsumerInfo info = new PowerConsumerInfo();
+        info.id = PowerStatsData.powerConsumerId(sipper);
+        sipper.sumPower();
+        info.powerMah = sipper.totalSmearedPowerMah;
+        switch (sipper.drainType) {
+            case APP: {
+                int uid = sipper.getUid();
+                info.details = String.format("UID: %d", uid);
+                String packageWithHighestDrain = sipper.packageWithHighestDrain;
+                if (uid == Process.ROOT_UID) {
+                    info.label = "<root>";
+                } else {
+                    String[] packages = packageManager.getPackagesForUid(uid);
+                    String primaryPackageName = null;
+                    if (uid == Process.SYSTEM_UID) {
+                        primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
+                    } else if (packages != null) {
+                        for (String name : packages) {
+                            primaryPackageName = name;
+                            if (name.equals(packageWithHighestDrain)) {
+                                break;
+                            }
+                        }
+                    }
+
+                    if (primaryPackageName != null) {
+                        try {
+                            ApplicationInfo applicationInfo =
+                                    packageManager.getApplicationInfo(primaryPackageName, 0);
+                            info.label = applicationInfo.loadLabel(packageManager);
+                            info.iconInfo = applicationInfo;
+                        } catch (PackageManager.NameNotFoundException e) {
+                            info.label = primaryPackageName;
+                        }
+                    } else if (packageWithHighestDrain != null) {
+                        info.label = packageWithHighestDrain;
+                    }
+
+                    if (packages != null && packages.length > 0) {
+                        StringBuilder sb = new StringBuilder();
+                        if (primaryPackageName != null) {
+                            sb.append(primaryPackageName);
+                        }
+                        for (String packageName : packages) {
+                            if (packageName.equals(primaryPackageName)) {
+                                continue;
+                            }
+
+                            if (sb.length() != 0) {
+                                sb.append(", ");
+                            }
+                            sb.append(packageName);
+                        }
+
+                        info.packages = sb;
+                    }
+                }
+                break;
+            }
+            case USER:
+                info.label = "User";
+                info.details = String.format(Locale.getDefault(), "User ID: %d", sipper.userId);
+                break;
+            case AMBIENT_DISPLAY:
+                info.label = "Ambient display";
+                break;
+            case BLUETOOTH:
+                info.label = "Bluetooth";
+                break;
+            case CAMERA:
+                info.label = "Camera";
+                break;
+            case CELL:
+                info.label = "Cell";
+                break;
+            case FLASHLIGHT:
+                info.label = "Flashlight";
+                break;
+            case IDLE:
+                info.label = "Idle";
+                break;
+            case MEMORY:
+                info.label = "Memory";
+                break;
+            case OVERCOUNTED:
+                info.label = "Overcounted";
+                break;
+            case PHONE:
+                info.label = "Phone";
+                break;
+            case SCREEN:
+                info.label = "Screen";
+                break;
+            case UNACCOUNTED:
+                info.label = "Unaccounted";
+                break;
+            case WIFI:
+                info.label = "WiFi";
+                break;
+        }
+        // Default the app icon to System Server. This includes root, dex2oat and other UIDs.
+        if (info.iconInfo == null) {
+            try {
+                info.iconInfo =
+                        packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
+            } catch (PackageManager.NameNotFoundException nameNotFoundException) {
+                // Won't happen
+            }
+        }
+        return info;
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerActivity.java
new file mode 100644
index 0000000..f56d113
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerActivity.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 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.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.activity.result.contract.ActivityResultContract;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.tabs.TabLayout;
+
+/**
+ * Picker, showing a sorted lists of applications and other types of entities consuming power.
+ * Returns the selected entity ID or null.
+ */
+public class PowerConsumerPickerActivity extends FragmentActivity {
+
+    public static final ActivityResultContract<Void, String> CONTRACT =
+            new ActivityResultContract<Void, String>() {
+                @NonNull
+                @Override
+                public Intent createIntent(@NonNull Context context, Void aVoid) {
+                    return new Intent(context, PowerConsumerPickerActivity.class);
+                }
+
+                @Override
+                public String parseResult(int resultCode, @Nullable Intent intent) {
+                    if (resultCode != RESULT_OK || intent == null) {
+                        return null;
+                    }
+                    return intent.getStringExtra(Intent.EXTRA_RETURN_RESULT);
+                }
+            };
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        getActionBar().setDisplayHomeAsUpEnabled(true);
+
+        setContentView(R.layout.power_consumer_picker_activity_layout);
+
+        ViewPager viewPager = findViewById(R.id.pager);
+
+        FragmentStatePagerAdapter adapter = new FragmentStatePagerAdapter(
+                getSupportFragmentManager()) {
+
+            @Override
+            public int getCount() {
+                return 2;
+            }
+
+            @NonNull
+            @Override
+            public Fragment getItem(int position) {
+                switch (position) {
+                    case 0:
+                        return new PowerConsumerPickerFragment(
+                                PowerConsumerPickerFragment.PICKER_TYPE_APP);
+                    case 1:
+                    default:
+                        return new PowerConsumerPickerFragment(
+                                PowerConsumerPickerFragment.PICKER_TYPE_DRAIN);
+                }
+            }
+
+            @Override
+            public CharSequence getPageTitle(int position) {
+                switch (position) {
+                    case 0:
+                        return "Apps";
+                    case 1:
+                        return "Drains";
+                }
+                return null;
+            }
+        };
+
+        viewPager.setAdapter(adapter);
+        TabLayout tabLayout = findViewById(R.id.tab_layout);
+        tabLayout.setupWithViewPager(viewPager);
+    }
+
+    public void setSelectedPowerConsumer(String id) {
+        Intent intent = new Intent();
+        intent.putExtra(Intent.EXTRA_RETURN_RESULT, id);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+
+    @Override
+    public boolean onNavigateUp() {
+        onBackPressed();
+        return true;
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerFragment.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerFragment.java
new file mode 100644
index 0000000..25225b8
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerFragment.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2008 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.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.frameworks.core.powerstatsviewer.PowerConsumerInfoHelper.PowerConsumerInfo;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Picker, showing a sorted lists of applications or other types of entities consuming power.
+ * Returns the selected entity ID or null.
+ */
+public class PowerConsumerPickerFragment extends Fragment {
+    private static final String TAG = "AppPicker";
+
+    public static final String PICKER_TYPE = "pickertype";
+
+    public static final int PICKER_TYPE_APP = 0;
+    public static final int PICKER_TYPE_DRAIN = 1;
+
+    private PowerConsumerListAdapter mPowerConsumerListAdapter;
+    private RecyclerView mAppList;
+    private View mLoadingView;
+
+    private interface OnPowerConsumerSelectedListener {
+        void onPowerConsumerSelected(String uid);
+    }
+
+    public PowerConsumerPickerFragment(int pickerType) {
+        Bundle args = new Bundle();
+        args.putInt(PICKER_TYPE, pickerType);
+        setArguments(args);
+    }
+
+    public PowerConsumerPickerFragment() {
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.power_consumer_picker_layout, container, false);
+        mLoadingView = view.findViewById(R.id.loading_view);
+
+        mAppList = view.findViewById(R.id.list_view);
+        mAppList.setLayoutManager(new LinearLayoutManager(getContext()));
+        mPowerConsumerListAdapter = new PowerConsumerListAdapter(
+                PowerConsumerPickerFragment.this::setSelectedPowerConsumer);
+        mAppList.setAdapter(mPowerConsumerListAdapter);
+
+        LoaderManager.getInstance(this).initLoader(0, getArguments(),
+                new PowerConsumerListLoaderCallbacks());
+        return view;
+    }
+
+    public void setSelectedPowerConsumer(String id) {
+        ((PowerConsumerPickerActivity) getActivity()).setSelectedPowerConsumer(id);
+    }
+
+    private static class PowerConsumerListLoader extends
+            AsyncLoaderCompat<List<PowerConsumerInfo>> {
+        private final BatteryStatsHelper mStatsHelper;
+        private final int mPickerType;
+        private final UserManager mUserManager;
+        private final PackageManager mPackageManager;
+
+        PowerConsumerListLoader(Context context, int pickerType) {
+            super(context);
+            mUserManager = context.getSystemService(UserManager.class);
+            mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */);
+            mPickerType = pickerType;
+            mStatsHelper.create((Bundle) null);
+            mStatsHelper.clearStats();
+            mPackageManager = context.getPackageManager();
+        }
+
+        @Override
+        public List<PowerConsumerInfo> loadInBackground() {
+            List<PowerConsumerInfoHelper.PowerConsumerInfo> powerConsumerList = new ArrayList<>();
+
+            mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
+
+            final List<BatterySipper> usageList = mStatsHelper.getUsageList();
+            for (BatterySipper sipper : usageList) {
+                switch (mPickerType) {
+                    case PICKER_TYPE_APP:
+                        if (sipper.drainType != BatterySipper.DrainType.APP) {
+                            continue;
+                        }
+                        break;
+                    case PICKER_TYPE_DRAIN:
+                    default:
+                        if (sipper.drainType == BatterySipper.DrainType.APP) {
+                            continue;
+                        }
+                }
+
+                powerConsumerList.add(
+                        PowerConsumerInfoHelper.makePowerConsumerInfo(mPackageManager, sipper));
+            }
+
+            powerConsumerList.sort(
+                    Comparator.comparing((PowerConsumerInfo a) -> a.powerMah).reversed());
+            return powerConsumerList;
+        }
+
+        @Override
+        protected void onDiscardResult(List<PowerConsumerInfo> result) {
+        }
+    }
+
+    private class PowerConsumerListLoaderCallbacks implements
+            LoaderManager.LoaderCallbacks<List<PowerConsumerInfo>> {
+
+        @NonNull
+        @Override
+        public Loader<List<PowerConsumerInfo>> onCreateLoader(int id, Bundle args) {
+            return new PowerConsumerListLoader(getContext(), args.getInt(PICKER_TYPE));
+        }
+
+        @Override
+        public void onLoadFinished(@NonNull Loader<List<PowerConsumerInfo>> loader,
+                List<PowerConsumerInfoHelper.PowerConsumerInfo> powerConsumerList) {
+            mPowerConsumerListAdapter.setPowerConsumerList(powerConsumerList);
+            mAppList.setVisibility(View.VISIBLE);
+            mLoadingView.setVisibility(View.GONE);
+        }
+
+        @Override
+        public void onLoaderReset(
+                @NonNull Loader<List<PowerConsumerInfoHelper.PowerConsumerInfo>> loader) {
+        }
+    }
+
+    public class PowerConsumerListAdapter extends RecyclerView.Adapter<PowerConsumerViewHolder> {
+        private final OnPowerConsumerSelectedListener mListener;
+        private List<PowerConsumerInfo> mPowerConsumerList;
+
+        public PowerConsumerListAdapter(OnPowerConsumerSelectedListener listener) {
+            mListener = listener;
+        }
+
+        void setPowerConsumerList(List<PowerConsumerInfo> powerConsumerList) {
+            mPowerConsumerList = powerConsumerList;
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public int getItemCount() {
+            return mPowerConsumerList.size();
+        }
+
+        @NonNull
+        @Override
+        public PowerConsumerViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+                int position) {
+            LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
+            View view = layoutInflater.inflate(R.layout.power_consumer_info_layout, viewGroup,
+                    false);
+            return new PowerConsumerViewHolder(view, mListener);
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull PowerConsumerViewHolder viewHolder, int position) {
+            PowerConsumerInfoHelper.PowerConsumerInfo item = mPowerConsumerList.get(position);
+            viewHolder.id = item.id;
+            viewHolder.titleView.setText(item.label);
+            if (item.details != null) {
+                viewHolder.detailsView.setText(item.details);
+                viewHolder.detailsView.setVisibility(View.VISIBLE);
+            } else {
+                viewHolder.detailsView.setVisibility(View.GONE);
+            }
+            viewHolder.powerView.setText(
+                    String.format(Locale.getDefault(), "%.1f mAh", item.powerMah));
+            viewHolder.iconView.setImageDrawable(
+                    item.iconInfo.loadIcon(getContext().getPackageManager()));
+            if (item.packages != null) {
+                viewHolder.packagesView.setText(item.packages);
+                viewHolder.packagesView.setVisibility(View.VISIBLE);
+            } else {
+                viewHolder.packagesView.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    // View Holder used when displaying apps
+    public static class PowerConsumerViewHolder extends RecyclerView.ViewHolder
+            implements View.OnClickListener {
+        private final OnPowerConsumerSelectedListener mListener;
+
+        public String id;
+        public TextView titleView;
+        public TextView detailsView;
+        public ImageView iconView;
+        public TextView packagesView;
+        public TextView powerView;
+
+        PowerConsumerViewHolder(View view, OnPowerConsumerSelectedListener listener) {
+            super(view);
+            mListener = listener;
+            view.setOnClickListener(this);
+            titleView = view.findViewById(android.R.id.title);
+            detailsView = view.findViewById(R.id.details);
+            iconView = view.findViewById(android.R.id.icon);
+            packagesView = view.findViewById(R.id.packages);
+            powerView = view.findViewById(R.id.power_mah);
+            powerView.setVisibility(View.VISIBLE);
+        }
+
+        @Override
+        public void onClick(View v) {
+            mListener.onPowerConsumerSelected(id);
+        }
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
index 09f20ba..099ee22 100644
--- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
@@ -44,11 +44,11 @@
         public double total;
     }
 
-    private final AppInfoHelper.AppInfo mAppInfo;
+    private final PowerConsumerInfoHelper.PowerConsumerInfo mPowerConsumerInfo;
     private final List<Entry> mEntries = new ArrayList<>();
 
     public PowerStatsData(Context context, BatteryStatsHelper batteryStatsHelper,
-            int uid) {
+            String powerConsumerId) {
         List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
 
         double totalPowerMah = 0;
@@ -81,18 +81,14 @@
         long totalAudioTimeMs = 0;
         long totalVideoTimeMs = 0;
 
-        BatterySipper uidSipper = null;
+        BatterySipper requestedPowerConsumer = null;
         for (BatterySipper sipper : usageList) {
             if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
                 totalScreenPower = sipper.sumPower();
             }
 
-            if (isHiddenDrainType(sipper.drainType)) {
-                continue;
-            }
-
-            if (sipper.drainType == BatterySipper.DrainType.APP && sipper.getUid() == uid) {
-                uidSipper = sipper;
+            if (powerConsumerId(sipper).equals(powerConsumerId)) {
+                requestedPowerConsumer = sipper;
             }
 
             totalPowerMah += sipper.sumPower();
@@ -129,79 +125,71 @@
             totalVideoTimeMs += sipper.videoTimeMs;
         }
 
-        mAppInfo = AppInfoHelper.makeApplicationInfo(context.getPackageManager(), uid, uidSipper);
-
-        if (uidSipper == null) {
+        if (requestedPowerConsumer == null) {
+            mPowerConsumerInfo = null;
             return;
         }
 
+        mPowerConsumerInfo = PowerConsumerInfoHelper.makePowerConsumerInfo(
+                context.getPackageManager(), requestedPowerConsumer);
+
         addEntry("Total power", EntryType.POWER,
-                uidSipper.totalSmearedPowerMah, totalSmearedPowerMah);
+                requestedPowerConsumer.totalSmearedPowerMah, totalSmearedPowerMah);
         addEntry("... excluding system", EntryType.POWER,
-                uidSipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
+                requestedPowerConsumer.totalSmearedPowerMah, totalPowerExcludeSystemMah);
         addEntry("Screen, smeared", EntryType.POWER,
-                uidSipper.screenPowerMah, totalScreenPower);
+                requestedPowerConsumer.screenPowerMah, totalScreenPower);
         addEntry("Other, smeared", EntryType.POWER,
-                uidSipper.proportionalSmearMah, totalProportionalSmearMah);
+                requestedPowerConsumer.proportionalSmearMah, totalProportionalSmearMah);
         addEntry("Excluding smeared", EntryType.POWER,
-                uidSipper.totalPowerMah, totalPowerMah);
+                requestedPowerConsumer.totalPowerMah, totalPowerMah);
         addEntry("CPU", EntryType.POWER,
-                uidSipper.cpuPowerMah, totalCpuPowerMah);
+                requestedPowerConsumer.cpuPowerMah, totalCpuPowerMah);
         addEntry("System services", EntryType.POWER,
-                uidSipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
-        addEntry("RAM", EntryType.POWER,
-                uidSipper.usagePowerMah, totalUsagePowerMah);
+                requestedPowerConsumer.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
+        addEntry("Usage", EntryType.POWER,
+                requestedPowerConsumer.usagePowerMah, totalUsagePowerMah);
         addEntry("Wake lock", EntryType.POWER,
-                uidSipper.wakeLockPowerMah, totalWakeLockPowerMah);
+                requestedPowerConsumer.wakeLockPowerMah, totalWakeLockPowerMah);
         addEntry("Mobile radio", EntryType.POWER,
-                uidSipper.mobileRadioPowerMah, totalMobileRadioPowerMah);
+                requestedPowerConsumer.mobileRadioPowerMah, totalMobileRadioPowerMah);
         addEntry("WiFi", EntryType.POWER,
-                uidSipper.wifiPowerMah, totalWifiPowerMah);
+                requestedPowerConsumer.wifiPowerMah, totalWifiPowerMah);
         addEntry("Bluetooth", EntryType.POWER,
-                uidSipper.bluetoothPowerMah, totalBluetoothPowerMah);
+                requestedPowerConsumer.bluetoothPowerMah, totalBluetoothPowerMah);
         addEntry("GPS", EntryType.POWER,
-                uidSipper.gpsPowerMah, totalGpsPowerMah);
+                requestedPowerConsumer.gpsPowerMah, totalGpsPowerMah);
         addEntry("Camera", EntryType.POWER,
-                uidSipper.cameraPowerMah, totalCameraPowerMah);
+                requestedPowerConsumer.cameraPowerMah, totalCameraPowerMah);
         addEntry("Flashlight", EntryType.POWER,
-                uidSipper.flashlightPowerMah, totalFlashlightPowerMah);
+                requestedPowerConsumer.flashlightPowerMah, totalFlashlightPowerMah);
         addEntry("Sensors", EntryType.POWER,
-                uidSipper.sensorPowerMah, totalSensorPowerMah);
+                requestedPowerConsumer.sensorPowerMah, totalSensorPowerMah);
         addEntry("Audio", EntryType.POWER,
-                uidSipper.audioPowerMah, totalAudioPowerMah);
+                requestedPowerConsumer.audioPowerMah, totalAudioPowerMah);
         addEntry("Video", EntryType.POWER,
-                uidSipper.videoPowerMah, totalVideoPowerMah);
+                requestedPowerConsumer.videoPowerMah, totalVideoPowerMah);
 
         addEntry("CPU time", EntryType.DURATION,
-                uidSipper.cpuTimeMs, totalCpuTimeMs);
+                requestedPowerConsumer.cpuTimeMs, totalCpuTimeMs);
         addEntry("CPU foreground time", EntryType.DURATION,
-                uidSipper.cpuFgTimeMs, totalCpuFgTimeMs);
+                requestedPowerConsumer.cpuFgTimeMs, totalCpuFgTimeMs);
         addEntry("Wake lock time", EntryType.DURATION,
-                uidSipper.wakeLockTimeMs, totalWakeLockTimeMs);
+                requestedPowerConsumer.wakeLockTimeMs, totalWakeLockTimeMs);
         addEntry("WiFi running time", EntryType.DURATION,
-                uidSipper.wifiRunningTimeMs, totalWifiRunningTimeMs);
+                requestedPowerConsumer.wifiRunningTimeMs, totalWifiRunningTimeMs);
         addEntry("Bluetooth time", EntryType.DURATION,
-                uidSipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
+                requestedPowerConsumer.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
         addEntry("GPS time", EntryType.DURATION,
-                uidSipper.gpsTimeMs, totalGpsTimeMs);
+                requestedPowerConsumer.gpsTimeMs, totalGpsTimeMs);
         addEntry("Camera time", EntryType.DURATION,
-                uidSipper.cameraTimeMs, totalCameraTimeMs);
+                requestedPowerConsumer.cameraTimeMs, totalCameraTimeMs);
         addEntry("Flashlight time", EntryType.DURATION,
-                uidSipper.flashlightTimeMs, totalFlashlightTimeMs);
+                requestedPowerConsumer.flashlightTimeMs, totalFlashlightTimeMs);
         addEntry("Audio time", EntryType.DURATION,
-                uidSipper.audioTimeMs, totalAudioTimeMs);
+                requestedPowerConsumer.audioTimeMs, totalAudioTimeMs);
         addEntry("Video time", EntryType.DURATION,
-                uidSipper.videoTimeMs, totalVideoTimeMs);
-    }
-
-    protected boolean isHiddenDrainType(BatterySipper.DrainType drainType) {
-        return drainType == BatterySipper.DrainType.IDLE
-                || drainType == BatterySipper.DrainType.CELL
-                || drainType == BatterySipper.DrainType.SCREEN
-                || drainType == BatterySipper.DrainType.UNACCOUNTED
-                || drainType == BatterySipper.DrainType.OVERCOUNTED
-                || drainType == BatterySipper.DrainType.BLUETOOTH
-                || drainType == BatterySipper.DrainType.WIFI;
+                requestedPowerConsumer.videoTimeMs, totalVideoTimeMs);
     }
 
     private boolean isSystemSipper(BatterySipper sipper) {
@@ -230,11 +218,15 @@
         mEntries.add(entry);
     }
 
-    public AppInfoHelper.AppInfo getAppInfo() {
-        return mAppInfo;
+    public PowerConsumerInfoHelper.PowerConsumerInfo getPowerConsumerInfo() {
+        return mPowerConsumerInfo;
     }
 
     public List<Entry> getEntries() {
         return mEntries;
     }
+
+    public static String powerConsumerId(BatterySipper sipper) {
+        return sipper.drainType + "|" + sipper.userId + "|" + sipper.getUid();
+    }
 }
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
index 1605e9c..78f2b91 100644
--- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
@@ -20,7 +20,7 @@
 import android.content.SharedPreferences;
 import android.os.BatteryStats;
 import android.os.Bundle;
-import android.os.Process;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -46,15 +46,15 @@
 
 public class PowerStatsViewerActivity extends ComponentActivity {
     private static final int POWER_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
-    public static final String PREF_SELECTED_UID = "selectedUid";
-    private static final String LOADER_ARG_UID = "uid";
+    public static final String PREF_SELECTED_POWER_CONSUMER = "powerConsumerId";
+    private static final String LOADER_ARG_POWER_CONSUMER_ID = "powerConsumerId";
 
     private PowerStatsDataAdapter mPowerStatsDataAdapter;
     private Runnable mPowerStatsRefresh = this::periodicPowerStatsRefresh;
     private SharedPreferences mSharedPref;
-    private int mUid = Process.INVALID_UID;
+    private String mPowerConsumerId;
     private TextView mTitleView;
-    private TextView mUidView;
+    private TextView mDetailsView;
     private ImageView mIconView;
     private TextView mPackagesView;
     private RecyclerView mPowerStatsDataView;
@@ -73,7 +73,7 @@
         appCard.setOnClickListener((e) -> startAppPicker());
 
         mTitleView = findViewById(android.R.id.title);
-        mUidView = findViewById(R.id.uid);
+        mDetailsView = findViewById(R.id.details);
         mIconView = findViewById(android.R.id.icon);
         mPackagesView = findViewById(R.id.packages);
 
@@ -85,9 +85,9 @@
         mLoadingView = findViewById(R.id.loading_view);
         mEmptyView = findViewById(R.id.empty_view);
 
-        mUid = mSharedPref.getInt(PREF_SELECTED_UID, Process.INVALID_UID);
+        mPowerConsumerId = mSharedPref.getString(PREF_SELECTED_POWER_CONSUMER, null);
         loadPowerStats();
-        if (mUid == Process.INVALID_UID) {
+        if (mPowerConsumerId == null) {
             startAppPicker();
         }
     }
@@ -105,18 +105,18 @@
     }
 
     private void startAppPicker() {
-        registerForActivityResult(AppPickerActivity.CONTRACT, this::onApplicationSelected)
+        registerForActivityResult(PowerConsumerPickerActivity.CONTRACT, this::onApplicationSelected)
                 .launch(null);
     }
 
-    private void onApplicationSelected(int uid) {
-        if (uid == -1) {
-            if (mUid == Process.INVALID_UID) {
+    private void onApplicationSelected(String powerConsumerId) {
+        if (powerConsumerId == null) {
+            if (mPowerConsumerId == null) {
                 finish();
             }
         } else {
-            mUid = uid;
-            mSharedPref.edit().putInt(PREF_SELECTED_UID, mUid).apply();
+            mPowerConsumerId = powerConsumerId;
+            mSharedPref.edit().putString(PREF_SELECTED_POWER_CONSUMER, mPowerConsumerId).apply();
             mLoadingView.setVisibility(View.VISIBLE);
             loadPowerStats();
         }
@@ -129,18 +129,18 @@
 
     private void loadPowerStats() {
         Bundle args = new Bundle();
-        args.putInt(LOADER_ARG_UID, mUid);
+        args.putString(LOADER_ARG_POWER_CONSUMER_ID, mPowerConsumerId);
         LoaderManager.getInstance(this).restartLoader(0, args, new PowerStatsDataLoaderCallbacks());
     }
 
     private static class PowerStatsDataLoader extends AsyncLoaderCompat<PowerStatsData> {
-        private final int mUid;
+        private final String mPowerConsumerId;
         private final BatteryStatsHelper mBatteryStatsHelper;
         private final UserManager mUserManager;
 
-        PowerStatsDataLoader(Context context, int uid) {
+        PowerStatsDataLoader(Context context, String powerConsumerId) {
             super(context);
-            mUid = uid;
+            mPowerConsumerId = powerConsumerId;
             mUserManager = context.getSystemService(UserManager.class);
             mBatteryStatsHelper = new BatteryStatsHelper(context,
                     false /* collectBatteryBroadcast */);
@@ -151,8 +151,8 @@
         @Override
         public PowerStatsData loadInBackground() {
             mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
-                    mUserManager.getUserProfiles());
-            return new PowerStatsData(getContext(), mBatteryStatsHelper, mUid);
+                    UserHandle.myUserId());
+            return new PowerStatsData(getContext(), mBatteryStatsHelper, mPowerConsumerId);
         }
 
         @Override
@@ -165,27 +165,38 @@
         @Override
         public Loader<PowerStatsData> onCreateLoader(int id, Bundle args) {
             return new PowerStatsDataLoader(PowerStatsViewerActivity.this,
-                    args.getInt(LOADER_ARG_UID, Process.INVALID_UID));
+                    args.getString(LOADER_ARG_POWER_CONSUMER_ID));
         }
 
         @Override
         public void onLoadFinished(@NonNull Loader<PowerStatsData> loader,
                 PowerStatsData powerStatsData) {
 
-            AppInfoHelper.AppInfo appInfo = powerStatsData.getAppInfo();
-            mTitleView.setText(appInfo.label);
-            mUidView.setText(String.format(Locale.getDefault(), "UID: %d", appInfo.uid));
-            mIconView.setImageDrawable(appInfo.iconInfo.loadIcon(getPackageManager()));
-
-            if (appInfo.packages != null) {
-                mPackagesView.setText(appInfo.packages);
-                mPackagesView.setVisibility(View.VISIBLE);
-            } else {
+            PowerConsumerInfoHelper.PowerConsumerInfo
+                    powerConsumerInfo = powerStatsData.getPowerConsumerInfo();
+            if (powerConsumerInfo == null) {
+                mTitleView.setText("Power consumer not found");
                 mPackagesView.setVisibility(View.GONE);
+            } else {
+                mTitleView.setText(powerConsumerInfo.label);
+                if (powerConsumerInfo.details != null) {
+                    mDetailsView.setText(powerConsumerInfo.details);
+                    mDetailsView.setVisibility(View.VISIBLE);
+                } else {
+                    mDetailsView.setVisibility(View.GONE);
+                }
+                mIconView.setImageDrawable(
+                        powerConsumerInfo.iconInfo.loadIcon(getPackageManager()));
+
+                if (powerConsumerInfo.packages != null) {
+                    mPackagesView.setText(powerConsumerInfo.packages);
+                    mPackagesView.setVisibility(View.VISIBLE);
+                } else {
+                    mPackagesView.setVisibility(View.GONE);
+                }
             }
 
             mPowerStatsDataAdapter.setEntries(powerStatsData.getEntries());
-
             if (powerStatsData.getEntries().isEmpty()) {
                 mEmptyView.setVisibility(View.VISIBLE);
                 mPowerStatsDataView.setVisibility(View.GONE);