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