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