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