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