1 /*
2  * Copyright (C) 2024 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.security;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.robolectric.Shadows.shadowOf;
22 
23 import android.app.admin.DevicePolicyManager;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.icu.text.MessageFormat;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 import android.provider.Settings;
30 
31 import androidx.fragment.app.Fragment;
32 import androidx.test.core.app.ApplicationProvider;
33 
34 import com.android.settings.R;
35 import com.android.settings.SettingsActivity;
36 import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
37 import com.android.settingslib.search.SearchIndexableRaw;
38 import com.android.settingslib.widget.FooterPreference;
39 
40 import org.junit.After;
41 import org.junit.Before;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 import org.robolectric.RobolectricTestRunner;
45 import org.robolectric.android.controller.ActivityController;
46 import org.robolectric.annotation.Config;
47 import org.robolectric.shadows.ShadowUserManager;
48 
49 import java.util.List;
50 import java.util.function.Consumer;
51 
52 @RunWith(RobolectricTestRunner.class)
53 @Config(shadows = ShadowLockPatternUtils.class)
54 public class ScreenPinningSettingsTest {
55     private static final String KEY_FOOTER = "screen_pinning_settings_screen_footer";
56     private Context mContext;
57     private UserManager mUserManager;
58 
59     @Before
setUp()60     public void setUp() {
61         mContext = ApplicationProvider.getApplicationContext();
62         mUserManager = mContext.getSystemService(UserManager.class);
63     }
64 
65     @After
tearDown()66     public void tearDown() {
67         ShadowLockPatternUtils.reset();
68     }
69 
70     @Test
getDynamicRawDataToIndex_numericPassword_shouldIndexUnlockPinTitle()71     public void getDynamicRawDataToIndex_numericPassword_shouldIndexUnlockPinTitle() {
72         ShadowLockPatternUtils.setKeyguardStoredPasswordQuality(
73                 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
74 
75         final List<SearchIndexableRaw> indexRaws =
76                 ScreenPinningSettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
77                         mContext, /* enabled= */ true);
78 
79         assertThat(indexRaws.size()).isEqualTo(1);
80         assertThat(indexRaws.get(0).title).isEqualTo(
81                 mContext.getString(R.string.screen_pinning_unlock_pin));
82     }
83 
84     @Test
getDynamicRawDataToIndex_alphabeticPassword_shouldIndexUnlockPasswordTitle()85     public void getDynamicRawDataToIndex_alphabeticPassword_shouldIndexUnlockPasswordTitle() {
86         ShadowLockPatternUtils.setKeyguardStoredPasswordQuality(
87                 DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
88 
89         final List<SearchIndexableRaw> indexRaws =
90                 ScreenPinningSettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
91                         mContext, /* enabled= */ true);
92 
93         assertThat(indexRaws.size()).isEqualTo(1);
94         assertThat(indexRaws.get(0).title).isEqualTo(
95                 mContext.getString(R.string.screen_pinning_unlock_password));
96     }
97 
98     @Test
getDynamicRawDataToIndex_patternPassword_shouldIndexUnlockPatternTitle()99     public void getDynamicRawDataToIndex_patternPassword_shouldIndexUnlockPatternTitle() {
100         ShadowLockPatternUtils.setKeyguardStoredPasswordQuality(
101                 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
102         ShadowLockPatternUtils.setIsLockPatternEnabled(
103                 UserHandle.myUserId(), /* isLockPatternEnabled= */ true);
104 
105         final List<SearchIndexableRaw> indexRaws =
106                 ScreenPinningSettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
107                         mContext, /* enabled= */ true);
108 
109         assertThat(indexRaws.size()).isEqualTo(1);
110         assertThat(indexRaws.get(0).title).isEqualTo(
111                 mContext.getString(R.string.screen_pinning_unlock_pattern));
112     }
113 
114     @Test
getDynamicRawDataToIndex_nonePassword_shouldIndexUnlockNoneTitle()115     public void getDynamicRawDataToIndex_nonePassword_shouldIndexUnlockNoneTitle() {
116         ShadowLockPatternUtils.setKeyguardStoredPasswordQuality(
117                 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
118 
119         final List<SearchIndexableRaw> indexRaws =
120                 ScreenPinningSettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
121                         mContext, /* enabled= */ true);
122 
123         assertThat(indexRaws.size()).isEqualTo(1);
124         assertThat(indexRaws.get(0).title).isEqualTo(
125                 mContext.getString(R.string.screen_pinning_unlock_none));
126     }
127 
128     @Test
onCreate_lockToAppEnabled_guestModeSupported_verifyFooterText()129     public void onCreate_lockToAppEnabled_guestModeSupported_verifyFooterText() {
130         setupLockToAppState(/* enabled= */ true);
131         setupGuestModeState(/* supported= */ true);
132 
133         launchFragmentAndRunTest(fragment -> {
134             FooterPreference footer = fragment.findPreference(KEY_FOOTER);
135 
136             assertThat(footer.getSummary())
137                     .isEqualTo(getExpectedFooterText(/* guestModeSupported= */ true));
138         });
139     }
140 
141     @Test
onCreate_lockToAppEnabled_guestModeNotSupported_verifyFooterText()142     public void onCreate_lockToAppEnabled_guestModeNotSupported_verifyFooterText() {
143         setupLockToAppState(/* enabled= */ true);
144         setupGuestModeState(/* supported= */ false);
145 
146         launchFragmentAndRunTest(fragment -> {
147             FooterPreference footer = fragment.findPreference(KEY_FOOTER);
148 
149             assertThat(footer.getSummary())
150                     .isEqualTo(getExpectedFooterText(/* guestModeSupported= */ false));
151         });
152     }
153 
154     @Test
onCreate_lockToAppDisabled_guestModeSupported_verifyFooterText()155     public void onCreate_lockToAppDisabled_guestModeSupported_verifyFooterText() {
156         setupLockToAppState(/* enabled= */ false);
157         setupGuestModeState(/* supported= */ true);
158 
159         launchFragmentAndRunTest(fragment -> {
160             FooterPreference footer = fragment.findPreference(KEY_FOOTER);
161 
162             assertThat(footer.getSummary())
163                     .isEqualTo(getExpectedFooterText(/* guestModeSupported= */ true));
164         });
165     }
166 
167     @Test
onCreate_lockToAppDisabled_guestModeNotSupported_verifyFooterText()168     public void onCreate_lockToAppDisabled_guestModeNotSupported_verifyFooterText() {
169         setupLockToAppState(/* enabled= */ false);
170         setupGuestModeState(/* supported= */ false);
171 
172         launchFragmentAndRunTest(fragment -> {
173             FooterPreference footer = fragment.findPreference(KEY_FOOTER);
174 
175             assertThat(footer.getSummary())
176                     .isEqualTo(getExpectedFooterText(/* guestModeSupported= */ false));
177         });
178     }
179 
getExpectedFooterText(boolean guestModeSupported)180     private CharSequence getExpectedFooterText(boolean guestModeSupported) {
181         final int stringResource = guestModeSupported
182                 ? R.string.screen_pinning_guest_user_description
183                 : R.string.screen_pinning_description;
184         return MessageFormat.format(mContext.getString(stringResource), 1, 2, 3);
185     }
186 
setupLockToAppState(boolean enabled)187     private void setupLockToAppState(boolean enabled) {
188         Settings.System.putInt(mContext.getContentResolver(), Settings.System.LOCK_TO_APP_ENABLED,
189                 enabled ? 1 : 0);
190     }
191 
setupGuestModeState(boolean supported)192     private void setupGuestModeState(boolean supported) {
193         ShadowUserManager shadowUserManager = shadowOf(mUserManager);
194         shadowUserManager.setSupportsMultipleUsers(supported);
195         shadowUserManager.setUserRestriction(
196                 UserHandle.of(UserHandle.myUserId()), UserManager.DISALLOW_USER_SWITCH, !supported);
197     }
198 
launchFragmentAndRunTest(Consumer<ScreenPinningSettings> test)199     private void launchFragmentAndRunTest(Consumer<ScreenPinningSettings> test) {
200         Intent intent = new Intent();
201         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
202                 SecurityAdvancedSettings.class.getName());
203         // ScreenPinningSettings is tightly coupled with the SettingsActivity
204         // In order to successfully launch the ScreenPinningSettings, have to use an indirect route
205         // to launch the SecurityAdvancedSetting first, then replace it with ScreenPinningSettings.
206         try (ActivityController<SettingsActivity> controller =
207                      ActivityController.of(new SettingsActivity(), intent)) {
208             controller.create().start().resume();
209 
210             controller.get().getSupportFragmentManager().beginTransaction().replace(
211                     R.id.main_content, ScreenPinningSettings.class, null).commitNow();
212             Fragment fragment = controller.get().getSupportFragmentManager()
213                     .findFragmentById(R.id.main_content);
214             assertThat(fragment).isNotNull();
215             assertThat(fragment).isInstanceOf(ScreenPinningSettings.class);
216 
217             test.accept((ScreenPinningSettings) fragment);
218         }
219     }
220 }
221