Files
packages_apps_Settings/src/com/android/settings/fuelgauge/BatteryEntry.java
Dmitri Plotnikov e02d6efe85 Adjust consumed power for SystemBatteryConsumers
The implementations of getConsumedPower() now consistently returns
the total power consumed, not the power unattributed to apps.

Bug: 183235836
Test: make RunSettingsRoboTests
Test: make RunSettingsGoogleRoboTests

Change-Id: I9ea1592581b50d1533b00c28c9db4eab1e88edac
2021-03-24 22:28:40 +00:00

517 lines
19 KiB
Java

/*
* Copyright (C) 2014 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.app.AppGlobals;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.os.BatteryConsumer;
import android.os.Handler;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import com.android.settings.R;
import com.android.settingslib.Utils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
/**
* Wraps the power usage data of a BatterySipper with information about package name
* and icon image.
*/
public class BatteryEntry {
public static final int MSG_UPDATE_NAME_ICON = 1;
public static final int MSG_REPORT_FULLY_DRAWN = 2;
private static final String TAG = "BatteryEntry";
private static final String PACKAGE_SYSTEM = "android";
static final HashMap<String, UidToDetail> sUidCache = new HashMap<>();
static final ArrayList<BatteryEntry> sRequestQueue = new ArrayList<BatteryEntry>();
static Handler sHandler;
static Locale sCurrentLocale = null;
static private class NameAndIconLoader extends Thread {
private boolean mAbort = false;
public NameAndIconLoader() {
super("BatteryUsage Icon Loader");
}
public void abort() {
mAbort = true;
}
@Override
public void run() {
while (true) {
BatteryEntry be;
synchronized (sRequestQueue) {
if (sRequestQueue.isEmpty() || mAbort) {
if (sHandler != null) {
sHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN);
}
return;
}
be = sRequestQueue.remove(0);
}
be.loadNameAndIcon();
}
}
}
private static NameAndIconLoader mRequestThread;
public static void startRequestQueue() {
if (sHandler != null) {
synchronized (sRequestQueue) {
if (!sRequestQueue.isEmpty()) {
if (mRequestThread != null) {
mRequestThread.abort();
}
mRequestThread = new NameAndIconLoader();
mRequestThread.setPriority(Thread.MIN_PRIORITY);
mRequestThread.start();
sRequestQueue.notify();
}
}
}
}
public static void stopRequestQueue() {
synchronized (sRequestQueue) {
if (mRequestThread != null) {
mRequestThread.abort();
mRequestThread = null;
sRequestQueue.clear();
sHandler = null;
}
}
}
public static void clearUidCache() {
sUidCache.clear();
}
public static final Comparator<BatteryEntry> COMPARATOR =
(a, b) -> Double.compare(b.getConsumedPower(), a.getConsumedPower());
private final Context mContext;
private final BatteryConsumer mBatteryConsumer;
private final boolean mIsHidden;
public String name;
public Drawable icon;
public int iconId; // For passing to the detail screen.
public double percent;
private String mDefaultPackageName;
private double mConsumedPower;
static class UidToDetail {
String name;
String packageName;
Drawable icon;
}
public BatteryEntry(Context context, Handler handler, UserManager um,
@NonNull BatteryConsumer batteryConsumer, boolean isHidden, String[] packages,
String packageName) {
sHandler = handler;
mContext = context;
mBatteryConsumer = batteryConsumer;
mIsHidden = isHidden;
mDefaultPackageName = packageName;
if (batteryConsumer instanceof UidBatteryConsumer) {
mConsumedPower = batteryConsumer.getConsumedPower();
UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
int uid = uidBatteryConsumer.getUid();
if (mDefaultPackageName == null) {
// Apps should only have one package
if (packages != null && packages.length == 1) {
mDefaultPackageName = packages[0];
} else {
mDefaultPackageName = uidBatteryConsumer.getPackageWithHighestDrain();
}
}
if (mDefaultPackageName != null) {
PackageManager pm = context.getPackageManager();
try {
ApplicationInfo appInfo =
pm.getApplicationInfo(mDefaultPackageName, 0 /* no flags */);
name = pm.getApplicationLabel(appInfo).toString();
} catch (NameNotFoundException e) {
Log.d(TAG, "PackageManager failed to retrieve ApplicationInfo for: "
+ mDefaultPackageName);
name = mDefaultPackageName;
}
}
getQuickNameIconForUid(uid, packages);
return;
} else if (batteryConsumer instanceof SystemBatteryConsumer) {
mConsumedPower = batteryConsumer.getConsumedPower()
- ((SystemBatteryConsumer) batteryConsumer).getPowerConsumedByApps();
final Pair<Integer, String> resourcePair = getResourcePairFromDrainType(
context, ((SystemBatteryConsumer) batteryConsumer).getDrainType());
iconId = resourcePair.first;
name = resourcePair.second;
} else if (batteryConsumer instanceof UserBatteryConsumer) {
mConsumedPower = batteryConsumer.getConsumedPower();
UserInfo info = um.getUserInfo(((UserBatteryConsumer) batteryConsumer).getUserId());
if (info != null) {
icon = Utils.getUserIcon(context, um, info);
name = Utils.getUserLabel(context, info);
} else {
icon = null;
name = context.getResources().getString(
R.string.running_process_item_removed_user_label);
}
}
if (iconId != 0) {
icon = context.getDrawable(iconId);
}
}
public Drawable getIcon() {
return icon;
}
/**
* Gets the application name
*/
public String getLabel() {
return name;
}
void getQuickNameIconForUid(final int uid, final String[] packages) {
// Locale sync to system config in Settings
final Locale locale = Locale.getDefault();
if (sCurrentLocale != locale) {
clearUidCache();
sCurrentLocale = locale;
}
final String uidString = Integer.toString(uid);
if (sUidCache.containsKey(uidString)) {
UidToDetail utd = sUidCache.get(uidString);
mDefaultPackageName = utd.packageName;
name = utd.name;
icon = utd.icon;
return;
}
if (packages == null || packages.length == 0) {
if (uid == 0) {
name = mContext.getResources().getString(R.string.process_kernel_label);
} else if ("mediaserver".equals(name)) {
name = mContext.getResources().getString(R.string.process_mediaserver_label);
} else if ("dex2oat".equals(name)) {
name = mContext.getResources().getString(R.string.process_dex2oat_label);
}
iconId = R.drawable.ic_power_system;
icon = mContext.getDrawable(iconId);
} else {
icon = mContext.getPackageManager().getDefaultActivityIcon();
}
if (sHandler != null) {
synchronized (sRequestQueue) {
sRequestQueue.add(this);
}
}
}
/**
* Loads the app label and icon image and stores into the cache.
*/
public void loadNameAndIcon() {
// Bail out if the current sipper is not an App sipper.
final int uid = getUid();
if (uid == 0 || uid == Process.INVALID_UID) {
return;
}
final PackageManager pm = mContext.getPackageManager();
final String[] packages;
if (uid == Process.SYSTEM_UID) {
packages = new String[]{PACKAGE_SYSTEM};
} else {
packages = pm.getPackagesForUid(uid);
}
if (packages != null) {
String[] packageLabels = new String[packages.length];
System.arraycopy(packages, 0, packageLabels, 0, packages.length);
// Convert package names to user-facing labels where possible
IPackageManager ipm = AppGlobals.getPackageManager();
final int userId = UserHandle.getUserId(uid);
for (int i = 0; i < packageLabels.length; i++) {
try {
final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i],
0 /* no flags */, userId);
if (ai == null) {
Log.d(TAG, "Retrieving null app info for package "
+ packageLabels[i] + ", user " + userId);
continue;
}
CharSequence label = ai.loadLabel(pm);
if (label != null) {
packageLabels[i] = label.toString();
}
if (ai.icon != 0) {
mDefaultPackageName = packages[i];
icon = ai.loadIcon(pm);
break;
}
} catch (RemoteException e) {
Log.d(TAG, "Error while retrieving app info for package "
+ packageLabels[i] + ", user " + userId, e);
}
}
if (packageLabels.length == 1) {
name = packageLabels[0];
} else {
// Look for an official name for this UID.
for (String pkgName : packages) {
try {
final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId);
if (pi == null) {
Log.d(TAG, "Retrieving null package info for package "
+ pkgName + ", user " + userId);
continue;
}
if (pi.sharedUserLabel != 0) {
final CharSequence nm = pm.getText(pkgName,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
name = nm.toString();
if (pi.applicationInfo.icon != 0) {
mDefaultPackageName = pkgName;
icon = pi.applicationInfo.loadIcon(pm);
}
break;
}
}
} catch (RemoteException e) {
Log.d(TAG, "Error while retrieving package info for package "
+ pkgName + ", user " + userId, e);
}
}
}
}
final String uidString = Integer.toString(uid);
if (name == null) {
name = uidString;
}
if (icon == null) {
icon = pm.getDefaultActivityIcon();
}
UidToDetail utd = new UidToDetail();
utd.name = name;
utd.icon = icon;
utd.packageName = mDefaultPackageName;
sUidCache.put(uidString, utd);
if (sHandler != null) {
sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this));
}
}
/**
* Returns a string that uniquely identifies this battery consumer.
*/
public String getKey() {
if (mBatteryConsumer instanceof UidBatteryConsumer) {
return Integer.toString(((UidBatteryConsumer) mBatteryConsumer).getUid());
} else if (mBatteryConsumer instanceof SystemBatteryConsumer) {
return "S|" + ((SystemBatteryConsumer) mBatteryConsumer).getDrainType();
} else if (mBatteryConsumer instanceof UserBatteryConsumer) {
return "U|" + ((UserBatteryConsumer) mBatteryConsumer).getUserId();
} else {
Log.w(TAG, "Unsupported BatteryConsumer: " + mBatteryConsumer);
return "";
}
}
/**
* Returns true if the entry is hidden from the battery usage summary list.
*/
public boolean isHidden() {
return mIsHidden;
}
/**
* Returns true if this entry describes an app (UID)
*/
public boolean isAppEntry() {
return mBatteryConsumer instanceof UidBatteryConsumer;
}
/**
* Returns true if this entry describes a User.
*/
public boolean isUserEntry() {
if (mBatteryConsumer instanceof UserBatteryConsumer) {
return true;
}
return false;
}
/**
* Returns the package name that should be used to represent the UID described
* by this entry.
*/
public String getDefaultPackageName() {
return mDefaultPackageName;
}
/**
* Returns the UID of the app described by this entry.
*/
public int getUid() {
if (mBatteryConsumer instanceof UidBatteryConsumer) {
return ((UidBatteryConsumer) mBatteryConsumer).getUid();
} else {
return Process.INVALID_UID;
}
}
/**
* Returns foreground foreground time (in milliseconds) that is attributed to this entry.
*/
public long getTimeInForegroundMs() {
if (mBatteryConsumer instanceof UidBatteryConsumer) {
return ((UidBatteryConsumer) mBatteryConsumer).getTimeInStateMs(
UidBatteryConsumer.STATE_FOREGROUND);
} else {
return mBatteryConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE);
}
}
/**
* Returns background activity time (in milliseconds) that is attributed to this entry.
*/
public long getTimeInBackgroundMs() {
if (mBatteryConsumer instanceof UidBatteryConsumer) {
return ((UidBatteryConsumer) mBatteryConsumer).getTimeInStateMs(
UidBatteryConsumer.STATE_BACKGROUND);
} else {
return 0;
}
}
/**
* Returns total amount of power (in milli-amp-hours) that is attributed to this entry.
*/
public double getConsumedPower() {
return mConsumedPower;
}
/**
* Adds the consumed power of the supplied BatteryConsumer to this entry. Also
* uses its package with highest drain, if necessary.
*/
public void add(BatteryConsumer batteryConsumer) {
mConsumedPower += batteryConsumer.getConsumedPower();
if (mDefaultPackageName == null && batteryConsumer instanceof UidBatteryConsumer) {
mDefaultPackageName =
((UidBatteryConsumer) batteryConsumer).getPackageWithHighestDrain();
}
}
/**
* Gets icon ID and name from system battery consumer drain type.
*/
public static Pair<Integer, String> getResourcePairFromDrainType(
Context context, int drainType) {
String name = null;
int iconId = 0;
switch (drainType) {
case SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY:
name = context.getResources().getString(R.string.ambient_display_screen_title);
iconId = R.drawable.ic_settings_aod;
break;
case SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH:
name = context.getResources().getString(R.string.power_bluetooth);
iconId = com.android.internal.R.drawable.ic_settings_bluetooth;
break;
case SystemBatteryConsumer.DRAIN_TYPE_CAMERA:
name = context.getResources().getString(R.string.power_camera);
iconId = R.drawable.ic_settings_camera;
break;
case SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO:
name = context.getResources().getString(R.string.power_cell);
iconId = R.drawable.ic_cellular_1_bar;
break;
case SystemBatteryConsumer.DRAIN_TYPE_FLASHLIGHT:
name = context.getResources().getString(R.string.power_flashlight);
iconId = R.drawable.ic_settings_display;
break;
case SystemBatteryConsumer.DRAIN_TYPE_PHONE:
name = context.getResources().getString(R.string.power_phone);
iconId = R.drawable.ic_settings_voice_calls;
break;
case SystemBatteryConsumer.DRAIN_TYPE_SCREEN:
name = context.getResources().getString(R.string.power_screen);
iconId = R.drawable.ic_settings_display;
break;
case SystemBatteryConsumer.DRAIN_TYPE_WIFI:
name = context.getResources().getString(R.string.power_wifi);
iconId = R.drawable.ic_settings_wireless;
break;
case SystemBatteryConsumer.DRAIN_TYPE_IDLE:
case SystemBatteryConsumer.DRAIN_TYPE_MEMORY:
name = context.getResources().getString(R.string.power_idle);
iconId = R.drawable.ic_settings_phone_idle;
break;
case SystemBatteryConsumer.DRAIN_TYPE_CUSTOM:
name = null;
iconId = R.drawable.ic_power_system;
break;
}
return new Pair<>(Integer.valueOf(iconId), name);
}
}