From a09eb383d90bb0b313518de22d5c1ca19e1b7b8c Mon Sep 17 00:00:00 2001 From: Christine Franks Date: Fri, 23 Jun 2017 14:28:21 -0700 Subject: [PATCH] Move logic for FR in demo mode out of Settings Bug: 62712426 Test: make \ ROBOTEST_FILTER="(MasterClearTest|FactoryResetPreferenceControllerTest |UtilsTest)" \ RunSettingsRoboTests -j100 Change-Id: I5e287c3d4a6a7050bda78187c52aec19e90b50bc --- src/com/android/settings/MasterClear.java | 34 ++--- .../android/settings/MasterClearConfirm.java | 1 - src/com/android/settings/Utils.java | 16 +- .../FactoryResetPreferenceController.java | 2 +- .../CarrierDemoPasswordDialogFragment.java | 144 ------------------ .../com/android/settings/MasterClearTest.java | 22 ++- .../src/com/android/settings/UtilsTest.java | 3 + .../FactoryResetPreferenceControllerTest.java | 12 +- .../testutils/shadow/ShadowUtils.java | 13 +- 9 files changed, 59 insertions(+), 188 deletions(-) delete mode 100644 src/com/android/settings/widget/CarrierDemoPasswordDialogFragment.java diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java index fe0111b2340..1bdd6f432fd 100644 --- a/src/com/android/settings/MasterClear.java +++ b/src/com/android/settings/MasterClear.java @@ -22,7 +22,7 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; import android.app.Activity; -import android.app.FragmentManager; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -38,6 +38,7 @@ import android.os.UserManager; import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.telephony.euicc.EuiccManager; +import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -54,7 +55,6 @@ import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.password.ConfirmLockPattern; -import com.android.settings.widget.CarrierDemoPasswordDialogFragment; import com.android.settingslib.RestrictedLockUtils; import java.util.List; @@ -69,8 +69,7 @@ import java.util.List; * * This is the initial screen. */ -public class MasterClear extends OptionsMenuFragment - implements CarrierDemoPasswordDialogFragment.Callback { +public class MasterClear extends OptionsMenuFragment { private static final String TAG = "MasterClear"; private static final int KEYGUARD_REQUEST = 55; @@ -137,15 +136,21 @@ public class MasterClear extends OptionsMenuFragment * If the user clicks to begin the reset sequence, we next require a * keyguard confirmation if the user has currently enabled one. If there * is no keyguard available, we simply go to the final confirmation prompt. + * + * If the user is in demo mode, route to the demo mode app for confirmation. */ - private final Button.OnClickListener mInitiateListener = new Button.OnClickListener() { + @VisibleForTesting + protected final Button.OnClickListener mInitiateListener = new Button.OnClickListener() { - public void onClick(View v) { - if ( Utils.isCarrierDemoUser(v.getContext())) { - // Require the carrier password before displaying the final confirmation. - final FragmentManager fm = getChildFragmentManager(); - if (fm != null && !fm.isDestroyed()) { - new CarrierDemoPasswordDialogFragment().show(fm, null /* tag */); + public void onClick(View view) { + final Context context = view.getContext(); + if (Utils.isDemoUser(context)) { + final String packageName = Utils.getDemoModePackageName(context); + if (!TextUtils.isEmpty(packageName)) { + final Intent requestFactoryReset = new Intent() + .setPackage(packageName) + .setAction(Intent.ACTION_FACTORY_RESET); + context.startActivity(requestFactoryReset); } } else if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) { showFinalConfirmation(); @@ -153,11 +158,6 @@ public class MasterClear extends OptionsMenuFragment } }; - @Override - public void onPasswordVerified() { - showFinalConfirmation(); - } - /** * In its initial state, the activity presents a button for the user to * click in order to initiate a confirmation sequence. This method is @@ -395,7 +395,7 @@ public class MasterClear extends OptionsMenuFragment final UserManager um = UserManager.get(context); final boolean disallow = !um.isAdminUser() || RestrictedLockUtils.hasBaseUserRestriction( context, UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId()); - if (disallow && !Utils.isCarrierDemoUser(context)) { + if (disallow && !Utils.isDemoUser(context)) { return inflater.inflate(R.layout.master_clear_disallowed_screen, null); } else if (admin != null) { View view = inflater.inflate(R.layout.admin_support_details_empty_view, null); diff --git a/src/com/android/settings/MasterClearConfirm.java b/src/com/android/settings/MasterClearConfirm.java index 7a85dbe15b8..39bf01a0d97 100644 --- a/src/com/android/settings/MasterClearConfirm.java +++ b/src/com/android/settings/MasterClearConfirm.java @@ -24,7 +24,6 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; -import android.provider.Settings; import android.service.oemlock.OemLockManager; import android.service.persistentdata.PersistentDataBlockManager; import android.view.LayoutInflater; diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index cfa8377b5f8..43106cff1bf 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -74,6 +74,7 @@ import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Profile; import android.provider.ContactsContract.RawContacts; import android.provider.Settings; +import android.provider.Settings.Secure; import android.support.annotation.StringRes; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceGroup; @@ -1271,14 +1272,13 @@ public final class Utils extends com.android.settingslib.Utils { } } - public static boolean isCarrierDemoUser(Context context) { - final String carrierDemoModeSetting = - context.getString(com.android.internal.R.string.config_carrierDemoModeSetting); - return UserManager.isDeviceInDemoMode(context) - && getUserManager(context).isDemoUser() - && !TextUtils.isEmpty(carrierDemoModeSetting) - && (Settings.Secure.getInt(context.getContentResolver(), - carrierDemoModeSetting, 0) == 1); + public static boolean isDemoUser(Context context) { + return UserManager.isDeviceInDemoMode(context) && getUserManager(context).isDemoUser(); + } + + public static String getDemoModePackageName(Context context) { + return context.getResources().getString( + com.android.internal.R.string.config_demoModePackage); } /** diff --git a/src/com/android/settings/system/FactoryResetPreferenceController.java b/src/com/android/settings/system/FactoryResetPreferenceController.java index a3cf10a5319..4379105da18 100644 --- a/src/com/android/settings/system/FactoryResetPreferenceController.java +++ b/src/com/android/settings/system/FactoryResetPreferenceController.java @@ -45,7 +45,7 @@ public class FactoryResetPreferenceController extends PreferenceController { /** Hide "Factory reset" settings for secondary users, except demo users. */ @Override public boolean isAvailable() { - return mUm.isAdminUser() || Utils.isCarrierDemoUser(mContext); + return mUm.isAdminUser() || Utils.isDemoUser(mContext); } @Override diff --git a/src/com/android/settings/widget/CarrierDemoPasswordDialogFragment.java b/src/com/android/settings/widget/CarrierDemoPasswordDialogFragment.java deleted file mode 100644 index 8482b9fb10c..00000000000 --- a/src/com/android/settings/widget/CarrierDemoPasswordDialogFragment.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2017 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.widget; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.Fragment; -import android.content.Context; -import android.content.DialogInterface; -import android.content.res.TypedArray; -import android.os.Bundle; -import android.text.Editable; -import android.text.InputType; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.Log; -import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.EditText; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.HexDump; -import com.android.settings.R; - -import com.android.settings.core.instrumentation.InstrumentedDialogFragment; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class CarrierDemoPasswordDialogFragment extends InstrumentedDialogFragment { - - private static final String TAG = "CarrierDemoPasswordDF"; - - private MessageDigest mMessageDigest; - - public CarrierDemoPasswordDialogFragment() { - try { - mMessageDigest = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - Log.e(TAG, "Unable to verify demo mode password", e); - } - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final AlertDialog dialog = new AlertDialog.Builder(getContext()) - .setPositiveButton(R.string.retail_demo_reset_next, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - final Fragment parentFragment = getParentFragment(); - if (parentFragment instanceof Callback - && which == DialogInterface.BUTTON_POSITIVE) { - ((Callback) parentFragment).onPasswordVerified(); - } - } - }) - .setNegativeButton(android.R.string.cancel, null) - .setMessage(R.string.retail_demo_reset_message) - .setTitle(R.string.retail_demo_reset_title) - .create(); - - final Context context = dialog.getContext(); - final EditText passwordField = new EditText(context); - passwordField.setSingleLine(); - passwordField.setInputType(InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_PASSWORD); - passwordField.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // no-op - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - verifyPassword(dialog, passwordField.getText().toString()); - } - - @Override - public void afterTextChanged(Editable s) { - // no-op - } - }); - - dialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialogInterface) { - verifyPassword(dialog, passwordField.getText().toString()); - passwordField.requestFocus(); - final InputMethodManager imm = (InputMethodManager) getContext().getSystemService( - Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(passwordField, InputMethodManager.SHOW_IMPLICIT); - } - }); - dialog.setCanceledOnTouchOutside(false); - - final TypedArray a = context.obtainStyledAttributes( - new int[] { android.R.attr.dialogPreferredPadding }); - final int sidePadding = a.getDimensionPixelSize(0, 0); - dialog.setView(passwordField, sidePadding, 0, sidePadding, 0); - a.recycle(); - - return dialog; - } - - private void verifyPassword(AlertDialog dialog, String input) { - final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); - if (mMessageDigest == null || TextUtils.isEmpty(input)) { - positiveButton.setEnabled(false); - return; - } - final String passwordHash = getContext().getString( - com.android.internal.R.string.config_carrierDemoModePassword); - if (passwordHash == null || TextUtils.isEmpty(passwordHash)) { - // This device does not support carrier demo mode. - return; - } - final byte[] inputDigest = mMessageDigest.digest(input.getBytes()); - final String inputHash = HexDump.toHexString(inputDigest, 0, inputDigest.length, false); - positiveButton.setEnabled(TextUtils.equals(passwordHash, inputHash)); - } - - @Override - public int getMetricsCategory() { - return MetricsEvent.CARRIER_DEMO_MODE_PASSWORD; - } - - public interface Callback { - void onPasswordVerified(); - } -} diff --git a/tests/robotests/src/com/android/settings/MasterClearTest.java b/tests/robotests/src/com/android/settings/MasterClearTest.java index 8e8578fa882..edad39164cd 100644 --- a/tests/robotests/src/com/android/settings/MasterClearTest.java +++ b/tests/robotests/src/com/android/settings/MasterClearTest.java @@ -27,6 +27,7 @@ import static org.robolectric.Shadows.shadowOf; import android.app.Activity; import android.app.Fragment; import android.content.ContentResolver; +import android.content.Intent; import android.os.Bundle; import android.provider.Settings; import android.view.LayoutInflater; @@ -36,6 +37,8 @@ import android.widget.LinearLayout; import android.widget.ScrollView; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settings.testutils.shadow.ShadowUtils; import org.junit.Before; import org.junit.Test; @@ -43,11 +46,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowActivity; - - @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class MasterClearTest { @@ -167,6 +169,22 @@ public class MasterClearTest { assertThat(mMasterClear.hasReachedBottom(mScrollView)).isTrue(); } + @Test + @Config(shadows = { ShadowUtils.class, SettingsShadowResources.class }) + public void testInitiateMasterClear_inDemoMode_sendsIntent() { + SettingsShadowResources.overrideResource( + com.android.internal.R.string.config_demoModePackage, "package"); + + ShadowUtils.setIsDemoUser(true); + + mMasterClear.mInitiateListener.onClick( + mContentView.findViewById(R.id.initiate_master_clear)); + final Intent intent = mShadowActivity.getNextStartedActivity(); + assertThat(Intent.ACTION_FACTORY_RESET).isEqualTo(intent.getAction()); + final String packageName = Utils.getDemoModePackageName(RuntimeEnvironment.application); + assertThat(packageName).isEqualTo(intent.getPackage()); + } + private void initScrollView(int height, int scrollY, int childBottom) { when(mScrollView.getHeight()).thenReturn(height); when(mScrollView.getScrollY()).thenReturn(scrollY); diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java index 93ab4063c48..eaa380c55f1 100644 --- a/tests/robotests/src/com/android/settings/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/UtilsTest.java @@ -16,15 +16,18 @@ import android.net.LinkProperties; import android.net.Network; import android.net.wifi.WifiManager; import android.os.Bundle; +import android.os.UserManager; import android.os.storage.DiskInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.format.DateUtils; import android.text.style.TtsSpan; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowSecureSettings; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java index b9c34f3f343..7310ae09eee 100644 --- a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.when; import android.accounts.AccountManager; import android.content.Context; import android.os.UserManager; -import android.provider.Settings; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; @@ -76,21 +75,16 @@ public class FactoryResetPreferenceControllerTest { @Config(shadows = { ShadowSecureSettings.class, ShadowUtils.class }) public void isAvailable_nonSystemUser() { when(mUserManager.isAdminUser()).thenReturn(false); - ShadowUtils.setIsCarrierDemoUser(false); + ShadowUtils.setIsDemoUser(false); assertThat(mController.isAvailable()).isFalse(); } @Test @Config(shadows = { ShadowSecureSettings.class, ShadowUtils.class }) - public void isAvailable_carrierDemoUser() { + public void isAvailable_demoUser() { when(mUserManager.isAdminUser()).thenReturn(false); - ShadowUtils.setIsCarrierDemoUser(true); - - final String carrierDemoModeSetting = "carrier_demo_mode"; - when(mContext.getString(com.android.internal.R.string.config_carrierDemoModeSetting)) - .thenReturn(carrierDemoModeSetting); - Settings.Secure.putInt(null, carrierDemoModeSetting, 1); + ShadowUtils.setIsDemoUser(true); assertThat(mController.isAvailable()).isTrue(); } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java index 208fae389c9..9b1e26eb86b 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java @@ -16,6 +16,7 @@ package com.android.settings.testutils.shadow; +import android.content.ComponentName; import android.content.Context; import com.android.settings.Utils; @@ -28,7 +29,7 @@ import org.robolectric.annotation.Implements; public class ShadowUtils { private static IFingerprintManager sFingerprintManager = null; - private static boolean sIsCarrierDemoUser; + private static boolean sIsDemoUser; @Implementation public static int enforceSameOwner(Context context, int userId) { @@ -46,7 +47,7 @@ public class ShadowUtils { public static void reset() { sFingerprintManager = null; - sIsCarrierDemoUser = false; + sIsDemoUser = false; } @Implementation @@ -54,12 +55,12 @@ public class ShadowUtils { return true; } - public static void setIsCarrierDemoUser(boolean isCarrierDemoUser) { - sIsCarrierDemoUser = isCarrierDemoUser; + public static void setIsDemoUser(boolean isDemoUser) { + sIsDemoUser = isDemoUser; } @Implementation - public static boolean isCarrierDemoUser(Context context) { - return sIsCarrierDemoUser; + public static boolean isDemoUser(Context context) { + return sIsDemoUser; } }