blob: 4b5f4660a7bed817fa89d45e12b6108546b9d432 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.build.annotations.MainDex;
import java.util.Map;
/**
* This class provides JNI-related methods to the native library.
*/
@MainDex
public class JNIUtils {
private static final String TAG = "JNIUtils";
private static Boolean sSelectiveJniRegistrationEnabled;
private static ClassLoader sJniClassLoader;
/**
* Returns a ClassLoader which can load Java classes from the specified split.
*
* @param splitName Name of the split, or empty string for the base split.
*/
@CalledByNative
private static ClassLoader getSplitClassLoader(String splitName) {
if (!splitName.isEmpty()) {
boolean isInstalled = BundleUtils.isIsolatedSplitInstalled(splitName);
Log.i(TAG, "Init JNI Classloader for %s. isInstalled=%b", splitName, isInstalled);
if (!isInstalled && BundleUtils.isBundle()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Address race condition on global ApplicationInfo being updated.
// https://crbug.com/1395191
ApplicationInfo globalAppInfo =
ContextUtils.getApplicationContext().getApplicationInfo();
ApplicationInfo freshAppInfo =
PackageUtils.getApplicationPackageInfo(0).applicationInfo;
globalAppInfo.splitNames = freshAppInfo.splitNames;
globalAppInfo.splitSourceDirs = freshAppInfo.splitSourceDirs;
globalAppInfo.splitPublicSourceDirs = freshAppInfo.splitPublicSourceDirs;
isInstalled = BundleUtils.isIsolatedSplitInstalled(splitName);
Log.i(TAG, "Init JNI Classloader for %s. isInstalled=%b", splitName, isInstalled);
assert isInstalled
: "Should not hit splitcompat mode for Android T+. You might have a "
+ "generate_jni() target that declares split_name, but includes "
+ "a .java file from the base split (https://crbug.com/1394148).";
}
if (isInstalled) {
return BundleUtils.getOrCreateSplitClassLoader(splitName);
} else {
// Split was installed by PlayCore in "compat" mode, meaning that our base module's
// ClassLoader was patched to add the splits' dex file to it.
}
}
return sJniClassLoader != null ? sJniClassLoader : JNIUtils.class.getClassLoader();
}
/**
* Sets the ClassLoader to be used for loading Java classes from native.
*
* @param classLoader the ClassLoader to use.
*/
public static void setClassLoader(ClassLoader classLoader) {
sJniClassLoader = classLoader;
}
/**
* @return whether or not the current process supports selective JNI registration.
*/
@CalledByNative
public static boolean isSelectiveJniRegistrationEnabled() {
if (sSelectiveJniRegistrationEnabled == null) {
sSelectiveJniRegistrationEnabled = false;
}
return sSelectiveJniRegistrationEnabled;
}
/**
* Allow this process to selectively perform JNI registration. This must be called before
* loading native libraries or it will have no effect.
*/
public static void enableSelectiveJniRegistration() {
assert sSelectiveJniRegistrationEnabled == null;
sSelectiveJniRegistrationEnabled = true;
}
/**
* Helper to convert from java maps to two arrays for JNI.
*/
public static <K, V> void splitMap(Map<K, V> map, K[] outKeys, V[] outValues) {
assert map.size() == outKeys.length;
assert outValues.length == outKeys.length;
int i = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
outKeys[i] = entry.getKey();
outValues[i] = entry.getValue();
i++;
}
}
}