Merge "2nd attempt to fix Slice strict mode." into pi-dev

This commit is contained in:
TreeHugger Robot
2018-05-25 19:44:32 +00:00
committed by Android (Google) Code Review
4 changed files with 116 additions and 52 deletions

View File

@@ -17,8 +17,6 @@ package com.android.settings.bluetooth;
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
import android.annotation.ColorInt; import android.annotation.ColorInt;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
@@ -28,6 +26,7 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.Uri; import android.net.Uri;
import android.provider.SettingsSlicesContract; import android.provider.SettingsSlicesContract;
import android.support.v4.graphics.drawable.IconCompat;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R; import com.android.settings.R;
@@ -42,8 +41,6 @@ import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder; import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction; import androidx.slice.builders.SliceAction;
import android.support.v4.graphics.drawable.IconCompat;
public class BluetoothSliceBuilder { public class BluetoothSliceBuilder {
private static final String TAG = "BluetoothSliceBuilder"; private static final String TAG = "BluetoothSliceBuilder";
@@ -71,7 +68,8 @@ public class BluetoothSliceBuilder {
INTENT_FILTER.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); INTENT_FILTER.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
} }
private BluetoothSliceBuilder() {} private BluetoothSliceBuilder() {
}
/** /**
* Return a Bluetooth Slice bound to {@link #BLUETOOTH_URI}. * Return a Bluetooth Slice bound to {@link #BLUETOOTH_URI}.
@@ -80,7 +78,7 @@ public class BluetoothSliceBuilder {
* Bluetooth. * Bluetooth.
*/ */
public static Slice getSlice(Context context) { public static Slice getSlice(Context context) {
final boolean isBluetoothEnabled = isBluetoothEnabled(context); final boolean isBluetoothEnabled = isBluetoothEnabled();
final CharSequence title = context.getText(R.string.bluetooth_settings); final CharSequence title = context.getText(R.string.bluetooth_settings);
final IconCompat icon = IconCompat.createWithResource(context, final IconCompat icon = IconCompat.createWithResource(context,
R.drawable.ic_settings_bluetooth); R.drawable.ic_settings_bluetooth);
@@ -115,9 +113,8 @@ public class BluetoothSliceBuilder {
// handle it. // handle it.
} }
private static boolean isBluetoothEnabled(Context context) { private static boolean isBluetoothEnabled() {
final LocalBluetoothAdapter adapter = LocalBluetoothManager.getInstance(context, final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
null /* callback */).getBluetoothAdapter();
return adapter.isEnabled(); return adapter.isEnabled();
} }

View File

@@ -23,23 +23,23 @@ import android.content.ContentResolver;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.Uri; import android.net.Uri;
import android.os.StrictMode;
import android.provider.Settings; import android.provider.Settings;
import android.provider.SettingsSlicesContract; import android.provider.SettingsSlicesContract;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v4.graphics.drawable.IconCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.KeyValueListParser; import android.util.KeyValueListParser;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import com.android.settings.location.LocationSliceBuilder; import com.android.settings.bluetooth.BluetoothSliceBuilder;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.location.LocationSliceBuilder;
import com.android.settings.notification.ZenModeSliceBuilder;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.wifi.WifiSliceBuilder; import com.android.settings.wifi.WifiSliceBuilder;
import com.android.settings.wifi.calling.WifiCallingSliceHelper; import com.android.settings.wifi.calling.WifiCallingSliceHelper;
import com.android.settings.bluetooth.BluetoothSliceBuilder;
import com.android.settings.notification.ZenModeSliceBuilder;
import com.android.settingslib.SliceBroadcastRelay; import com.android.settingslib.SliceBroadcastRelay;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
@@ -150,7 +150,7 @@ public class SettingsSliceProvider extends SliceProvider {
@Override @Override
public void onSlicePinned(Uri sliceUri) { public void onSlicePinned(Uri sliceUri) {
if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) {
registerIntentToUri(WifiSliceBuilder.INTENT_FILTER , sliceUri); registerIntentToUri(WifiSliceBuilder.INTENT_FILTER, sliceUri);
return; return;
} else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) { } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) {
registerIntentToUri(ZenModeSliceBuilder.INTENT_FILTER, sliceUri); registerIntentToUri(ZenModeSliceBuilder.INTENT_FILTER, sliceUri);
@@ -176,41 +176,51 @@ public class SettingsSliceProvider extends SliceProvider {
@Override @Override
public Slice onBindSlice(Uri sliceUri) { public Slice onBindSlice(Uri sliceUri) {
final Set<String> blockedKeys = getBlockedKeys(); final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
final String key = sliceUri.getLastPathSegment(); try {
if (blockedKeys.contains(key)) { if (!ThreadUtils.isMainThread()) {
Log.e(TAG, "Requested blocked slice with Uri: " + sliceUri); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
return null; .permitAll()
} .build());
}
final Set<String> blockedKeys = getBlockedKeys();
final String key = sliceUri.getLastPathSegment();
if (blockedKeys.contains(key)) {
Log.e(TAG, "Requested blocked slice with Uri: " + sliceUri);
return null;
}
// If adding a new Slice, do not directly match Slice URIs. // If adding a new Slice, do not directly match Slice URIs.
// Use {@link SlicesDatabaseAccessor}. // Use {@link SlicesDatabaseAccessor}.
if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) { if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) {
return FeatureFactory.getFactory(getContext()) return FeatureFactory.getFactory(getContext())
.getSlicesFeatureProvider() .getSlicesFeatureProvider()
.getNewWifiCallingSliceHelper(getContext()) .getNewWifiCallingSliceHelper(getContext())
.createWifiCallingSlice(sliceUri); .createWifiCallingSlice(sliceUri);
} else if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { } else if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) {
return WifiSliceBuilder.getSlice(getContext()); return WifiSliceBuilder.getSlice(getContext());
} else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) { } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) {
return ZenModeSliceBuilder.getSlice(getContext()); return ZenModeSliceBuilder.getSlice(getContext());
} else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) { } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) {
return BluetoothSliceBuilder.getSlice(getContext()); return BluetoothSliceBuilder.getSlice(getContext());
} else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) { } else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) {
return LocationSliceBuilder.getSlice(getContext()); return LocationSliceBuilder.getSlice(getContext());
} }
SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri); SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri);
if (cachedSliceData == null) { if (cachedSliceData == null) {
loadSliceInBackground(sliceUri); loadSliceInBackground(sliceUri);
return getSliceStub(sliceUri); return getSliceStub(sliceUri);
} }
// Remove the SliceData from the cache after it has been used to prevent a memory-leak. // Remove the SliceData from the cache after it has been used to prevent a memory-leak.
if (!mSliceDataCache.containsKey(sliceUri)) { if (!mSliceDataCache.containsKey(sliceUri)) {
mSliceWeakDataCache.remove(sliceUri); mSliceWeakDataCache.remove(sliceUri);
}
return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
} }
return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData);
} }
/** /**

View File

@@ -18,9 +18,7 @@
package com.android.settings.slices; package com.android.settings.slices;
import static android.content.ContentResolver.SCHEME_CONTENT; import static android.content.ContentResolver.SCHEME_CONTENT;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -38,19 +36,24 @@ import android.os.StrictMode;
import android.provider.SettingsSlicesContract; import android.provider.SettingsSlicesContract;
import android.util.ArraySet; import android.util.ArraySet;
import com.android.settings.location.LocationSliceBuilder;
import com.android.settings.wifi.WifiSliceBuilder;
import com.android.settings.bluetooth.BluetoothSliceBuilder; import com.android.settings.bluetooth.BluetoothSliceBuilder;
import com.android.settings.location.LocationSliceBuilder;
import com.android.settings.notification.ZenModeSliceBuilder; import com.android.settings.notification.ZenModeSliceBuilder;
import com.android.settings.testutils.DatabaseTestUtils; import com.android.settings.testutils.DatabaseTestUtils;
import com.android.settings.testutils.FakeToggleController; import com.android.settings.testutils.FakeToggleController;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settings.wifi.WifiSliceBuilder;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -66,6 +69,7 @@ import androidx.slice.Slice;
* TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider) * TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
*/ */
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = ShadowThreadUtils.class)
public class SettingsSliceProviderTest { public class SettingsSliceProviderTest {
private static final String KEY = "KEY"; private static final String KEY = "KEY";
@@ -98,6 +102,7 @@ public class SettingsSliceProviderTest {
public void setUp() { public void setUp() {
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mProvider = spy(new SettingsSliceProvider()); mProvider = spy(new SettingsSliceProvider());
ShadowStrictMode.reset();
mProvider.mSliceWeakDataCache = new HashMap<>(); mProvider.mSliceWeakDataCache = new HashMap<>();
mProvider.mSliceDataCache = new HashMap<>(); mProvider.mSliceDataCache = new HashMap<>();
mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext); mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
@@ -112,6 +117,7 @@ public class SettingsSliceProviderTest {
@After @After
public void cleanUp() { public void cleanUp() {
ShadowThreadUtils.reset();
DatabaseTestUtils.clearDb(mContext); DatabaseTestUtils.clearDb(mContext);
} }
@@ -184,7 +190,8 @@ public class SettingsSliceProviderTest {
} }
@Test @Test
public void onBindSlice_shouldNotOverrideStrictMode() { public void onBindSlice_mainThread_shouldNotOverrideStrictMode() {
ShadowThreadUtils.setIsMainThread(true);
final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy(); final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
SliceData data = getDummyData(); SliceData data = getDummyData();
mProvider.mSliceWeakDataCache.put(data.getUri(), data); mProvider.mSliceWeakDataCache.put(data.getUri(), data);
@@ -195,6 +202,18 @@ public class SettingsSliceProviderTest {
assertThat(newThreadPolicy.toString()).isEqualTo(oldThreadPolicy.toString()); 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 @Test
public void onBindSlice_requestsBlockedSlice_retunsNull() { public void onBindSlice_requestsBlockedSlice_retunsNull() {
final String blockedKey = "blocked_key"; final String blockedKey = "blocked_key";
@@ -456,7 +475,7 @@ public class SettingsSliceProviderTest {
mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values); mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values);
} }
private SliceData getDummyData() { private static SliceData getDummyData() {
return new SliceData.Builder() return new SliceData.Builder()
.setKey(KEY) .setKey(KEY)
.setTitle(TITLE) .setTitle(TITLE)
@@ -468,4 +487,24 @@ public class SettingsSliceProviderTest {
.setPreferenceControllerClassName(PREF_CONTROLLER) .setPreferenceControllerClassName(PREF_CONTROLLER)
.build(); .build();
} }
@Implements(value = StrictMode.class, inheritImplementationMethods = true)
public static class ShadowStrictMode {
private static int sSetThreadPolicyCount;
@Resetter
public static void reset() {
sSetThreadPolicyCount = 0;
}
@Implementation
public static void setThreadPolicy(final StrictMode.ThreadPolicy policy) {
sSetThreadPolicyCount++;
}
public static boolean isThreadPolicyOverridden() {
return sSetThreadPolicyCount != 0;
}
}
} }

View File

@@ -20,10 +20,18 @@ import com.android.settingslib.utils.ThreadUtils;
import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements; import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(ThreadUtils.class) @Implements(ThreadUtils.class)
public class ShadowThreadUtils { public class ShadowThreadUtils {
private static boolean sIsMainThread = true;
@Resetter
public static void reset() {
sIsMainThread = true;
}
@Implementation @Implementation
public static void postOnBackgroundThread(Runnable runnable) { public static void postOnBackgroundThread(Runnable runnable) {
runnable.run(); runnable.run();
@@ -33,4 +41,14 @@ public class ShadowThreadUtils {
public static void postOnMainThread(Runnable runnable) { public static void postOnMainThread(Runnable runnable) {
runnable.run(); runnable.run();
} }
@Implementation
public static boolean isMainThread() {
return sIsMainThread;
}
public static void setIsMainThread(boolean isMainThread) {
sIsMainThread = isMainThread;
}
} }