package com.lody.virtual.helper.compat; import android.annotation.TargetApi; import android.os.Build; import com.lody.virtual.helper.utils.Reflect; import com.lody.virtual.helper.utils.VLog; import java.io.File; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import mirror.com.android.internal.content.NativeLibraryHelper; import mirror.dalvik.system.VMRuntime; public class NativeLibraryHelperCompat { private static String TAG = NativeLibraryHelperCompat.class.getSimpleName(); public static 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 { return Reflect.on(NativeLibraryHelper.TYPE).call("copyNativeBinariesIfNeededLI", apkFile, sharedLibraryDir) .get(); } catch (Throwable e) { e.printStackTrace(); } return -1; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static int copyNativeBinariesAfterL(File apkFile, File sharedLibraryDir) { try { Object handle = NativeLibraryHelper.Handle.create.call(apkFile); if (handle == null) { return -1; } String abi = null; Set<String> abiSet = getABIsFromApk(apkFile.getAbsolutePath()); if (abiSet == null || abiSet.isEmpty()) { return 0; } boolean is64Bit = VMRuntime.is64Bit.call(VMRuntime.getRuntime.call()); if (is64Bit && isVM64(abiSet)) { if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { int abiIndex = NativeLibraryHelper.findSupportedAbi.call(handle, Build.SUPPORTED_64_BIT_ABIS); if (abiIndex >= 0) { abi = Build.SUPPORTED_64_BIT_ABIS[abiIndex]; } } } else { if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { int abiIndex = NativeLibraryHelper.findSupportedAbi.call(handle, Build.SUPPORTED_32_BIT_ABIS); if (abiIndex >= 0) { abi = Build.SUPPORTED_32_BIT_ABIS[abiIndex]; } } } if (abi == null) { VLog.e(TAG, "Not match any abi [%s].", apkFile.getPath()); return -1; } return NativeLibraryHelper.copyNativeBinaries.call(handle, sharedLibraryDir, abi); } catch (Throwable e) { VLog.d(TAG, "copyNativeBinaries with error : %s", e.getLocalizedMessage()); e.printStackTrace(); } return -1; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static boolean isVM64(Set<String> supportedABIs) { 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<String>(); 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); } } return supportedABIs; } catch (Exception e) { e.printStackTrace(); } return null; } }