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