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