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