1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.settings; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.any; 22 import static org.mockito.Mockito.doNothing; 23 import static org.mockito.Mockito.doReturn; 24 import static org.mockito.Mockito.eq; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.never; 27 import static org.mockito.Mockito.spy; 28 import static org.mockito.Mockito.times; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.when; 31 32 import android.accounts.Account; 33 import android.accounts.AccountManager; 34 import android.app.Activity; 35 import android.content.ComponentName; 36 import android.content.ContentResolver; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.pm.ActivityInfo; 40 import android.content.pm.PackageManager; 41 import android.content.pm.ResolveInfo; 42 import android.os.UserManager; 43 import android.provider.Settings; 44 import android.view.LayoutInflater; 45 import android.view.View; 46 import android.view.ViewTreeObserver; 47 import android.widget.CheckBox; 48 import android.widget.LinearLayout; 49 import android.widget.ScrollView; 50 51 import androidx.fragment.app.FragmentActivity; 52 53 import com.android.settings.testutils.shadow.ShadowUtils; 54 import com.android.settingslib.development.DevelopmentSettingsEnabler; 55 56 import org.junit.After; 57 import org.junit.Before; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 import org.mockito.ArgumentCaptor; 61 import org.mockito.Mock; 62 import org.mockito.MockitoAnnotations; 63 import org.robolectric.Robolectric; 64 import org.robolectric.RobolectricTestRunner; 65 import org.robolectric.Shadows; 66 import org.robolectric.annotation.Config; 67 import org.robolectric.shadows.ShadowActivity; 68 import org.robolectric.shadows.ShadowUserManager; 69 70 @RunWith(RobolectricTestRunner.class) 71 @Config(shadows = ShadowUtils.class) 72 public class MasterClearTest { 73 74 private static final String TEST_ACCOUNT_TYPE = "android.test.account.type"; 75 private static final String TEST_CONFIRMATION_PACKAGE = "android.test.conf.pkg"; 76 private static final String TEST_CONFIRMATION_CLASS = "android.test.conf.pkg.ConfActivity"; 77 private static final String TEST_ACCOUNT_NAME = "test@example.com"; 78 79 @Mock 80 private ScrollView mScrollView; 81 @Mock 82 private LinearLayout mLinearLayout; 83 84 @Mock 85 private PackageManager mPackageManager; 86 87 @Mock 88 private AccountManager mAccountManager; 89 90 @Mock 91 private FragmentActivity mMockActivity; 92 93 @Mock 94 private Intent mMockIntent; 95 96 private MasterClear mMasterClear; 97 private ShadowActivity mShadowActivity; 98 private FragmentActivity mActivity; 99 private ShadowUserManager mShadowUserManager; 100 private View mContentView; 101 102 @Before setUp()103 public void setUp() { 104 MockitoAnnotations.initMocks(this); 105 mMasterClear = spy(new MasterClear()); 106 mActivity = Robolectric.setupActivity(FragmentActivity.class); 107 mShadowActivity = Shadows.shadowOf(mActivity); 108 UserManager userManager = mActivity.getSystemService(UserManager.class); 109 mShadowUserManager = Shadows.shadowOf(userManager); 110 mShadowUserManager.setIsAdminUser(true); 111 mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null); 112 113 // Make scrollView only have one child 114 when(mScrollView.getChildAt(0)).thenReturn(mLinearLayout); 115 when(mScrollView.getChildCount()).thenReturn(1); 116 } 117 118 @After tearDown()119 public void tearDown() { 120 mShadowUserManager.setIsAdminUser(false); 121 } 122 123 @Test testShowFinalConfirmation_eraseEsimVisible_eraseEsimChecked()124 public void testShowFinalConfirmation_eraseEsimVisible_eraseEsimChecked() { 125 final Context context = mock(Context.class); 126 when(mMasterClear.getContext()).thenReturn(context); 127 128 mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); 129 mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); 130 mMasterClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); 131 mMasterClear.mEsimStorageContainer.setVisibility(View.VISIBLE); 132 mMasterClear.mEsimStorage.setChecked(true); 133 mMasterClear.showFinalConfirmation(); 134 135 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 136 137 verify(context).startActivity(intent.capture()); 138 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 139 .getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false)) 140 .isTrue(); 141 } 142 143 @Test testShowFinalConfirmation_eraseEsimVisible_eraseEsimUnchecked()144 public void testShowFinalConfirmation_eraseEsimVisible_eraseEsimUnchecked() { 145 final Context context = mock(Context.class); 146 when(mMasterClear.getContext()).thenReturn(context); 147 148 mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); 149 mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); 150 mMasterClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); 151 mMasterClear.mEsimStorageContainer.setVisibility(View.VISIBLE); 152 mMasterClear.mEsimStorage.setChecked(false); 153 mMasterClear.showFinalConfirmation(); 154 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 155 156 verify(context).startActivity(intent.capture()); 157 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 158 .getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false)) 159 .isFalse(); 160 } 161 162 @Test testShowFinalConfirmation_eraseEsimGone_eraseEsimChecked()163 public void testShowFinalConfirmation_eraseEsimGone_eraseEsimChecked() { 164 final Context context = mock(Context.class); 165 when(mMasterClear.getContext()).thenReturn(context); 166 167 mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); 168 mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); 169 mMasterClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); 170 mMasterClear.mEsimStorageContainer.setVisibility(View.GONE); 171 mMasterClear.mEsimStorage.setChecked(true); 172 mMasterClear.showFinalConfirmation(); 173 174 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 175 176 verify(context).startActivity(intent.capture()); 177 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 178 .getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false)) 179 .isTrue(); 180 } 181 182 @Test testShowFinalConfirmation_eraseEsimGone_eraseEsimUnchecked()183 public void testShowFinalConfirmation_eraseEsimGone_eraseEsimUnchecked() { 184 final Context context = mock(Context.class); 185 when(mMasterClear.getContext()).thenReturn(context); 186 187 mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); 188 mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); 189 mMasterClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); 190 mMasterClear.mEsimStorageContainer.setVisibility(View.GONE); 191 mMasterClear.mEsimStorage.setChecked(false); 192 mMasterClear.showFinalConfirmation(); 193 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 194 195 verify(context).startActivity(intent.capture()); 196 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 197 .getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false)) 198 .isFalse(); 199 } 200 201 @Test testShowWipeEuicc_euiccDisabled()202 public void testShowWipeEuicc_euiccDisabled() { 203 prepareEuiccState( 204 false /* isEuiccEnabled */, 205 true /* isEuiccProvisioned */, 206 false /* isDeveloper */); 207 assertThat(mMasterClear.showWipeEuicc()).isFalse(); 208 } 209 210 @Test testShowWipeEuicc_euiccEnabled_unprovisioned()211 public void testShowWipeEuicc_euiccEnabled_unprovisioned() { 212 prepareEuiccState( 213 true /* isEuiccEnabled */, 214 false /* isEuiccProvisioned */, 215 false /* isDeveloper */); 216 assertThat(mMasterClear.showWipeEuicc()).isFalse(); 217 } 218 219 @Test testShowWipeEuicc_euiccEnabled_provisioned()220 public void testShowWipeEuicc_euiccEnabled_provisioned() { 221 prepareEuiccState( 222 true /* isEuiccEnabled */, 223 true /* isEuiccProvisioned */, 224 false /* isDeveloper */); 225 assertThat(mMasterClear.showWipeEuicc()).isTrue(); 226 } 227 228 @Test testShowWipeEuicc_developerMode_unprovisioned()229 public void testShowWipeEuicc_developerMode_unprovisioned() { 230 prepareEuiccState( 231 true /* isEuiccEnabled */, 232 false /* isEuiccProvisioned */, 233 true /* isDeveloper */); 234 assertThat(mMasterClear.showWipeEuicc()).isTrue(); 235 } 236 237 @Test testEsimRecheckBoxDefaultChecked()238 public void testEsimRecheckBoxDefaultChecked() { 239 assertThat(((CheckBox) mContentView.findViewById(R.id.erase_esim)).isChecked()).isTrue(); 240 } 241 242 @Test testHasReachedBottom_NotScrollDown_returnFalse()243 public void testHasReachedBottom_NotScrollDown_returnFalse() { 244 initScrollView(100, 0, 200); 245 246 assertThat(mMasterClear.hasReachedBottom(mScrollView)).isFalse(); 247 } 248 249 @Test testHasReachedBottom_CanNotScroll_returnTrue()250 public void testHasReachedBottom_CanNotScroll_returnTrue() { 251 initScrollView(100, 0, 80); 252 253 assertThat(mMasterClear.hasReachedBottom(mScrollView)).isTrue(); 254 } 255 256 @Test testHasReachedBottom_ScrollToBottom_returnTrue()257 public void testHasReachedBottom_ScrollToBottom_returnTrue() { 258 initScrollView(100, 100, 200); 259 260 assertThat(mMasterClear.hasReachedBottom(mScrollView)).isTrue(); 261 } 262 263 @Test testInitiateMasterClear_inDemoMode_sendsIntent()264 public void testInitiateMasterClear_inDemoMode_sendsIntent() { 265 ShadowUtils.setIsDemoUser(true); 266 267 final ComponentName componentName = 268 ComponentName.unflattenFromString("com.android.retaildemo/.DeviceAdminReceiver"); 269 ShadowUtils.setDeviceOwnerComponent(componentName); 270 271 mMasterClear.mInitiateListener.onClick(mContentView); 272 final Intent intent = mShadowActivity.getNextStartedActivity(); 273 assertThat(Intent.ACTION_FACTORY_RESET).isEqualTo(intent.getAction()); 274 assertThat(componentName).isNotNull(); 275 assertThat(componentName.getPackageName()).isEqualTo(intent.getPackage()); 276 } 277 278 @Test testOnActivityResultInternal_invalideRequest()279 public void testOnActivityResultInternal_invalideRequest() { 280 int invalidRequestCode = -1; 281 doReturn(false).when(mMasterClear).isValidRequestCode(eq(invalidRequestCode)); 282 283 mMasterClear.onActivityResultInternal(invalidRequestCode, Activity.RESULT_OK, null); 284 285 verify(mMasterClear, times(1)).isValidRequestCode(eq(invalidRequestCode)); 286 verify(mMasterClear, times(0)).establishInitialState(); 287 verify(mMasterClear, times(0)).getAccountConfirmationIntent(); 288 verify(mMasterClear, times(0)).showFinalConfirmation(); 289 } 290 291 @Test testOnActivityResultInternal_resultCanceled()292 public void testOnActivityResultInternal_resultCanceled() { 293 doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); 294 doNothing().when(mMasterClear).establishInitialState(); 295 296 mMasterClear 297 .onActivityResultInternal(MasterClear.KEYGUARD_REQUEST, Activity.RESULT_CANCELED, 298 null); 299 300 verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); 301 verify(mMasterClear, times(1)).establishInitialState(); 302 verify(mMasterClear, times(0)).getAccountConfirmationIntent(); 303 verify(mMasterClear, times(0)).showFinalConfirmation(); 304 } 305 306 @Test testOnActivityResultInternal_keyguardRequestTriggeringConfirmAccount()307 public void testOnActivityResultInternal_keyguardRequestTriggeringConfirmAccount() { 308 doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); 309 doReturn(mMockIntent).when(mMasterClear).getAccountConfirmationIntent(); 310 doNothing().when(mMasterClear).showAccountCredentialConfirmation(eq(mMockIntent)); 311 312 mMasterClear 313 .onActivityResultInternal(MasterClear.KEYGUARD_REQUEST, Activity.RESULT_OK, null); 314 315 verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); 316 verify(mMasterClear, times(0)).establishInitialState(); 317 verify(mMasterClear, times(1)).getAccountConfirmationIntent(); 318 verify(mMasterClear, times(1)).showAccountCredentialConfirmation(eq(mMockIntent)); 319 } 320 321 @Test testOnActivityResultInternal_keyguardRequestTriggeringShowFinal()322 public void testOnActivityResultInternal_keyguardRequestTriggeringShowFinal() { 323 doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); 324 doReturn(null).when(mMasterClear).getAccountConfirmationIntent(); 325 doNothing().when(mMasterClear).showFinalConfirmation(); 326 327 mMasterClear 328 .onActivityResultInternal(MasterClear.KEYGUARD_REQUEST, Activity.RESULT_OK, null); 329 330 verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST)); 331 verify(mMasterClear, times(0)).establishInitialState(); 332 verify(mMasterClear, times(1)).getAccountConfirmationIntent(); 333 verify(mMasterClear, times(1)).showFinalConfirmation(); 334 } 335 336 @Test testOnActivityResultInternal_confirmRequestTriggeringShowFinal()337 public void testOnActivityResultInternal_confirmRequestTriggeringShowFinal() { 338 doReturn(true).when(mMasterClear) 339 .isValidRequestCode(eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); 340 doNothing().when(mMasterClear).showFinalConfirmation(); 341 342 mMasterClear.onActivityResultInternal( 343 MasterClear.CREDENTIAL_CONFIRM_REQUEST, Activity.RESULT_OK, null); 344 345 verify(mMasterClear, times(1)) 346 .isValidRequestCode(eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); 347 verify(mMasterClear, times(0)).establishInitialState(); 348 verify(mMasterClear, times(0)).getAccountConfirmationIntent(); 349 verify(mMasterClear, times(1)).showFinalConfirmation(); 350 } 351 352 @Test testGetAccountConfirmationIntent_unsupported()353 public void testGetAccountConfirmationIntent_unsupported() { 354 when(mMasterClear.getActivity()).thenReturn(mActivity); 355 /* Using the default resources, account confirmation shouldn't trigger */ 356 assertThat(mMasterClear.getAccountConfirmationIntent()).isNull(); 357 } 358 359 @Test testGetAccountConfirmationIntent_no_relevant_accounts()360 public void testGetAccountConfirmationIntent_no_relevant_accounts() { 361 when(mMasterClear.getActivity()).thenReturn(mMockActivity); 362 when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); 363 when(mMockActivity.getString(R.string.account_confirmation_package)) 364 .thenReturn(TEST_CONFIRMATION_PACKAGE); 365 when(mMockActivity.getString(R.string.account_confirmation_class)) 366 .thenReturn(TEST_CONFIRMATION_CLASS); 367 368 Account[] accounts = new Account[0]; 369 when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); 370 when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); 371 assertThat(mMasterClear.getAccountConfirmationIntent()).isNull(); 372 } 373 374 @Test testGetAccountConfirmationIntent_unresolved()375 public void testGetAccountConfirmationIntent_unresolved() { 376 when(mMasterClear.getActivity()).thenReturn(mMockActivity); 377 when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); 378 when(mMockActivity.getString(R.string.account_confirmation_package)) 379 .thenReturn(TEST_CONFIRMATION_PACKAGE); 380 when(mMockActivity.getString(R.string.account_confirmation_class)) 381 .thenReturn(TEST_CONFIRMATION_CLASS); 382 Account[] accounts = new Account[]{new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE)}; 383 when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); 384 when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); 385 // The package manager should not resolve the confirmation intent targeting the non-existent 386 // confirmation package. 387 when(mMockActivity.getPackageManager()).thenReturn(mPackageManager); 388 assertThat(mMasterClear.getAccountConfirmationIntent()).isNull(); 389 } 390 391 @Test testTryShowAccountConfirmation_ok()392 public void testTryShowAccountConfirmation_ok() { 393 when(mMasterClear.getActivity()).thenReturn(mMockActivity); 394 // Only try to show account confirmation if the appropriate resource overlays are available. 395 when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); 396 when(mMockActivity.getString(R.string.account_confirmation_package)) 397 .thenReturn(TEST_CONFIRMATION_PACKAGE); 398 when(mMockActivity.getString(R.string.account_confirmation_class)) 399 .thenReturn(TEST_CONFIRMATION_CLASS); 400 // Add accounts to trigger the search for a resolving intent. 401 Account[] accounts = new Account[]{new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE)}; 402 when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); 403 when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); 404 // The package manager should not resolve the confirmation intent targeting the non-existent 405 // confirmation package. 406 when(mMockActivity.getPackageManager()).thenReturn(mPackageManager); 407 408 ActivityInfo activityInfo = new ActivityInfo(); 409 activityInfo.packageName = TEST_CONFIRMATION_PACKAGE; 410 ResolveInfo resolveInfo = new ResolveInfo(); 411 resolveInfo.activityInfo = activityInfo; 412 when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo); 413 414 Intent actualIntent = mMasterClear.getAccountConfirmationIntent(); 415 assertThat(TEST_CONFIRMATION_PACKAGE).isEqualTo( 416 actualIntent.getComponent().getPackageName()); 417 assertThat(TEST_CONFIRMATION_CLASS).isEqualTo(actualIntent.getComponent().getClassName()); 418 } 419 420 @Test testShowAccountCredentialConfirmation()421 public void testShowAccountCredentialConfirmation() { 422 // Finally mock out the startActivityForResultCall 423 doNothing().when(mMasterClear) 424 .startActivityForResult(eq(mMockIntent), 425 eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); 426 mMasterClear.showAccountCredentialConfirmation(mMockIntent); 427 verify(mMasterClear, times(1)) 428 .startActivityForResult(eq(mMockIntent), 429 eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); 430 } 431 432 @Test testIsValidRequestCode()433 public void testIsValidRequestCode() { 434 assertThat(mMasterClear.isValidRequestCode(MasterClear.KEYGUARD_REQUEST)).isTrue(); 435 assertThat(mMasterClear.isValidRequestCode(MasterClear.CREDENTIAL_CONFIRM_REQUEST)) 436 .isTrue(); 437 assertThat(mMasterClear.isValidRequestCode(0)).isFalse(); 438 } 439 440 @Test testOnGlobalLayout_shouldNotRemoveListener()441 public void testOnGlobalLayout_shouldNotRemoveListener() { 442 final ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class); 443 mMasterClear.mScrollView = mScrollView; 444 doNothing().when(mMasterClear).onGlobalLayout(); 445 doReturn(true).when(mMasterClear).hasReachedBottom(any()); 446 when(mScrollView.getViewTreeObserver()).thenReturn(viewTreeObserver); 447 448 mMasterClear.onGlobalLayout(); 449 450 verify(viewTreeObserver, never()).removeOnGlobalLayoutListener(mMasterClear); 451 } 452 prepareEuiccState( boolean isEuiccEnabled, boolean isEuiccProvisioned, boolean isDeveloper)453 private void prepareEuiccState( 454 boolean isEuiccEnabled, boolean isEuiccProvisioned, boolean isDeveloper) { 455 doReturn(mActivity).when(mMasterClear).getContext(); 456 doReturn(isEuiccEnabled).when(mMasterClear).isEuiccEnabled(any()); 457 ContentResolver cr = mActivity.getContentResolver(); 458 Settings.Global.putInt(cr, Settings.Global.EUICC_PROVISIONED, isEuiccProvisioned ? 1 : 0); 459 DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(mActivity, isDeveloper); 460 } 461 initScrollView(int height, int scrollY, int childBottom)462 private void initScrollView(int height, int scrollY, int childBottom) { 463 when(mScrollView.getHeight()).thenReturn(height); 464 when(mScrollView.getScrollY()).thenReturn(scrollY); 465 when(mLinearLayout.getBottom()).thenReturn(childBottom); 466 } 467 } 468