1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.settings.fuelgauge; 16 17 import android.app.AppOpsManager; 18 import android.app.admin.DevicePolicyManager; 19 import android.content.Context; 20 import android.os.UserManager; 21 22 import androidx.annotation.VisibleForTesting; 23 import androidx.preference.Preference; 24 25 import com.android.settings.R; 26 import com.android.settings.Utils; 27 import com.android.settings.core.InstrumentedPreferenceFragment; 28 import com.android.settings.core.PreferenceControllerMixin; 29 import com.android.settings.fuelgauge.batterytip.AppInfo; 30 import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment; 31 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; 32 import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; 33 import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; 34 import com.android.settingslib.RestrictedPreference; 35 import com.android.settingslib.core.AbstractPreferenceController; 36 import com.android.settingslib.fuelgauge.PowerWhitelistBackend; 37 38 /** 39 * Controller to control whether an app can run in the background 40 */ 41 public class BackgroundActivityPreferenceController extends AbstractPreferenceController 42 implements PreferenceControllerMixin { 43 44 private static final String TAG = "BgActivityPrefContr"; 45 @VisibleForTesting 46 static final String KEY_BACKGROUND_ACTIVITY = "background_activity"; 47 48 private final AppOpsManager mAppOpsManager; 49 private final UserManager mUserManager; 50 private final int mUid; 51 @VisibleForTesting 52 DevicePolicyManager mDpm; 53 @VisibleForTesting 54 BatteryUtils mBatteryUtils; 55 private InstrumentedPreferenceFragment mFragment; 56 private String mTargetPackage; 57 private PowerWhitelistBackend mPowerWhitelistBackend; 58 BackgroundActivityPreferenceController(Context context, InstrumentedPreferenceFragment fragment, int uid, String packageName)59 public BackgroundActivityPreferenceController(Context context, 60 InstrumentedPreferenceFragment fragment, int uid, String packageName) { 61 this(context, fragment, uid, packageName, PowerWhitelistBackend.getInstance(context)); 62 } 63 64 @VisibleForTesting BackgroundActivityPreferenceController(Context context, InstrumentedPreferenceFragment fragment, int uid, String packageName, PowerWhitelistBackend backend)65 BackgroundActivityPreferenceController(Context context, InstrumentedPreferenceFragment fragment, 66 int uid, String packageName, PowerWhitelistBackend backend) { 67 super(context); 68 mPowerWhitelistBackend = backend; 69 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 70 mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 71 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 72 mUid = uid; 73 mFragment = fragment; 74 mTargetPackage = packageName; 75 mBatteryUtils = BatteryUtils.getInstance(context); 76 } 77 78 @Override updateState(Preference preference)79 public void updateState(Preference preference) { 80 final RestrictedPreference restrictedPreference = (RestrictedPreference) preference; 81 if (restrictedPreference.isDisabledByAdmin()) { 82 // If disabled, let RestrictedPreference handle it and do nothing here 83 return; 84 } 85 final int mode = mAppOpsManager 86 .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); 87 final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage); 88 if (whitelisted || mode == AppOpsManager.MODE_ERRORED 89 || Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) { 90 preference.setEnabled(false); 91 } else { 92 preference.setEnabled(true); 93 } 94 updateSummary(preference); 95 } 96 97 @Override isAvailable()98 public boolean isAvailable() { 99 return mTargetPackage != null; 100 } 101 102 @Override getPreferenceKey()103 public String getPreferenceKey() { 104 return KEY_BACKGROUND_ACTIVITY; 105 } 106 107 @Override handlePreferenceTreeClick(Preference preference)108 public boolean handlePreferenceTreeClick(Preference preference) { 109 if (KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) { 110 final int mode = mAppOpsManager 111 .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); 112 final boolean restricted = mode == AppOpsManager.MODE_IGNORED; 113 showDialog(restricted); 114 } 115 116 return false; 117 } 118 updateSummary(Preference preference)119 public void updateSummary(Preference preference) { 120 if (mPowerWhitelistBackend.isWhitelisted(mTargetPackage)) { 121 preference.setSummary(R.string.background_activity_summary_whitelisted); 122 return; 123 } 124 final int mode = mAppOpsManager 125 .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); 126 127 if (mode == AppOpsManager.MODE_ERRORED) { 128 preference.setSummary(R.string.background_activity_summary_disabled); 129 } else { 130 final boolean restricted = mode == AppOpsManager.MODE_IGNORED; 131 preference.setSummary(restricted ? R.string.restricted_true_label 132 : R.string.restricted_false_label); 133 } 134 } 135 136 @VisibleForTesting showDialog(boolean restricted)137 void showDialog(boolean restricted) { 138 final AppInfo appInfo = new AppInfo.Builder() 139 .setUid(mUid) 140 .setPackageName(mTargetPackage) 141 .build(); 142 BatteryTip tip = restricted 143 ? new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo) 144 : new RestrictAppTip(BatteryTip.StateType.NEW, appInfo); 145 146 final BatteryTipDialogFragment dialogFragment = BatteryTipDialogFragment.newInstance(tip, 147 mFragment.getMetricsCategory()); 148 dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */); 149 dialogFragment.show(mFragment.getFragmentManager(), TAG); 150 } 151 } 152