/* * 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.fuelgauge; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.UserManager; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.fuelgauge.batterytip.AppInfo; import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; import com.android.settingslib.RestrictedPreference; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.fuelgauge.PowerWhitelistBackend; /** * Controller to control whether an app can run in the background */ public class BackgroundActivityPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin { private static final String TAG = "BgActivityPrefContr"; @VisibleForTesting static final String KEY_BACKGROUND_ACTIVITY = "background_activity"; private final AppOpsManager mAppOpsManager; private final UserManager mUserManager; private final int mUid; @VisibleForTesting DevicePolicyManager mDpm; @VisibleForTesting BatteryUtils mBatteryUtils; private InstrumentedPreferenceFragment mFragment; private String mTargetPackage; private PowerWhitelistBackend mPowerWhitelistBackend; public BackgroundActivityPreferenceController(Context context, InstrumentedPreferenceFragment fragment, int uid, String packageName) { this(context, fragment, uid, packageName, PowerWhitelistBackend.getInstance(context)); } @VisibleForTesting BackgroundActivityPreferenceController(Context context, InstrumentedPreferenceFragment fragment, int uid, String packageName, PowerWhitelistBackend backend) { super(context); mPowerWhitelistBackend = backend; mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mUid = uid; mFragment = fragment; mTargetPackage = packageName; mBatteryUtils = BatteryUtils.getInstance(context); } @Override public void updateState(Preference preference) { final RestrictedPreference restrictedPreference = (RestrictedPreference) preference; if (restrictedPreference.isDisabledByAdmin()) { // If disabled, let RestrictedPreference handle it and do nothing here return; } final int mode = mAppOpsManager .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage); if (whitelisted || mode == AppOpsManager.MODE_ERRORED || Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) { preference.setEnabled(false); } else { preference.setEnabled(true); } updateSummary(preference); } @Override public boolean isAvailable() { return mTargetPackage != null; } @Override public String getPreferenceKey() { return KEY_BACKGROUND_ACTIVITY; } @Override public boolean handlePreferenceTreeClick(Preference preference) { if (KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) { final int mode = mAppOpsManager .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); final boolean restricted = mode == AppOpsManager.MODE_IGNORED; showDialog(restricted); } return false; } public void updateSummary(Preference preference) { if (mPowerWhitelistBackend.isWhitelisted(mTargetPackage)) { preference.setSummary(R.string.background_activity_summary_whitelisted); return; } final int mode = mAppOpsManager .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); if (mode == AppOpsManager.MODE_ERRORED) { preference.setSummary(R.string.background_activity_summary_disabled); } else { final boolean restricted = mode == AppOpsManager.MODE_IGNORED; preference.setSummary(restricted ? R.string.restricted_true_label : R.string.restricted_false_label); } } @VisibleForTesting void showDialog(boolean restricted) { final AppInfo appInfo = new AppInfo.Builder() .setUid(mUid) .setPackageName(mTargetPackage) .build(); BatteryTip tip = restricted ? new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo) : new RestrictAppTip(BatteryTip.StateType.NEW, appInfo); final BatteryTipDialogFragment dialogFragment = BatteryTipDialogFragment.newInstance(tip, mFragment.getMetricsCategory()); dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */); dialogFragment.show(mFragment.getFragmentManager(), TAG); } }