Move force stop button into option menu.

- only show 1 button in the app info page
- move all handling for the force stop button from
AppActionButtonPreferenceController to a new option menu controller

Bug: 71778950
Test: make RunSettingsRoboTests
Change-Id: Iaa2c9784162a5f1acaaf9e11e3ce988945b40538
This commit is contained in:
Doris Ling
2018-01-19 14:43:34 -08:00
parent 35a7f0d642
commit 604a3d2248
6 changed files with 442 additions and 184 deletions

View File

@@ -16,18 +16,14 @@
package com.android.settings.applications.appinfo; package com.android.settings.applications.appinfo;
import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
@@ -35,7 +31,6 @@ import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
import android.webkit.IWebViewUpdateService; import android.webkit.IWebViewUpdateService;
import com.android.settings.R; import com.android.settings.R;
@@ -71,16 +66,6 @@ public class AppActionButtonPreferenceController extends BasePreferenceControlle
private UserManager mUserManager; private UserManager mUserManager;
private PackageManager mPm; 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, public AppActionButtonPreferenceController(Context context, AppInfoDashboardFragment parent,
String packageName) { String packageName) {
super(context, KEY_ACTION_BUTTONS); super(context, KEY_ACTION_BUTTONS);
@@ -101,9 +86,7 @@ public class AppActionButtonPreferenceController extends BasePreferenceControlle
public void displayPreference(PreferenceScreen screen) { public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen); super.displayPreference(screen);
mActionButtons = ((ActionButtonPreference) screen.findPreference(KEY_ACTION_BUTTONS)) mActionButtons = ((ActionButtonPreference) screen.findPreference(KEY_ACTION_BUTTONS))
.setButton2Text(R.string.force_stop) .setButton2Visible(false);
.setButton2Positive(false)
.setButton2Enabled(false);
} }
@Override @Override
@@ -140,7 +123,6 @@ public class AppActionButtonPreferenceController extends BasePreferenceControlle
} }
} }
checkForceStop(appEntry, packageInfo);
initUninstallButtons(appEntry, packageInfo); initUninstallButtons(appEntry, packageInfo);
} }
@@ -269,41 +251,6 @@ public class AppActionButtonPreferenceController extends BasePreferenceControlle
return disableable; 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) { private boolean signaturesMatch(String pkg1, String pkg2) {
if (pkg1 != null && pkg2 != null) { if (pkg1 != null && pkg2 != null) {
try { try {

View File

@@ -19,7 +19,6 @@ package com.android.settings.applications.appinfo;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.app.DialogFragment; import android.app.DialogFragment;
@@ -45,19 +44,15 @@ import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.DeviceAdminAdd; import com.android.settings.DeviceAdminAdd;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment; 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.applications.manageapplications.ManageApplications;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.widget.PreferenceCategoryController; import com.android.settings.widget.PreferenceCategoryController;
import com.android.settings.wrapper.DevicePolicyManagerWrapper; import com.android.settings.wrapper.DevicePolicyManagerWrapper;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
@@ -89,6 +84,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
// Menu identifiers // Menu identifiers
private static final int UNINSTALL_ALL_USERS_MENU = 1; private static final int UNINSTALL_ALL_USERS_MENU = 1;
private static final int UNINSTALL_UPDATES = 2; private static final int UNINSTALL_UPDATES = 2;
static final int FORCE_STOP_MENU = 3;
// Result code identifiers // Result code identifiers
@VisibleForTesting @VisibleForTesting
@@ -103,7 +99,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
// Dialog identifiers used in showDialog // Dialog identifiers used in showDialog
private static final int DLG_BASE = 0; private static final int DLG_BASE = 0;
private static final int DLG_FORCE_STOP = DLG_BASE + 1; static final int DLG_FORCE_STOP = DLG_BASE + 1;
private static final int DLG_DISABLE = DLG_BASE + 2; private static final int DLG_DISABLE = DLG_BASE + 2;
private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
@@ -141,6 +137,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController;
private AppActionButtonPreferenceController mAppActionButtonPreferenceController; private AppActionButtonPreferenceController mAppActionButtonPreferenceController;
private ForceStopOptionsMenuController mForceStopOptionsMenuController;
/** /**
* Callback to invoke when app info has been changed. * Callback to invoke when app info has been changed.
@@ -172,6 +169,9 @@ public class AppInfoDashboardFragment extends DashboardFragment
return; return;
} }
mForceStopOptionsMenuController =
new ForceStopOptionsMenuController(activity, this /* parent */, mDpm,
mMetricsFeatureProvider, getLifecycle());
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@@ -268,6 +268,10 @@ public class AppInfoDashboardFragment extends DashboardFragment
return mAppEntry; return mAppEntry;
} }
void setAppEntry(ApplicationsState.AppEntry appEntry) {
mAppEntry = appEntry;
}
PackageInfo getPackageInfo() { PackageInfo getPackageInfo() {
if (mAppEntry == null) { if (mAppEntry == null) {
retrieveAppEntry(); retrieveAppEntry();
@@ -275,6 +279,10 @@ public class AppInfoDashboardFragment extends DashboardFragment
return mPackageInfo; return mPackageInfo;
} }
ApplicationsState getAppState() {
return mState;
}
@Override @Override
public void onPackageSizeChanged(String packageName) { public void onPackageSizeChanged(String packageName) {
if (!TextUtils.equals(packageName, mPackageName)) { if (!TextUtils.equals(packageName, mPackageName)) {
@@ -315,6 +323,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
if (mFinishing) { if (mFinishing) {
return; return;
} }
super.onPrepareOptionsMenu(menu);
menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry));
mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES);
@@ -335,7 +344,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
uninstallPkg(mAppEntry.info.packageName, false, false); uninstallPkg(mAppEntry.info.packageName, false, false);
return true; return true;
} }
return false; return super.onOptionsItemSelected(item);
} }
@Override @Override
@@ -465,18 +474,10 @@ public class AppInfoDashboardFragment extends DashboardFragment
}) })
.setNegativeButton(R.string.dlg_cancel, null) .setNegativeButton(R.string.dlg_cancel, null)
.create(); .create();
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);
} }
}) final AlertDialog dialog = mForceStopOptionsMenuController.createDialog(id);
.setNegativeButton(R.string.dlg_cancel, null) if (dialog != null) {
.create(); return dialog;
} }
return mInstantAppButtonPreferenceController.createDialog(id); return mInstantAppButtonPreferenceController.createDialog(id);
} }
@@ -493,21 +494,6 @@ public class AppInfoDashboardFragment extends DashboardFragment
mDisableAfterUninstall = andDisable; 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, public static void startAppInfoFragment(Class<?> fragment, int title,
SettingsPreferenceFragment caller, AppEntry appEntry) { SettingsPreferenceFragment caller, AppEntry appEntry) {
// start new fragment to display extended information // start new fragment to display extended information
@@ -568,20 +554,6 @@ 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 */ /** Returns whether there is only one user on this device, not including the system-only user */
private boolean isSingleUser() { private boolean isSingleUser() {
final int userCount = mUserManager.getUserCount(); final int userCount = mUserManager.getUserCount();
@@ -679,7 +651,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
} }
} }
private void setIntentAndFinish(boolean finish, boolean appChanged) { void setIntentAndFinish(boolean finish, boolean appChanged) {
if (localLOGV) Log.i(TAG, "appChanged="+appChanged); if (localLOGV) Log.i(TAG, "appChanged="+appChanged);
final Intent intent = new Intent(); final Intent intent = new Intent();
intent.putExtra(ManageApplications.APP_CHG, appChanged); intent.putExtra(ManageApplications.APP_CHG, appChanged);

View File

@@ -0,0 +1,198 @@
/*
* 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();
}
}

View File

@@ -18,27 +18,18 @@ package com.android.settings.applications.appinfo;
import static com.google.common.truth.Truth.assertThat; 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.doNothing;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
@@ -120,16 +111,14 @@ public class AppActionButtonPreferenceControllerTest {
} }
@Test @Test
public void displayPreference_shouldInitializeForceStopButton() { public void displayPreference_shouldSetButton2Invisible() {
final PreferenceScreen screen = mock(PreferenceScreen.class); final PreferenceScreen screen = mock(PreferenceScreen.class);
final ActionButtonPreference preference = spy(new ActionButtonPreference(mContext)); final ActionButtonPreference preference = spy(new ActionButtonPreference(mContext));
when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference); when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference);
mController.displayPreference(screen); mController.displayPreference(screen);
verify(preference).setButton2Positive(false); verify(preference).setButton2Visible(false);
verify(preference).setButton2Text(R.string.force_stop);
verify(preference).setButton2Enabled(false);
} }
@Test @Test
@@ -138,14 +127,12 @@ public class AppActionButtonPreferenceControllerTest {
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
final ApplicationInfo info = new ApplicationInfo(); final ApplicationInfo info = new ApplicationInfo();
appEntry.info = info; appEntry.info = info;
doNothing().when(mController).checkForceStop(appEntry, packageInfo);
doNothing().when(mController).initUninstallButtons(appEntry, packageInfo); doNothing().when(mController).initUninstallButtons(appEntry, packageInfo);
when(mFragment.getAppEntry()).thenReturn(appEntry); when(mFragment.getAppEntry()).thenReturn(appEntry);
when(mFragment.getPackageInfo()).thenReturn(packageInfo); when(mFragment.getPackageInfo()).thenReturn(packageInfo);
mController.refreshUi(); mController.refreshUi();
verify(mController).checkForceStop(appEntry, packageInfo);
verify(mController).initUninstallButtons(appEntry, packageInfo); verify(mController).initUninstallButtons(appEntry, packageInfo);
} }
@@ -198,71 +185,6 @@ public class AppActionButtonPreferenceControllerTest {
assertThat(mController.initUninstallButtonForUserApp()).isFalse(); 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 @Test
public void handleDisableable_appIsHomeApp_buttonShouldNotWork() { public void handleDisableable_appIsHomeApp_buttonShouldNotWork() {
final ApplicationInfo info = new ApplicationInfo(); final ApplicationInfo info = new ApplicationInfo();

View File

@@ -42,7 +42,9 @@ import com.android.settings.R;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.LayoutPreference;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before; import org.junit.Before;
@@ -53,6 +55,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric; import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -103,7 +106,8 @@ public class AppHeaderViewPreferenceControllerTest {
appEntry.info = info; appEntry.info = info;
when(mFragment.getAppEntry()).thenReturn(appEntry); when(mFragment.getAppEntry()).thenReturn(appEntry);
when(mFragment.getPackageInfo()).thenReturn(packageInfo); 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 title = mHeader.findViewById(R.id.entity_header_title);
final TextView summary = mHeader.findViewById(R.id.entity_header_summary); final TextView summary = mHeader.findViewById(R.id.entity_header_summary);

View File

@@ -0,0 +1,215 @@
/*
* 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));
}
}