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 MainClearTest { 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 MainClear mMainClear; 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 mMainClear = spy(new MainClear() { 106 @Override 107 boolean showAnySubscriptionInfo(Context context) { return true; } 108 }); 109 mActivity = Robolectric.setupActivity(FragmentActivity.class); 110 mShadowActivity = Shadows.shadowOf(mActivity); 111 UserManager userManager = mActivity.getSystemService(UserManager.class); 112 mShadowUserManager = Shadows.shadowOf(userManager); 113 mShadowUserManager.setIsAdminUser(true); 114 mContentView = LayoutInflater.from(mActivity).inflate(R.layout.main_clear, null); 115 116 // Make scrollView only have one child 117 when(mScrollView.getChildAt(0)).thenReturn(mLinearLayout); 118 when(mScrollView.getChildCount()).thenReturn(1); 119 } 120 121 @After tearDown()122 public void tearDown() { 123 mShadowUserManager.setIsAdminUser(false); 124 } 125 126 @Test testShowFinalConfirmation_eraseEsimVisible_eraseEsimChecked()127 public void testShowFinalConfirmation_eraseEsimVisible_eraseEsimChecked() { 128 final Context context = mock(Context.class); 129 when(mMainClear.getContext()).thenReturn(context); 130 131 mMainClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); 132 mMainClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); 133 mMainClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); 134 mMainClear.mEsimStorageContainer.setVisibility(View.VISIBLE); 135 mMainClear.mEsimStorage.setChecked(true); 136 mMainClear.showFinalConfirmation(); 137 138 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 139 140 verify(context).startActivity(intent.capture()); 141 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 142 .getBoolean(MainClear.ERASE_ESIMS_EXTRA, false)) 143 .isTrue(); 144 } 145 146 @Test testShowFinalConfirmation_eraseEsimVisible_eraseEsimUnchecked()147 public void testShowFinalConfirmation_eraseEsimVisible_eraseEsimUnchecked() { 148 final Context context = mock(Context.class); 149 when(mMainClear.getContext()).thenReturn(context); 150 151 mMainClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); 152 mMainClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); 153 mMainClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); 154 mMainClear.mEsimStorageContainer.setVisibility(View.VISIBLE); 155 mMainClear.mEsimStorage.setChecked(false); 156 mMainClear.showFinalConfirmation(); 157 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 158 159 verify(context).startActivity(intent.capture()); 160 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 161 .getBoolean(MainClear.ERASE_ESIMS_EXTRA, false)) 162 .isFalse(); 163 } 164 165 @Test testShowFinalConfirmation_eraseEsimGone_eraseEsimChecked()166 public void testShowFinalConfirmation_eraseEsimGone_eraseEsimChecked() { 167 final Context context = mock(Context.class); 168 when(mMainClear.getContext()).thenReturn(context); 169 170 mMainClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); 171 mMainClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); 172 mMainClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); 173 mMainClear.mEsimStorageContainer.setVisibility(View.GONE); 174 mMainClear.mEsimStorage.setChecked(true); 175 mMainClear.showFinalConfirmation(); 176 177 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 178 179 verify(context).startActivity(intent.capture()); 180 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 181 .getBoolean(MainClear.ERASE_ESIMS_EXTRA, false)) 182 .isTrue(); 183 } 184 185 @Test testShowFinalConfirmation_eraseEsimGone_eraseEsimUnchecked()186 public void testShowFinalConfirmation_eraseEsimGone_eraseEsimUnchecked() { 187 final Context context = mock(Context.class); 188 when(mMainClear.getContext()).thenReturn(context); 189 190 mMainClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim); 191 mMainClear.mExternalStorage = mContentView.findViewById(R.id.erase_external); 192 mMainClear.mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container); 193 mMainClear.mEsimStorageContainer.setVisibility(View.GONE); 194 mMainClear.mEsimStorage.setChecked(false); 195 mMainClear.showFinalConfirmation(); 196 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 197 198 verify(context).startActivity(intent.capture()); 199 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 200 .getBoolean(MainClear.ERASE_ESIMS_EXTRA, false)) 201 .isFalse(); 202 } 203 204 @Test testShowWipeEuicc_euiccDisabled()205 public void testShowWipeEuicc_euiccDisabled() { 206 prepareEuiccState( 207 false /* isEuiccEnabled */, 208 true /* isEuiccProvisioned */, 209 false /* isDeveloper */); 210 assertThat(mMainClear.showWipeEuicc()).isFalse(); 211 } 212 213 @Test testShowWipeEuicc_euiccEnabled_unprovisioned()214 public void testShowWipeEuicc_euiccEnabled_unprovisioned() { 215 prepareEuiccState( 216 true /* isEuiccEnabled */, 217 false /* isEuiccProvisioned */, 218 false /* isDeveloper */); 219 assertThat(mMainClear.showWipeEuicc()).isFalse(); 220 } 221 222 @Test testShowWipeEuicc_euiccEnabled_provisioned()223 public void testShowWipeEuicc_euiccEnabled_provisioned() { 224 prepareEuiccState( 225 true /* isEuiccEnabled */, 226 true /* isEuiccProvisioned */, 227 false /* isDeveloper */); 228 assertThat(mMainClear.showWipeEuicc()).isTrue(); 229 } 230 231 @Test 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