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:
jeffreyhuang
2017-10-27 15:33:26 -07:00
parent 9e1570c5f3
commit c682766176

View File

@@ -54,7 +54,9 @@ 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) {
sResourceOverrides.put(id, value); synchronized (sResourceOverrides) {
sResourceOverrides.put(id, value);
}
} }
public static void overrideResource(String name, Object value) { public static void overrideResource(String name, Object value) {
@@ -67,7 +69,9 @@ public class SettingsShadowResources extends ShadowResources {
} }
public static void reset() { public static void reset() {
sResourceOverrides.clear(); synchronized (sResourceOverrides) {
sResourceOverrides.clear();
}
} }
@Implementation @Implementation
@@ -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,59 +176,70 @@ 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) {
for (int i = 0; i < set.getAttributeCount(); ++i) { synchronized (set) {
String attributeValue = set.getAttributeValue(i); for (int i = 0; i < set.getAttributeCount(); ++i) {
Node node = ReflectionHelpers.callInstanceMethod( final String attributeValue = set.getAttributeValue(i);
XmlResourceParserImpl.class, set, "getAttributeAt", final Node node = ReflectionHelpers.callInstanceMethod(
ReflectionHelpers.ClassParameter.from(int.class, i)); XmlResourceParserImpl.class, set, "getAttributeAt",
if (attributeValue.contains("attr/fingerprint_layout_theme")) { ReflectionHelpers.ClassParameter.from(int.class, i));
// Workaround for https://github.com/robolectric/robolectric/issues/2641 if (attributeValue.contains("attr/fingerprint_layout_theme")) {
node.setNodeValue("@style/FingerprintLayoutTheme"); // Workaround for https://github.com/robolectric/robolectric/issues/2641
} else if (attributeValue.startsWith("@*android:string")) { node.setNodeValue("@style/FingerprintLayoutTheme");
node.setNodeValue("PLACEHOLDER"); } else if (attributeValue.startsWith("@*android:string")) {
node.setNodeValue("PLACEHOLDER");
}
} }
} }
} }
// 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.
// it here because it's private. // We can't use
List<Object /* OverlayedStyle */> overlayedStyles =
ReflectionHelpers.getField(appliedStyles, "styles"); // it here because it's private.
for (Object appliedStyle : overlayedStyles) { final List<Object /* OverlayedStyle */> overlayedStyles =
StyleResolver styleResolver = ReflectionHelpers.getField(appliedStyle, "style"); ReflectionHelpers.getField(appliedStyles, "styles");
List<StyleData> styleDatas = for (Object appliedStyle : overlayedStyles) {
ReflectionHelpers.getField(styleResolver, "styles"); final StyleResolver styleResolver = ReflectionHelpers.getField(appliedStyle,
for (StyleData styleData : styleDatas) { "style");
if (styleData.getParent() != null && final List<StyleData> styleDatas =
styleData.getParent().startsWith("@*android:style")) { ReflectionHelpers.getField(styleResolver, "styles");
ReflectionHelpers.setField(StyleData.class, styleData, "parent", null); for (StyleData styleData : styleDatas) {
if (styleData.getParent() != null &&
styleData.getParent().startsWith("@*android:style")) {
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;