• 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.testutils.DatabaseTestUtils;
57 import com.android.settings.testutils.FakeToggleController;
58 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
59 import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
60 import com.android.settings.testutils.shadow.ShadowThreadUtils;
61 import com.android.settings.testutils.shadow.ShadowUserManager;
62 import com.android.settings.testutils.shadow.ShadowUtils;
63 
64 import org.junit.After;
65 import org.junit.Before;
66 import org.junit.Ignore;
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 import org.mockito.Mock;
70 import org.mockito.MockitoAnnotations;
71 import org.robolectric.Robolectric;
72 import org.robolectric.RobolectricTestRunner;
73 import org.robolectric.RuntimeEnvironment;
74 import org.robolectric.Shadows;
75 import org.robolectric.annotation.Config;
76 import org.robolectric.annotation.Implementation;
77 import org.robolectric.annotation.Implements;
78 import org.robolectric.annotation.Resetter;
79 import org.robolectric.shadow.api.Shadow;
80 import org.robolectric.shadows.ShadowAccessibilityManager;
81 import org.robolectric.shadows.ShadowBinder;
82 import org.robolectric.shadows.ShadowPackageManager;
83 
84 import java.util.ArrayList;
85 import java.util.Arrays;
86 import java.util.Collection;
87 import java.util.Collections;
88 import java.util.HashMap;
89 import java.util.HashSet;
90 import java.util.List;
91 import java.util.Set;
92 
93 /**
94  * TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
95  */
96 @RunWith(RobolectricTestRunner.class)
97 @Config(shadows = {ShadowUserManager.class, ShadowUtils.class,
98         SlicesDatabaseAccessorTest.ShadowApplicationPackageManager.class,
99         ShadowBluetoothAdapter.class, ShadowLockPatternUtils.class,
100         SettingsSliceProviderTest.ShadowTheme.class})
101 public class SettingsSliceProviderTest {
102 
103     private static final String KEY = "KEY";
104     private static final Uri INTENT_SLICE_URI =
105             new Uri.Builder().scheme(SCHEME_CONTENT)
106                     .authority(SettingsSliceProvider.SLICE_AUTHORITY)
107                     .appendPath(SettingsSlicesContract.PATH_SETTING_INTENT)
108                     .appendPath(KEY)
109                     .build();
110     private static final Uri ACTION_SLICE_URI =
111             new Uri.Builder().scheme(SCHEME_CONTENT)
112                     .authority(SettingsSlicesContract.AUTHORITY)
113                     .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
114                     .appendPath(KEY)
115                     .build();
116 
117     private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
118 
119     private Context mContext;
120     private SettingsSliceProvider mProvider;
121     private ShadowPackageManager mPackageManager;
122     private ShadowUserManager mShadowUserManager;
123 
124     @Mock
125     private SliceManager mManager;
126 
127     private static final List<Uri> SPECIAL_CASE_PLATFORM_URIS = Arrays.asList(
128             CustomSliceRegistry.WIFI_SLICE_URI,
129             CustomSliceRegistry.BLUETOOTH_URI,
130             CustomSliceRegistry.LOCATION_SLICE_URI
131     );
132 
133     private static final List<Uri> SPECIAL_CASE_OEM_URIS = Arrays.asList(
134             CustomSliceRegistry.ZEN_MODE_SLICE_URI,
135             CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
136             CustomSliceRegistry.MOBILE_DATA_SLICE_URI,
137             CustomSliceRegistry.WIFI_CALLING_URI
138     );
139 
140     @Before
setUp()141     public void setUp() {
142         MockitoAnnotations.initMocks(this);
143         mContext = spy(RuntimeEnvironment.application);
144         // Register the fake a11y Service
145         ShadowAccessibilityManager shadowAccessibilityManager = Shadow.extract(
146                 RuntimeEnvironment.application.getSystemService(AccessibilityManager.class));
147         shadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
148 
149         mProvider = spy(new SettingsSliceProvider());
150         ShadowStrictMode.reset();
151         mProvider.mSliceWeakDataCache = new HashMap<>();
152         mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
153         when(mProvider.getContext()).thenReturn(mContext);
154 
155         SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
156 
157         doReturn(mManager).when(mContext).getSystemService(SliceManager.class);
158         when(mManager.getPinnedSlices()).thenReturn(Collections.emptyList());
159 
160         mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
161         mShadowUserManager = ShadowUserManager.getShadow();
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_cachedEntryRemovedOnUnpinned()217     public void testLoadSlice_cachedEntryRemovedOnUnpinned() {
218         SliceData data = getMockData();
219         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
220         mProvider.onSliceUnpinned(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
onBindSlice_guestRestricted_returnsNull()298     public void onBindSlice_guestRestricted_returnsNull() {
299         final String key = "enable_usb_tethering";
300         mShadowUserManager.setGuestUser(true);
301         final Uri testUri = new Uri.Builder()
302             .scheme(ContentResolver.SCHEME_CONTENT)
303             .authority(SettingsSliceProvider.SLICE_AUTHORITY)
304             .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
305             .appendPath(key)
306             .build();
307 
308         final Slice slice = mProvider.onBindSlice(testUri);
309 
310         assertThat(slice).isNull();
311     }
312 
313     @Test
onBindSlice_notGuestRestricted_returnsNotNull()314     public void onBindSlice_notGuestRestricted_returnsNotNull() {
315         final String key = "enable_usb_tethering";
316         final Uri testUri = new Uri.Builder()
317             .scheme(ContentResolver.SCHEME_CONTENT)
318             .authority(SettingsSliceProvider.SLICE_AUTHORITY)
319             .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
320             .appendPath(key)
321             .build();
322 
323         final Slice slice = mProvider.onBindSlice(testUri);
324 
325         assertThat(slice).isNotNull();
326     }
327 
328     @Test
getDescendantUris_fullActionUri_returnsSelf()329     public void getDescendantUris_fullActionUri_returnsSelf() {
330         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
331 
332         assertThat(descendants).containsExactly(ACTION_SLICE_URI);
333     }
334 
335     @Test
getDescendantUris_fullIntentUri_returnsSelf()336     public void getDescendantUris_fullIntentUri_returnsSelf() {
337 
338         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
339 
340         assertThat(descendants).containsExactly(ACTION_SLICE_URI);
341     }
342 
343     @Test
getDescendantUris_wrongPath_returnsEmpty()344     public void getDescendantUris_wrongPath_returnsEmpty() {
345         final Uri uri = new Uri.Builder()
346                 .scheme(SCHEME_CONTENT)
347                 .authority(SettingsSlicesContract.AUTHORITY)
348                 .appendPath("invalid_path")
349                 .build();
350 
351         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
352 
353         assertThat(descendants).isEmpty();
354     }
355 
356     @Test
getDescendantUris_invalidPath_returnsEmpty()357     public void getDescendantUris_invalidPath_returnsEmpty() {
358         final String key = "platform_key";
359         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
360                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
361         final Uri uri = new Uri.Builder()
362                 .scheme(SCHEME_CONTENT)
363                 .authority(SettingsSlicesContract.AUTHORITY)
364                 .appendPath("invalid")
365                 .build();
366 
367         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
368         descendants.removeAll(SPECIAL_CASE_OEM_URIS);
369 
370         assertThat(descendants).isEmpty();
371     }
372 
373     @Test
getDescendantUris_platformSlice_doesNotReturnOEMSlice()374     public void getDescendantUris_platformSlice_doesNotReturnOEMSlice() {
375         SliceTestUtils.insertSliceToDb(mContext, "oem_key", false /* isPlatformSlice */,
376                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
377         final Uri uri = new Uri.Builder()
378                 .scheme(SCHEME_CONTENT)
379                 .authority(SettingsSlicesContract.AUTHORITY)
380                 .build();
381 
382         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
383         descendants.removeAll(SPECIAL_CASE_PLATFORM_URIS);
384 
385         assertThat(descendants).isEmpty();
386     }
387 
388     @Test
getDescendantUris_oemSlice_doesNotReturnPlatformSlice()389     public void getDescendantUris_oemSlice_doesNotReturnPlatformSlice() {
390         SliceTestUtils.insertSliceToDb(mContext, "platform_key", true /* isPlatformSlice */,
391                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
392         final Uri uri = new Uri.Builder()
393                 .scheme(SCHEME_CONTENT)
394                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
395                 .build();
396 
397         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
398         descendants.removeAll(SPECIAL_CASE_OEM_URIS);
399 
400         assertThat(descendants).isEmpty();
401     }
402 
403     @Test
getDescendantUris_oemSlice_returnsOEMUriDescendant()404     public void getDescendantUris_oemSlice_returnsOEMUriDescendant() {
405         final String key = "oem_key";
406         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
407                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
408         final Uri uri = new Uri.Builder()
409                 .scheme(SCHEME_CONTENT)
410                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
411                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
412                 .build();
413         final Collection<Uri> expectedUris = new HashSet<>();
414         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
415         expectedUris.add(new Uri.Builder()
416                 .scheme(SCHEME_CONTENT)
417                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
418                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
419                 .appendPath(key)
420                 .build());
421 
422         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
423 
424         assertThat(descendants).containsExactlyElementsIn(expectedUris);
425     }
426 
427     @Test
getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant()428     public void getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant() {
429         final String key = "oem_key";
430         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
431                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
432         final Uri uri = new Uri.Builder()
433                 .scheme(SCHEME_CONTENT)
434                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
435                 .build();
436         final Collection<Uri> expectedUris = new HashSet<>();
437         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
438         expectedUris.add(new Uri.Builder()
439                 .scheme(SCHEME_CONTENT)
440                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
441                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
442                 .appendPath(key)
443                 .build());
444 
445         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
446 
447         assertThat(descendants).containsExactlyElementsIn(expectedUris);
448     }
449 
450     @Test
getDescendantUris_oemSliceNoPath_notContainPrivateUri()451     public void getDescendantUris_oemSliceNoPath_notContainPrivateUri() {
452         final String key = "oem_key";
453         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
454                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
455         final Uri uri = new Uri.Builder()
456                 .scheme(SCHEME_CONTENT)
457                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
458                 .build();
459         final Uri expectedUri = new Uri.Builder()
460                 .scheme(SCHEME_CONTENT)
461                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
462                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
463                 .appendPath(key)
464                 .build();
465 
466         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
467 
468         assertThat(descendants).doesNotContain(expectedUri);
469     }
470 
471     @Test
getDescendantUris_platformSlice_returnsPlatformUriDescendant()472     public void getDescendantUris_platformSlice_returnsPlatformUriDescendant() {
473         final String key = "platform_key";
474         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
475                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
476         final Uri uri = new Uri.Builder()
477                 .scheme(SCHEME_CONTENT)
478                 .authority(SettingsSlicesContract.AUTHORITY)
479                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
480                 .build();
481         final Collection<Uri> expectedUris = new HashSet<>();
482         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
483         expectedUris.add(new Uri.Builder()
484                 .scheme(SCHEME_CONTENT)
485                 .authority(SettingsSlicesContract.AUTHORITY)
486                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
487                 .appendPath(key)
488                 .build());
489 
490         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
491 
492         assertThat(descendants).containsExactlyElementsIn(expectedUris);
493     }
494 
495     @Test
getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant()496     public void getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant() {
497         final String key = "platform_key";
498         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
499                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
500         final Uri uri = new Uri.Builder()
501                 .scheme(SCHEME_CONTENT)
502                 .authority(SettingsSlicesContract.AUTHORITY)
503                 .build();
504         final Collection<Uri> expectedUris = new HashSet<>();
505         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
506         expectedUris.add(new Uri.Builder()
507                 .scheme(SCHEME_CONTENT)
508                 .authority(SettingsSlicesContract.AUTHORITY)
509                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
510                 .appendPath(key)
511                 .build());
512 
513         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
514 
515         assertThat(descendants).containsExactlyElementsIn(expectedUris);
516     }
517 
518     @Test
getDescendantUris_noAuthorityNorPath_returnsAllUris()519     public void getDescendantUris_noAuthorityNorPath_returnsAllUris() {
520         final String platformKey = "platform_key";
521         final String oemKey = "oemKey";
522         SliceTestUtils.insertSliceToDb(mContext, platformKey, true /* isPlatformSlice */,
523                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
524         SliceTestUtils.insertSliceToDb(mContext, oemKey, false /* isPlatformSlice */,
525                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
526         final Uri uri = new Uri.Builder()
527                 .scheme(SCHEME_CONTENT)
528                 .build();
529         final Collection<Uri> expectedUris = new HashSet<>();
530         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
531         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
532         expectedUris.add(new Uri.Builder()
533                 .scheme(SCHEME_CONTENT)
534                 .authority(SettingsSlicesContract.AUTHORITY)
535                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
536                 .appendPath(platformKey)
537                 .build());
538         expectedUris.add(new Uri.Builder()
539                 .scheme(SCHEME_CONTENT)
540                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
541                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
542                 .appendPath(oemKey)
543                 .build());
544 
545         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
546 
547         assertThat(descendants).containsExactlyElementsIn(expectedUris);
548     }
549 
550     @Test
551     @Config(qualifiers = "mcc999")
getDescendantUris_privateSlicesNeeded_containsPrivateSliceUri()552     public void getDescendantUris_privateSlicesNeeded_containsPrivateSliceUri() {
553         final String privateKey = "test_private";
554         final Uri specialUri = Uri.parse("content://com.android.settings.slices/test");
555         doReturn(true).when(mProvider).isPrivateSlicesNeeded(specialUri);
556         SliceTestUtils.insertSliceToDb(mContext, privateKey /* key */, false /* isPlatformSlice */,
557                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
558         final Collection<Uri> expectedUris = new HashSet<>();
559         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
560         expectedUris.add(new Uri.Builder()
561                 .scheme(SCHEME_CONTENT)
562                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
563                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
564                 .appendPath(privateKey)
565                 .build());
566 
567         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(specialUri);
568 
569         assertThat(descendants).containsExactlyElementsIn(expectedUris);
570     }
571 
572     @Test
573     @Config(qualifiers = "mcc999")
getDescendantUris_privateSlicesNotNeeded_notContainPrivateSliceUri()574     public void getDescendantUris_privateSlicesNotNeeded_notContainPrivateSliceUri() {
575         final Uri specialUri = Uri.parse("content://com.android.settings.slices/test");
576         doReturn(false).when(mProvider).isPrivateSlicesNeeded(specialUri);
577         SliceTestUtils.insertSliceToDb(mContext,
578                 "test_private" /* key */, false /* isPlatformSlice */,
579                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
580         final Uri expectedUri = new Uri.Builder()
581                 .scheme(SCHEME_CONTENT)
582                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
583                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
584                 .appendPath("test_private")
585                 .build();
586 
587         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(specialUri);
588 
589         assertThat(descendants).doesNotContain(expectedUri);
590     }
591 
592     @Test
onCreatePermissionRequest_returnsSettingIntent()593     public void onCreatePermissionRequest_returnsSettingIntent() {
594         final PendingIntent pendingIntent = mProvider.onCreatePermissionRequest(
595                 CustomSliceRegistry.FLASHLIGHT_SLICE_URI, "com.android.whaaaat");
596         final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS)
597                 .setPackage(Utils.SETTINGS_PACKAGE_NAME);
598         PendingIntent settingsPendingIntent =
599                 PendingIntent.getActivity(mContext, 0, settingsIntent,
600                         PendingIntent.FLAG_IMMUTABLE);
601 
602         assertThat(pendingIntent).isEqualTo(settingsPendingIntent);
603     }
604 
605     @Test
bindSlice_flashlightSlice_returnsFlashlightSlice()606     public void bindSlice_flashlightSlice_returnsFlashlightSlice() {
607         Settings.Secure.putInt(
608                 mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
609 
610         final Slice flashlightSlice = mProvider.onBindSlice(
611                 CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
612 
613         assertThat(flashlightSlice.getUri()).isEqualTo(CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
614     }
615 
616     @Test
onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash()617     public void onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash() {
618         final Uri uri = new Uri.Builder()
619                 .scheme(ContentResolver.SCHEME_CONTENT)
620                 .authority(SettingsSlicesContract.AUTHORITY)
621                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
622                 .appendPath(SettingsSlicesContract.KEY_LOCATION)
623                 .build();
624 
625         mProvider.onSlicePinned(uri);
626     }
627 
628     @Test
629     @Config(qualifiers = "mcc998")
grantAllowlistedPackagePermissions_noAllowlist_shouldNotGrant()630     public void grantAllowlistedPackagePermissions_noAllowlist_shouldNotGrant() {
631         final List<Uri> uris = new ArrayList<>();
632         uris.add(Uri.parse("content://settings/slice"));
633 
634         SettingsSliceProvider.grantAllowlistedPackagePermissions(mContext, uris);
635 
636         verify(mManager, never()).grantSlicePermission(anyString(), any(Uri.class));
637     }
638 
639     @Ignore
640     @Test
641     @Config(qualifiers = "mcc999")
grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant()642     public void grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant() {
643         final List<Uri> uris = new ArrayList<>();
644         uris.add(Uri.parse("content://settings/slice"));
645 
646         SettingsSliceProvider.grantAllowlistedPackagePermissions(mContext, uris);
647 
648         verify(mManager)
649                 .grantSlicePermission("com.android.settings.slice_allowlist_package", uris.get(0));
650     }
651 
652     @Test
653     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_incorrectUri_returnFalse()654     public void isPrivateSlicesNeeded_incorrectUri_returnFalse() {
655         final Uri uri = Uri.parse("content://com.android.settings.slices/test123");
656 
657         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
658     }
659 
660     @Test
isPrivateSlicesNeeded_noUri_returnFalse()661     public void isPrivateSlicesNeeded_noUri_returnFalse() {
662         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
663 
664         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
665     }
666 
667     @Ignore
668     @Test
669     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_correctUriWithPermissionAndIsSI_returnTrue()670     public void isPrivateSlicesNeeded_correctUriWithPermissionAndIsSI_returnTrue() {
671         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
672         ShadowBinder.setCallingUid(123);
673         doReturn(PERMISSION_GRANTED)
674                 .when(mContext).checkPermission(anyString(), anyInt(), anyInt());
675         mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.intelligence"});
676 
677         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isTrue();
678     }
679 
680     @Test
681     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_correctUriWithPermissionNotSI_returnFalse()682     public void isPrivateSlicesNeeded_correctUriWithPermissionNotSI_returnFalse() {
683         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
684         ShadowBinder.setCallingUid(123);
685         doReturn(PERMISSION_GRANTED)
686                 .when(mContext).checkPermission(anyString(), anyInt(), anyInt());
687         mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.test"});
688 
689         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
690     }
691 
692     @Test
693     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_correctUriNoPermission_returnFalse()694     public void isPrivateSlicesNeeded_correctUriNoPermission_returnFalse() {
695         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
696         ShadowBinder.setCallingUid(123);
697         doReturn(PERMISSION_DENIED).when(mContext).checkPermission(anyString(), anyInt(), anyInt());
698         mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.intelligence"});
699 
700         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
701     }
702 
getMockData()703     private static SliceData getMockData() {
704         return new SliceData.Builder()
705                 .setKey(KEY)
706                 .setUri(URI)
707                 .setTitle(SliceTestUtils.FAKE_TITLE)
708                 .setSummary(SliceTestUtils.FAKE_SUMMARY)
709                 .setScreenTitle(SliceTestUtils.FAKE_SCREEN_TITLE)
710                 .setIcon(SliceTestUtils.FAKE_ICON)
711                 .setFragmentName(SliceTestUtils.FAKE_FRAGMENT_NAME)
712                 .setPreferenceControllerClassName(SliceTestUtils.FAKE_CONTROLLER_NAME)
713                 .setHighlightMenuRes(SliceTestUtils.FAKE_HIGHLIGHT_MENU_RES)
714                 .build();
715     }
716 
717     @Implements(value = StrictMode.class)
718     public static class ShadowStrictMode {
719 
720         private static int sSetThreadPolicyCount;
721 
722         @Resetter
reset()723         public static void reset() {
724             sSetThreadPolicyCount = 0;
725         }
726 
727         @Implementation
setThreadPolicy(final StrictMode.ThreadPolicy policy)728         protected static void setThreadPolicy(final StrictMode.ThreadPolicy policy) {
729             sSetThreadPolicyCount++;
730         }
731 
isThreadPolicyOverridden()732         private static boolean isThreadPolicyOverridden() {
733             return sSetThreadPolicyCount != 0;
734         }
735     }
736 
737     @Implements(Theme.class)
738     public static class ShadowTheme {
739         private static boolean sThemeRebased;
740 
741         @Resetter
reset()742         public static void reset() {
743             sThemeRebased = false;
744         }
745 
746         @Implementation
rebase()747         public void rebase() {
748             sThemeRebased = true;
749         }
750 
isThemeRebased()751         static boolean isThemeRebased() {
752             return sThemeRebased;
753         }
754     }
755 }
756