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

Commit 55d9a604 authored by Winson Chiu's avatar Winson Chiu Committed by Android (Google) Code Review
Browse files

Merge "Add function to return path for last resolved resource"

parents 1f49aca8 2f3669b7
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -732,6 +732,38 @@ public final class AssetManager implements AutoCloseable {
        }
    }

    /**
     * Enable resource resolution logging to track the steps taken to resolve the last resource
     * entry retrieved. Stores the configuration and package names for each step.
     *
     * Default disabled.
     *
     * @param enabled Boolean indicating whether to enable or disable logging.
     *
     * @hide
     */
    public void setResourceResolutionLoggingEnabled(boolean enabled) {
        synchronized (this) {
            ensureValidLocked();
            nativeSetResourceResolutionLoggingEnabled(mObject, enabled);
        }
    }

    /**
     * Retrieve the last resource resolution path logged.
     *
     * @return Formatted string containing last resource ID/name and steps taken to resolve final
     * entry, including configuration and package names.
     *
     * @hide
     */
    public @Nullable String getLastResourceResolution() {
        synchronized (this) {
            ensureValidLocked();
            return nativeGetLastResourceResolution(mObject);
        }
    }

    CharSequence getPooledStringForCookie(int cookie, int id) {
        // Cookies map to ApkAssets starting at 1.
        return getApkAssets()[cookie - 1].getStringFromPool(id);
@@ -1383,6 +1415,8 @@ public final class AssetManager implements AutoCloseable {
    private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
    private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
    private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
    private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled);
    private static native @Nullable String nativeGetLastResourceResolution(long ptr);

    // Style attribute retrieval native methods.
    private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
+19 −5
Original line number Diff line number Diff line
@@ -2027,6 +2027,20 @@ public class Resources {
        return mResourcesImpl.getResourceEntryName(resid);
    }

    /**
     * Return formatted log of the last retrieved resource's resolution path.
     *
     * @return A string holding a formatted log of the steps taken to resolve the last resource.
     *
     * @throws NotFoundException Throws NotFoundException if there hasn't been a resource
     * resolved yet.
     *
     * @hide
     */
    public String getLastResourceResolution() throws NotFoundException {
        return mResourcesImpl.getLastResourceResolution();
    }
    
    /**
     * Parse a series of {@link android.R.styleable#Extra <extra>} tags from
     * an XML file.  You call this when you are at the parent tag of the
+7 −0
Original line number Diff line number Diff line
@@ -298,6 +298,13 @@ public class ResourcesImpl {
                + Integer.toHexString(resid));
    }

    @NonNull
    String getLastResourceResolution() throws NotFoundException {
        String str = mAssets.getLastResourceResolution();
        if (str != null) return str;
        throw new NotFoundException("Associated AssetManager hasn't resolved a resource");
    }

    @NonNull
    CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException {
        PluralRules rule = getPluralRule();
+27 −28
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@
#include "androidfw/MutexGuard.h"
#include "androidfw/PosixUtils.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"

#include "core_jni_helpers.h"
#include "jni.h"
#include "nativehelper/JNIHelp.h"
@@ -975,34 +977,7 @@ static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, j
    return nullptr;
  }

  std::string result;
  if (name.package != nullptr) {
    result.append(name.package, name.package_len);
  }

  if (name.type != nullptr || name.type16 != nullptr) {
    if (!result.empty()) {
      result += ":";
    }

    if (name.type != nullptr) {
      result.append(name.type, name.type_len);
    } else {
      result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
    }
  }

  if (name.entry != nullptr || name.entry16 != nullptr) {
    if (!result.empty()) {
      result += "/";
    }

    if (name.entry != nullptr) {
      result.append(name.entry, name.entry_len);
    } else {
      result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
    }
  }
  std::string result = ToFormattedResourceString(&name);
  return env->NewStringUTF(result.c_str());
}

@@ -1049,6 +1024,26 @@ static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong p
  return nullptr;
}

static void NativeSetResourceResolutionLoggingEnabled(JNIEnv* /*env*/,
                                                      jclass /*clazz*/,
                                                      jlong ptr,
                                                      jboolean enabled) {
  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
  assetmanager->SetResourceResolutionLoggingEnabled(enabled);
}

static jstring NativeGetLastResourceResolution(JNIEnv* env,
                                               jclass /*clazz*/,
                                               jlong ptr) {
  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
  std::string resolution = assetmanager->GetLastResourceResolution();
  if (resolution.empty()) {
    return nullptr;
  } else {
    return env->NewStringUTF(resolution.c_str());
  }
}

static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
                                     jboolean exclude_system) {
  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
@@ -1452,6 +1447,10 @@ static const JNINativeMethod gAssetManagerMethods[] = {
    {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
    {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
    {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
    {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
     (void*) NativeSetResourceResolutionLoggingEnabled},
    {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
     (void*) NativeGetLastResourceResolution},
    {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
    {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
     (void*)NativeGetSizeConfigurations},
+154 −53
Original line number Diff line number Diff line
@@ -20,8 +20,9 @@

#include <algorithm>
#include <iterator>
#include <set>
#include <map>
#include <set>
#include <sstream>

#include "android-base/logging.h"
#include "android-base/stringprintf.h"
@@ -372,6 +373,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
  uint32_t best_offset = 0u;
  uint32_t type_flags = 0u;

  Resolution::Step::Type resolution_type;
  std::vector<Resolution::Step> resolution_steps;

  // If desired_config is the same as the set configuration, then we can use our filtered list
  // and we don't need to match the configurations, since they already matched.
  const bool use_fast_path = desired_config == &configuration_;
@@ -403,8 +407,8 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
    // If the package is an overlay, then even configurations that are the same MUST be chosen.
    const bool package_is_overlay = loaded_package->IsOverlay();

    const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
    if (use_fast_path) {
      const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
      const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
      const size_t type_count = candidate_configs.size();
      for (uint32_t i = 0; i < type_count; i++) {
@@ -412,21 +416,34 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri

        // We can skip calling ResTable_config::match() because we know that all candidate
        // configurations that do NOT match have been filtered-out.
        if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
            (package_is_overlay && this_config.compare(*best_config) == 0)) {
        if (best_config == nullptr) {
          resolution_type = Resolution::Step::Type::INITIAL;
        } else if (this_config.isBetterThan(*best_config, desired_config)) {
          resolution_type = Resolution::Step::Type::BETTER_MATCH;
        } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
          resolution_type = Resolution::Step::Type::OVERLAID;
        } else {
          continue;
        }

        // The configuration matches and is better than the previous selection.
        // Find the entry value if it exists for this configuration.
          const ResTable_type* type_chunk = filtered_group.types[i];
          const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx);
        const ResTable_type* type = filtered_group.types[i];
        const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx);
        if (offset == ResTable_type::NO_ENTRY) {
          continue;
        }

        best_cookie = cookie;
        best_package = loaded_package;
          best_type = type_chunk;
        best_type = type;
        best_config = &this_config;
        best_offset = offset;

        if (resource_resolution_logging_enabled_) {
          resolution_steps.push_back(Resolution::Step{resolution_type,
                                                      this_config.toString(),
                                                      &loaded_package->GetPackageName()});
        }
      }
    } else {
@@ -440,9 +457,20 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
        ResTable_config this_config;
        this_config.copyFromDtoH((*iter)->config);

        if (this_config.match(*desired_config)) {
          if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
              (package_is_overlay && this_config.compare(*best_config) == 0)) {
        if (!this_config.match(*desired_config)) {
          continue;
        }

        if (best_config == nullptr) {
          resolution_type = Resolution::Step::Type::INITIAL;
        } else if (this_config.isBetterThan(*best_config, desired_config)) {
          resolution_type = Resolution::Step::Type::BETTER_MATCH;
        } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
          resolution_type = Resolution::Step::Type::OVERLAID;
        } else {
          continue;
        }

        // The configuration matches and is better than the previous selection.
        // Find the entry value if it exists for this configuration.
        const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
@@ -456,7 +484,11 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
        best_config_copy = this_config;
        best_config = &best_config_copy;
        best_offset = offset;
          }

        if (resource_resolution_logging_enabled_) {
          resolution_steps.push_back(Resolution::Step{resolution_type,
                                                      this_config.toString(),
                                                      &loaded_package->GetPackageName()});
        }
      }
    }
@@ -478,44 +510,113 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
  out_entry->entry_string_ref =
      StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
  out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;

  if (resource_resolution_logging_enabled_) {
    last_resolution.resid = resid;
    last_resolution.cookie = best_cookie;
    last_resolution.steps = resolution_steps;

    // Cache only the type/entry refs since that's all that's needed to build name
    last_resolution.type_string_ref =
        StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
    last_resolution.entry_string_ref =
        StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
  }

  return best_cookie;
}

bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
  FindEntryResult entry;
  ApkAssetsCookie cookie =
      FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry);
void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
  resource_resolution_logging_enabled_ = enabled;

  if (!enabled) {
    last_resolution.cookie = kInvalidCookie;
    last_resolution.resid = 0;
    last_resolution.steps.clear();
    last_resolution.type_string_ref = StringPoolRef();
    last_resolution.entry_string_ref = StringPoolRef();
  }
}

std::string AssetManager2::GetLastResourceResolution() const {
  if (!resource_resolution_logging_enabled_) {
    LOG(ERROR) << "Must enable resource resolution logging before getting path.";
    return std::string();
  }

  auto cookie = last_resolution.cookie;
  if (cookie == kInvalidCookie) {
    return false;
    LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
    return std::string();
  }

  uint32_t resid = last_resolution.resid;
  std::vector<Resolution::Step>& steps = last_resolution.steps;

  ResourceName resource_name;
  std::string resource_name_string;

  const LoadedPackage* package =
      apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
  if (package == nullptr) {
    return false;

  if (package != nullptr) {
    ToResourceName(last_resolution.type_string_ref,
                   last_resolution.entry_string_ref,
                   package,
                   &resource_name);
    resource_name_string = ToFormattedResourceString(&resource_name);
  }

  std::stringstream log_stream;
  log_stream << base::StringPrintf("Resolution for 0x%08x ", resid)
            << resource_name_string
            << "\n\tFor config -"
            << configuration_.toString();

  std::string prefix;
  for (Resolution::Step step : steps) {
    switch (step.type) {
      case Resolution::Step::Type::INITIAL:
        prefix = "Found initial";
        break;
      case Resolution::Step::Type::BETTER_MATCH:
        prefix = "Found better";
        break;
      case Resolution::Step::Type::OVERLAID:
        prefix = "Overlaid";
        break;
    }

  out_name->package = package->GetPackageName().data();
  out_name->package_len = package->GetPackageName().size();
    if (!prefix.empty()) {
      log_stream << "\n\t" << prefix << ": " << *step.package_name;

  out_name->type = entry.type_string_ref.string8(&out_name->type_len);
  out_name->type16 = nullptr;
  if (out_name->type == nullptr) {
    out_name->type16 = entry.type_string_ref.string16(&out_name->type_len);
    if (out_name->type16 == nullptr) {
      return false;
      if (!step.config_name.isEmpty()) {
        log_stream << " -" << step.config_name;
      }
    }
  }

  return log_stream.str();
}

  out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len);
  out_name->entry16 = nullptr;
  if (out_name->entry == nullptr) {
    out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len);
    if (out_name->entry16 == nullptr) {
bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
  FindEntryResult entry;
  ApkAssetsCookie cookie =
      FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry);
  if (cookie == kInvalidCookie) {
    return false;
  }

  const LoadedPackage* package =
      apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
  if (package == nullptr) {
    return false;
  }
  return true;

  return ToResourceName(entry.type_string_ref,
                        entry.entry_string_ref,
                        package,
                        out_name);
}

bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
Loading