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

Commit 767e34fb authored by Ryan Mitchell's avatar Ryan Mitchell
Browse files

Rebase ThemeImpl rather than reallocate memory

Memory churn is high when swapping the ResourcesImpl of a Resources
object. Each time Resources#setImpl is invoked, all themes based on
that Resources object are assigned new ThemeImpl objects that are
created using the new ResourcesImpl.

ThemeImpls can only belong to one Theme object, so the old
implementation is discarded and the theme takes ownership of the new
ThemeImp.

This creates performance problems when framework overlays are toggled.
Toggling overlays targeting the framework causes all themes across all
processes to recreate and reallocate all of their themes. By rebasing
the ThemeImpl on the new ResourcesImpl without deallocating the native
theme memory, we reduce churn and produce less garbage that needs to
be garbage collected.

Bug: 141198925
Test: atest libandroidfw_tests
Test: atest ResourcesPerfWorkloads
Change-Id: I03fb31ee09c9cfdbd3c41bcf0b605607dab54ed7
parent 3c6480c8
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -1187,6 +1188,31 @@ public final class AssetManager implements AutoCloseable {
        }
    }

    AssetManager rebaseTheme(long themePtr, @NonNull AssetManager newAssetManager,
            @StyleRes int[] styleIds, @StyleRes boolean[] force, int count) {
        // Exchange ownership of the theme with the new asset manager.
        if (this != newAssetManager) {
            synchronized (this) {
                ensureValidLocked();
                decRefsLocked(themePtr);
            }
            synchronized (newAssetManager) {
                newAssetManager.ensureValidLocked();
                newAssetManager.incRefsLocked(themePtr);
            }
        }

        try {
            synchronized (newAssetManager) {
                newAssetManager.ensureValidLocked();
                nativeThemeRebase(newAssetManager.mObject, themePtr, styleIds, force, count);
            }
        } finally {
            Reference.reachabilityFence(newAssetManager);
        }
        return newAssetManager;
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) {
        synchronized (this) {
@@ -1557,9 +1583,10 @@ public final class AssetManager implements AutoCloseable {
    private static native void nativeThemeDestroy(long themePtr);
    private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
            boolean force);
    private static native void nativeThemeRebase(long ptr, long themePtr, @NonNull int[] styleIds,
            @NonNull boolean[] force, int styleSize);
    private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr,
            long srcAssetManagerPtr, long srcThemePtr);
    static native void nativeThemeClear(long themePtr);
    private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
            @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
    private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
+9 −9
Original line number Diff line number Diff line
@@ -341,7 +341,7 @@ public class Resources {

    /**
     * Set the underlying implementation (containing all the resources and caches)
     * and updates all Theme references to new implementations as well.
     * and updates all Theme implementations as well.
     * @hide
     */
    @UnsupportedAppUsage
@@ -353,14 +353,14 @@ public class Resources {
        mBaseApkAssetsSize = ArrayUtils.size(impl.getAssets().getApkAssets());
        mResourcesImpl = impl;

        // Create new ThemeImpls that are identical to the ones we have.
        // Rebase the ThemeImpls using the new ResourcesImpl.
        synchronized (mThemeRefs) {
            final int count = mThemeRefs.size();
            for (int i = 0; i < count; i++) {
                WeakReference<Theme> weakThemeRef = mThemeRefs.get(i);
                Theme theme = weakThemeRef != null ? weakThemeRef.get() : null;
                if (theme != null) {
                    theme.setNewResourcesImpl(mResourcesImpl);
                    theme.rebase(mResourcesImpl);
                }
            }
        }
@@ -1515,12 +1515,6 @@ public class Resources {
            }
        }

        void setNewResourcesImpl(ResourcesImpl resImpl) {
            synchronized (mLock) {
                mThemeImpl = resImpl.newThemeImpl(mThemeImpl.getKey());
            }
        }

        /**
         * Place new attribute values into the theme.  The style resource
         * specified by <var>resid</var> will be retrieved from this Theme's
@@ -1847,6 +1841,12 @@ public class Resources {
            }
        }

        void rebase(ResourcesImpl resImpl) {
            synchronized (mLock) {
                mThemeImpl.rebase(resImpl.mAssets);
            }
        }

        /**
         * Returns the resource ID for the style specified using {@code style="..."} in the
         * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise if not
+12 −18
Original line number Diff line number Diff line
@@ -1265,16 +1265,6 @@ public class ResourcesImpl {
        return new ThemeImpl();
    }

    /**
     * Creates a new ThemeImpl which is already set to the given Resources.ThemeKey.
     */
    ThemeImpl newThemeImpl(Resources.ThemeKey key) {
        ThemeImpl impl = new ThemeImpl();
        impl.mKey.setTo(key);
        impl.rebase();
        return impl;
    }

    public class ThemeImpl {
        /**
         * Unique key for the series of styles applied to this theme.
@@ -1282,7 +1272,7 @@ public class ResourcesImpl {
        private final Resources.ThemeKey mKey = new Resources.ThemeKey();

        @SuppressWarnings("hiding")
        private final AssetManager mAssets;
        private AssetManager mAssets;
        private final long mTheme;

        /**
@@ -1404,14 +1394,18 @@ public class ResourcesImpl {
         * {@link #applyStyle(int, boolean)}.
         */
        void rebase() {
            AssetManager.nativeThemeClear(mTheme);

            // Reapply the same styles in the same order.
            for (int i = 0; i < mKey.mCount; i++) {
                final int resId = mKey.mResId[i];
                final boolean force = mKey.mForce[i];
                mAssets.applyStyleToTheme(mTheme, resId, force);
            rebase(mAssets);
        }

        /**
         * Rebases the theme against the {@code newAssets} by re-applying the styles passed to
         * {@link #applyStyle(int, boolean)}.
         *
         * The theme will use {@code newAssets} for all future invocations of
         * {@link #applyStyle(int, boolean)}.
         */
        void rebase(AssetManager newAssets) {
            mAssets = mAssets.rebaseTheme(mTheme, newAssets, mKey.mResId, mKey.mForce, mKey.mCount);
        }

        /**
+34 −5
Original line number Diff line number Diff line
@@ -1264,6 +1264,38 @@ static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlon
  // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
}

static void NativeThemeRebase(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
                              jintArray style_ids, jbooleanArray force,
                              jint style_count) {
  // Lock both the original asset manager of the theme and the new asset manager to be used for the
  // theme.
  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));

  uint32_t* style_id_args = nullptr;
  if (style_ids != nullptr) {
    CHECK(style_count <= env->GetArrayLength(style_ids));
    style_id_args = reinterpret_cast<uint32_t*>(env->GetPrimitiveArrayCritical(style_ids, nullptr));
    if (style_id_args == nullptr) {
      return;
    }
  }

  jboolean* force_args = nullptr;
  if (force != nullptr) {
    CHECK(style_count <= env->GetArrayLength(force));
    force_args = reinterpret_cast<jboolean*>(env->GetPrimitiveArrayCritical(force, nullptr));
    if (force_args == nullptr) {
      env->ReleasePrimitiveArrayCritical(style_ids, style_id_args, JNI_ABORT);
      return;
    }
  }

  auto theme = reinterpret_cast<Theme*>(theme_ptr);
  theme->Rebase(&(*assetmanager), style_id_args, force_args, static_cast<size_t>(style_count));
  env->ReleasePrimitiveArrayCritical(style_ids, style_id_args, JNI_ABORT);
  env->ReleasePrimitiveArrayCritical(force, force_args, JNI_ABORT);
}

static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr,
                            jlong dst_theme_ptr, jlong src_asset_manager_ptr, jlong src_theme_ptr) {
  Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
@@ -1284,10 +1316,6 @@ static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manag
  }
}

static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
  reinterpret_cast<Theme*>(theme_ptr)->Clear();
}

static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
                                         jint resid, jobject typed_value,
                                         jboolean resolve_references) {
@@ -1448,8 +1476,9 @@ static const JNINativeMethod gAssetManagerMethods[] = {
    {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
    {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
    {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
    {"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase},

    {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
    {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
    {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
     (void*)NativeThemeGetAttributeValue},
    {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
+12 −0
Original line number Diff line number Diff line
@@ -1417,6 +1417,18 @@ base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid,
  return {};
}

void Theme::Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t* force,
                   size_t style_count) {
  ATRACE_NAME("Theme::Rebase");
  // Reset the entries without changing the vector capacity to prevent reallocations during
  // ApplyStyle.
  entries_.clear();
  asset_manager_ = am;
  for (size_t i = 0; i < style_count; i++) {
    ApplyStyle(style_ids[i], force[i]);
  }
}

std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const {

  constexpr const uint32_t kMaxIterations = 20;
Loading