| /* |
| * Copyright (C) 2016 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; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.mockito.Mockito.any; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.accounts.Account; |
| import android.accounts.AccountManager; |
| import android.app.Activity; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.provider.Settings; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewTreeObserver; |
| import android.widget.CheckBox; |
| import android.widget.LinearLayout; |
| import android.widget.ScrollView; |
| |
| import androidx.fragment.app.FragmentActivity; |
| |
| import com.android.settings.testutils.shadow.ShadowUtils; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.robolectric.Robolectric; |
| import org.robolectric.RobolectricTestRunner; |
| import org.robolectric.Shadows; |
| import org.robolectric.annotation.Config; |
| import org.robolectric.shadows.ShadowActivity; |
| |
| @RunWith(RobolectricTestRunner.class) |
| @Config(shadows = ShadowUtils.class) |
| public class MasterClearTest { |
| |
| private static final String TEST_ACCOUNT_TYPE = "android.test.account.type"; |
| private static final String TEST_CONFIRMATION_PACKAGE = "android.test.conf.pkg"; |
| private static final String TEST_CONFIRMATION_CLASS = "android.test.conf.pkg.ConfActivity"; |
| private static final String TEST_ACCOUNT_NAME = "test@example.com"; |
| |
| @Mock |
| private ScrollView mScrollView; |
| @Mock |
| private LinearLayout mLinearLayout; |
| |
| @Mock |
| private PackageManager mPackageManager; |
| |
| @Mock |
| private AccountManager mAccountManager; |
| |
| @Mock |
| private FragmentActivity mMockActivity; |
| |
| @Mock |
| private Intent mMockIntent; |
| |
| private MasterClear mMasterClear; |
| private ShadowActivity mShadowActivity; |
| private FragmentActivity mActivity; |
| private View mContentView; |
| |
| @Before |
| public void setUp() { |
| MockitoAnnotations.initMocks(this); |
| mMasterClear = spy(new MasterClear()); |
| mActivity = Robolectric.setupActivity(FragmentActivity.class); |
| mShadowActivity = Shadows.shadowOf(mActivity); |
| mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null); |
| |
| // Make scrollView only have one child |
| when(mScrollView.getChildAt(0)).thenReturn(mLinearLayout); |
| when(mScrollView.getChildCount()).thenReturn(1); |
| } |
| |
| @Test |
| public void testShowFinalConfirmation_eraseEsimVisible_eraseEsimChecked() { |
| final Context context = mock(Context.class); |
| when(mMasterClear.getContext()).thenReturn(context); |
| |
| mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); |
| mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); |
| mMasterClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); |
| mMasterClear.mEsimStorageContainer.setVisibility(View.VISIBLE); |
| mMasterClear.mEsimStorage.setChecked(true); |
| mMasterClear.showFinalConfirmation(); |
| |
| final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); |
| |
| verify(context).startActivity(intent.capture()); |
| assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) |
| .getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false)) |
| .isTrue(); |
| } |
| |
| @Test |
| public void testShowFinalConfirmation_eraseEsimVisible_eraseEsimUnchecked() { |
| final Context context = mock(Context.class); |
| when(mMasterClear.getContext()).thenReturn(context); |
| |
| mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); |
| mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); |
| mMasterClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); |
| mMasterClear.mEsimStorageContainer.setVisibility(View.VISIBLE); |
| mMasterClear.mEsimStorage.setChecked(false); |
| mMasterClear.showFinalConfirmation(); |
| final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); |
| |
| verify(context).startActivity(intent.capture()); |
| assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) |
| .getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false)) |
| .isFalse(); |
| } |
| |
| @Test |
| public void testShowFinalConfirmation_eraseEsimGone_eraseEsimChecked() { |
| final Context context = mock(Context.class); |
| when(mMasterClear.getContext()).thenReturn(context); |
| |
| mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); |
| mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); |
| mMasterClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); |
| mMasterClear.mEsimStorageContainer.setVisibility(View.GONE); |
| mMasterClear.mEsimStorage.setChecked(true); |
| mMasterClear.showFinalConfirmation(); |
| |
| final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); |
| |
| verify(context).startActivity(intent.capture()); |
| assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) |
| .getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false)) |
| .isTrue(); |
| } |
| |
| @Test |
| public void testShowFinalConfirmation_eraseEsimGone_eraseEsimUnchecked() { |
| final Context context = mock(Context.class); |
| when(mMasterClear.getContext()).thenReturn(context); |
| |
| mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); |
| mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); |
| mMasterClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); |
| mMasterClear.mEsimStorageContainer.setVisibility(View.GONE); |
| mMasterClear.mEsimStorage.setChecked(false); |
| mMasterClear.showFinalConfirmation(); |
| final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); |
| |
| verify(context).startActivity(intent.capture()); |
| assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) |
| .getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false)) |
| .isFalse(); |
| } |
| |
| @Test |
| public void testShowWipeEuicc_euiccDisabled() { |
| prepareEuiccState( |
| false /* isEuiccEnabled */, |
| true /* isEuiccProvisioned */, |
| false /* isDeveloper */); |
| assertThat(mMasterClear.showWipeEuicc()).isFalse(); |
| } |
| |
| @Test |
| public void testShowWipeEuicc_euiccEnabled_unprovisioned() { |
| prepareEuiccState( |
| true /* isEuiccEnabled */, |
| false /* isEuiccProvisioned */, |
| false /* isDeveloper */); |
| assertThat(mMasterClear.showWipeEuicc()).isFalse(); |
| } |
| |
| @Test |
| public void testShowWipeEuicc_euiccEnabled_provisioned() { |
| prepareEuiccState( |
| true /* isEuiccEnabled */, |
| true /* isEuiccProvisioned */, |
| false /* isDeveloper */); |
| assertThat(mMasterClear.showWipeEuicc()).isTrue(); |
| } |
| |
| @Test |
| public void testShowWipeEuicc_developerMode_unprovisioned() { |
| prepareEuiccState( |
| true /* isEuiccEnabled */, |
| false /* isEuiccProvisioned */, |
| true /* isDeveloper */); |
| assertThat(mMasterClear.showWipeEuicc()).isTrue(); |
| } |
| |
| @Test |
| public void testEsimRecheckBoxDefaultChecked() { |
| assertThat(((CheckBox) mContentView.findViewById(R.id.erase_esim)).isChecked()).isTrue(); |
| } |
| |
| @Test |
| public void testHasReachedBottom_NotScrollDown_returnFalse() { |
| initScrollView(100, 0, 200); |
| |
| assertThat(mMasterClear.hasReachedBottom(mScrollView)).isFalse(); |
| } |
| |
| @Test |
| public void testHasReachedBottom_CanNotScroll_returnTrue() { |
| initScrollView(100, 0, 80); |
| |
| assertThat(mMasterClear.hasReachedBottom(mScrollView)).isTrue(); |
| } |
| |
| @Test |
| public void testHasReachedBottom_ScrollToBottom_returnTrue() { |
| initScrollView(100, 100, 200); |
| |
| assertThat(mMasterClear.hasReachedBottom(mScrollView)).isTrue(); |
| } |
| |
| @Test |
| public void testInitiateMasterClear_inDemoMode_sendsIntent() { |
| ShadowUtils.setIsDemoUser(true); |
| |
| final ComponentName componentName = |
| ComponentName.unflattenFromString("com.android.retaildemo/.DeviceAdminReceiver"); |
| ShadowUtils.setDeviceOwnerComponent(componentName); |
| |
| mMasterClear.mInitiateListener.onClick(mContentView); |
| final Intent intent = mShadowActivity.getNextStartedActivity(); |
| assertThat(Intent.ACTION_FACTORY_RESET).isEqualTo(intent.getAction()); |
| assertThat(componentName).isNotNull(); |
| assertThat(componentName.getPackageName()).isEqualTo(intent.getPackage()); |
| } |
| |
| @Test |
| public void testOnActivityResultInternal_invalideRequest() { |
| int invalidRequestCode = -1; |
| doReturn(false).when(mMasterClear).isValidRequestCode(eq(invalidRequestCode)); |
| |
| mMasterClear.onActivityResultInternal(invalidRequestCode, Activity.RESULT_OK, null); |
| |
| verify(mMasterClear, times(1)).isValidRequestCode(eq(invalidRequestCode)); |
| verify(mMasterClear, times(0)).establishInitialState(); |
| verify(mMasterClear, times(0)).getAccountConfirmationIntent(); |
| verify(mMasterClear, times(0)).showFinalConfirmation(); |
| } |
| |
| @Test |
| public void testOnActivityResultInternal_resultCanceled() { |
| doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); |
| doNothing().when(mMasterClear).establishInitialState(); |
| |
| mMasterClear |
| .onActivityResultInternal(MasterClear.KEYGUARD_REQUEST, Activity.RESULT_CANCELED, |
| null); |
| |
| verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); |
| verify(mMasterClear, times(1)).establishInitialState(); |
| verify(mMasterClear, times(0)).getAccountConfirmationIntent(); |
| verify(mMasterClear, times(0)).showFinalConfirmation(); |
| } |
| |
| @Test |
| public void testOnActivityResultInternal_keyguardRequestTriggeringConfirmAccount() { |
| doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); |
| doReturn(mMockIntent).when(mMasterClear).getAccountConfirmationIntent(); |
| doNothing().when(mMasterClear).showAccountCredentialConfirmation(eq(mMockIntent)); |
| |
| mMasterClear |
| .onActivityResultInternal(MasterClear.KEYGUARD_REQUEST, Activity.RESULT_OK, null); |
| |
| verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); |
| verify(mMasterClear, times(0)).establishInitialState(); |
| verify(mMasterClear, times(1)).getAccountConfirmationIntent(); |
| verify(mMasterClear, times(1)).showAccountCredentialConfirmation(eq(mMockIntent)); |
| } |
| |
| @Test |
| public void testOnActivityResultInternal_keyguardRequestTriggeringShowFinal() { |
| doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); |
| doReturn(null).when(mMasterClear).getAccountConfirmationIntent(); |
| doNothing().when(mMasterClear).showFinalConfirmation(); |
| |
| mMasterClear |
| .onActivityResultInternal(MasterClear.KEYGUARD_REQUEST, Activity.RESULT_OK, null); |
| |
| verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); |
| verify(mMasterClear, times(0)).establishInitialState(); |
| verify(mMasterClear, times(1)).getAccountConfirmationIntent(); |
| verify(mMasterClear, times(1)).showFinalConfirmation(); |
| } |
| |
| @Test |
| public void testOnActivityResultInternal_confirmRequestTriggeringShowFinal() { |
| doReturn(true).when(mMasterClear) |
| .isValidRequestCode(eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); |
| doNothing().when(mMasterClear).showFinalConfirmation(); |
| |
| mMasterClear.onActivityResultInternal( |
| MasterClear.CREDENTIAL_CONFIRM_REQUEST, Activity.RESULT_OK, null); |
| |
| verify(mMasterClear, times(1)) |
| .isValidRequestCode(eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); |
| verify(mMasterClear, times(0)).establishInitialState(); |
| verify(mMasterClear, times(0)).getAccountConfirmationIntent(); |
| verify(mMasterClear, times(1)).showFinalConfirmation(); |
| } |
| |
| @Test |
| public void testGetAccountConfirmationIntent_unsupported() { |
| when(mMasterClear.getActivity()).thenReturn(mActivity); |
| /* Using the default resources, account confirmation shouldn't trigger */ |
| assertThat(mMasterClear.getAccountConfirmationIntent()).isNull(); |
| } |
| |
| @Test |
| public void testGetAccountConfirmationIntent_no_relevant_accounts() { |
| when(mMasterClear.getActivity()).thenReturn(mMockActivity); |
| when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); |
| when(mMockActivity.getString(R.string.account_confirmation_package)) |
| .thenReturn(TEST_CONFIRMATION_PACKAGE); |
| when(mMockActivity.getString(R.string.account_confirmation_class)) |
| .thenReturn(TEST_CONFIRMATION_CLASS); |
| |
| Account[] accounts = new Account[0]; |
| when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); |
| when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); |
| assertThat(mMasterClear.getAccountConfirmationIntent()).isNull(); |
| } |
| |
| @Test |
| public void testGetAccountConfirmationIntent_unresolved() { |
| when(mMasterClear.getActivity()).thenReturn(mMockActivity); |
| when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); |
| when(mMockActivity.getString(R.string.account_confirmation_package)) |
| .thenReturn(TEST_CONFIRMATION_PACKAGE); |
| when(mMockActivity.getString(R.string.account_confirmation_class)) |
| .thenReturn(TEST_CONFIRMATION_CLASS); |
| Account[] accounts = new Account[]{new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE)}; |
| when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); |
| when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); |
| // The package manager should not resolve the confirmation intent targeting the non-existent |
| // confirmation package. |
| when(mMockActivity.getPackageManager()).thenReturn(mPackageManager); |
| assertThat(mMasterClear.getAccountConfirmationIntent()).isNull(); |
| } |
| |
| @Test |
| public void testTryShowAccountConfirmation_ok() { |
| when(mMasterClear.getActivity()).thenReturn(mMockActivity); |
| // Only try to show account confirmation if the appropriate resource overlays are available. |
| when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); |
| when(mMockActivity.getString(R.string.account_confirmation_package)) |
| .thenReturn(TEST_CONFIRMATION_PACKAGE); |
| when(mMockActivity.getString(R.string.account_confirmation_class)) |
| .thenReturn(TEST_CONFIRMATION_CLASS); |
| // Add accounts to trigger the search for a resolving intent. |
| Account[] accounts = new Account[]{new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE)}; |
| when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); |
| when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); |
| // The package manager should not resolve the confirmation intent targeting the non-existent |
| // confirmation package. |
| when(mMockActivity.getPackageManager()).thenReturn(mPackageManager); |
| |
| ActivityInfo activityInfo = new ActivityInfo(); |
| activityInfo.packageName = TEST_CONFIRMATION_PACKAGE; |
| ResolveInfo resolveInfo = new ResolveInfo(); |
| resolveInfo.activityInfo = activityInfo; |
| when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo); |
| |
| Intent actualIntent = mMasterClear.getAccountConfirmationIntent(); |
| assertThat(TEST_CONFIRMATION_PACKAGE).isEqualTo( |
| actualIntent.getComponent().getPackageName()); |
| assertThat(TEST_CONFIRMATION_CLASS).isEqualTo(actualIntent.getComponent().getClassName()); |
| } |
| |
| @Test |
| public void testShowAccountCredentialConfirmation() { |
| // Finally mock out the startActivityForResultCall |
| doNothing().when(mMasterClear) |
| .startActivityForResult(eq(mMockIntent), |
| eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); |
| mMasterClear.showAccountCredentialConfirmation(mMockIntent); |
| verify(mMasterClear, times(1)) |
| .startActivityForResult(eq(mMockIntent), |
| eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); |
| } |
| |
| @Test |
| public void testIsValidRequestCode() { |
| assertThat(mMasterClear.isValidRequestCode(MasterClear.KEYGUARD_REQUEST)).isTrue(); |
| assertThat(mMasterClear.isValidRequestCode(MasterClear.CREDENTIAL_CONFIRM_REQUEST)) |
| .isTrue(); |
| assertThat(mMasterClear.isValidRequestCode(0)).isFalse(); |
| } |
| |
| @Test |
| public void testOnGlobalLayout_shouldNotRemoveListener() { |
| final ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class); |
| mMasterClear.mScrollView = mScrollView; |
| doNothing().when(mMasterClear).onGlobalLayout(); |
| doReturn(true).when(mMasterClear).hasReachedBottom(any()); |
| when(mScrollView.getViewTreeObserver()).thenReturn(viewTreeObserver); |
| |
| mMasterClear.onGlobalLayout(); |
| |
| verify(viewTreeObserver, never()).removeOnGlobalLayoutListener(mMasterClear); |
| } |
| |
| private void prepareEuiccState( |
| boolean isEuiccEnabled, boolean isEuiccProvisioned, boolean isDeveloper) { |
| doReturn(mActivity).when(mMasterClear).getContext(); |
| doReturn(isEuiccEnabled).when(mMasterClear).isEuiccEnabled(any()); |
| ContentResolver cr = mActivity.getContentResolver(); |
| Settings.Global.putInt(cr, Settings.Global.EUICC_PROVISIONED, isEuiccProvisioned ? 1 : 0); |
| Settings.Global.putInt( |
| cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, isDeveloper ? 1 : 0); |
| } |
| |
| private void initScrollView(int height, int scrollY, int childBottom) { |
| when(mScrollView.getHeight()).thenReturn(height); |
| when(mScrollView.getScrollY()).thenReturn(scrollY); |
| when(mLinearLayout.getBottom()).thenReturn(childBottom); |
| } |
| } |