• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
18 package com.android.settings.slices;
19 
20 import static android.content.ContentResolver.SCHEME_CONTENT;
21 import static android.content.pm.PackageManager.PERMISSION_DENIED;
22 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
23 import static android.content.res.Configuration.UI_MODE_NIGHT_NO;
24 import static android.content.res.Configuration.UI_MODE_NIGHT_YES;
25 
26 import static com.google.common.truth.Truth.assertThat;
27 
28 import static org.mockito.ArgumentMatchers.any;
29 import static org.mockito.ArgumentMatchers.anyInt;
30 import static org.mockito.ArgumentMatchers.anyString;
31 import static org.mockito.ArgumentMatchers.eq;
32 import static org.mockito.Mockito.doReturn;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.spy;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.when;
38 
39 import android.app.PendingIntent;
40 import android.app.slice.SliceManager;
41 import android.content.ContentResolver;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.res.Resources.Theme;
45 import android.net.Uri;
46 import android.os.StrictMode;
47 import android.provider.Settings;
48 import android.provider.SettingsSlicesContract;
49 import android.util.ArraySet;
50 import android.view.accessibility.AccessibilityManager;
51 
52 import androidx.slice.Slice;
53 import androidx.slice.SliceProvider;
54 import androidx.slice.widget.SliceLiveData;
55 
56 import com.android.settings.Utils;
57 import com.android.settings.testutils.DatabaseTestUtils;
58 import com.android.settings.testutils.FakeToggleController;
59 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
60 import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
61 import com.android.settings.testutils.shadow.ShadowThreadUtils;
62 import com.android.settings.testutils.shadow.ShadowUserManager;
63 import com.android.settings.testutils.shadow.ShadowUtils;
64 import com.android.settings.wifi.slice.WifiScanWorker;
65 import com.android.settingslib.wifi.WifiTracker;
66 
67 import org.junit.After;
68 import org.junit.Before;
69 import org.junit.Test;
70 import org.junit.runner.RunWith;
71 import org.mockito.Mock;
72 import org.mockito.MockitoAnnotations;
73 import org.robolectric.Robolectric;
74 import org.robolectric.RobolectricTestRunner;
75 import org.robolectric.RuntimeEnvironment;
76 import org.robolectric.Shadows;
77 import org.robolectric.annotation.Config;
78 import org.robolectric.annotation.Implementation;
79 import org.robolectric.annotation.Implements;
80 import org.robolectric.annotation.Resetter;
81 import org.robolectric.shadow.api.Shadow;
82 import org.robolectric.shadows.ShadowAccessibilityManager;
83 import org.robolectric.shadows.ShadowBinder;
84 import org.robolectric.shadows.ShadowPackageManager;
85 
86 import java.util.ArrayList;
87 import java.util.Arrays;
88 import java.util.Collection;
89 import java.util.Collections;
90 import java.util.HashMap;
91 import java.util.HashSet;
92 import java.util.List;
93 import java.util.Set;
94 
95 /**
96  * TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
97  */
98 @RunWith(RobolectricTestRunner.class)
99 @Config(shadows = {ShadowUserManager.class, ShadowUtils.class,
100         SlicesDatabaseAccessorTest.ShadowApplicationPackageManager.class,
101         ShadowBluetoothAdapter.class, ShadowLockPatternUtils.class,
102         SettingsSliceProviderTest.ShadowWifiScanWorker.class,
103         SettingsSliceProviderTest.ShadowTheme.class})
104 public class SettingsSliceProviderTest {
105 
106     private static final String KEY = "KEY";
107     private static final Uri INTENT_SLICE_URI =
108             new Uri.Builder().scheme(SCHEME_CONTENT)
109                     .authority(SettingsSliceProvider.SLICE_AUTHORITY)
110                     .appendPath(SettingsSlicesContract.PATH_SETTING_INTENT)
111                     .appendPath(KEY)
112                     .build();
113     private static final Uri ACTION_SLICE_URI =
114             new Uri.Builder().scheme(SCHEME_CONTENT)
115                     .authority(SettingsSlicesContract.AUTHORITY)
116                     .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
117                     .appendPath(KEY)
118                     .build();
119 
120     private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
121 
122     private Context mContext;
123     private SettingsSliceProvider mProvider;
124     private ShadowPackageManager mPackageManager;
125     @Mock
126     private SliceManager mManager;
127 
128     private static final List<Uri> SPECIAL_CASE_PLATFORM_URIS = Arrays.asList(
129             CustomSliceRegistry.WIFI_SLICE_URI,
130             CustomSliceRegistry.BLUETOOTH_URI,
131             CustomSliceRegistry.LOCATION_SLICE_URI
132     );
133 
134     private static final List<Uri> SPECIAL_CASE_OEM_URIS = Arrays.asList(
135             CustomSliceRegistry.ZEN_MODE_SLICE_URI,
136             CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
137             CustomSliceRegistry.MOBILE_DATA_SLICE_URI,
138             CustomSliceRegistry.WIFI_CALLING_URI
139     );
140 
141     @Before
setUp()142     public void setUp() {
143         MockitoAnnotations.initMocks(this);
144         mContext = spy(RuntimeEnvironment.application);
145         // Register the fake a11y Service
146         ShadowAccessibilityManager shadowAccessibilityManager = Shadow.extract(
147                 RuntimeEnvironment.application.getSystemService(AccessibilityManager.class));
148         shadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
149 
150         mProvider = spy(new SettingsSliceProvider());
151         ShadowStrictMode.reset();
152         mProvider.mSliceWeakDataCache = new HashMap<>();
153         mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
154         when(mProvider.getContext()).thenReturn(mContext);
155 
156         SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
157 
158         doReturn(mManager).when(mContext).getSystemService(SliceManager.class);
159         when(mManager.getPinnedSlices()).thenReturn(Collections.emptyList());
160 
161         mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
162 
163         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
164     }
165 
166     @After
cleanUp()167     public void cleanUp() {
168         ShadowThreadUtils.reset();
169         ShadowTheme.reset();
170         DatabaseTestUtils.clearDb(mContext);
171     }
172 
173     @Test
testInitialSliceReturned_emptySlice()174     public void testInitialSliceReturned_emptySlice() {
175         SliceTestUtils.insertSliceToDb(mContext, KEY);
176         Slice slice = mProvider.onBindSlice(INTENT_SLICE_URI);
177 
178         assertThat(slice.getUri()).isEqualTo(INTENT_SLICE_URI);
179         assertThat(slice.getItems()).isEmpty();
180     }
181 
182     @Test
testLoadSlice_returnsSliceFromAccessor()183     public void testLoadSlice_returnsSliceFromAccessor() {
184         SliceTestUtils.insertSliceToDb(mContext, KEY);
185 
186         mProvider.loadSlice(INTENT_SLICE_URI);
187         SliceData data = mProvider.mSliceWeakDataCache.get(INTENT_SLICE_URI);
188 
189         assertThat(data.getKey()).isEqualTo(KEY);
190         assertThat(data.getTitle()).isEqualTo(SliceTestUtils.FAKE_TITLE);
191     }
192 
193     @Test
loadSlice_registersIntentFilter()194     public void loadSlice_registersIntentFilter() {
195         SliceTestUtils.insertSliceToDb(mContext, KEY);
196 
197         mProvider.loadSlice(INTENT_SLICE_URI);
198 
199         verify(mProvider)
200                 .registerIntentToUri(eq(FakeToggleController.INTENT_FILTER), eq(INTENT_SLICE_URI));
201     }
202 
203     @Test
loadSlice_registersBackgroundListener()204     public void loadSlice_registersBackgroundListener() {
205         SliceTestUtils.insertSliceToDb(mContext, KEY);
206 
207         mProvider.loadSlice(INTENT_SLICE_URI);
208 
209         Robolectric.flushForegroundThreadScheduler();
210         Robolectric.flushBackgroundThreadScheduler();
211 
212         assertThat(mProvider.mPinnedWorkers.get(INTENT_SLICE_URI).getClass())
213                 .isEqualTo(FakeToggleController.TestWorker.class);
214     }
215 
216     @Test
testLoadSlice_cachedEntryRemovedOnBuild()217     public void testLoadSlice_cachedEntryRemovedOnBuild() {
218         SliceData data = getMockData();
219         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
220         mProvider.onBindSlice(data.getUri());
221         SliceTestUtils.insertSliceToDb(mContext, data.getKey());
222 
223         SliceData cachedData = mProvider.mSliceWeakDataCache.get(data.getUri());
224 
225         assertThat(cachedData).isNull();
226     }
227 
228     @Test
onBindSlice_mainThread_shouldNotOverrideStrictMode()229     public void onBindSlice_mainThread_shouldNotOverrideStrictMode() {
230         ShadowThreadUtils.setIsMainThread(true);
231         final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
232         SliceData data = getMockData();
233         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
234         mProvider.onBindSlice(data.getUri());
235 
236         final StrictMode.ThreadPolicy newThreadPolicy = StrictMode.getThreadPolicy();
237 
238         assertThat(newThreadPolicy.toString()).isEqualTo(oldThreadPolicy.toString());
239     }
240 
241     @Test
242     @Config(shadows = ShadowStrictMode.class)
onBindSlice_backgroundThread_shouldOverrideStrictMode()243     public void onBindSlice_backgroundThread_shouldOverrideStrictMode() {
244         ShadowThreadUtils.setIsMainThread(false);
245 
246         SliceData data = getMockData();
247         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
248         mProvider.onBindSlice(data.getUri());
249 
250         assertThat(ShadowStrictMode.isThreadPolicyOverridden()).isTrue();
251     }
252 
253     @Test
onBindSlice_requestsBlockedSlice_returnsNull()254     public void onBindSlice_requestsBlockedSlice_returnsNull() {
255         final String blockedKey = "blocked_key";
256         final Set<String> blockedSet = new ArraySet<>();
257         blockedSet.add(blockedKey);
258         doReturn(blockedSet).when(mProvider).getBlockedKeys();
259         final Uri blockedUri = new Uri.Builder()
260                 .scheme(ContentResolver.SCHEME_CONTENT)
261                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
262                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
263                 .appendPath(blockedKey)
264                 .build();
265 
266         final Slice slice = mProvider.onBindSlice(blockedUri);
267 
268         assertThat(slice).isNull();
269     }
270 
271     @Test
onBindSlice_nightModeChanged_shouldReloadTheme()272     public void onBindSlice_nightModeChanged_shouldReloadTheme() {
273         mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_NO;
274         final SliceData data = getMockData();
275         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
276         mProvider.onBindSlice(data.getUri());
277 
278         mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_YES;
279         mProvider.onBindSlice(data.getUri());
280 
281         assertThat(ShadowTheme.isThemeRebased()).isTrue();
282     }
283 
284     @Test
onBindSlice_nightModeNotChanged_shouldNotReloadTheme()285     public void onBindSlice_nightModeNotChanged_shouldNotReloadTheme() {
286         mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_NO;
287         SliceData data = getMockData();
288         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
289         mProvider.onBindSlice(data.getUri());
290 
291         mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_NO;
292         mProvider.onBindSlice(data.getUri());
293 
294         assertThat(ShadowTheme.isThemeRebased()).isFalse();
295     }
296 
297     @Test
getDescendantUris_fullActionUri_returnsSelf()298     public void getDescendantUris_fullActionUri_returnsSelf() {
299         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
300 
301         assertThat(descendants).containsExactly(ACTION_SLICE_URI);
302     }
303 
304     @Test
getDescendantUris_fullIntentUri_returnsSelf()305     public void getDescendantUris_fullIntentUri_returnsSelf() {
306 
307         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
308 
309         assertThat(descendants).containsExactly(ACTION_SLICE_URI);
310     }
311 
312     @Test
getDescendantUris_wrongPath_returnsEmpty()313     public void getDescendantUris_wrongPath_returnsEmpty() {
314         final Uri uri = new Uri.Builder()
315                 .scheme(SCHEME_CONTENT)
316                 .authority(SettingsSlicesContract.AUTHORITY)
317                 .appendPath("invalid_path")
318                 .build();
319 
320         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
321 
322         assertThat(descendants).isEmpty();
323     }
324 
325     @Test
getDescendantUris_invalidPath_returnsEmpty()326     public void getDescendantUris_invalidPath_returnsEmpty() {
327         final String key = "platform_key";
328         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
329                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
330         final Uri uri = new Uri.Builder()
331                 .scheme(SCHEME_CONTENT)
332                 .authority(SettingsSlicesContract.AUTHORITY)
333                 .appendPath("invalid")
334                 .build();
335 
336         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
337         descendants.removeAll(SPECIAL_CASE_OEM_URIS);
338 
339         assertThat(descendants).isEmpty();
340     }
341 
342     @Test
getDescendantUris_platformSlice_doesNotReturnOEMSlice()343     public void getDescendantUris_platformSlice_doesNotReturnOEMSlice() {
344         SliceTestUtils.insertSliceToDb(mContext, "oem_key", false /* isPlatformSlice */,
345                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
346         final Uri uri = new Uri.Builder()
347                 .scheme(SCHEME_CONTENT)
348                 .authority(SettingsSlicesContract.AUTHORITY)
349                 .build();
350 
351         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
352         descendants.removeAll(SPECIAL_CASE_PLATFORM_URIS);
353 
354         assertThat(descendants).isEmpty();
355     }
356 
357     @Test
getDescendantUris_oemSlice_doesNotReturnPlatformSlice()358     public void getDescendantUris_oemSlice_doesNotReturnPlatformSlice() {
359         SliceTestUtils.insertSliceToDb(mContext, "platform_key", true /* isPlatformSlice */,
360                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
361         final Uri uri = new Uri.Builder()
362                 .scheme(SCHEME_CONTENT)
363                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
364                 .build();
365 
366         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
367         descendants.removeAll(SPECIAL_CASE_OEM_URIS);
368 
369         assertThat(descendants).isEmpty();
370     }
371 
372     @Test
getDescendantUris_oemSlice_returnsOEMUriDescendant()373     public void getDescendantUris_oemSlice_returnsOEMUriDescendant() {
374         final String key = "oem_key";
375         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
376                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
377         final Uri uri = new Uri.Builder()
378                 .scheme(SCHEME_CONTENT)
379                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
380                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
381                 .build();
382         final Collection<Uri> expectedUris = new HashSet<>();
383         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
384         expectedUris.add(new Uri.Builder()
385                 .scheme(SCHEME_CONTENT)
386                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
387                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
388                 .appendPath(key)
389                 .build());
390 
391         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
392 
393         assertThat(descendants).containsExactlyElementsIn(expectedUris);
394     }
395 
396     @Test
getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant()397     public void getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant() {
398         final String key = "oem_key";
399         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
400                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
401         final Uri uri = new Uri.Builder()
402                 .scheme(SCHEME_CONTENT)
403                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
404                 .build();
405         final Collection<Uri> expectedUris = new HashSet<>();
406         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
407         expectedUris.add(new Uri.Builder()
408                 .scheme(SCHEME_CONTENT)
409                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
410                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
411                 .appendPath(key)
412                 .build());
413 
414         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
415 
416         assertThat(descendants).containsExactlyElementsIn(expectedUris);
417     }
418 
419     @Test
getDescendantUris_oemSliceNoPath_notContainPrivateUri()420     public void getDescendantUris_oemSliceNoPath_notContainPrivateUri() {
421         final String key = "oem_key";
422         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
423                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
424         final Uri uri = new Uri.Builder()
425                 .scheme(SCHEME_CONTENT)
426                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
427                 .build();
428         final Uri expectedUri = new Uri.Builder()
429                 .scheme(SCHEME_CONTENT)
430                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
431                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
432                 .appendPath(key)
433                 .build();
434 
435         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
436 
437         assertThat(descendants).doesNotContain(expectedUri);
438     }
439 
440     @Test
getDescendantUris_platformSlice_returnsPlatformUriDescendant()441     public void getDescendantUris_platformSlice_returnsPlatformUriDescendant() {
442         final String key = "platform_key";
443         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
444                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
445         final Uri uri = new Uri.Builder()
446                 .scheme(SCHEME_CONTENT)
447                 .authority(SettingsSlicesContract.AUTHORITY)
448                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
449                 .build();
450         final Collection<Uri> expectedUris = new HashSet<>();
451         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
452         expectedUris.add(new Uri.Builder()
453                 .scheme(SCHEME_CONTENT)
454                 .authority(SettingsSlicesContract.AUTHORITY)
455                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
456                 .appendPath(key)
457                 .build());
458 
459         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
460 
461         assertThat(descendants).containsExactlyElementsIn(expectedUris);
462     }
463 
464     @Test
getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant()465     public void getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant() {
466         final String key = "platform_key";
467         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
468                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
469         final Uri uri = new Uri.Builder()
470                 .scheme(SCHEME_CONTENT)
471                 .authority(SettingsSlicesContract.AUTHORITY)
472                 .build();
473         final Collection<Uri> expectedUris = new HashSet<>();
474         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
475         expectedUris.add(new Uri.Builder()
476                 .scheme(SCHEME_CONTENT)
477                 .authority(SettingsSlicesContract.AUTHORITY)
478                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
479                 .appendPath(key)
480                 .build());
481 
482         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
483 
484         assertThat(descendants).containsExactlyElementsIn(expectedUris);
485     }
486 
487     @Test
getDescendantUris_noAuthorityNorPath_returnsAllUris()488     public void getDescendantUris_noAuthorityNorPath_returnsAllUris() {
489         final String platformKey = "platform_key";
490         final String oemKey = "oemKey";
491         SliceTestUtils.insertSliceToDb(mContext, platformKey, true /* isPlatformSlice */,
492                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
493         SliceTestUtils.insertSliceToDb(mContext, oemKey, false /* isPlatformSlice */,
494                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
495         final Uri uri = new Uri.Builder()
496                 .scheme(SCHEME_CONTENT)
497                 .build();
498         final Collection<Uri> expectedUris = new HashSet<>();
499         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
500         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
501         expectedUris.add(new Uri.Builder()
502                 .scheme(SCHEME_CONTENT)
503                 .authority(SettingsSlicesContract.AUTHORITY)
504                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
505                 .appendPath(platformKey)
506                 .build());
507         expectedUris.add(new Uri.Builder()
508                 .scheme(SCHEME_CONTENT)
509                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
510                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
511                 .appendPath(oemKey)
512                 .build());
513 
514         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
515 
516         assertThat(descendants).containsExactlyElementsIn(expectedUris);
517     }
518 
519     @Test
520     @Config(qualifiers = "mcc999")
getDescendantUris_privateSlicesNeeded_containsPrivateSliceUri()521     public void getDescendantUris_privateSlicesNeeded_containsPrivateSliceUri() {
522         final String privateKey = "test_private";
523         final Uri specialUri = Uri.parse("content://com.android.settings.slices/test");
524         doReturn(true).when(mProvider).isPrivateSlicesNeeded(specialUri);
525         SliceTestUtils.insertSliceToDb(mContext, privateKey /* key */, false /* isPlatformSlice */,
526                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
527         final Collection<Uri> expectedUris = new HashSet<>();
528         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
529         expectedUris.add(new Uri.Builder()
530                 .scheme(SCHEME_CONTENT)
531                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
532                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
533                 .appendPath(privateKey)
534                 .build());
535 
536         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(specialUri);
537 
538         assertThat(descendants).containsExactlyElementsIn(expectedUris);
539     }
540 
541     @Test
542     @Config(qualifiers = "mcc999")
getDescendantUris_privateSlicesNotNeeded_notContainPrivateSliceUri()543     public void getDescendantUris_privateSlicesNotNeeded_notContainPrivateSliceUri() {
544         final Uri specialUri = Uri.parse("content://com.android.settings.slices/test");
545         doReturn(false).when(mProvider).isPrivateSlicesNeeded(specialUri);
546         SliceTestUtils.insertSliceToDb(mContext,
547                 "test_private" /* key */, false /* isPlatformSlice */,
548                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
549         final Uri expectedUri = new Uri.Builder()
550                 .scheme(SCHEME_CONTENT)
551                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
552                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
553                 .appendPath("test_private")
554                 .build();
555 
556         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(specialUri);
557 
558         assertThat(descendants).doesNotContain(expectedUri);
559     }
560 
561     @Test
onCreatePermissionRequest_returnsSettingIntent()562     public void onCreatePermissionRequest_returnsSettingIntent() {
563         final PendingIntent pendingIntent = mProvider.onCreatePermissionRequest(
564                 CustomSliceRegistry.FLASHLIGHT_SLICE_URI, "com.android.whaaaat");
565         final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS)
566                 .setPackage(Utils.SETTINGS_PACKAGE_NAME);
567         PendingIntent settingsPendingIntent =
568                 PendingIntent.getActivity(mContext, 0, settingsIntent,
569                         PendingIntent.FLAG_IMMUTABLE);
570 
571         assertThat(pendingIntent).isEqualTo(settingsPendingIntent);
572     }
573 
574     @Test
bindSlice_wifiSlice_returnsWifiSlice()575     public void bindSlice_wifiSlice_returnsWifiSlice() {
576         final Slice wifiSlice = mProvider.onBindSlice(CustomSliceRegistry.WIFI_SLICE_URI);
577 
578         assertThat(wifiSlice.getUri()).isEqualTo(CustomSliceRegistry.WIFI_SLICE_URI);
579     }
580 
581     @Test
bindSlice_flashlightSlice_returnsFlashlightSlice()582     public void bindSlice_flashlightSlice_returnsFlashlightSlice() {
583         Settings.Secure.putInt(
584                 mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
585 
586         final Slice flashlightSlice = mProvider.onBindSlice(
587                 CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
588 
589         assertThat(flashlightSlice.getUri()).isEqualTo(CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
590     }
591 
592     @Test
onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash()593     public void onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash() {
594         final Uri uri = new Uri.Builder()
595                 .scheme(ContentResolver.SCHEME_CONTENT)
596                 .authority(SettingsSlicesContract.AUTHORITY)
597                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
598                 .appendPath(SettingsSlicesContract.KEY_LOCATION)
599                 .build();
600 
601         mProvider.onSlicePinned(uri);
602     }
603 
604     @Test
onSlicePinned_backgroundWorker_started()605     public void onSlicePinned_backgroundWorker_started() {
606         mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
607 
608         verify(ShadowWifiScanWorker.getWifiTracker()).onStart();
609     }
610 
611     @Test
onSlicePinned_backgroundWorker_stopped()612     public void onSlicePinned_backgroundWorker_stopped() {
613         mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
614         mProvider.onSliceUnpinned(CustomSliceRegistry.WIFI_SLICE_URI);
615 
616         verify(ShadowWifiScanWorker.getWifiTracker()).onStop();
617     }
618 
619     @Test
shutdown_backgroundWorker_closed()620     public void shutdown_backgroundWorker_closed() {
621         mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
622         mProvider.shutdown();
623 
624         verify(ShadowWifiScanWorker.getWifiTracker()).onDestroy();
625     }
626 
627     @Test
628     @Config(qualifiers = "mcc998")
grantAllowlistedPackagePermissions_noAllowlist_shouldNotGrant()629     public void grantAllowlistedPackagePermissions_noAllowlist_shouldNotGrant() {
630         final List<Uri> uris = new ArrayList<>();
631         uris.add(Uri.parse("content://settings/slice"));
632 
633         SettingsSliceProvider.grantAllowlistedPackagePermissions(mContext, uris);
634 
635         verify(mManager, never()).grantSlicePermission(anyString(), any(Uri.class));
636     }
637 
638     @Test
639     @Config(qualifiers = "mcc999")
grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant()640     public void grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant() {
641         final List<Uri> uris = new ArrayList<>();
642         uris.add(Uri.parse("content://settings/slice"));
643 
644         SettingsSliceProvider.grantAllowlistedPackagePermissions(mContext, uris);
645 
646         verify(mManager)
647                 .grantSlicePermission("com.android.settings.slice_allowlist_package", uris.get(0));
648     }
649 
650     @Test
651     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_incorrectUri_returnFalse()652     public void isPrivateSlicesNeeded_incorrectUri_returnFalse() {
653         final Uri uri = Uri.parse("content://com.android.settings.slices/test123");
654 
655         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
656     }
657 
658     @Test
isPrivateSlicesNeeded_noUri_returnFalse()659     public void isPrivateSlicesNeeded_noUri_returnFalse() {
660         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
661 
662         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
663     }
664 
665     @Test
666     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_correctUriWithPermissionAndIsSI_returnTrue()667     public void isPrivateSlicesNeeded_correctUriWithPermissionAndIsSI_returnTrue() {
668         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
669         ShadowBinder.setCallingUid(123);
670         doReturn(PERMISSION_GRANTED)
671                 .when(mContext).checkPermission(anyString(), anyInt(), anyInt());
672         mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.intelligence"});
673 
674         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isTrue();
675     }
676 
677     @Test
678     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_correctUriWithPermissionNotSI_returnFalse()679     public void isPrivateSlicesNeeded_correctUriWithPermissionNotSI_returnFalse() {
680         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
681         ShadowBinder.setCallingUid(123);
682         doReturn(PERMISSION_GRANTED)
683                 .when(mContext).checkPermission(anyString(), anyInt(), anyInt());
684         mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.test"});
685 
686         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
687     }
688 
689     @Test
690     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_correctUriNoPermission_returnFalse()691     public void isPrivateSlicesNeeded_correctUriNoPermission_returnFalse() {
692         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
693         ShadowBinder.setCallingUid(123);
694         doReturn(PERMISSION_DENIED).when(mContext).checkPermission(anyString(), anyInt(), anyInt());
695         mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.intelligence"});
696 
697         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
698     }
699 
getMockData()700     private static SliceData getMockData() {
701         return new SliceData.Builder()
702                 .setKey(KEY)
703                 .setUri(URI)
704                 .setTitle(SliceTestUtils.FAKE_TITLE)
705                 .setSummary(SliceTestUtils.FAKE_SUMMARY)
706                 .setScreenTitle(SliceTestUtils.FAKE_SCREEN_TITLE)
707                 .setIcon(SliceTestUtils.FAKE_ICON)
708                 .setFragmentName(SliceTestUtils.FAKE_FRAGMENT_NAME)
709                 .setPreferenceControllerClassName(SliceTestUtils.FAKE_CONTROLLER_NAME)
710                 .build();
711     }
712 
713     @Implements(WifiScanWorker.class)
714     public static class ShadowWifiScanWorker {
715         private static WifiTracker mWifiTracker;
716 
717         @Implementation
onSlicePinned()718         protected void onSlicePinned() {
719             mWifiTracker = mock(WifiTracker.class);
720             mWifiTracker.onStart();
721         }
722 
723         @Implementation
onSliceUnpinned()724         protected void onSliceUnpinned() {
725             mWifiTracker.onStop();
726         }
727 
728         @Implementation
close()729         protected void close() {
730             mWifiTracker.onDestroy();
731         }
732 
getWifiTracker()733         static WifiTracker getWifiTracker() {
734             return mWifiTracker;
735         }
736     }
737 
738     @Implements(value = StrictMode.class)
739     public static class ShadowStrictMode {
740 
741         private static int sSetThreadPolicyCount;
742 
743         @Resetter
reset()744         public static void reset() {
745             sSetThreadPolicyCount = 0;
746         }
747 
748         @Implementation
setThreadPolicy(final StrictMode.ThreadPolicy policy)749         protected static void setThreadPolicy(final StrictMode.ThreadPolicy policy) {
750             sSetThreadPolicyCount++;
751         }
752 
isThreadPolicyOverridden()753         private static boolean isThreadPolicyOverridden() {
754             return sSetThreadPolicyCount != 0;
755         }
756     }
757 
758     @Implements(Theme.class)
759     public static class ShadowTheme {
760         private static boolean sThemeRebased;
761 
762         @Resetter
reset()763         public static void reset() {
764             sThemeRebased = false;
765         }
766 
767         @Implementation
rebase()768         public void rebase() {
769             sThemeRebased = true;
770         }
771 
isThemeRebased()772         static boolean isThemeRebased() {
773             return sThemeRebased;
774         }
775     }
776 }
777