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 
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 = 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         mShadowUserManager = ShadowUserManager.getShadow();
163 
164         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
165     }
166 
167     @After
cleanUp()168     public void cleanUp() {
169         ShadowThreadUtils.reset();
170         ShadowTheme.reset();
171         DatabaseTestUtils.clearDb(mContext);
172     }
173 
174     @Test
testInitialSliceReturned_emptySlice()175     public void testInitialSliceReturned_emptySlice() {
176         SliceTestUtils.insertSliceToDb(mContext, KEY);
177         Slice slice = mProvider.onBindSlice(INTENT_SLICE_URI);
178 
179         assertThat(slice.getUri()).isEqualTo(INTENT_SLICE_URI);
180         assertThat(slice.getItems()).isEmpty();
181     }
182 
183     @Test
testLoadSlice_returnsSliceFromAccessor()184     public void testLoadSlice_returnsSliceFromAccessor() {
185         SliceTestUtils.insertSliceToDb(mContext, KEY);
186 
187         mProvider.loadSlice(INTENT_SLICE_URI);
188         SliceData data = mProvider.mSliceWeakDataCache.get(INTENT_SLICE_URI);
189 
190         assertThat(data.getKey()).isEqualTo(KEY);
191         assertThat(data.getTitle()).isEqualTo(SliceTestUtils.FAKE_TITLE);
192     }
193 
194     @Test
loadSlice_registersIntentFilter()195     public void loadSlice_registersIntentFilter() {
196         SliceTestUtils.insertSliceToDb(mContext, KEY);
197 
198         mProvider.loadSlice(INTENT_SLICE_URI);
199 
200         verify(mProvider)
201                 .registerIntentToUri(eq(FakeToggleController.INTENT_FILTER), eq(INTENT_SLICE_URI));
202     }
203 
204     @Ignore("b/314925256")
205     @Test
loadSlice_registersBackgroundListener()206     public void loadSlice_registersBackgroundListener() {
207         SliceTestUtils.insertSliceToDb(mContext, KEY);
208 
209         mProvider.loadSlice(INTENT_SLICE_URI);
210 
211         Robolectric.flushForegroundThreadScheduler();
212         Robolectric.flushBackgroundThreadScheduler();
213 
214         assertThat(mProvider.mPinnedWorkers.get(INTENT_SLICE_URI).getClass())
215                 .isEqualTo(FakeToggleController.TestWorker.class);
216     }
217 
218     @Test
testLoadSlice_cachedEntryRemovedOnUnpinned()219     public void testLoadSlice_cachedEntryRemovedOnUnpinned() {
220         SliceData data = getMockData();
221         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
222         mProvider.onSliceUnpinned(data.getUri());
223         SliceTestUtils.insertSliceToDb(mContext, data.getKey());
224 
225         SliceData cachedData = mProvider.mSliceWeakDataCache.get(data.getUri());
226 
227         assertThat(cachedData).isNull();
228     }
229 
230     @Test
onBindSlice_mainThread_shouldNotOverrideStrictMode()231     public void onBindSlice_mainThread_shouldNotOverrideStrictMode() {
232         ShadowThreadUtils.setIsMainThread(true);
233         final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
234         SliceData data = getMockData();
235         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
236         mProvider.onBindSlice(data.getUri());
237 
238         final StrictMode.ThreadPolicy newThreadPolicy = StrictMode.getThreadPolicy();
239 
240         assertThat(newThreadPolicy.toString()).isEqualTo(oldThreadPolicy.toString());
241     }
242 
243     @Test
244     @Config(shadows = ShadowStrictMode.class)
onBindSlice_backgroundThread_shouldOverrideStrictMode()245     public void onBindSlice_backgroundThread_shouldOverrideStrictMode() {
246         ShadowThreadUtils.setIsMainThread(false);
247 
248         SliceData data = getMockData();
249         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
250         mProvider.onBindSlice(data.getUri());
251 
252         assertThat(ShadowStrictMode.isThreadPolicyOverridden()).isTrue();
253     }
254 
255     @Test
onBindSlice_requestsBlockedSlice_returnsNull()256     public void onBindSlice_requestsBlockedSlice_returnsNull() {
257         final String blockedKey = "blocked_key";
258         final Set<String> blockedSet = new ArraySet<>();
259         blockedSet.add(blockedKey);
260         doReturn(blockedSet).when(mProvider).getBlockedKeys();
261         final Uri blockedUri = new Uri.Builder()
262                 .scheme(ContentResolver.SCHEME_CONTENT)
263                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
264                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
265                 .appendPath(blockedKey)
266                 .build();
267 
268         final Slice slice = mProvider.onBindSlice(blockedUri);
269 
270         assertThat(slice).isNull();
271     }
272 
273     @Test
onBindSlice_nightModeChanged_shouldReloadTheme()274     public void onBindSlice_nightModeChanged_shouldReloadTheme() {
275         mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_NO;
276         final SliceData data = getMockData();
277         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
278         mProvider.onBindSlice(data.getUri());
279 
280         mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_YES;
281         mProvider.onBindSlice(data.getUri());
282 
283         assertThat(ShadowTheme.isThemeRebased()).isTrue();
284     }
285 
286     @Test
onBindSlice_nightModeNotChanged_shouldNotReloadTheme()287     public void onBindSlice_nightModeNotChanged_shouldNotReloadTheme() {
288         mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_NO;
289         SliceData data = getMockData();
290         mProvider.mSliceWeakDataCache.put(data.getUri(), data);
291         mProvider.onBindSlice(data.getUri());
292 
293         mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_NO;
294         mProvider.onBindSlice(data.getUri());
295 
296         assertThat(ShadowTheme.isThemeRebased()).isFalse();
297     }
298 
299     @Test
onBindSlice_guestRestricted_returnsNull()300     public void onBindSlice_guestRestricted_returnsNull() {
301         final String key = "enable_usb_tethering";
302         mShadowUserManager.setGuestUser(true);
303         final Uri testUri = new Uri.Builder()
304             .scheme(ContentResolver.SCHEME_CONTENT)
305             .authority(SettingsSliceProvider.SLICE_AUTHORITY)
306             .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
307             .appendPath(key)
308             .build();
309 
310         final Slice slice = mProvider.onBindSlice(testUri);
311 
312         assertThat(slice).isNull();
313     }
314 
315     @Test
onBindSlice_notGuestRestricted_returnsNotNull()316     public void onBindSlice_notGuestRestricted_returnsNotNull() {
317         final String key = "enable_usb_tethering";
318         final Uri testUri = new Uri.Builder()
319             .scheme(ContentResolver.SCHEME_CONTENT)
320             .authority(SettingsSliceProvider.SLICE_AUTHORITY)
321             .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
322             .appendPath(key)
323             .build();
324 
325         final Slice slice = mProvider.onBindSlice(testUri);
326 
327         assertThat(slice).isNotNull();
328     }
329 
330     @Test
getDescendantUris_fullActionUri_returnsSelf()331     public void getDescendantUris_fullActionUri_returnsSelf() {
332         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
333 
334         assertThat(descendants).containsExactly(ACTION_SLICE_URI);
335     }
336 
337     @Test
getDescendantUris_fullIntentUri_returnsSelf()338     public void getDescendantUris_fullIntentUri_returnsSelf() {
339 
340         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
341 
342         assertThat(descendants).containsExactly(ACTION_SLICE_URI);
343     }
344 
345     @Test
getDescendantUris_wrongPath_returnsEmpty()346     public void getDescendantUris_wrongPath_returnsEmpty() {
347         final Uri uri = new Uri.Builder()
348                 .scheme(SCHEME_CONTENT)
349                 .authority(SettingsSlicesContract.AUTHORITY)
350                 .appendPath("invalid_path")
351                 .build();
352 
353         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
354 
355         assertThat(descendants).isEmpty();
356     }
357 
358     @Test
getDescendantUris_invalidPath_returnsEmpty()359     public void getDescendantUris_invalidPath_returnsEmpty() {
360         final String key = "platform_key";
361         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
362                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
363         final Uri uri = new Uri.Builder()
364                 .scheme(SCHEME_CONTENT)
365                 .authority(SettingsSlicesContract.AUTHORITY)
366                 .appendPath("invalid")
367                 .build();
368 
369         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
370         descendants.removeAll(SPECIAL_CASE_OEM_URIS);
371 
372         assertThat(descendants).isEmpty();
373     }
374 
375     @Test
getDescendantUris_platformSlice_doesNotReturnOEMSlice()376     public void getDescendantUris_platformSlice_doesNotReturnOEMSlice() {
377         SliceTestUtils.insertSliceToDb(mContext, "oem_key", false /* isPlatformSlice */,
378                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
379         final Uri uri = new Uri.Builder()
380                 .scheme(SCHEME_CONTENT)
381                 .authority(SettingsSlicesContract.AUTHORITY)
382                 .build();
383 
384         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
385         descendants.removeAll(SPECIAL_CASE_PLATFORM_URIS);
386 
387         assertThat(descendants).isEmpty();
388     }
389 
390     @Test
getDescendantUris_oemSlice_doesNotReturnPlatformSlice()391     public void getDescendantUris_oemSlice_doesNotReturnPlatformSlice() {
392         SliceTestUtils.insertSliceToDb(mContext, "platform_key", true /* isPlatformSlice */,
393                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
394         final Uri uri = new Uri.Builder()
395                 .scheme(SCHEME_CONTENT)
396                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
397                 .build();
398 
399         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
400         descendants.removeAll(SPECIAL_CASE_OEM_URIS);
401 
402         assertThat(descendants).isEmpty();
403     }
404 
405     @Test
getDescendantUris_oemSlice_returnsOEMUriDescendant()406     public void getDescendantUris_oemSlice_returnsOEMUriDescendant() {
407         final String key = "oem_key";
408         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
409                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
410         final Uri uri = new Uri.Builder()
411                 .scheme(SCHEME_CONTENT)
412                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
413                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
414                 .build();
415         final Collection<Uri> expectedUris = new HashSet<>();
416         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
417         expectedUris.add(new Uri.Builder()
418                 .scheme(SCHEME_CONTENT)
419                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
420                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
421                 .appendPath(key)
422                 .build());
423 
424         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
425 
426         assertThat(descendants).containsExactlyElementsIn(expectedUris);
427     }
428 
429     @Test
getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant()430     public void getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant() {
431         final String key = "oem_key";
432         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
433                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
434         final Uri uri = new Uri.Builder()
435                 .scheme(SCHEME_CONTENT)
436                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
437                 .build();
438         final Collection<Uri> expectedUris = new HashSet<>();
439         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
440         expectedUris.add(new Uri.Builder()
441                 .scheme(SCHEME_CONTENT)
442                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
443                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
444                 .appendPath(key)
445                 .build());
446 
447         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
448 
449         assertThat(descendants).containsExactlyElementsIn(expectedUris);
450     }
451 
452     @Test
getDescendantUris_oemSliceNoPath_notContainPrivateUri()453     public void getDescendantUris_oemSliceNoPath_notContainPrivateUri() {
454         final String key = "oem_key";
455         SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
456                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
457         final Uri uri = new Uri.Builder()
458                 .scheme(SCHEME_CONTENT)
459                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
460                 .build();
461         final Uri expectedUri = new Uri.Builder()
462                 .scheme(SCHEME_CONTENT)
463                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
464                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
465                 .appendPath(key)
466                 .build();
467 
468         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
469 
470         assertThat(descendants).doesNotContain(expectedUri);
471     }
472 
473     @Test
getDescendantUris_platformSlice_returnsPlatformUriDescendant()474     public void getDescendantUris_platformSlice_returnsPlatformUriDescendant() {
475         final String key = "platform_key";
476         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
477                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
478         final Uri uri = new Uri.Builder()
479                 .scheme(SCHEME_CONTENT)
480                 .authority(SettingsSlicesContract.AUTHORITY)
481                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
482                 .build();
483         final Collection<Uri> expectedUris = new HashSet<>();
484         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
485         expectedUris.add(new Uri.Builder()
486                 .scheme(SCHEME_CONTENT)
487                 .authority(SettingsSlicesContract.AUTHORITY)
488                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
489                 .appendPath(key)
490                 .build());
491 
492         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
493 
494         assertThat(descendants).containsExactlyElementsIn(expectedUris);
495     }
496 
497     @Test
getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant()498     public void getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant() {
499         final String key = "platform_key";
500         SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
501                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
502         final Uri uri = new Uri.Builder()
503                 .scheme(SCHEME_CONTENT)
504                 .authority(SettingsSlicesContract.AUTHORITY)
505                 .build();
506         final Collection<Uri> expectedUris = new HashSet<>();
507         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
508         expectedUris.add(new Uri.Builder()
509                 .scheme(SCHEME_CONTENT)
510                 .authority(SettingsSlicesContract.AUTHORITY)
511                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
512                 .appendPath(key)
513                 .build());
514 
515         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
516 
517         assertThat(descendants).containsExactlyElementsIn(expectedUris);
518     }
519 
520     @Test
getDescendantUris_noAuthorityNorPath_returnsAllUris()521     public void getDescendantUris_noAuthorityNorPath_returnsAllUris() {
522         final String platformKey = "platform_key";
523         final String oemKey = "oemKey";
524         SliceTestUtils.insertSliceToDb(mContext, platformKey, true /* isPlatformSlice */,
525                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
526         SliceTestUtils.insertSliceToDb(mContext, oemKey, false /* isPlatformSlice */,
527                 null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
528         final Uri uri = new Uri.Builder()
529                 .scheme(SCHEME_CONTENT)
530                 .build();
531         final Collection<Uri> expectedUris = new HashSet<>();
532         expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
533         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
534         expectedUris.add(new Uri.Builder()
535                 .scheme(SCHEME_CONTENT)
536                 .authority(SettingsSlicesContract.AUTHORITY)
537                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
538                 .appendPath(platformKey)
539                 .build());
540         expectedUris.add(new Uri.Builder()
541                 .scheme(SCHEME_CONTENT)
542                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
543                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
544                 .appendPath(oemKey)
545                 .build());
546 
547         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
548 
549         assertThat(descendants).containsExactlyElementsIn(expectedUris);
550     }
551 
552     @Test
553     @Config(qualifiers = "mcc999")
getDescendantUris_privateSlicesNeeded_containsPrivateSliceUri()554     public void getDescendantUris_privateSlicesNeeded_containsPrivateSliceUri() {
555         final String privateKey = "test_private";
556         final Uri specialUri = Uri.parse("content://com.android.settings.slices/test");
557         doReturn(true).when(mProvider).isPrivateSlicesNeeded(specialUri);
558         SliceTestUtils.insertSliceToDb(mContext, privateKey /* key */, false /* isPlatformSlice */,
559                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
560         final Collection<Uri> expectedUris = new HashSet<>();
561         expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
562         expectedUris.add(new Uri.Builder()
563                 .scheme(SCHEME_CONTENT)
564                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
565                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
566                 .appendPath(privateKey)
567                 .build());
568 
569         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(specialUri);
570 
571         assertThat(descendants).containsExactlyElementsIn(expectedUris);
572     }
573 
574     @Test
575     @Config(qualifiers = "mcc999")
getDescendantUris_privateSlicesNotNeeded_notContainPrivateSliceUri()576     public void getDescendantUris_privateSlicesNotNeeded_notContainPrivateSliceUri() {
577         final Uri specialUri = Uri.parse("content://com.android.settings.slices/test");
578         doReturn(false).when(mProvider).isPrivateSlicesNeeded(specialUri);
579         SliceTestUtils.insertSliceToDb(mContext,
580                 "test_private" /* key */, false /* isPlatformSlice */,
581                 null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
582         final Uri expectedUri = new Uri.Builder()
583                 .scheme(SCHEME_CONTENT)
584                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
585                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
586                 .appendPath("test_private")
587                 .build();
588 
589         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(specialUri);
590 
591         assertThat(descendants).doesNotContain(expectedUri);
592     }
593 
594     @Test
onCreatePermissionRequest_returnsSettingIntent()595     public void onCreatePermissionRequest_returnsSettingIntent() {
596         final PendingIntent pendingIntent = mProvider.onCreatePermissionRequest(
597                 CustomSliceRegistry.FLASHLIGHT_SLICE_URI, "com.android.whaaaat");
598         final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS)
599                 .setPackage(Utils.SETTINGS_PACKAGE_NAME);
600         PendingIntent settingsPendingIntent =
601                 PendingIntent.getActivity(mContext, 0, settingsIntent,
602                         PendingIntent.FLAG_IMMUTABLE);
603 
604         assertThat(pendingIntent).isEqualTo(settingsPendingIntent);
605     }
606 
607     @Test
bindSlice_flashlightSlice_returnsFlashlightSlice()608     public void bindSlice_flashlightSlice_returnsFlashlightSlice() {
609         Settings.Secure.putInt(
610                 mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
611 
612         final Slice flashlightSlice = mProvider.onBindSlice(
613                 CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
614 
615         assertThat(flashlightSlice.getUri()).isEqualTo(CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
616     }
617 
618     @Test
onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash()619     public void onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash() {
620         final Uri uri = new Uri.Builder()
621                 .scheme(ContentResolver.SCHEME_CONTENT)
622                 .authority(SettingsSlicesContract.AUTHORITY)
623                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
624                 .appendPath(SettingsSlicesContract.KEY_LOCATION)
625                 .build();
626 
627         mProvider.onSlicePinned(uri);
628     }
629 
630     @Test
631     @Config(qualifiers = "mcc998")
grantAllowlistedPackagePermissions_noAllowlist_shouldNotGrant()632     public void grantAllowlistedPackagePermissions_noAllowlist_shouldNotGrant() {
633         final List<Uri> uris = new ArrayList<>();
634         uris.add(Uri.parse("content://settings/slice"));
635 
636         SettingsSliceProvider.grantAllowlistedPackagePermissions(mContext, uris);
637 
638         verify(mManager, never()).grantSlicePermission(anyString(), any(Uri.class));
639     }
640 
641     @Test
642     @Config(qualifiers = "mcc999")
grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant()643     public void grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant() {
644         final List<Uri> uris = new ArrayList<>();
645         uris.add(Uri.parse("content://settings/slice"));
646 
647         SettingsSliceProvider.grantAllowlistedPackagePermissions(mContext, uris);
648 
649         verify(mManager)
650                 .grantSlicePermission("com.android.settings.slice_allowlist_package", uris.get(0));
651     }
652 
653     @Test
654     @Config(qualifiers = "mcc999")
isPrivateSlicesNeeded_incorrectUri_returnFalse()655     public void isPrivateSlicesNeeded_incorrectUri_returnFalse() {
656         final Uri uri = Uri.parse("content://com.android.settings.slices/test123");
657 
658         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
659     }
660 
661     @Test
isPrivateSlicesNeeded_noUri_returnFalse()662     public void isPrivateSlicesNeeded_noUri_returnFalse() {
663         final Uri uri = Uri.parse("content://com.android.settings.slices/test");
664 
665         assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
666     }
667 
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