A malicious app is able to obtain this pending intent.
It can then mutate all fields except for the action and
launch the intent. This can be used to launch any activity
with the ACTION_SETTINGS action.
So, we enfore assign the package name for this intent,
it only can launch the settings app.
Fix: 147355897
Test: a) Install the new settings apk, and it won't launch other screen.
(See details in bug)
b) Start the settings search, slice search results work as normal.
Change-Id: Ie954d8a4b7153d6a4cac40621f363b45185990f2
(cherry picked from commit b3c0a2a6c1)
Merged-In: Ie954d8a4b7153d6a4cac40621f363b45185990f2
726 lines
28 KiB
Java
726 lines
28 KiB
Java
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
package com.android.settings.slices;
|
|
|
|
import static android.content.ContentResolver.SCHEME_CONTENT;
|
|
import static android.content.pm.PackageManager.PERMISSION_DENIED;
|
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.anyInt;
|
|
import static org.mockito.ArgumentMatchers.anyString;
|
|
import static org.mockito.ArgumentMatchers.eq;
|
|
import static org.mockito.Mockito.doReturn;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.never;
|
|
import static org.mockito.Mockito.spy;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
import android.app.PendingIntent;
|
|
import android.app.slice.SliceManager;
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.StrictMode;
|
|
import android.provider.Settings;
|
|
import android.provider.SettingsSlicesContract;
|
|
import android.util.ArraySet;
|
|
import android.view.accessibility.AccessibilityManager;
|
|
|
|
import androidx.slice.Slice;
|
|
import androidx.slice.SliceProvider;
|
|
import androidx.slice.widget.SliceLiveData;
|
|
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.testutils.DatabaseTestUtils;
|
|
import com.android.settings.testutils.FakeToggleController;
|
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
|
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
|
import com.android.settings.testutils.shadow.ShadowThreadUtils;
|
|
import com.android.settings.testutils.shadow.ShadowUserManager;
|
|
import com.android.settings.testutils.shadow.ShadowUtils;
|
|
import com.android.settings.wifi.slice.WifiScanWorker;
|
|
import com.android.settingslib.wifi.WifiTracker;
|
|
|
|
import org.junit.After;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.mockito.Mock;
|
|
import org.mockito.MockitoAnnotations;
|
|
import org.robolectric.Robolectric;
|
|
import org.robolectric.RobolectricTestRunner;
|
|
import org.robolectric.RuntimeEnvironment;
|
|
import org.robolectric.Shadows;
|
|
import org.robolectric.annotation.Config;
|
|
import org.robolectric.annotation.Implementation;
|
|
import org.robolectric.annotation.Implements;
|
|
import org.robolectric.annotation.Resetter;
|
|
import org.robolectric.shadow.api.Shadow;
|
|
import org.robolectric.shadows.ShadowAccessibilityManager;
|
|
import org.robolectric.shadows.ShadowBinder;
|
|
import org.robolectric.shadows.ShadowPackageManager;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
|
|
*/
|
|
@RunWith(RobolectricTestRunner.class)
|
|
@Config(shadows = {ShadowUserManager.class, ShadowUtils.class,
|
|
SlicesDatabaseAccessorTest.ShadowApplicationPackageManager.class,
|
|
ShadowBluetoothAdapter.class, ShadowLockPatternUtils.class,
|
|
SettingsSliceProviderTest.ShadowWifiScanWorker.class})
|
|
public class SettingsSliceProviderTest {
|
|
|
|
private static final String KEY = "KEY";
|
|
private static final Uri INTENT_SLICE_URI =
|
|
new Uri.Builder().scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_INTENT)
|
|
.appendPath(KEY)
|
|
.build();
|
|
private static final Uri ACTION_SLICE_URI =
|
|
new Uri.Builder().scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(KEY)
|
|
.build();
|
|
|
|
private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
|
|
|
|
private Context mContext;
|
|
private SettingsSliceProvider mProvider;
|
|
private ShadowPackageManager mPackageManager;
|
|
@Mock
|
|
private SliceManager mManager;
|
|
|
|
private static final List<Uri> SPECIAL_CASE_PLATFORM_URIS = Arrays.asList(
|
|
CustomSliceRegistry.WIFI_SLICE_URI,
|
|
CustomSliceRegistry.BLUETOOTH_URI,
|
|
CustomSliceRegistry.LOCATION_SLICE_URI
|
|
);
|
|
|
|
private static final List<Uri> SPECIAL_CASE_OEM_URIS = Arrays.asList(
|
|
CustomSliceRegistry.ZEN_MODE_SLICE_URI,
|
|
CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
|
|
CustomSliceRegistry.MOBILE_DATA_SLICE_URI,
|
|
CustomSliceRegistry.WIFI_CALLING_URI
|
|
);
|
|
|
|
@Before
|
|
public void setUp() {
|
|
MockitoAnnotations.initMocks(this);
|
|
mContext = spy(RuntimeEnvironment.application);
|
|
// Register the fake a11y Service
|
|
ShadowAccessibilityManager shadowAccessibilityManager = Shadow.extract(
|
|
RuntimeEnvironment.application.getSystemService(AccessibilityManager.class));
|
|
shadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
|
|
|
|
mProvider = spy(new SettingsSliceProvider());
|
|
ShadowStrictMode.reset();
|
|
mProvider.mSliceWeakDataCache = new HashMap<>();
|
|
mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
|
|
when(mProvider.getContext()).thenReturn(mContext);
|
|
|
|
SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
|
|
|
|
doReturn(mManager).when(mContext).getSystemService(SliceManager.class);
|
|
when(mManager.getPinnedSlices()).thenReturn(Collections.emptyList());
|
|
|
|
mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
|
|
|
|
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
|
|
}
|
|
|
|
@After
|
|
public void cleanUp() {
|
|
ShadowThreadUtils.reset();
|
|
DatabaseTestUtils.clearDb(mContext);
|
|
}
|
|
|
|
@Test
|
|
public void testInitialSliceReturned_emptySlice() {
|
|
SliceTestUtils.insertSliceToDb(mContext, KEY);
|
|
Slice slice = mProvider.onBindSlice(INTENT_SLICE_URI);
|
|
|
|
assertThat(slice.getUri()).isEqualTo(INTENT_SLICE_URI);
|
|
assertThat(slice.getItems()).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void testLoadSlice_returnsSliceFromAccessor() {
|
|
SliceTestUtils.insertSliceToDb(mContext, KEY);
|
|
|
|
mProvider.loadSlice(INTENT_SLICE_URI);
|
|
SliceData data = mProvider.mSliceWeakDataCache.get(INTENT_SLICE_URI);
|
|
|
|
assertThat(data.getKey()).isEqualTo(KEY);
|
|
assertThat(data.getTitle()).isEqualTo(SliceTestUtils.FAKE_TITLE);
|
|
}
|
|
|
|
@Test
|
|
public void loadSlice_registersIntentFilter() {
|
|
SliceTestUtils.insertSliceToDb(mContext, KEY);
|
|
|
|
mProvider.loadSlice(INTENT_SLICE_URI);
|
|
|
|
verify(mProvider)
|
|
.registerIntentToUri(eq(FakeToggleController.INTENT_FILTER), eq(INTENT_SLICE_URI));
|
|
}
|
|
|
|
@Test
|
|
public void loadSlice_registersBackgroundListener() {
|
|
SliceTestUtils.insertSliceToDb(mContext, KEY);
|
|
|
|
mProvider.loadSlice(INTENT_SLICE_URI);
|
|
|
|
Robolectric.flushForegroundThreadScheduler();
|
|
Robolectric.flushBackgroundThreadScheduler();
|
|
|
|
assertThat(mProvider.mPinnedWorkers.get(INTENT_SLICE_URI).getClass())
|
|
.isEqualTo(FakeToggleController.TestWorker.class);
|
|
}
|
|
|
|
@Test
|
|
public void testLoadSlice_cachedEntryRemovedOnBuild() {
|
|
SliceData data = getDummyData();
|
|
mProvider.mSliceWeakDataCache.put(data.getUri(), data);
|
|
mProvider.onBindSlice(data.getUri());
|
|
SliceTestUtils.insertSliceToDb(mContext, data.getKey());
|
|
|
|
SliceData cachedData = mProvider.mSliceWeakDataCache.get(data.getUri());
|
|
|
|
assertThat(cachedData).isNull();
|
|
}
|
|
|
|
@Test
|
|
public void onBindSlice_mainThread_shouldNotOverrideStrictMode() {
|
|
ShadowThreadUtils.setIsMainThread(true);
|
|
final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
|
|
SliceData data = getDummyData();
|
|
mProvider.mSliceWeakDataCache.put(data.getUri(), data);
|
|
mProvider.onBindSlice(data.getUri());
|
|
|
|
final StrictMode.ThreadPolicy newThreadPolicy = StrictMode.getThreadPolicy();
|
|
|
|
assertThat(newThreadPolicy.toString()).isEqualTo(oldThreadPolicy.toString());
|
|
}
|
|
|
|
@Test
|
|
@Config(shadows = ShadowStrictMode.class)
|
|
public void onBindSlice_backgroundThread_shouldOverrideStrictMode() {
|
|
ShadowThreadUtils.setIsMainThread(false);
|
|
|
|
SliceData data = getDummyData();
|
|
mProvider.mSliceWeakDataCache.put(data.getUri(), data);
|
|
mProvider.onBindSlice(data.getUri());
|
|
|
|
assertThat(ShadowStrictMode.isThreadPolicyOverridden()).isTrue();
|
|
}
|
|
|
|
@Test
|
|
public void onBindSlice_requestsBlockedSlice_returnsNull() {
|
|
final String blockedKey = "blocked_key";
|
|
final Set<String> blockedSet = new ArraySet<>();
|
|
blockedSet.add(blockedKey);
|
|
doReturn(blockedSet).when(mProvider).getBlockedKeys();
|
|
final Uri blockedUri = new Uri.Builder()
|
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(blockedKey)
|
|
.build();
|
|
|
|
final Slice slice = mProvider.onBindSlice(blockedUri);
|
|
|
|
assertThat(slice).isNull();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_fullActionUri_returnsSelf() {
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
|
|
|
|
assertThat(descendants).containsExactly(ACTION_SLICE_URI);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_fullIntentUri_returnsSelf() {
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
|
|
|
|
assertThat(descendants).containsExactly(ACTION_SLICE_URI);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_wrongPath_returnsEmpty() {
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath("invalid_path")
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_invalidPath_returnsEmpty() {
|
|
final String key = "platform_key";
|
|
SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath("invalid")
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
descendants.removeAll(SPECIAL_CASE_OEM_URIS);
|
|
|
|
assertThat(descendants).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_platformSlice_doesNotReturnOEMSlice() {
|
|
SliceTestUtils.insertSliceToDb(mContext, "oem_key", false /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
descendants.removeAll(SPECIAL_CASE_PLATFORM_URIS);
|
|
|
|
assertThat(descendants).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_oemSlice_doesNotReturnPlatformSlice() {
|
|
SliceTestUtils.insertSliceToDb(mContext, "platform_key", true /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
descendants.removeAll(SPECIAL_CASE_OEM_URIS);
|
|
|
|
assertThat(descendants).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_oemSlice_returnsOEMUriDescendant() {
|
|
final String key = "oem_key";
|
|
SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.build();
|
|
final Collection<Uri> expectedUris = new HashSet<>();
|
|
expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
|
|
expectedUris.add(new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build());
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactlyElementsIn(expectedUris);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant() {
|
|
final String key = "oem_key";
|
|
SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.build();
|
|
final Collection<Uri> expectedUris = new HashSet<>();
|
|
expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
|
|
expectedUris.add(new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build());
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactlyElementsIn(expectedUris);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_oemSliceNoPath_notContainPrivateUri() {
|
|
final String key = "oem_key";
|
|
SliceTestUtils.insertSliceToDb(mContext, key, false /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.build();
|
|
final Uri expectedUri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).doesNotContain(expectedUri);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_platformSlice_returnsPlatformUriDescendant() {
|
|
final String key = "platform_key";
|
|
SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.build();
|
|
final Collection<Uri> expectedUris = new HashSet<>();
|
|
expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
|
|
expectedUris.add(new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build());
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactlyElementsIn(expectedUris);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant() {
|
|
final String key = "platform_key";
|
|
SliceTestUtils.insertSliceToDb(mContext, key, true /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.build();
|
|
final Collection<Uri> expectedUris = new HashSet<>();
|
|
expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
|
|
expectedUris.add(new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build());
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactlyElementsIn(expectedUris);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_noAuthorityNorPath_returnsAllUris() {
|
|
final String platformKey = "platform_key";
|
|
final String oemKey = "oemKey";
|
|
SliceTestUtils.insertSliceToDb(mContext, platformKey, true /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
SliceTestUtils.insertSliceToDb(mContext, oemKey, false /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, true /* isPublicSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.build();
|
|
final Collection<Uri> expectedUris = new HashSet<>();
|
|
expectedUris.addAll(SPECIAL_CASE_PLATFORM_URIS);
|
|
expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
|
|
expectedUris.add(new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(platformKey)
|
|
.build());
|
|
expectedUris.add(new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(oemKey)
|
|
.build());
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactlyElementsIn(expectedUris);
|
|
}
|
|
|
|
@Test
|
|
@Config(qualifiers = "mcc999")
|
|
public void getDescendantUris_privateSlicesNeeded_containsPrivateSliceUri() {
|
|
final String privateKey = "test_private";
|
|
final Uri specialUri = Uri.parse("content://com.android.settings.slices/test");
|
|
doReturn(true).when(mProvider).isPrivateSlicesNeeded(specialUri);
|
|
SliceTestUtils.insertSliceToDb(mContext, privateKey /* key */, false /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
|
|
final Collection<Uri> expectedUris = new HashSet<>();
|
|
expectedUris.addAll(SPECIAL_CASE_OEM_URIS);
|
|
expectedUris.add(new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(privateKey)
|
|
.build());
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(specialUri);
|
|
|
|
assertThat(descendants).containsExactlyElementsIn(expectedUris);
|
|
}
|
|
|
|
@Test
|
|
@Config(qualifiers = "mcc999")
|
|
public void getDescendantUris_privateSlicesNotNeeded_notContainPrivateSliceUri() {
|
|
final Uri specialUri = Uri.parse("content://com.android.settings.slices/test");
|
|
doReturn(false).when(mProvider).isPrivateSlicesNeeded(specialUri);
|
|
SliceTestUtils.insertSliceToDb(mContext,
|
|
"test_private" /* key */, false /* isPlatformSlice */,
|
|
null /* customizedUnavailableSliceSubtitle */, false /* isPublicSlice */);
|
|
final Uri expectedUri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath("test_private")
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(specialUri);
|
|
|
|
assertThat(descendants).doesNotContain(expectedUri);
|
|
}
|
|
|
|
@Test
|
|
public void onCreatePermissionRequest_returnsSettingIntent() {
|
|
final PendingIntent pendingIntent = mProvider.onCreatePermissionRequest(
|
|
CustomSliceRegistry.FLASHLIGHT_SLICE_URI, "com.android.whaaaat");
|
|
final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS)
|
|
.setPackage(Utils.SETTINGS_PACKAGE_NAME);
|
|
PendingIntent settingsPendingIntent =
|
|
PendingIntent.getActivity(mContext, 0, settingsIntent, 0);
|
|
|
|
assertThat(pendingIntent).isEqualTo(settingsPendingIntent);
|
|
}
|
|
|
|
@Test
|
|
public void bindSlice_wifiSlice_returnsWifiSlice() {
|
|
final Slice wifiSlice = mProvider.onBindSlice(CustomSliceRegistry.WIFI_SLICE_URI);
|
|
|
|
assertThat(wifiSlice.getUri()).isEqualTo(CustomSliceRegistry.WIFI_SLICE_URI);
|
|
}
|
|
|
|
@Test
|
|
public void bindSlice_flashlightSlice_returnsFlashlightSlice() {
|
|
Settings.Secure.putInt(
|
|
mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
|
|
|
|
final Slice flashlightSlice = mProvider.onBindSlice(
|
|
CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
|
|
|
|
assertThat(flashlightSlice.getUri()).isEqualTo(CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
|
|
}
|
|
|
|
@Test
|
|
public void onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash() {
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(SettingsSlicesContract.KEY_LOCATION)
|
|
.build();
|
|
|
|
mProvider.onSlicePinned(uri);
|
|
}
|
|
|
|
@Test
|
|
public void onSlicePinned_backgroundWorker_started() {
|
|
mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
|
|
|
|
verify(ShadowWifiScanWorker.getWifiTracker()).onStart();
|
|
}
|
|
|
|
@Test
|
|
public void onSlicePinned_backgroundWorker_stopped() {
|
|
mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
|
|
mProvider.onSliceUnpinned(CustomSliceRegistry.WIFI_SLICE_URI);
|
|
|
|
verify(ShadowWifiScanWorker.getWifiTracker()).onStop();
|
|
}
|
|
|
|
@Test
|
|
public void shutdown_backgroundWorker_closed() {
|
|
mProvider.onSlicePinned(CustomSliceRegistry.WIFI_SLICE_URI);
|
|
mProvider.shutdown();
|
|
|
|
verify(ShadowWifiScanWorker.getWifiTracker()).onDestroy();
|
|
}
|
|
|
|
@Test
|
|
@Config(qualifiers = "mcc998")
|
|
public void grantWhitelistedPackagePermissions_noWhitelist_shouldNotGrant() {
|
|
final List<Uri> uris = new ArrayList<>();
|
|
uris.add(Uri.parse("content://settings/slice"));
|
|
|
|
SettingsSliceProvider.grantWhitelistedPackagePermissions(mContext, uris);
|
|
|
|
verify(mManager, never()).grantSlicePermission(anyString(), any(Uri.class));
|
|
}
|
|
|
|
@Test
|
|
@Config(qualifiers = "mcc999")
|
|
public void grantWhitelistedPackagePermissions_hasPackageWhitelist_shouldGrant() {
|
|
final List<Uri> uris = new ArrayList<>();
|
|
uris.add(Uri.parse("content://settings/slice"));
|
|
|
|
SettingsSliceProvider.grantWhitelistedPackagePermissions(mContext, uris);
|
|
|
|
verify(mManager)
|
|
.grantSlicePermission("com.android.settings.slice_whitelist_package", uris.get(0));
|
|
}
|
|
|
|
@Test
|
|
@Config(qualifiers = "mcc999")
|
|
public void isPrivateSlicesNeeded_incorrectUri_returnFalse() {
|
|
final Uri uri = Uri.parse("content://com.android.settings.slices/test123");
|
|
|
|
assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
|
|
}
|
|
|
|
@Test
|
|
public void isPrivateSlicesNeeded_noUri_returnFalse() {
|
|
final Uri uri = Uri.parse("content://com.android.settings.slices/test");
|
|
|
|
assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@Config(qualifiers = "mcc999")
|
|
public void isPrivateSlicesNeeded_correctUriWithPermissionAndIsSI_returnTrue() {
|
|
final Uri uri = Uri.parse("content://com.android.settings.slices/test");
|
|
ShadowBinder.setCallingUid(123);
|
|
doReturn(PERMISSION_GRANTED)
|
|
.when(mContext).checkPermission(anyString(), anyInt(), anyInt());
|
|
mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.intelligence"});
|
|
|
|
assertThat(mProvider.isPrivateSlicesNeeded(uri)).isTrue();
|
|
}
|
|
|
|
@Test
|
|
@Config(qualifiers = "mcc999")
|
|
public void isPrivateSlicesNeeded_correctUriWithPermissionNotSI_returnFalse() {
|
|
final Uri uri = Uri.parse("content://com.android.settings.slices/test");
|
|
ShadowBinder.setCallingUid(123);
|
|
doReturn(PERMISSION_GRANTED)
|
|
.when(mContext).checkPermission(anyString(), anyInt(), anyInt());
|
|
mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.test"});
|
|
|
|
assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@Config(qualifiers = "mcc999")
|
|
public void isPrivateSlicesNeeded_correctUriNoPermission_returnFalse() {
|
|
final Uri uri = Uri.parse("content://com.android.settings.slices/test");
|
|
ShadowBinder.setCallingUid(123);
|
|
doReturn(PERMISSION_DENIED).when(mContext).checkPermission(anyString(), anyInt(), anyInt());
|
|
mPackageManager.setPackagesForUid(123, new String[]{"com.android.settings.intelligence"});
|
|
|
|
assertThat(mProvider.isPrivateSlicesNeeded(uri)).isFalse();
|
|
}
|
|
|
|
private static SliceData getDummyData() {
|
|
return new SliceData.Builder()
|
|
.setKey(KEY)
|
|
.setUri(URI)
|
|
.setTitle(SliceTestUtils.FAKE_TITLE)
|
|
.setSummary(SliceTestUtils.FAKE_SUMMARY)
|
|
.setScreenTitle(SliceTestUtils.FAKE_SCREEN_TITLE)
|
|
.setIcon(SliceTestUtils.FAKE_ICON)
|
|
.setFragmentName(SliceTestUtils.FAKE_FRAGMENT_NAME)
|
|
.setPreferenceControllerClassName(SliceTestUtils.FAKE_CONTROLLER_NAME)
|
|
.build();
|
|
}
|
|
|
|
@Implements(WifiScanWorker.class)
|
|
public static class ShadowWifiScanWorker {
|
|
private static WifiTracker mWifiTracker;
|
|
|
|
@Implementation
|
|
protected void onSlicePinned() {
|
|
mWifiTracker = mock(WifiTracker.class);
|
|
mWifiTracker.onStart();
|
|
}
|
|
|
|
@Implementation
|
|
protected void onSliceUnpinned() {
|
|
mWifiTracker.onStop();
|
|
}
|
|
|
|
@Implementation
|
|
protected void close() {
|
|
mWifiTracker.onDestroy();
|
|
}
|
|
|
|
static WifiTracker getWifiTracker() {
|
|
return mWifiTracker;
|
|
}
|
|
}
|
|
|
|
@Implements(value = StrictMode.class)
|
|
public static class ShadowStrictMode {
|
|
|
|
private static int sSetThreadPolicyCount;
|
|
|
|
@Resetter
|
|
public static void reset() {
|
|
sSetThreadPolicyCount = 0;
|
|
}
|
|
|
|
@Implementation
|
|
protected static void setThreadPolicy(final StrictMode.ThreadPolicy policy) {
|
|
sSetThreadPolicyCount++;
|
|
}
|
|
|
|
private static boolean isThreadPolicyOverridden() {
|
|
return sSetThreadPolicyCount != 0;
|
|
}
|
|
}
|
|
}
|