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