Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6827661 authored by jeffreyhuang's avatar jeffreyhuang
Browse files

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
parent 9e1570c5
Loading
Loading
Loading
Loading
+60 −36
Original line number Diff line number Diff line
@@ -54,8 +54,10 @@ public class SettingsShadowResources extends ShadowResources {
    private static SparseArray<Object> sResourceOverrides = new SparseArray<>();

    public static void overrideResource(int id, Object value) {
        synchronized (sResourceOverrides) {
            sResourceOverrides.put(id, value);
        }
    }

    public static void overrideResource(String name, Object value) {
        final Resources res = application.getResources();
@@ -67,8 +69,10 @@ public class SettingsShadowResources extends ShadowResources {
    }

    public static void reset() {
        synchronized (sResourceOverrides) {
            sResourceOverrides.clear();
        }
    }

    @Implementation
    public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
@@ -129,7 +133,10 @@ public class SettingsShadowResources extends ShadowResources {

    @Implementation
    public String getString(int id) {
        final Object override = sResourceOverrides.get(id);
        final Object override;
        synchronized (sResourceOverrides) {
            override = sResourceOverrides.get(id);
        }
        if (override instanceof String) {
            return (String) override;
        }
@@ -139,7 +146,10 @@ public class SettingsShadowResources extends ShadowResources {

    @Implementation
    public int getInteger(int id) {
        final Object override = sResourceOverrides.get(id);
        final Object override;
        synchronized (sResourceOverrides) {
            override = sResourceOverrides.get(id);
        }
        if (override instanceof Integer) {
            return (Integer) override;
        }
@@ -149,7 +159,10 @@ public class SettingsShadowResources extends ShadowResources {

    @Implementation
    public boolean getBoolean(int id) {
        final Object override = sResourceOverrides.get(id);
        final Object override;
        synchronized (sResourceOverrides) {
            override = sResourceOverrides.get(id);
        }
        if (override instanceof Boolean) {
            return (boolean) override;
        }
@@ -163,14 +176,18 @@ public class SettingsShadowResources extends ShadowResources {
        @RealObject
        Theme realTheme;

        private ShadowAssetManager mAssetManager = shadowOf(
                RuntimeEnvironment.application.getAssets());

        @Implementation
        public TypedArray obtainStyledAttributes(
                AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
            // Replace all private string references with a placeholder.
            if (set != null) {
                synchronized (set) {
                    for (int i = 0; i < set.getAttributeCount(); ++i) {
                    String attributeValue = set.getAttributeValue(i);
                    Node node = ReflectionHelpers.callInstanceMethod(
                        final String attributeValue = set.getAttributeValue(i);
                        final Node node = ReflectionHelpers.callInstanceMethod(
                                XmlResourceParserImpl.class, set, "getAttributeAt",
                                ReflectionHelpers.ClassParameter.from(int.class, i));
                        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.
            ShadowAssetManager assetManager = shadowOf(RuntimeEnvironment.application.getAssets());
            Map<Long, Object /* NativeTheme */> appliedStylesList =
                    ReflectionHelpers.getField(assetManager, "nativeThemes");
            final Map<Long, Object /* NativeTheme */> appliedStylesList =
                    ReflectionHelpers.getField(mAssetManager, "nativeThemes");
            synchronized (appliedStylesList) {
                for (Long idx : appliedStylesList.keySet()) {
                ThemeStyleSet appliedStyles = ReflectionHelpers.getField(
                    final ThemeStyleSet appliedStyles = ReflectionHelpers.getField(
                            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.
                List<Object /* OverlayedStyle */> overlayedStyles =
                    final List<Object /* OverlayedStyle */> overlayedStyles =
                            ReflectionHelpers.getField(appliedStyles, "styles");
                    for (Object appliedStyle : overlayedStyles) {
                    StyleResolver styleResolver = ReflectionHelpers.getField(appliedStyle, "style");
                    List<StyleData> styleDatas =
                        final StyleResolver styleResolver = ReflectionHelpers.getField(appliedStyle,
                                "style");
                        final List<StyleData> styleDatas =
                                ReflectionHelpers.getField(styleResolver, "styles");
                        for (StyleData styleData : styleDatas) {
                            if (styleData.getParent() != null &&
                                    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);
        }

        @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
            // preferenceTheme attribute in the layout. Let's do it ourselves.
            if (getResources().getResourceName(resid)
                    .equals("com.android.settings:attr/preferenceTheme")) {
                int preferenceThemeResId =
                final int preferenceThemeResId =
                        getResources().getIdentifier(
                                "PreferenceTheme", "style", "com.android.settings");
                outValue.type = TYPE_REFERENCE;