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:
Suprabh Shukla
2017-09-07 16:27:08 -07:00
parent 14e4964be0
commit 4c64777b2a
4 changed files with 190 additions and 85 deletions

View File

@@ -16,14 +16,25 @@
package com.android.settings.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.app.AlertDialog;
import android.app.AppOpsManager;
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.UserManager;
import android.support.v14.preference.SwitchPreference;
import android.widget.Button;
import com.android.settings.R;
import com.android.settings.TestConfig;
@@ -38,22 +49,17 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.robolectric.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowDialog;
import org.robolectric.util.FragmentTestUtil;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class BackgroundActivityPreferenceControllerTest {
private static final int UID_NORMAL = 1234;
private static final int UID_SPECIAL = 2345;
private static final int UID_LOW_SDK = 1234;
private static final int UID_HIGH_SDK = 3456;
private static final String HIGH_SDK_PACKAGE = "com.android.package.high";
private static final String LOW_SDK_PACKAGE = "com.android.package.low";
private static final String[] PACKAGES_NORMAL = {LOW_SDK_PACKAGE};
private static final String[] PACKAGES_SPECIAL = {HIGH_SDK_PACKAGE, LOW_SDK_PACKAGE};
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@@ -71,6 +77,10 @@ public class BackgroundActivityPreferenceControllerTest {
private DevicePolicyManager mDevicePolicyManager;
@Mock
private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper;
@Mock
private AdvancedPowerUsageDetail mFragment;
@Mock
private PowerWhitelistBackend mPowerWhitelistBackend;
private BackgroundActivityPreferenceController mController;
private SwitchPreference mPreference;
private Context mShadowContext;
@@ -85,19 +95,19 @@ public class BackgroundActivityPreferenceControllerTest {
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
mDevicePolicyManager);
when(mPackageManager.getPackagesForUid(UID_NORMAL)).thenReturn(PACKAGES_NORMAL);
when(mPackageManager.getPackagesForUid(UID_SPECIAL)).thenReturn(PACKAGES_SPECIAL);
when(mPackageManager.getApplicationInfo(HIGH_SDK_PACKAGE, PackageManager.GET_META_DATA))
.thenReturn(mHighApplicationInfo);
when(mPackageManager.getApplicationInfo(LOW_SDK_PACKAGE, PackageManager.GET_META_DATA))
.thenReturn(mLowApplicationInfo);
when(mPowerWhitelistBackend.isWhitelisted(LOW_SDK_PACKAGE)).thenReturn(false);
mHighApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
mLowApplicationInfo.targetSdkVersion = Build.VERSION_CODES.L;
mPreference = new SwitchPreference(mShadowContext);
mController = spy(new BackgroundActivityPreferenceController(mContext, UID_NORMAL));
mController.isAvailable();
mController = spy(new BackgroundActivityPreferenceController(
mContext, mFragment, UID_LOW_SDK, LOW_SDK_PACKAGE, mPowerWhitelistBackend));
mController.mDpm = mDevicePolicyManagerWrapper;
}
@@ -105,49 +115,66 @@ public class BackgroundActivityPreferenceControllerTest {
public void testOnPreferenceChange_TurnOnCheck_MethodInvoked() {
mController.onPreferenceChange(mPreference, true);
verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_NORMAL,
mController.getTargetPackage(), AppOpsManager.MODE_ALLOWED);
verify(mController).updateSummary(mPreference);
verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
assertThat(mPreference.getSummary())
.isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
}
@Test
public void testOnPreferenceChange_TurnOffCheck_MethodInvoked() {
mController.onPreferenceChange(mPreference, false);
verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_NORMAL,
mController.getTargetPackage(), AppOpsManager.MODE_IGNORED);
verify(mController).updateSummary(mPreference);
public void testOnPreferenceChange_TurnOnCheckHighSDK_MethodInvoked() {
mController = new BackgroundActivityPreferenceController(mContext, mFragment, UID_HIGH_SDK,
HIGH_SDK_PACKAGE, mPowerWhitelistBackend);
mController.onPreferenceChange(mPreference, true);
verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_HIGH_SDK,
HIGH_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
verify(mAppOpsManager, never()).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_HIGH_SDK,
HIGH_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
assertThat(mPreference.getSummary())
.isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
}
@Test
public void testUpdateState_CheckOn_SetCheckedTrue() {
when(mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_NORMAL, LOW_SDK_PACKAGE))
.thenReturn(AppOpsManager.MODE_DEFAULT);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
assertThat(mPreference.isEnabled()).isTrue();
verify(mController).updateSummary(mPreference);
}
@Test
public void testUpdateState_CheckOff_SetCheckedFalse() {
when(mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_NORMAL, LOW_SDK_PACKAGE))
.thenReturn(AppOpsManager.MODE_IGNORED);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
assertThat(mPreference.isEnabled()).isTrue();
verify(mController).updateSummary(mPreference);
}
@Test
public void testUpdateState_whitelisted() {
when(mPowerWhitelistBackend.isWhitelisted(LOW_SDK_PACKAGE)).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
assertThat(mPreference.isEnabled()).isFalse();
assertThat(mPreference.getSummary()).isEqualTo(
mShadowContext.getText(R.string.background_activity_summary_whitelisted));
}
@Test
public void testUpdateSummary_modeError_showSummaryDisabled() {
when(mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_NORMAL, LOW_SDK_PACKAGE))
.thenReturn(AppOpsManager.MODE_ERRORED);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ERRORED);
final CharSequence expectedSummary = mShadowContext.getText(
R.string.background_activity_summary_disabled);
mController.updateSummary(mPreference);
@@ -157,9 +184,8 @@ public class BackgroundActivityPreferenceControllerTest {
@Test
public void testUpdateSummary_modeDefault_showSummaryOn() {
when(mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_NORMAL, LOW_SDK_PACKAGE))
.thenReturn(AppOpsManager.MODE_DEFAULT);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_DEFAULT);
final CharSequence expectedSummary = mShadowContext.getText(
R.string.background_activity_summary_on);
@@ -170,9 +196,8 @@ public class BackgroundActivityPreferenceControllerTest {
@Test
public void testUpdateSummary_modeIgnored_showSummaryOff() {
when(mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_NORMAL, LOW_SDK_PACKAGE))
.thenReturn(AppOpsManager.MODE_IGNORED);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
final CharSequence expectedSummary = mShadowContext.getText(
R.string.background_activity_summary_off);
@@ -182,31 +207,30 @@ public class BackgroundActivityPreferenceControllerTest {
}
@Test
public void testIsPackageAvailable_SdkLowerThanO_ReturnTrue() {
public void testIsLegacyApp_SdkLowerThanO_ReturnTrue() {
assertThat(mController.isLegacyApp(LOW_SDK_PACKAGE)).isTrue();
}
@Test
public void testIsPackageAvailable_SdkLargerOrEqualThanO_ReturnFalse() {
public void testIsLegacyApp_SdkLargerOrEqualThanO_ReturnFalse() {
assertThat(mController.isLegacyApp(HIGH_SDK_PACKAGE)).isFalse();
}
@Test
public void testMultiplePackages_ReturnStatusForTargetPackage() {
mController = new BackgroundActivityPreferenceController(mContext, UID_SPECIAL);
mController.mDpm = mDevicePolicyManagerWrapper;
when(mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_SPECIAL, LOW_SDK_PACKAGE))
.thenReturn(AppOpsManager.MODE_ALLOWED);
when(mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_SPECIAL, HIGH_SDK_PACKAGE))
.thenReturn(AppOpsManager.MODE_IGNORED);
public void testIsAvailable_ReturnTrue() {
assertThat(mController.isAvailable()).isTrue();
}
final boolean available = mController.isAvailable();
mController.updateState(mPreference);
assertThat(available).isTrue();
// Should get status from LOW_SDK_PACKAGE
assertThat(mPreference.isChecked()).isTrue();
@Test
public void testWarningDialog() {
BackgroundActivityPreferenceController.WarningDialogFragment dialogFragment =
new BackgroundActivityPreferenceController.WarningDialogFragment();
dialogFragment.setTargetFragment(mFragment, 0);
FragmentTestUtil.startFragment(dialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
final Button okButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
shadowDialog.clickOn(okButton.getId());
verify(mFragment).onLimitBackgroundActivity();
}
}