diff --git a/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java b/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java index 1a5a285da1e..130138c376f 100644 --- a/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java @@ -16,14 +16,18 @@ package com.android.settings.applications.appinfo; +import android.app.Activity; import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; @@ -31,6 +35,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.support.annotation.VisibleForTesting; import android.support.v7.preference.PreferenceScreen; +import android.util.Log; import android.webkit.IWebViewUpdateService; import com.android.settings.R; @@ -66,6 +71,16 @@ public class AppActionButtonPreferenceController extends BasePreferenceControlle private UserManager mUserManager; private PackageManager mPm; + private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final boolean enabled = getResultCode() != Activity.RESULT_CANCELED; + Log.d(TAG, "Got broadcast response: Restart status for " + + mParent.getAppEntry().info.packageName + " " + enabled); + updateForceStopButton(enabled); + } + }; + public AppActionButtonPreferenceController(Context context, AppInfoDashboardFragment parent, String packageName) { super(context, KEY_ACTION_BUTTONS); @@ -86,7 +101,9 @@ public class AppActionButtonPreferenceController extends BasePreferenceControlle public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mActionButtons = ((ActionButtonPreference) screen.findPreference(KEY_ACTION_BUTTONS)) - .setButton2Visible(false); + .setButton2Text(R.string.force_stop) + .setButton2Positive(false) + .setButton2Enabled(false); } @Override @@ -123,6 +140,7 @@ public class AppActionButtonPreferenceController extends BasePreferenceControlle } } + checkForceStop(appEntry, packageInfo); initUninstallButtons(appEntry, packageInfo); } @@ -251,6 +269,41 @@ public class AppActionButtonPreferenceController extends BasePreferenceControlle return disableable; } + private void updateForceStopButton(boolean enabled) { + final boolean disallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction( + mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId); + mActionButtons + .setButton2Enabled(disallowedBySystem ? false : enabled) + .setButton2OnClickListener( + disallowedBySystem ? null : v -> mParent.handleForceStopButtonClick()); + } + + void checkForceStop(AppEntry appEntry, PackageInfo packageInfo) { + if (mDpm.packageHasActiveAdmins(packageInfo.packageName)) { + // User can't force stop device admin. + Log.w(TAG, "User can't force stop device admin"); + updateForceStopButton(false); + } else if (AppUtils.isInstant(packageInfo.applicationInfo)) { + updateForceStopButton(false); + mActionButtons.setButton2Visible(false); + } else if ((appEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) { + // If the app isn't explicitly stopped, then always show the + // force stop button. + Log.w(TAG, "App is not explicitly stopped"); + updateForceStopButton(true); + } else { + final Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, + Uri.fromParts("package", appEntry.info.packageName, null)); + intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { appEntry.info.packageName }); + intent.putExtra(Intent.EXTRA_UID, appEntry.info.uid); + intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(appEntry.info.uid)); + Log.d(TAG, "Sending broadcast to query restart status for " + + appEntry.info.packageName); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, + mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); + } + } + private boolean signaturesMatch(String pkg1, String pkg2) { if (pkg1 != null && pkg2 != null) { try { diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index b878bc8353a..7428e532a66 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -19,6 +19,7 @@ package com.android.settings.applications.appinfo; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; @@ -44,16 +45,20 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.DeviceAdminAdd; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.Utils; +import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.widget.EntityHeaderController; import com.android.settings.widget.PreferenceCategoryController; import com.android.settings.wrapper.DevicePolicyManagerWrapper; import com.android.settingslib.RestrictedLockUtils; @@ -85,8 +90,7 @@ public class AppInfoDashboardFragment extends DashboardFragment // Menu identifiers @VisibleForTesting static final int UNINSTALL_ALL_USERS_MENU = 1; @VisibleForTesting static final int UNINSTALL_UPDATES = 2; - static final int FORCE_STOP_MENU = 3; - static final int INSTALL_INSTANT_APP_MENU = 4; + static final int INSTALL_INSTANT_APP_MENU = 3; // Result code identifiers @VisibleForTesting @@ -101,7 +105,7 @@ public class AppInfoDashboardFragment extends DashboardFragment // Dialog identifiers used in showDialog private static final int DLG_BASE = 0; - static final int DLG_FORCE_STOP = DLG_BASE + 1; + private static final int DLG_FORCE_STOP = DLG_BASE + 1; private static final int DLG_DISABLE = DLG_BASE + 2; private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; static final int DLG_CLEAR_INSTANT_APP = DLG_BASE + 4; @@ -140,7 +144,6 @@ public class AppInfoDashboardFragment extends DashboardFragment private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; private AppActionButtonPreferenceController mAppActionButtonPreferenceController; - private ForceStopOptionsMenuController mForceStopOptionsMenuController; /** * Callback to invoke when app info has been changed. @@ -171,9 +174,6 @@ public class AppInfoDashboardFragment extends DashboardFragment startListeningToPackageRemove(); - mForceStopOptionsMenuController = - new ForceStopOptionsMenuController(activity, this /* parent */, mDpm, - mMetricsFeatureProvider, getLifecycle()); setHasOptionsMenu(true); } @@ -285,10 +285,6 @@ public class AppInfoDashboardFragment extends DashboardFragment return mPackageInfo; } - ApplicationsState getAppState() { - return mState; - } - @Override public void onPackageSizeChanged(String packageName) { if (!TextUtils.equals(packageName, mPackageName)) { @@ -488,10 +484,18 @@ public class AppInfoDashboardFragment extends DashboardFragment }) .setNegativeButton(R.string.dlg_cancel, null) .create(); - } - final AlertDialog dialog = mForceStopOptionsMenuController.createDialog(id); - if (dialog != null) { - return dialog; + case DLG_FORCE_STOP: + return new AlertDialog.Builder(getActivity()) + .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) + .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) + .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // Force stop + forceStopPackage(mAppEntry.info.packageName); + } + }) + .setNegativeButton(R.string.dlg_cancel, null) + .create(); } return mInstantAppButtonPreferenceController.createDialog(id); } @@ -508,6 +512,21 @@ public class AppInfoDashboardFragment extends DashboardFragment mDisableAfterUninstall = andDisable; } + private void forceStopPackage(String pkgName) { + mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName); + final ActivityManager am = (ActivityManager) getActivity().getSystemService( + Context.ACTIVITY_SERVICE); + Log.d(TAG, "Stopping package " + pkgName); + am.forceStopPackage(pkgName); + final int userId = UserHandle.getUserId(mAppEntry.info.uid); + mState.invalidatePackage(pkgName, userId); + final AppEntry newEnt = mState.getEntry(pkgName, userId); + if (newEnt != null) { + mAppEntry = newEnt; + } + mAppActionButtonPreferenceController.checkForceStop(mAppEntry, mPackageInfo); + } + public static void startAppInfoFragment(Class fragment, int title, Bundle args, SettingsPreferenceFragment caller, AppEntry appEntry) { // start new fragment to display extended information @@ -573,6 +592,20 @@ public class AppInfoDashboardFragment extends DashboardFragment } } + void handleForceStopButtonClick() { + if (mAppEntry == null) { + setIntentAndFinish(true, true); + return; + } + if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent( + getActivity(), mAppsControlDisallowedAdmin); + } else { + showDialogInner(DLG_FORCE_STOP, 0); + //forceStopPackage(mAppInfo.packageName); + } + } + /** Returns whether there is only one user on this device, not including the system-only user */ private boolean isSingleUser() { final int userCount = mUserManager.getUserCount(); @@ -671,7 +704,7 @@ public class AppInfoDashboardFragment extends DashboardFragment } } - void setIntentAndFinish(boolean finish, boolean appChanged) { + private void setIntentAndFinish(boolean finish, boolean appChanged) { if (localLOGV) Log.i(TAG, "appChanged="+appChanged); final Intent intent = new Intent(); intent.putExtra(ManageApplications.APP_CHG, appChanged); diff --git a/src/com/android/settings/applications/appinfo/ForceStopOptionsMenuController.java b/src/com/android/settings/applications/appinfo/ForceStopOptionsMenuController.java deleted file mode 100644 index cf8714795a2..00000000000 --- a/src/com/android/settings/applications/appinfo/ForceStopOptionsMenuController.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2018 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.applications.appinfo; - -import static com.android.settings.applications.appinfo.AppInfoDashboardFragment.FORCE_STOP_MENU; -import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; - -import android.app.Activity; -import android.app.ActivityManager; -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.net.Uri; -import android.os.UserHandle; -import android.os.UserManager; -import android.support.annotation.VisibleForTesting; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settings.R; -import com.android.settings.wrapper.DevicePolicyManagerWrapper; -import com.android.settingslib.RestrictedLockUtils; -import com.android.settingslib.applications.AppUtils; -import com.android.settingslib.applications.ApplicationsState; -import com.android.settingslib.applications.ApplicationsState.AppEntry; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.core.lifecycle.Lifecycle; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu; -import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected; -import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu; - -public class ForceStopOptionsMenuController implements LifecycleObserver, OnCreateOptionsMenu, - OnPrepareOptionsMenu, OnOptionsItemSelected { - - private static final String TAG = "ForceStopMenuController"; - - private final Context mContext; - private final AppInfoDashboardFragment mParent; - private final DevicePolicyManagerWrapper mDpm; - private final MetricsFeatureProvider mMetricsFeatureProvider; - - private int mUserId; - private MenuItem mForceStopMenu; - - private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final boolean enabled = getResultCode() != Activity.RESULT_CANCELED; - Log.d(TAG, "Got broadcast response: Restart status for " - + mParent.getAppEntry().info.packageName + " " + enabled); - enableForceStopMenu(enabled); - } - }; - - public ForceStopOptionsMenuController(Context context, AppInfoDashboardFragment parent, - DevicePolicyManagerWrapper devicePolicyManager, - MetricsFeatureProvider metricsFeatureProvider, Lifecycle lifecycle) { - mContext = context; - mParent = parent; - mDpm = devicePolicyManager; - mMetricsFeatureProvider = metricsFeatureProvider; - mUserId = UserHandle.myUserId(); - if (lifecycle != null) { - lifecycle.addObserver(this); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - menu.add(0, FORCE_STOP_MENU, 2, R.string.force_stop) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - } - - @Override - public boolean onOptionsItemSelected(MenuItem menuItem) { - if (menuItem.getItemId() == FORCE_STOP_MENU) { - handleForceStopMenuClick(); - return true; - } - return false; - } - - @Override - public void onPrepareOptionsMenu(Menu menu) { - mForceStopMenu = menu.findItem(FORCE_STOP_MENU); - updateForceStopMenu(mParent.getAppEntry(), mParent.getPackageInfo()); - } - - @VisibleForTesting - void updateForceStopMenu(AppEntry appEntry, PackageInfo packageInfo) { - boolean enabled = false; - if (mDpm.packageHasActiveAdmins(packageInfo.packageName)) { - // User can't force stop device admin. - Log.w(TAG, "User can't force stop device admin"); - } else if (AppUtils.isInstant(packageInfo.applicationInfo)) { - // No force stop for instant app - if (mForceStopMenu != null) { - mForceStopMenu.setVisible(false); - } - } else if ((appEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) { - // If the app isn't explicitly stopped, then always show the - // force stop button. - Log.w(TAG, "App is not explicitly stopped"); - enabled = true; - } else { - final Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, - Uri.fromParts("package", appEntry.info.packageName, null)); - intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { appEntry.info.packageName }); - intent.putExtra(Intent.EXTRA_UID, appEntry.info.uid); - intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(appEntry.info.uid)); - Log.d(TAG, "Sending broadcast to query restart status for " - + appEntry.info.packageName); - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, - mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); - } - enableForceStopMenu(enabled); - } - - private void enableForceStopMenu(boolean enabled) { - if (mForceStopMenu != null) { - final boolean disallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction( - mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId); - mForceStopMenu.setEnabled(disallowedBySystem ? false : enabled); - } - } - - @VisibleForTesting - void handleForceStopMenuClick() { - if (mParent.getAppEntry() == null) { - mParent.setIntentAndFinish(true, true); - return; - } - final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced( - mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId); - final boolean disallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction( - mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId); - if (admin != null && !disallowedBySystem) { - RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin); - } else { - mParent.showDialogInner(mParent.DLG_FORCE_STOP, 0); - } - } - - private void forceStopPackage(String pkgName) { - mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_APP_FORCE_STOP, pkgName); - final ActivityManager am = (ActivityManager) mContext.getSystemService( - Context.ACTIVITY_SERVICE); - Log.d(TAG, "Stopping package " + pkgName); - am.forceStopPackage(pkgName); - final int userId = UserHandle.getUserId(mParent.getAppEntry().info.uid); - final ApplicationsState appState = mParent.getAppState(); - appState.invalidatePackage(pkgName, userId); - final AppEntry newEnt = appState.getEntry(pkgName, userId); - if (newEnt != null) { - mParent.setAppEntry(newEnt); - } - } - - public AlertDialog createDialog(int id) { - if (id != mParent.DLG_FORCE_STOP) { - return null; - } - return new AlertDialog.Builder(mContext) - .setTitle(mContext.getText(R.string.force_stop_dlg_title)) - .setMessage(mContext.getText(R.string.force_stop_dlg_text)) - .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Force stop - forceStopPackage(mParent.getAppEntry().info.packageName); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - } - -} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java index 70b9cc972d8..7d5eb318026 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java @@ -18,18 +18,27 @@ package com.android.settings.applications.appinfo; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; import android.os.UserManager; import android.support.v7.preference.PreferenceScreen; @@ -111,14 +120,16 @@ public class AppActionButtonPreferenceControllerTest { } @Test - public void displayPreference_shouldSetButton2Invisible() { + public void displayPreference_shouldInitializeForceStopButton() { final PreferenceScreen screen = mock(PreferenceScreen.class); final ActionButtonPreference preference = spy(new ActionButtonPreference(mContext)); when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference); mController.displayPreference(screen); - verify(preference).setButton2Visible(false); + verify(preference).setButton2Positive(false); + verify(preference).setButton2Text(R.string.force_stop); + verify(preference).setButton2Enabled(false); } @Test @@ -127,12 +138,14 @@ public class AppActionButtonPreferenceControllerTest { final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); final ApplicationInfo info = new ApplicationInfo(); appEntry.info = info; + doNothing().when(mController).checkForceStop(appEntry, packageInfo); doNothing().when(mController).initUninstallButtons(appEntry, packageInfo); when(mFragment.getAppEntry()).thenReturn(appEntry); when(mFragment.getPackageInfo()).thenReturn(packageInfo); mController.refreshUi(); + verify(mController).checkForceStop(appEntry, packageInfo); verify(mController).initUninstallButtons(appEntry, packageInfo); } @@ -185,6 +198,71 @@ public class AppActionButtonPreferenceControllerTest { assertThat(mController.initUninstallButtonForUserApp()).isFalse(); } + // Tests that we don't show the force stop button for instant apps (they aren't allowed to run + // when they aren't in the foreground). + @Test + public void checkForceStop_instantApps_shouldNotShowForceStop() { + // Make this app appear to be instant. + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> true)); + final PackageInfo packageInfo = mock(PackageInfo.class); + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + final ApplicationInfo info = new ApplicationInfo(); + appEntry.info = info; + + mController.checkForceStop(appEntry, packageInfo); + + verify(mController.mActionButtons).setButton2Visible(false); + } + + @Test + public void checkForceStop_hasActiveAdmin_shouldDisableForceStop() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + final String packageName = "Package1"; + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = packageName; + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + when(mDevicePolicyManager.packageHasActiveAdmins(packageName)).thenReturn(true); + + mController.checkForceStop(appEntry, packageInfo); + + verify(mController.mActionButtons).setButton2Enabled(false); + } + + @Test + public void checkForceStop_appRunning_shouldEnableForceStop() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + final PackageInfo packageInfo = mock(PackageInfo.class); + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + final ApplicationInfo info = new ApplicationInfo(); + appEntry.info = info; + + mController.checkForceStop(appEntry, packageInfo); + + verify(mController.mActionButtons).setButton2Enabled(true); + } + + @Test + public void checkForceStop_appStopped_shouldQueryPackageRestart() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + final PackageInfo packageInfo = mock(PackageInfo.class); + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + final ApplicationInfo info = new ApplicationInfo(); + appEntry.info = info; + info.flags = ApplicationInfo.FLAG_STOPPED; + info.packageName = "com.android.setting"; + + mController.checkForceStop(appEntry, packageInfo); + + verify(mContext).sendOrderedBroadcastAsUser(argThat(intent-> intent != null + && intent.getAction().equals(Intent.ACTION_QUERY_PACKAGE_RESTART)), + any(UserHandle.class), nullable(String.class), any(BroadcastReceiver.class), + nullable(Handler.class), anyInt(), nullable(String.class), nullable(Bundle.class)); + } + @Test public void handleDisableable_appIsHomeApp_buttonShouldNotWork() { final ApplicationInfo info = new ApplicationInfo(); diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppHeaderViewPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppHeaderViewPreferenceControllerTest.java index 7108ef0820b..ee870b748bd 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppHeaderViewPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppHeaderViewPreferenceControllerTest.java @@ -42,9 +42,7 @@ import com.android.settings.R; import com.android.settings.TestConfig; import com.android.settings.applications.LayoutPreference; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; -import com.android.settingslib.applications.instantapps.InstantAppDataProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -55,7 +53,6 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.util.ReflectionHelpers; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @@ -106,8 +103,7 @@ public class AppHeaderViewPreferenceControllerTest { appEntry.info = info; when(mFragment.getAppEntry()).thenReturn(appEntry); when(mFragment.getPackageInfo()).thenReturn(packageInfo); - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); + final TextView title = mHeader.findViewById(R.id.entity_header_title); final TextView summary = mHeader.findViewById(R.id.entity_header_summary); diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/ForceStopOptionsMenuControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/ForceStopOptionsMenuControllerTest.java deleted file mode 100644 index 47190086a51..00000000000 --- a/tests/robotests/src/com/android/settings/applications/appinfo/ForceStopOptionsMenuControllerTest.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2018 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.applications.appinfo; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.os.Handler; -import android.os.UserHandle; -import android.os.UserManager; -import android.view.Menu; -import android.view.MenuItem; - -import com.android.settings.R; -import com.android.settings.SettingsActivity; -import com.android.settings.TestConfig; -import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.wrapper.DevicePolicyManagerWrapper; -import com.android.settingslib.applications.AppUtils; -import com.android.settingslib.applications.ApplicationsState.AppEntry; -import com.android.settingslib.applications.instantapps.InstantAppDataProvider; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; -import org.robolectric.util.ReflectionHelpers; - -@RunWith(SettingsRobolectricTestRunner.class) -@Config( - manifest = TestConfig.MANIFEST_PATH, - sdk = TestConfig.SDK_VERSION -) -public final class ForceStopOptionsMenuControllerTest { - - private static final String PACKAGE_NAME = "test_package_name"; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private UserManager mUserManager; - @Mock - private SettingsActivity mActivity; - @Mock - private DevicePolicyManagerWrapper mDevicePolicyManager; - @Mock - private PackageManager mPackageManager; - - private AppInfoDashboardFragment mFragment; - private ForceStopOptionsMenuController mController; - private Context mShadowContext; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mShadowContext = spy(RuntimeEnvironment.application); - mFragment = spy(new AppInfoDashboardFragment()); - ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager); - ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); - doReturn(mActivity).when(mFragment).getActivity(); - doReturn(mShadowContext).when(mFragment).getContext(); - doReturn(mPackageManager).when(mActivity).getPackageManager(); - when(mShadowContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); - mController = spy(new ForceStopOptionsMenuController( - mShadowContext, mFragment, mDevicePolicyManager, - null /* metricsFeatureProvider */, null /* lifecycle */)); - - // Default to not considering any apps to be instant (individual tests can override this). - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - } - - @Test - public void onCreateOptionsMenu_shouldAddForceStop() { - final Menu menu = mock(Menu.class); - when(menu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mock(MenuItem.class)); - - mController.onCreateOptionsMenu(menu, null /* inflater */); - - verify(menu).add(anyInt(), eq(AppInfoDashboardFragment.FORCE_STOP_MENU), anyInt(), - eq(R.string.force_stop)); - } - - @Test - public void onPrepareOptionsMenu_shouldUpdateForceStopMenu() { - final Menu menu = mock(Menu.class); - doNothing().when(mController).updateForceStopMenu(any(), any()); - doReturn(mock(AppEntry.class)).when(mFragment).getAppEntry(); - doReturn(mock(PackageInfo.class)).when(mFragment).getPackageInfo(); - - mController.onPrepareOptionsMenu(menu); - - verify(mController).updateForceStopMenu(any(), any()); - } - - @Test - public void onOptionsItemSelected_shouldHandleForceStopMenuClick() { - doReturn(mock(AppEntry.class)).when(mFragment).getAppEntry(); - doNothing().when(mController).handleForceStopMenuClick(); - final MenuItem menu = mock(MenuItem.class); - when(menu.getItemId()).thenReturn(AppInfoDashboardFragment.FORCE_STOP_MENU); - - mController.onOptionsItemSelected(menu); - - verify(mController).handleForceStopMenuClick(); - } - - // Tests that we don't show the force stop button for instant apps (they aren't allowed to run - // when they aren't in the foreground). - @Test - public void updateForceStopMenu_instantApps_shouldNotShowForceStop() { - when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); - final MenuItem forceStopMenu = mock(MenuItem.class); - ReflectionHelpers.setField(mController, "mForceStopMenu", forceStopMenu); - // Make this app appear to be instant. - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - final PackageInfo packageInfo = mock(PackageInfo.class); - final AppEntry appEntry = mock(AppEntry.class); - final ApplicationInfo info = new ApplicationInfo(); - appEntry.info = info; - - mController.updateForceStopMenu(appEntry, packageInfo); - - verify(forceStopMenu).setVisible(false); - } - - @Test - public void updateForceStopMenu_hasActiveAdmin_shouldDisableForceStop() { - when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); - final MenuItem forceStopMenu = mock(MenuItem.class); - ReflectionHelpers.setField(mController, "mForceStopMenu", forceStopMenu); - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - final String packageName = "Package1"; - final PackageInfo packageInfo = new PackageInfo(); - packageInfo.packageName = packageName; - final AppEntry appEntry = mock(AppEntry.class); - when(mDevicePolicyManager.packageHasActiveAdmins(packageName)).thenReturn(true); - - mController.updateForceStopMenu(appEntry, packageInfo); - - verify(forceStopMenu).setEnabled(false); - } - - @Test - public void updateForceStopMenu_appRunning_shouldEnableForceStop() { - when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); - final MenuItem forceStopMenu = mock(MenuItem.class); - ReflectionHelpers.setField(mController, "mForceStopMenu", forceStopMenu); - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - final PackageInfo packageInfo = mock(PackageInfo.class); - final AppEntry appEntry = mock(AppEntry.class); - final ApplicationInfo info = new ApplicationInfo(); - appEntry.info = info; - - mController.updateForceStopMenu(appEntry, packageInfo); - - verify(forceStopMenu).setEnabled(true); - } - - @Test - public void updateForceStopMenu_appStopped_shouldQueryPackageRestart() { - when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - final PackageInfo packageInfo = mock(PackageInfo.class); - final AppEntry appEntry = mock(AppEntry.class); - final ApplicationInfo info = new ApplicationInfo(); - appEntry.info = info; - info.flags = ApplicationInfo.FLAG_STOPPED; - info.packageName = "com.android.setting"; - - mController.updateForceStopMenu(appEntry, packageInfo); - - verify(mShadowContext).sendOrderedBroadcastAsUser(argThat(intent-> intent != null - && intent.getAction().equals(Intent.ACTION_QUERY_PACKAGE_RESTART)), - any(UserHandle.class), nullable(String.class), any(BroadcastReceiver.class), - nullable(Handler.class), anyInt(), nullable(String.class), nullable(Bundle.class)); - } - -}