diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java index b86f2f49477..a93bfbd4fef 100644 --- a/src/com/android/settings/applications/AppInfoBase.java +++ b/src/com/android/settings/applications/AppInfoBase.java @@ -42,6 +42,7 @@ import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; @@ -62,6 +63,7 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment protected EnforcedAdmin mAppsControlDisallowedAdmin; protected boolean mAppsControlDisallowedBySystem; + protected ApplicationFeatureProvider mApplicationFeatureProvider; protected ApplicationsState mState; protected ApplicationsState.Session mSession; protected ApplicationsState.AppEntry mAppEntry; @@ -84,13 +86,14 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mFinishing = false; - - mState = ApplicationsState.getInstance(getActivity().getApplication()); + final Activity activity = getActivity(); + mApplicationFeatureProvider = FeatureFactory.getFactory(activity) + .getApplicationFeatureProvider(activity); + mState = ApplicationsState.getInstance(activity.getApplication()); mSession = mState.newSession(this); - Context context = getActivity(); - mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - mPm = context.getPackageManager(); + mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); + mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); + mPm = activity.getPackageManager(); IBinder b = ServiceManager.getService(Context.USB_SERVICE); mUsbManager = IUsbManager.Stub.asInterface(b); diff --git a/src/com/android/settings/applications/ApplicationFeatureProvider.java b/src/com/android/settings/applications/ApplicationFeatureProvider.java index bff93b8e748..eae23d11401 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProvider.java +++ b/src/com/android/settings/applications/ApplicationFeatureProvider.java @@ -24,6 +24,7 @@ import android.view.View; import com.android.settings.applications.instantapps.InstantAppButtonsController; import java.util.List; +import java.util.Set; public interface ApplicationFeatureProvider { @@ -86,6 +87,11 @@ public interface ApplicationFeatureProvider { */ List findPersistentPreferredActivities(@UserIdInt int userId, Intent[] intents); + /** + * Returns a list of package names that should be kept enabled. + */ + Set getKeepEnabledPackages(); + /** * Callback that receives the number of packages installed on the device. */ diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java index 36e0965e101..10b61c5570b 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java +++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java @@ -129,6 +129,11 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide return preferredActivities; } + @Override + public Set getKeepEnabledPackages() { + return new ArraySet<>(); + } + private static class CurrentUserAndManagedProfilePolicyInstalledAppCounter extends InstalledAppCounter { private NumberOfAppsCallback mCallback; diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 463a7e0f760..60a08efe08a 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -91,7 +91,6 @@ import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.notification.AppNotificationSettings; import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend.AppRow; -import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.AppItem; import com.android.settingslib.RestrictedLockUtils; @@ -228,7 +227,8 @@ public class InstalledAppDetails extends AppInfoBase } }; - private boolean handleDisableable(Button button) { + @VisibleForTesting + boolean handleDisableable(Button button) { boolean disableable = false; // Try to prevent the user from bricking their phone // by not allowing disabling of apps signed with the @@ -239,7 +239,8 @@ public class InstalledAppDetails extends AppInfoBase button.setText(R.string.disable_text); } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { button.setText(R.string.disable_text); - disableable = true; + disableable = !mApplicationFeatureProvider.getKeepEnabledPackages() + .contains(mAppEntry.info.packageName); } else { button.setText(R.string.enable_text); disableable = true; @@ -1214,9 +1215,7 @@ public class InstalledAppDetails extends AppInfoBase void maybeAddInstantAppButtons() { if (AppUtils.isInstant(mPackageInfo.applicationInfo)) { LayoutPreference buttons = (LayoutPreference) findPreference(KEY_INSTANT_APP_BUTTONS); - final Activity activity = getActivity(); - mInstantAppButtonsController = FeatureFactory.getFactory(activity) - .getApplicationFeatureProvider(activity) + mInstantAppButtonsController = mApplicationFeatureProvider .newInstantAppButtonsController(this, buttons.findViewById(R.id.instant_app_button_container), id -> showDialogInner(id, 0)) diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java index 4c4ec46cce6..672c5b7c03d 100644 --- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java @@ -245,6 +245,12 @@ public final class ApplicationFeatureProviderImplTest { .isEqualTo(expectedManagedUserActivities); } + @Test + public void getKeepEnabledPackages_shouldContainNothing() { + assertThat(mProvider.getKeepEnabledPackages()) + .isEmpty(); + } + private void setUpUsersAndInstalledApps() { when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList( new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN), diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java index f57bfe332bf..c2ffed1cec9 100644 --- a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java +++ b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java @@ -17,19 +17,6 @@ package com.android.settings.applications; -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Matchers.anyDouble; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - import android.app.AlertDialog; import android.app.Fragment; import android.app.admin.DevicePolicyManager; @@ -38,6 +25,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.os.BatteryStats; import android.os.UserManager; import android.support.v7.preference.Preference; @@ -56,6 +44,7 @@ import com.android.settings.applications.instantapps.InstantAppButtonsController import com.android.settings.applications.instantapps.InstantAppButtonsController.ShowDialogDelegate; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settingslib.Utils; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.StorageStatsSource.AppStorageStats; @@ -69,11 +58,26 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @@ -87,8 +91,6 @@ public final class InstalledAppDetailsTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; - @Mock - ApplicationFeatureProvider mApplicationFeatureProvider; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private UserManager mUserManager; @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -107,15 +109,17 @@ public final class InstalledAppDetailsTest { private PackageManager mPackageManager; @Mock private BatteryUtils mBatteryUtils; + private FakeFeatureFactory mFeatureFactory; private InstalledAppDetails mAppDetail; private Context mShadowContext; private Preference mBatteryPreference; + @Before public void setUp() { MockitoAnnotations.initMocks(this); - + mFeatureFactory = FakeFeatureFactory.setupForTest(mContext); mShadowContext = RuntimeEnvironment.application; mAppDetail = spy(new InstalledAppDetails()); mAppDetail.mBatteryUtils = mBatteryUtils; @@ -347,16 +351,14 @@ public final class InstalledAppDetailsTest { final InstalledAppDetailsWithMockInstantButtons fragment = new InstalledAppDetailsWithMockInstantButtons(); ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo); + ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider", + mFeatureFactory.applicationFeatureProvider); final InstantAppButtonsController buttonsController = mock(InstantAppButtonsController.class); when(buttonsController.setPackageName(nullable(String.class))) .thenReturn(buttonsController); - - FakeFeatureFactory.setupForTest(mContext); - FakeFeatureFactory factory = - (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); - when(factory.applicationFeatureProvider.newInstantAppButtonsController( + when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController( nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class))) .thenReturn(buttonsController); @@ -461,4 +463,79 @@ public final class InstalledAppDetailsTest { public void isBatteryStatsAvailable_parametersNull_returnFalse() { assertThat(mAppDetail.isBatteryStatsAvailable()).isFalse(); } + + @Test + public void handleDisableable_appIsHomeApp_buttonShouldNotWork() { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = "pkg"; + info.enabled = true; + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = info; + final HashSet homePackages = new HashSet<>(); + homePackages.add(info.packageName); + + ReflectionHelpers.setField(mAppDetail, "mHomePackages", homePackages); + ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); + final Button button = mock(Button.class); + + assertThat(mAppDetail.handleDisableable(button)).isFalse(); + verify(button).setText(R.string.disable_text); + } + + @Test + @Config(shadows = ShadowUtils.class) + public void handleDisableable_appIsEnabled_buttonShouldWork() { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = "pkg"; + info.enabled = true; + info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = info; + when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( + new HashSet<>()); + + ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider", + mFeatureFactory.applicationFeatureProvider); + ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); + final Button button = mock(Button.class); + + assertThat(mAppDetail.handleDisableable(button)).isTrue(); + verify(button).setText(R.string.disable_text); + } + + @Test + @Config(shadows = ShadowUtils.class) + public void handleDisableable_appIsEnabledAndInKeepEnabledWhitelist_buttonShouldNotWork() { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = "pkg"; + info.enabled = true; + info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = info; + + final HashSet packages = new HashSet<>(); + packages.add(info.packageName); + when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( + packages); + + ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider", + mFeatureFactory.applicationFeatureProvider); + ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); + + final Button button = mock(Button.class); + + assertThat(mAppDetail.handleDisableable(button)).isFalse(); + verify(button).setText(R.string.disable_text); + } + + @Implements(Utils.class) + public static class ShadowUtils { + @Implementation + public static boolean isSystemPackage(Resources resources, PackageManager pm, + PackageInfo pkg) { + return false; + } + } }