The gist of the issue is that many apps have two UIDs associated with them: a regular, "real" UID, e.g. 10123, and a shared group GID, e.g. 50123, which is used for multiuser support. Prior to this fix, the code in BatteryAppListPreferenceController, would go over the list of all UidBatteryConsumers and would randomly encounter either the "real" UID or the shared GID for each app first. The UidBatteryConsumer for a shared GID does not have all of the properties of the real UID, so some information, such as time-in-foreground and time-in-background would be lost with a high probability. After this fix, we process "real" UIDs before shared GIDs ensuring that time-in-* and other properties such as package names are obtained for the real UID. When we later encounter a shared GID for the same app, we just add the consumed power and time-in-* durations to the real UID's BatteryEntry. Bug: 188656360 Test: make RunSettingsRoboTests Test: make RunSettingsGoogleRoboTests Change-Id: I4bfea813ac5eb8f866804b2c4a9153eb877fb325
237 lines
8.7 KiB
Java
237 lines
8.7 KiB
Java
/*
|
|
* Copyright (C) 2017 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.applications.appinfo;
|
|
|
|
import android.content.Context;
|
|
import android.content.pm.PackageInfo;
|
|
import android.os.AsyncTask;
|
|
import android.os.BatteryUsageStats;
|
|
import android.os.Bundle;
|
|
import android.os.UidBatteryConsumer;
|
|
import android.os.UserManager;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.loader.app.LoaderManager;
|
|
import androidx.loader.content.Loader;
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.PreferenceScreen;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.core.BasePreferenceController;
|
|
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
|
|
import com.android.settings.fuelgauge.BatteryChartPreferenceController;
|
|
import com.android.settings.fuelgauge.BatteryDiffEntry;
|
|
import com.android.settings.fuelgauge.BatteryEntry;
|
|
import com.android.settings.fuelgauge.BatteryUsageStatsLoader;
|
|
import com.android.settings.fuelgauge.BatteryUtils;
|
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
import com.android.settingslib.core.lifecycle.events.OnPause;
|
|
import com.android.settingslib.core.lifecycle.events.OnResume;
|
|
|
|
import java.util.List;
|
|
|
|
public class AppBatteryPreferenceController extends BasePreferenceController
|
|
implements LifecycleObserver, OnResume, OnPause {
|
|
|
|
private static final String KEY_BATTERY = "battery";
|
|
|
|
@VisibleForTesting
|
|
final BatteryUsageStatsLoaderCallbacks mBatteryUsageStatsLoaderCallbacks =
|
|
new BatteryUsageStatsLoaderCallbacks();
|
|
@VisibleForTesting
|
|
BatteryUtils mBatteryUtils;
|
|
@VisibleForTesting
|
|
BatteryUsageStats mBatteryUsageStats;
|
|
@VisibleForTesting
|
|
UidBatteryConsumer mUidBatteryConsumer;
|
|
|
|
private Preference mPreference;
|
|
private final AppInfoDashboardFragment mParent;
|
|
private String mBatteryPercent;
|
|
private final String mPackageName;
|
|
private final int mUid;
|
|
private BatteryDiffEntry mBatteryDiffEntry;
|
|
private boolean mBatteryUsageStatsLoaded = false;
|
|
private boolean mBatteryDiffEntriesLoaded = false;
|
|
|
|
public AppBatteryPreferenceController(Context context, AppInfoDashboardFragment parent,
|
|
String packageName, int uid, Lifecycle lifecycle) {
|
|
super(context, KEY_BATTERY);
|
|
mParent = parent;
|
|
mBatteryUtils = BatteryUtils.getInstance(mContext);
|
|
mPackageName = packageName;
|
|
mUid = uid;
|
|
if (lifecycle != null) {
|
|
lifecycle.addObserver(this);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getAvailabilityStatus() {
|
|
return mContext.getResources().getBoolean(R.bool.config_show_app_info_settings_battery)
|
|
? AVAILABLE
|
|
: CONDITIONALLY_UNAVAILABLE;
|
|
}
|
|
|
|
@Override
|
|
public void displayPreference(PreferenceScreen screen) {
|
|
super.displayPreference(screen);
|
|
mPreference = screen.findPreference(getPreferenceKey());
|
|
mPreference.setEnabled(false);
|
|
loadBatteryDiffEntries();
|
|
}
|
|
|
|
@Override
|
|
public boolean handlePreferenceTreeClick(Preference preference) {
|
|
if (!KEY_BATTERY.equals(preference.getKey())) {
|
|
return false;
|
|
}
|
|
if (mBatteryDiffEntry != null) {
|
|
AdvancedPowerUsageDetail.startBatteryDetailPage(
|
|
mParent.getActivity(),
|
|
mParent,
|
|
mBatteryDiffEntry,
|
|
mBatteryPercent,
|
|
/*isValidToShowSummary=*/ true,
|
|
/*slotInformation=*/ null);
|
|
return true;
|
|
}
|
|
|
|
if (isBatteryStatsAvailable()) {
|
|
final UserManager userManager =
|
|
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
|
final BatteryEntry entry = new BatteryEntry(mContext, /* handler */null, userManager,
|
|
mUidBatteryConsumer, /* isHidden */ false,
|
|
mUidBatteryConsumer.getUid(), /* packages */ null, mPackageName);
|
|
AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
|
|
entry, mBatteryPercent);
|
|
} else {
|
|
AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
|
|
mPackageName);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
mParent.getLoaderManager().restartLoader(
|
|
AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS, Bundle.EMPTY,
|
|
mBatteryUsageStatsLoaderCallbacks);
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
mParent.getLoaderManager().destroyLoader(
|
|
AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS);
|
|
}
|
|
|
|
private void loadBatteryDiffEntries() {
|
|
new AsyncTask<Void, Void, BatteryDiffEntry>() {
|
|
@Override
|
|
protected BatteryDiffEntry doInBackground(Void... unused) {
|
|
final List<BatteryDiffEntry> batteryDiffEntries =
|
|
BatteryChartPreferenceController.getBatteryLast24HrUsageData(mContext);
|
|
if (batteryDiffEntries != null) {
|
|
for (BatteryDiffEntry batteryDiffEntry : batteryDiffEntries) {
|
|
if (batteryDiffEntry.mBatteryHistEntry.mUid == mUid) {
|
|
return batteryDiffEntry;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(BatteryDiffEntry batteryDiffEntry) {
|
|
mBatteryDiffEntry = batteryDiffEntry;
|
|
mBatteryDiffEntriesLoaded = true;
|
|
mPreference.setEnabled(mBatteryUsageStatsLoaded);
|
|
}
|
|
}.execute();
|
|
}
|
|
|
|
private void onLoadFinished() {
|
|
if (mBatteryUsageStats == null) {
|
|
return;
|
|
}
|
|
|
|
final PackageInfo packageInfo = mParent.getPackageInfo();
|
|
if (packageInfo != null) {
|
|
mUidBatteryConsumer = findTargetUidBatteryConsumer(mBatteryUsageStats,
|
|
packageInfo.applicationInfo.uid);
|
|
if (mParent.getActivity() != null) {
|
|
updateBattery();
|
|
}
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void updateBattery() {
|
|
mBatteryUsageStatsLoaded = true;
|
|
mPreference.setEnabled(mBatteryDiffEntriesLoaded);
|
|
if (isBatteryStatsAvailable()) {
|
|
final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent(
|
|
mUidBatteryConsumer.getConsumedPower(), mBatteryUsageStats.getConsumedPower(),
|
|
mBatteryUsageStats.getDischargePercentage());
|
|
mBatteryPercent = Utils.formatPercentage(percentOfMax);
|
|
mPreference.setSummary(mContext.getString(R.string.battery_summary, mBatteryPercent));
|
|
} else {
|
|
mPreference.setSummary(mContext.getString(R.string.no_battery_summary));
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
boolean isBatteryStatsAvailable() {
|
|
return mUidBatteryConsumer != null;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
UidBatteryConsumer findTargetUidBatteryConsumer(BatteryUsageStats batteryUsageStats, int uid) {
|
|
final List<UidBatteryConsumer> usageList = batteryUsageStats.getUidBatteryConsumers();
|
|
for (int i = 0, size = usageList.size(); i < size; i++) {
|
|
final UidBatteryConsumer consumer = usageList.get(i);
|
|
if (consumer.getUid() == uid) {
|
|
return consumer;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private class BatteryUsageStatsLoaderCallbacks
|
|
implements LoaderManager.LoaderCallbacks<BatteryUsageStats> {
|
|
@Override
|
|
@NonNull
|
|
public Loader<BatteryUsageStats> onCreateLoader(int id, Bundle args) {
|
|
return new BatteryUsageStatsLoader(mContext, /* includeBatteryHistory */ false);
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<BatteryUsageStats> loader,
|
|
BatteryUsageStats batteryUsageStats) {
|
|
mBatteryUsageStats = batteryUsageStats;
|
|
AppBatteryPreferenceController.this.onLoadFinished();
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(Loader<BatteryUsageStats> loader) {
|
|
}
|
|
}
|
|
}
|