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