Adding switch for Forced App Standby
Reusing the 'Background activity' switch found in App Info. The switch now needs to be shown for all apps and will toggle another app op RUN_ANY_IN_BACKGROUND which controls whether jobs or alarms are run for background apps. Also fixed handling of multiple packages with shared uid. The controller was picking the first package for uid but the order of packages can change on a reboot which would cause wrong app ops settings across packages of the same uid. Test: make -j32 RunSettingsRoboTests Bug: 65176793 Change-Id: I2a9b96bc02730776172c3ae317cb7f7f890bec30
This commit is contained in:
@@ -14,12 +14,17 @@
|
||||
|
||||
package com.android.settings.fuelgauge;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.v14.preference.SwitchPreference;
|
||||
@@ -29,6 +34,7 @@ import android.util.Log;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.enterprise.DevicePolicyManagerWrapper;
|
||||
import com.android.settings.enterprise.DevicePolicyManagerWrapperImpl;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
@@ -45,54 +51,72 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
private final PackageManager mPackageManager;
|
||||
private final AppOpsManager mAppOpsManager;
|
||||
private final UserManager mUserManager;
|
||||
private final String[] mPackages;
|
||||
private final int mUid;
|
||||
@VisibleForTesting
|
||||
DevicePolicyManagerWrapper mDpm;
|
||||
|
||||
private Fragment mFragment;
|
||||
private String mTargetPackage;
|
||||
private boolean mIsPreOApp;
|
||||
private PowerWhitelistBackend mPowerWhitelistBackend;
|
||||
|
||||
public BackgroundActivityPreferenceController(Context context, int uid) {
|
||||
public BackgroundActivityPreferenceController(Context context, Fragment fragment,
|
||||
int uid, String packageName) {
|
||||
this(context, fragment, uid, packageName, PowerWhitelistBackend.getInstance());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
BackgroundActivityPreferenceController(Context context, Fragment fragment,
|
||||
int uid, String packageName, PowerWhitelistBackend backend) {
|
||||
super(context);
|
||||
mPowerWhitelistBackend = backend;
|
||||
mPackageManager = context.getPackageManager();
|
||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
mDpm = new DevicePolicyManagerWrapperImpl(
|
||||
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE));
|
||||
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
mUid = uid;
|
||||
mPackages = mPackageManager.getPackagesForUid(mUid);
|
||||
mFragment = fragment;
|
||||
mTargetPackage = packageName;
|
||||
mIsPreOApp = isLegacyApp(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
final int mode = mAppOpsManager
|
||||
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage);
|
||||
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
|
||||
final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage);
|
||||
// Set checked or not before we may set it disabled
|
||||
if (mode != AppOpsManager.MODE_ERRORED) {
|
||||
final boolean checked = mode != AppOpsManager.MODE_IGNORED;
|
||||
final boolean checked = whitelisted || mode != AppOpsManager.MODE_IGNORED;
|
||||
((SwitchPreference) preference).setChecked(checked);
|
||||
}
|
||||
if (mode == AppOpsManager.MODE_ERRORED
|
||||
if (whitelisted || mode == AppOpsManager.MODE_ERRORED
|
||||
|| Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) {
|
||||
preference.setEnabled(false);
|
||||
} else {
|
||||
preference.setEnabled(true);
|
||||
}
|
||||
|
||||
updateSummary(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
if (mPackages == null) {
|
||||
return false;
|
||||
}
|
||||
for (final String packageName : mPackages) {
|
||||
if (isLegacyApp(packageName)) {
|
||||
mTargetPackage = packageName;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return mTargetPackage != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
/**
|
||||
* Called from the warning dialog, if the user decides to go ahead and disable background
|
||||
* activity for this package
|
||||
*/
|
||||
public void setUnchecked(Preference preference) {
|
||||
if (mIsPreOApp) {
|
||||
mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage,
|
||||
AppOpsManager.MODE_IGNORED);
|
||||
}
|
||||
mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage,
|
||||
AppOpsManager.MODE_IGNORED);
|
||||
((SwitchPreference) preference).setChecked(false);
|
||||
updateSummary(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,19 +126,23 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
boolean switchOn = (Boolean) newValue;
|
||||
mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage,
|
||||
switchOn ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
|
||||
|
||||
final boolean switchOn = (Boolean) newValue;
|
||||
if (!switchOn) {
|
||||
final WarningDialogFragment dialogFragment = new WarningDialogFragment();
|
||||
dialogFragment.setTargetFragment(mFragment, 0);
|
||||
dialogFragment.show(mFragment.getFragmentManager(), TAG);
|
||||
return false;
|
||||
}
|
||||
if (mIsPreOApp) {
|
||||
mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage,
|
||||
AppOpsManager.MODE_ALLOWED);
|
||||
}
|
||||
mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage,
|
||||
AppOpsManager.MODE_ALLOWED);
|
||||
updateSummary(preference);
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String getTargetPackage() {
|
||||
return mTargetPackage;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isLegacyApp(final String packageName) {
|
||||
try {
|
||||
@@ -131,8 +159,12 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
|
||||
@VisibleForTesting
|
||||
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_IN_BACKGROUND, mUid, mTargetPackage);
|
||||
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
|
||||
|
||||
if (mode == AppOpsManager.MODE_ERRORED) {
|
||||
preference.setSummary(R.string.background_activity_summary_disabled);
|
||||
@@ -142,4 +174,37 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
: R.string.background_activity_summary_off);
|
||||
}
|
||||
}
|
||||
|
||||
interface WarningConfirmationListener {
|
||||
void onLimitBackgroundActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning dialog to show to the user as turning off background activity can lead to
|
||||
* apps misbehaving as their background task scheduling guarantees will no longer be honored.
|
||||
*/
|
||||
public static class WarningDialogFragment extends InstrumentedDialogFragment {
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
// TODO (b/65494831): add metric id
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final WarningConfirmationListener listener =
|
||||
(WarningConfirmationListener) getTargetFragment();
|
||||
return new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.background_activity_warning_dialog_title)
|
||||
.setMessage(R.string.background_activity_warning_dialog_text)
|
||||
.setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
listener.onLimitBackgroundActivity();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.dlg_cancel, null)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user