package com.morgoo.helper.compat; import android.annotation.TargetApi; import android.os.Build; import com.morgoo.droidplugin.pm.PluginManager; import com.morgoo.droidplugin.reflect.MethodUtils; import com.morgoo.helper.Log; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class NativeLibraryHelperCompat { private static final String TAG = NativeLibraryHelperCompat.class.getSimpleName(); private static final Class nativeLibraryHelperClass() throws ClassNotFoundException { return Class.forName("com.android.internal.content.NativeLibraryHelper"); } private static final Class handleClass() throws ClassNotFoundException { return Class.forName("com.android.internal.content.NativeLibraryHelper$Handle"); } public static final int copyNativeBinaries(File apkFile, File sharedLibraryDir) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return copyNativeBinariesAfterL(apkFile, sharedLibraryDir); } else { return copyNativeBinariesBeforeL(apkFile, sharedLibraryDir); } } private static int copyNativeBinariesBeforeL(File apkFile, File sharedLibraryDir) { try { Object[] args = new Object[2]; args[0] = apkFile; args[1] = sharedLibraryDir; return (int) MethodUtils.invokeStaticMethod(nativeLibraryHelperClass(), "copyNativeBinariesIfNeededLI", args); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return -1; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static int copyNativeBinariesAfterL(File apkFile, File sharedLibraryDir) { try { Object handleInstance = MethodUtils.invokeStaticMethod(handleClass(), "create", apkFile); if (handleInstance == null) { return -1; } String abi = null; //在64位处理器中,如果导入的so库未包含64位的,比如只导入了armeabi,此时就会找不到该abi。 //应该在32位abi中再次寻找。 if (isVM64()) { if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { Set<String> abis = getAbisFromApk(apkFile.getAbsolutePath()); if (abis == null || abis.isEmpty()) { return 0; } int abiIndex = (int) MethodUtils.invokeStaticMethod(nativeLibraryHelperClass(), "findSupportedAbi", handleInstance, Build.SUPPORTED_64_BIT_ABIS); if (abiIndex >= 0) { abi = Build.SUPPORTED_64_BIT_ABIS[abiIndex]; } } } //else { //如果abi为空,再次查找。 if(abi == null){ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { Set<String> abis = getAbisFromApk(apkFile.getAbsolutePath()); if (abis == null || abis.isEmpty()) { return 0; } int abiIndex = (int) MethodUtils.invokeStaticMethod(nativeLibraryHelperClass(), "findSupportedAbi", handleInstance, Build.SUPPORTED_32_BIT_ABIS); if (abiIndex >= 0) { abi = Build.SUPPORTED_32_BIT_ABIS[abiIndex]; } } } if (abi == null) { return -1; } Object[] args = new Object[3]; args[0] = handleInstance; args[1] = sharedLibraryDir; args[2] = abi; return (int) MethodUtils.invokeStaticMethod(nativeLibraryHelperClass(), "copyNativeBinaries", args); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return -1; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static boolean isVM64() { Set<String> supportedAbis = getAbisFromApk(getHostApk()); if (Build.SUPPORTED_64_BIT_ABIS.length == 0) { return false; } if (supportedAbis == null || supportedAbis.isEmpty()) { return true; } for (String supportedAbi : supportedAbis) { if ("arm64-v8a".endsWith(supportedAbi) || "x86_64".equals(supportedAbi) || "mips64".equals(supportedAbi)) { return true; } } return false; } private static Set<String> getAbisFromApk(String apk) { try { ZipFile apkFile = new ZipFile(apk); Enumeration<? extends ZipEntry> entries = apkFile.entries(); Set<String> supportedAbis = new HashSet<>(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); if (name.contains("../")) { continue; } if (name.startsWith("lib/") && !entry.isDirectory() && name.endsWith(".so")) { String supportedAbi = name.substring(name.indexOf("/") + 1, name.lastIndexOf("/")); supportedAbis.add(supportedAbi); } } Log.d(TAG, "supportedAbis : %s", supportedAbis); return supportedAbis; } catch (Exception e) { Log.e(TAG, "get supportedAbis failure", e); } return null; } private static String getHostApk() { return PluginManager.getInstance().getHostContext().getApplicationInfo().sourceDir; } }