1 /* 2 * Copyright (C) 2015 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.app.AppOpsManager; 20 import android.app.Dialog; 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.DialogInterface.OnClickListener; 25 import android.content.pm.PackageManager; 26 import android.content.pm.PackageManager.NameNotFoundException; 27 import android.os.Bundle; 28 import android.view.View; 29 import android.widget.Checkable; 30 import android.widget.TextView; 31 32 import androidx.annotation.VisibleForTesting; 33 import androidx.appcompat.app.AlertDialog; 34 import androidx.fragment.app.Fragment; 35 36 import com.android.settings.R; 37 import com.android.settings.applications.AppInfoBase; 38 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 39 import com.android.settings.overlay.FeatureFactory; 40 import com.android.settingslib.applications.ApplicationsState.AppEntry; 41 import com.android.settingslib.fuelgauge.PowerWhitelistBackend; 42 43 public class HighPowerDetail extends InstrumentedDialogFragment implements OnClickListener, 44 View.OnClickListener { 45 46 private static final String ARG_DEFAULT_ON = "default_on"; 47 48 @VisibleForTesting 49 PowerWhitelistBackend mBackend; 50 @VisibleForTesting 51 BatteryUtils mBatteryUtils; 52 @VisibleForTesting 53 String mPackageName; 54 @VisibleForTesting 55 int mPackageUid; 56 private CharSequence mLabel; 57 private boolean mDefaultOn; 58 @VisibleForTesting 59 boolean mIsEnabled; 60 private Checkable mOptionOn; 61 private Checkable mOptionOff; 62 63 @Override getMetricsCategory()64 public int getMetricsCategory() { 65 return SettingsEnums.DIALOG_HIGH_POWER_DETAILS; 66 } 67 68 @Override onCreate(Bundle savedInstanceState)69 public void onCreate(Bundle savedInstanceState) { 70 super.onCreate(savedInstanceState); 71 final Context context = getContext(); 72 mBatteryUtils = BatteryUtils.getInstance(context); 73 mBackend = PowerWhitelistBackend.getInstance(context); 74 75 mPackageName = getArguments().getString(AppInfoBase.ARG_PACKAGE_NAME); 76 mPackageUid = getArguments().getInt(AppInfoBase.ARG_PACKAGE_UID); 77 final PackageManager pm = context.getPackageManager(); 78 try { 79 mLabel = pm.getApplicationInfo(mPackageName, 0).loadLabel(pm); 80 } catch (NameNotFoundException e) { 81 mLabel = mPackageName; 82 } 83 mDefaultOn = getArguments().getBoolean(ARG_DEFAULT_ON); 84 mIsEnabled = mDefaultOn || mBackend.isWhitelisted(mPackageName); 85 } 86 setup(View view, boolean on)87 public Checkable setup(View view, boolean on) { 88 ((TextView) view.findViewById(android.R.id.title)).setText(on 89 ? R.string.ignore_optimizations_on : R.string.ignore_optimizations_off); 90 ((TextView) view.findViewById(android.R.id.summary)).setText(on 91 ? R.string.ignore_optimizations_on_desc : R.string.ignore_optimizations_off_desc); 92 view.setClickable(true); 93 view.setOnClickListener(this); 94 if (!on && mBackend.isSysWhitelisted(mPackageName)) { 95 view.setEnabled(false); 96 } 97 return (Checkable) view; 98 } 99 100 @Override onCreateDialog(Bundle savedInstanceState)101 public Dialog onCreateDialog(Bundle savedInstanceState) { 102 AlertDialog.Builder b = new AlertDialog.Builder(getContext()) 103 .setTitle(mLabel) 104 .setNegativeButton(R.string.cancel, null) 105 .setView(R.layout.ignore_optimizations_content); 106 if (!mBackend.isSysWhitelisted(mPackageName)) { 107 b.setPositiveButton(R.string.done, this); 108 } 109 return b.create(); 110 } 111 112 @Override onStart()113 public void onStart() { 114 super.onStart(); 115 mOptionOn = setup(getDialog().findViewById(R.id.ignore_on), true); 116 mOptionOff = setup(getDialog().findViewById(R.id.ignore_off), false); 117 updateViews(); 118 } 119 updateViews()120 private void updateViews() { 121 mOptionOn.setChecked(mIsEnabled); 122 mOptionOff.setChecked(!mIsEnabled); 123 } 124 125 @Override onClick(View v)126 public void onClick(View v) { 127 if (v == mOptionOn) { 128 mIsEnabled = true; 129 updateViews(); 130 } else if (v == mOptionOff) { 131 mIsEnabled = false; 132 updateViews(); 133 } 134 } 135 136 @Override onClick(DialogInterface dialog, int which)137 public void onClick(DialogInterface dialog, int which) { 138 if (which == DialogInterface.BUTTON_POSITIVE) { 139 boolean newValue = mIsEnabled; 140 boolean oldValue = mBackend.isWhitelisted(mPackageName); 141 if (newValue != oldValue) { 142 logSpecialPermissionChange(newValue, mPackageName, getContext()); 143 if (newValue) { 144 mBatteryUtils.setForceAppStandby(mPackageUid, mPackageName, 145 AppOpsManager.MODE_ALLOWED); 146 mBackend.addApp(mPackageName); 147 } else { 148 mBackend.removeApp(mPackageName); 149 } 150 } 151 } 152 } 153 154 @VisibleForTesting logSpecialPermissionChange(boolean whitelist, String packageName, Context context)155 static void logSpecialPermissionChange(boolean whitelist, String packageName, Context context) { 156 int logCategory = whitelist ? SettingsEnums.APP_SPECIAL_PERMISSION_BATTERY_DENY 157 : SettingsEnums.APP_SPECIAL_PERMISSION_BATTERY_ALLOW; 158 FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context, logCategory, 159 packageName); 160 } 161 162 @Override onDismiss(DialogInterface dialog)163 public void onDismiss(DialogInterface dialog) { 164 super.onDismiss(dialog); 165 Fragment target = getTargetFragment(); 166 if (target != null && target.getActivity() != null) { 167 target.onActivityResult(getTargetRequestCode(), 0, null); 168 } 169 } 170 getSummary(Context context, AppEntry entry)171 public static CharSequence getSummary(Context context, AppEntry entry) { 172 return getSummary(context, entry.info.packageName); 173 } 174 getSummary(Context context, String pkg)175 public static CharSequence getSummary(Context context, String pkg) { 176 return getSummary(context, PowerWhitelistBackend.getInstance(context), pkg); 177 } 178 179 @VisibleForTesting getSummary(Context context, PowerWhitelistBackend powerWhitelist, String pkg)180 static CharSequence getSummary(Context context, PowerWhitelistBackend powerWhitelist, 181 String pkg) { 182 return context.getString( 183 powerWhitelist.isSysWhitelisted(pkg) || powerWhitelist.isDefaultActiveApp(pkg) 184 ? R.string.high_power_system 185 : powerWhitelist.isWhitelisted(pkg) 186 ? R.string.high_power_on 187 : R.string.high_power_off); 188 } 189 show(Fragment caller, int uid, String packageName, int requestCode)190 public static void show(Fragment caller, int uid, String packageName, int requestCode) { 191 HighPowerDetail fragment = new HighPowerDetail(); 192 Bundle args = new Bundle(); 193 args.putString(AppInfoBase.ARG_PACKAGE_NAME, packageName); 194 args.putInt(AppInfoBase.ARG_PACKAGE_UID, uid); 195 fragment.setArguments(args); 196 fragment.setTargetFragment(caller, requestCode); 197 fragment.show(caller.getFragmentManager(), HighPowerDetail.class.getSimpleName()); 198 } 199 } 200