Make SettingsShadowResources Thread Safe
- Add synchronization locks to data structures in SettingsShadowResources to avoid ConcurrentModificationException - Make temporary variables final variables Change-Id: I39a894c67d0b5e5b21bc0b9aa89180ba6989f01d Fixes: 67850343 Test: make RunSettingsRoboTests -j40
This commit is contained in:
@@ -54,8 +54,10 @@ public class SettingsShadowResources extends ShadowResources {
|
|||||||
private static SparseArray<Object> sResourceOverrides = new SparseArray<>();
|
private static SparseArray<Object> sResourceOverrides = new SparseArray<>();
|
||||||
|
|
||||||
public static void overrideResource(int id, Object value) {
|
public static void overrideResource(int id, Object value) {
|
||||||
|
synchronized (sResourceOverrides) {
|
||||||
sResourceOverrides.put(id, value);
|
sResourceOverrides.put(id, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void overrideResource(String name, Object value) {
|
public static void overrideResource(String name, Object value) {
|
||||||
final Resources res = application.getResources();
|
final Resources res = application.getResources();
|
||||||
@@ -67,8 +69,10 @@ public class SettingsShadowResources extends ShadowResources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void reset() {
|
public static void reset() {
|
||||||
|
synchronized (sResourceOverrides) {
|
||||||
sResourceOverrides.clear();
|
sResourceOverrides.clear();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
|
public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
|
||||||
@@ -129,7 +133,10 @@ public class SettingsShadowResources extends ShadowResources {
|
|||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
public String getString(int id) {
|
public String getString(int id) {
|
||||||
final Object override = sResourceOverrides.get(id);
|
final Object override;
|
||||||
|
synchronized (sResourceOverrides) {
|
||||||
|
override = sResourceOverrides.get(id);
|
||||||
|
}
|
||||||
if (override instanceof String) {
|
if (override instanceof String) {
|
||||||
return (String) override;
|
return (String) override;
|
||||||
}
|
}
|
||||||
@@ -139,7 +146,10 @@ public class SettingsShadowResources extends ShadowResources {
|
|||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
public int getInteger(int id) {
|
public int getInteger(int id) {
|
||||||
final Object override = sResourceOverrides.get(id);
|
final Object override;
|
||||||
|
synchronized (sResourceOverrides) {
|
||||||
|
override = sResourceOverrides.get(id);
|
||||||
|
}
|
||||||
if (override instanceof Integer) {
|
if (override instanceof Integer) {
|
||||||
return (Integer) override;
|
return (Integer) override;
|
||||||
}
|
}
|
||||||
@@ -149,7 +159,10 @@ public class SettingsShadowResources extends ShadowResources {
|
|||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
public boolean getBoolean(int id) {
|
public boolean getBoolean(int id) {
|
||||||
final Object override = sResourceOverrides.get(id);
|
final Object override;
|
||||||
|
synchronized (sResourceOverrides) {
|
||||||
|
override = sResourceOverrides.get(id);
|
||||||
|
}
|
||||||
if (override instanceof Boolean) {
|
if (override instanceof Boolean) {
|
||||||
return (boolean) override;
|
return (boolean) override;
|
||||||
}
|
}
|
||||||
@@ -163,14 +176,18 @@ public class SettingsShadowResources extends ShadowResources {
|
|||||||
@RealObject
|
@RealObject
|
||||||
Theme realTheme;
|
Theme realTheme;
|
||||||
|
|
||||||
|
private ShadowAssetManager mAssetManager = shadowOf(
|
||||||
|
RuntimeEnvironment.application.getAssets());
|
||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
public TypedArray obtainStyledAttributes(
|
public TypedArray obtainStyledAttributes(
|
||||||
AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
|
AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
|
||||||
// Replace all private string references with a placeholder.
|
// Replace all private string references with a placeholder.
|
||||||
if (set != null) {
|
if (set != null) {
|
||||||
|
synchronized (set) {
|
||||||
for (int i = 0; i < set.getAttributeCount(); ++i) {
|
for (int i = 0; i < set.getAttributeCount(); ++i) {
|
||||||
String attributeValue = set.getAttributeValue(i);
|
final String attributeValue = set.getAttributeValue(i);
|
||||||
Node node = ReflectionHelpers.callInstanceMethod(
|
final Node node = ReflectionHelpers.callInstanceMethod(
|
||||||
XmlResourceParserImpl.class, set, "getAttributeAt",
|
XmlResourceParserImpl.class, set, "getAttributeAt",
|
||||||
ReflectionHelpers.ClassParameter.from(int.class, i));
|
ReflectionHelpers.ClassParameter.from(int.class, i));
|
||||||
if (attributeValue.contains("attr/fingerprint_layout_theme")) {
|
if (attributeValue.contains("attr/fingerprint_layout_theme")) {
|
||||||
@@ -181,41 +198,48 @@ public class SettingsShadowResources extends ShadowResources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Track down all styles and remove all inheritance from private styles.
|
// Track down all styles and remove all inheritance from private styles.
|
||||||
ShadowAssetManager assetManager = shadowOf(RuntimeEnvironment.application.getAssets());
|
final Map<Long, Object /* NativeTheme */> appliedStylesList =
|
||||||
Map<Long, Object /* NativeTheme */> appliedStylesList =
|
ReflectionHelpers.getField(mAssetManager, "nativeThemes");
|
||||||
ReflectionHelpers.getField(assetManager, "nativeThemes");
|
synchronized (appliedStylesList) {
|
||||||
for (Long idx : appliedStylesList.keySet()) {
|
for (Long idx : appliedStylesList.keySet()) {
|
||||||
ThemeStyleSet appliedStyles = ReflectionHelpers.getField(
|
final ThemeStyleSet appliedStyles = ReflectionHelpers.getField(
|
||||||
appliedStylesList.get(idx), "themeStyleSet");
|
appliedStylesList.get(idx), "themeStyleSet");
|
||||||
// The Object's below are actually ShadowAssetManager.OverlayedStyle. We can't use
|
// The Object's below are actually ShadowAssetManager.OverlayedStyle.
|
||||||
|
// We can't use
|
||||||
|
|
||||||
// it here because it's private.
|
// it here because it's private.
|
||||||
List<Object /* OverlayedStyle */> overlayedStyles =
|
final List<Object /* OverlayedStyle */> overlayedStyles =
|
||||||
ReflectionHelpers.getField(appliedStyles, "styles");
|
ReflectionHelpers.getField(appliedStyles, "styles");
|
||||||
for (Object appliedStyle : overlayedStyles) {
|
for (Object appliedStyle : overlayedStyles) {
|
||||||
StyleResolver styleResolver = ReflectionHelpers.getField(appliedStyle, "style");
|
final StyleResolver styleResolver = ReflectionHelpers.getField(appliedStyle,
|
||||||
List<StyleData> styleDatas =
|
"style");
|
||||||
|
final List<StyleData> styleDatas =
|
||||||
ReflectionHelpers.getField(styleResolver, "styles");
|
ReflectionHelpers.getField(styleResolver, "styles");
|
||||||
for (StyleData styleData : styleDatas) {
|
for (StyleData styleData : styleDatas) {
|
||||||
if (styleData.getParent() != null &&
|
if (styleData.getParent() != null &&
|
||||||
styleData.getParent().startsWith("@*android:style")) {
|
styleData.getParent().startsWith("@*android:style")) {
|
||||||
ReflectionHelpers.setField(StyleData.class, styleData, "parent", null);
|
ReflectionHelpers.setField(StyleData.class, styleData, "parent",
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return super.obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes);
|
return super.obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
|
public synchronized boolean resolveAttribute(int resid, TypedValue outValue,
|
||||||
|
boolean resolveRefs) {
|
||||||
// The real Resources instance in Robolectric tests somehow fails to find the
|
// The real Resources instance in Robolectric tests somehow fails to find the
|
||||||
// preferenceTheme attribute in the layout. Let's do it ourselves.
|
// preferenceTheme attribute in the layout. Let's do it ourselves.
|
||||||
if (getResources().getResourceName(resid)
|
if (getResources().getResourceName(resid)
|
||||||
.equals("com.android.settings:attr/preferenceTheme")) {
|
.equals("com.android.settings:attr/preferenceTheme")) {
|
||||||
int preferenceThemeResId =
|
final int preferenceThemeResId =
|
||||||
getResources().getIdentifier(
|
getResources().getIdentifier(
|
||||||
"PreferenceTheme", "style", "com.android.settings");
|
"PreferenceTheme", "style", "com.android.settings");
|
||||||
outValue.type = TYPE_REFERENCE;
|
outValue.type = TYPE_REFERENCE;
|
||||||
|
Reference in New Issue
Block a user