1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.fuelgauge; 18 19 import android.annotation.UserIdInt; 20 import android.app.Activity; 21 import android.app.ActivityManager; 22 import android.app.settings.SettingsEnums; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.os.BatteryStats; 27 import android.os.Bundle; 28 import android.os.UserHandle; 29 import android.text.TextUtils; 30 import android.util.Log; 31 import android.view.View; 32 33 import androidx.annotation.VisibleForTesting; 34 import androidx.preference.Preference; 35 36 import com.android.internal.os.BatterySipper; 37 import com.android.internal.os.BatteryStatsHelper; 38 import com.android.internal.util.ArrayUtils; 39 import com.android.settings.R; 40 import com.android.settings.SettingsActivity; 41 import com.android.settings.Utils; 42 import com.android.settings.applications.appinfo.AppButtonsPreferenceController; 43 import com.android.settings.applications.appinfo.ButtonActionDialogFragment; 44 import com.android.settings.core.InstrumentedPreferenceFragment; 45 import com.android.settings.core.SubSettingLauncher; 46 import com.android.settings.dashboard.DashboardFragment; 47 import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController; 48 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; 49 import com.android.settings.widget.EntityHeaderController; 50 import com.android.settingslib.applications.AppUtils; 51 import com.android.settingslib.applications.ApplicationsState; 52 import com.android.settingslib.core.AbstractPreferenceController; 53 import com.android.settingslib.utils.StringUtil; 54 import com.android.settingslib.widget.LayoutPreference; 55 56 import java.util.ArrayList; 57 import java.util.List; 58 59 /** 60 * Power usage detail fragment for each app, this fragment contains 61 * 62 * 1. Detail battery usage information for app(i.e. usage time, usage amount) 63 * 2. Battery related controls for app(i.e uninstall, force stop) 64 */ 65 public class AdvancedPowerUsageDetail extends DashboardFragment implements 66 ButtonActionDialogFragment.AppButtonsDialogListener, 67 BatteryTipPreferenceController.BatteryTipListener { 68 69 public static final String TAG = "AdvancedPowerDetail"; 70 public static final String EXTRA_UID = "extra_uid"; 71 public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; 72 public static final String EXTRA_FOREGROUND_TIME = "extra_foreground_time"; 73 public static final String EXTRA_BACKGROUND_TIME = "extra_background_time"; 74 public static final String EXTRA_LABEL = "extra_label"; 75 public static final String EXTRA_ICON_ID = "extra_icon_id"; 76 public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent"; 77 public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount"; 78 79 private static final String KEY_PREF_FOREGROUND = "app_usage_foreground"; 80 private static final String KEY_PREF_BACKGROUND = "app_usage_background"; 81 private static final String KEY_PREF_HEADER = "header_view"; 82 83 private static final int REQUEST_UNINSTALL = 0; 84 private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; 85 86 @VisibleForTesting 87 LayoutPreference mHeaderPreference; 88 @VisibleForTesting 89 ApplicationsState mState; 90 @VisibleForTesting 91 ApplicationsState.AppEntry mAppEntry; 92 @VisibleForTesting 93 BatteryUtils mBatteryUtils; 94 95 @VisibleForTesting 96 Preference mForegroundPreference; 97 @VisibleForTesting 98 Preference mBackgroundPreference; 99 private AppButtonsPreferenceController mAppButtonsPreferenceController; 100 private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController; 101 102 private String mPackageName; 103 104 @VisibleForTesting startBatteryDetailPage(Activity caller, BatteryUtils batteryUtils, InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which, BatteryEntry entry, String usagePercent)105 static void startBatteryDetailPage(Activity caller, BatteryUtils batteryUtils, 106 InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which, 107 BatteryEntry entry, String usagePercent) { 108 // Initialize mStats if necessary. 109 helper.getStats(); 110 111 final Bundle args = new Bundle(); 112 final BatterySipper sipper = entry.sipper; 113 final BatteryStats.Uid uid = sipper.uidObj; 114 final boolean isTypeApp = sipper.drainType == BatterySipper.DrainType.APP; 115 116 final long foregroundTimeMs = isTypeApp ? batteryUtils.getProcessTimeMs( 117 BatteryUtils.StatusType.FOREGROUND, uid, which) : sipper.usageTimeMs; 118 final long backgroundTimeMs = isTypeApp ? batteryUtils.getProcessTimeMs( 119 BatteryUtils.StatusType.BACKGROUND, uid, which) : 0; 120 121 if (ArrayUtils.isEmpty(sipper.mPackages)) { 122 // populate data for system app 123 args.putString(EXTRA_LABEL, entry.getLabel()); 124 args.putInt(EXTRA_ICON_ID, entry.iconId); 125 args.putString(EXTRA_PACKAGE_NAME, null); 126 } else { 127 // populate data for normal app 128 args.putString(EXTRA_PACKAGE_NAME, entry.defaultPackageName != null 129 ? entry.defaultPackageName 130 : sipper.mPackages[0]); 131 } 132 133 args.putInt(EXTRA_UID, sipper.getUid()); 134 args.putLong(EXTRA_BACKGROUND_TIME, backgroundTimeMs); 135 args.putLong(EXTRA_FOREGROUND_TIME, foregroundTimeMs); 136 args.putString(EXTRA_POWER_USAGE_PERCENT, usagePercent); 137 args.putInt(EXTRA_POWER_USAGE_AMOUNT, (int) sipper.totalPowerMah); 138 139 new SubSettingLauncher(caller) 140 .setDestination(AdvancedPowerUsageDetail.class.getName()) 141 .setTitleRes(R.string.battery_details_title) 142 .setArguments(args) 143 .setSourceMetricsCategory(fragment.getMetricsCategory()) 144 .setUserHandle(new UserHandle(getUserIdToLaunchAdvancePowerUsageDetail(sipper))) 145 .launch(); 146 } 147 148 private static @UserIdInt getUserIdToLaunchAdvancePowerUsageDetail(BatterySipper bs)149 int getUserIdToLaunchAdvancePowerUsageDetail(BatterySipper bs) { 150 if (bs.drainType == BatterySipper.DrainType.USER) { 151 return ActivityManager.getCurrentUser(); 152 } 153 return UserHandle.getUserId(bs.getUid()); 154 } 155 startBatteryDetailPage(Activity caller, InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which, BatteryEntry entry, String usagePercent)156 public static void startBatteryDetailPage(Activity caller, 157 InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which, 158 BatteryEntry entry, String usagePercent) { 159 startBatteryDetailPage(caller, BatteryUtils.getInstance(caller), fragment, helper, which, 160 entry, usagePercent); 161 } 162 startBatteryDetailPage(Activity caller, InstrumentedPreferenceFragment fragment, String packageName)163 public static void startBatteryDetailPage(Activity caller, 164 InstrumentedPreferenceFragment fragment, String packageName) { 165 final Bundle args = new Bundle(3); 166 final PackageManager packageManager = caller.getPackageManager(); 167 args.putString(EXTRA_PACKAGE_NAME, packageName); 168 args.putString(EXTRA_POWER_USAGE_PERCENT, Utils.formatPercentage(0)); 169 try { 170 args.putInt(EXTRA_UID, packageManager.getPackageUid(packageName, 0 /* no flag */)); 171 } catch (PackageManager.NameNotFoundException e) { 172 Log.w(TAG, "Cannot find package: " + packageName, e); 173 } 174 175 new SubSettingLauncher(caller) 176 .setDestination(AdvancedPowerUsageDetail.class.getName()) 177 .setTitleRes(R.string.battery_details_title) 178 .setArguments(args) 179 .setSourceMetricsCategory(fragment.getMetricsCategory()) 180 .launch(); 181 } 182 183 @Override onAttach(Activity activity)184 public void onAttach(Activity activity) { 185 super.onAttach(activity); 186 187 mState = ApplicationsState.getInstance(getActivity().getApplication()); 188 mBatteryUtils = BatteryUtils.getInstance(getContext()); 189 } 190 191 @Override onCreate(Bundle icicle)192 public void onCreate(Bundle icicle) { 193 super.onCreate(icicle); 194 195 mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME); 196 mForegroundPreference = findPreference(KEY_PREF_FOREGROUND); 197 mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND); 198 mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER); 199 200 if (mPackageName != null) { 201 mAppEntry = mState.getEntry(mPackageName, UserHandle.myUserId()); 202 } 203 } 204 205 @Override onResume()206 public void onResume() { 207 super.onResume(); 208 209 initHeader(); 210 initPreference(); 211 } 212 213 @VisibleForTesting initHeader()214 void initHeader() { 215 final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); 216 final Activity context = getActivity(); 217 final Bundle bundle = getArguments(); 218 EntityHeaderController controller = EntityHeaderController 219 .newInstance(context, this, appSnippet) 220 .setRecyclerView(getListView(), getSettingsLifecycle()) 221 .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, 222 EntityHeaderController.ActionType.ACTION_NONE); 223 224 if (mAppEntry == null) { 225 controller.setLabel(bundle.getString(EXTRA_LABEL)); 226 227 final int iconId = bundle.getInt(EXTRA_ICON_ID, 0); 228 if (iconId == 0) { 229 controller.setIcon(context.getPackageManager().getDefaultActivityIcon()); 230 } else { 231 controller.setIcon(context.getDrawable(bundle.getInt(EXTRA_ICON_ID))); 232 } 233 } else { 234 mState.ensureIcon(mAppEntry); 235 controller.setLabel(mAppEntry); 236 controller.setIcon(mAppEntry); 237 boolean isInstantApp = AppUtils.isInstant(mAppEntry.info); 238 controller.setIsInstantApp(AppUtils.isInstant(mAppEntry.info)); 239 } 240 241 controller.done(context, true /* rebindActions */); 242 } 243 244 @VisibleForTesting initPreference()245 void initPreference() { 246 final Bundle bundle = getArguments(); 247 final Context context = getContext(); 248 249 final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME); 250 final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME); 251 mForegroundPreference.setSummary( 252 TextUtils.expandTemplate(getText(R.string.battery_used_for), 253 StringUtil.formatElapsedTime(context, foregroundTimeMs, false))); 254 mBackgroundPreference.setSummary( 255 TextUtils.expandTemplate(getText(R.string.battery_active_for), 256 StringUtil.formatElapsedTime(context, backgroundTimeMs, false))); 257 } 258 259 @Override getMetricsCategory()260 public int getMetricsCategory() { 261 return SettingsEnums.FUELGAUGE_POWER_USAGE_DETAIL; 262 } 263 264 @Override getLogTag()265 protected String getLogTag() { 266 return TAG; 267 } 268 269 @Override getPreferenceScreenResId()270 protected int getPreferenceScreenResId() { 271 return R.xml.power_usage_detail; 272 } 273 274 @Override createPreferenceControllers(Context context)275 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 276 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 277 final Bundle bundle = getArguments(); 278 final int uid = bundle.getInt(EXTRA_UID, 0); 279 final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); 280 281 mBackgroundActivityPreferenceController = new BackgroundActivityPreferenceController( 282 context, this, uid, packageName); 283 controllers.add(mBackgroundActivityPreferenceController); 284 controllers.add(new BatteryOptimizationPreferenceController( 285 (SettingsActivity) getActivity(), this, packageName)); 286 mAppButtonsPreferenceController = new AppButtonsPreferenceController( 287 (SettingsActivity) getActivity(), this, getSettingsLifecycle(), packageName, mState, 288 REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); 289 controllers.add(mAppButtonsPreferenceController); 290 291 return controllers; 292 } 293 294 @Override onActivityResult(int requestCode, int resultCode, Intent data)295 public void onActivityResult(int requestCode, int resultCode, Intent data) { 296 super.onActivityResult(requestCode, resultCode, data); 297 if (mAppButtonsPreferenceController != null) { 298 mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data); 299 } 300 } 301 302 @Override handleDialogClick(int id)303 public void handleDialogClick(int id) { 304 if (mAppButtonsPreferenceController != null) { 305 mAppButtonsPreferenceController.handleDialogClick(id); 306 } 307 } 308 309 @Override onBatteryTipHandled(BatteryTip batteryTip)310 public void onBatteryTipHandled(BatteryTip batteryTip) { 311 mBackgroundActivityPreferenceController.updateSummary( 312 findPreference(mBackgroundActivityPreferenceController.getPreferenceKey())); 313 } 314 } 315