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