/* * * Copyright 2018 iQIYI.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.qiyi.pluginlibrary.utils; import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Build; import android.text.TextUtils; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class CpuAbiUtils { private static final String TAG = "CpuAbiUtils"; /* apk 中 lib 目录的前缀标示。比如 lib/armeabi/libshare_v2.so */ private static final String APK_LIB_DIR_PREFIX = "lib/"; /* libs目录so后缀 */ private static final String APK_LIB_SUFFIX = ".so"; /* 当前手机指令集 */ private static String currentInstructionSet = null; /* 当前APP运行的主abi */ private static String primaryCpuAbi = null; private static final Map<String, String> INSTRUCTION_SET_TO_ABI_MAP = new HashMap<>(); static { // arm --> armeabi, armeabi-v7a,不放入map INSTRUCTION_SET_TO_ABI_MAP.put("mips", "mips"); INSTRUCTION_SET_TO_ABI_MAP.put("mips64", "mips64"); INSTRUCTION_SET_TO_ABI_MAP.put("x86", "x86"); INSTRUCTION_SET_TO_ABI_MAP.put("x86_64", "x86_64"); INSTRUCTION_SET_TO_ABI_MAP.put("arm64", "arm64-v8a"); } /** * 获取当前运行的指令集 */ public static String getCurrentInstructionSet() { if (!TextUtils.isEmpty(currentInstructionSet)) { return currentInstructionSet; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // 5.0以上VMRuntime才有这个方法 try { Class<?> clazz = Class.forName("dalvik.system.VMRuntime"); Method currentGet = clazz.getDeclaredMethod("getCurrentInstructionSet"); currentGet.setAccessible(true); currentInstructionSet = (String) currentGet.invoke(null); return currentInstructionSet; } catch (Exception e) { e.printStackTrace(); } } else { // 4.x只支持arm指令集 currentInstructionSet = "arm"; } //默认返回arm指令集 return "arm"; } /** * 获取设备支持的abi列表 */ public static String[] getSupportAbis() { String[] cpuAbis; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cpuAbis = Build.SUPPORTED_ABIS; } else { cpuAbis = new String[]{Build.CPU_ABI, Build.CPU_ABI2}; } // avoid NPE if (cpuAbis == null) { cpuAbis = new String[0]; } return cpuAbis; } /** * 获取设备运行的cpuAbi模式 */ public static String getPrimaryCpuAbi(Context context) { if (!TextUtils.isEmpty(primaryCpuAbi)) { return primaryCpuAbi; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // 5.0以上Application才有primaryCpuAbi字段 try { ApplicationInfo appInfo = context.getApplicationInfo(); Field abiField = ApplicationInfo.class.getDeclaredField("primaryCpuAbi"); abiField.setAccessible(true); primaryCpuAbi = (String) abiField.get(appInfo); } catch (Exception e) { e.printStackTrace(); } } if (TextUtils.isEmpty(primaryCpuAbi)) { // 反射失败,读取指令集匹配 String instrSet = getCurrentInstructionSet(); String matchAbi = INSTRUCTION_SET_TO_ABI_MAP.get(instrSet); if (!TextUtils.isEmpty(matchAbi)) { primaryCpuAbi = matchAbi; } else { // arm指令集对应可能是armeabi或者armeabi-v7a,根据apk内的abi目录结构做匹配 primaryCpuAbi = findMatchedAbi(context); } } return primaryCpuAbi; } /** * 由于4.x的手机只能获取最多两个abi, 部分x86手机主ABI是x86,次ABI是armeabi-v7a * 但是兼容armeabi,因此在末尾追加一个armeabi配置 */ private static String[] getCompatSupportAbis() { ArrayList<String> compatAbis = new ArrayList<>(); String[] supportAbis = getSupportAbis(); boolean hasArmeabi = false; for (String cpuAbi : supportAbis) { if ("armeabi".equals(cpuAbi)) { hasArmeabi = true; } compatAbis.add(cpuAbi); } if (!hasArmeabi) { compatAbis.add("armeabi"); } String[] toArray = new String[compatAbis.size()]; return compatAbis.toArray(toArray); } /** * 根据宿主APK内放置的libs文件夹类型和 * 设备支持的abi列表选择最match的abi */ private static String findMatchedAbi(Context context) { String[] cpuAbis = getCompatSupportAbis(); boolean[] abiResolved = new boolean[cpuAbis.length]; String apkPath = context.getApplicationInfo().sourceDir; ZipFile zipFile = null; try { zipFile = new ZipFile(apkPath); Enumeration<? extends ZipEntry> entries = zipFile.entries(); ZipEntry entry; while (entries.hasMoreElements()) { entry = entries.nextElement(); String name = entry.getName(); if (!name.startsWith(APK_LIB_DIR_PREFIX) || !name.endsWith(APK_LIB_SUFFIX)) { continue; } String[] splits = name.split("/"); if (splits.length < 3) { continue; } // entry: lib/armeabi/libimagepipeline.so String arch = splits[1]; for (int index = 0; index < cpuAbis.length; index++) { if (TextUtils.equals(cpuAbis[index], arch)) { // 对应arch目录下存在so库 abiResolved[index] = true; break; } } } } catch (IOException e) { e.printStackTrace(); } finally { FileUtils.closeQuietly(zipFile); } String matchedAbi = null; for (int index = 0; index < cpuAbis.length; index++) { if (abiResolved[index]) { // 对应的abi目录下找到了so库 matchedAbi = cpuAbis[index]; break; } } // 宿主没有放置lib目录,则按默认的模式运行 if (TextUtils.isEmpty(matchedAbi)) { matchedAbi = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Build.SUPPORTED_ABIS[0] : Build.CPU_ABI; } return matchedAbi; } /** * 获取与primaryCpuAbi能够兼容的abi * 比如32位:armeabi与armeabi-v7a是兼容的 */ public static String getCompatCpuAbi(String primaryCpuAbi) { if (TextUtils.equals(primaryCpuAbi, "armeabi") && isCompatible("armeabi-v7a")) { // 主abi是armeabi,如果设备支持armeabi-v7a,兼容模式 return "armeabi-v7a"; } if (TextUtils.equals(primaryCpuAbi, "armeabi-v7a") && isCompatible("armeabi")) { // 主abi是armeabi-v7a,如果设备支持armeabi,兼容模式 return "armeabi"; } return ""; } /** * 指定的cpuArch是否与设备兼容 */ private static boolean isCompatible(String cpuArch) { String[] cpuAbis = getSupportAbis(); for (String cpuAbi : cpuAbis) { if (TextUtils.equals(cpuAbi, cpuArch)) { return true; } } return false; } /** * APP进程是否运行在64位模式 */ public static boolean is64Bit() { // 5.0以下默认不支持64位 boolean is64bit = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 6.0以上使用公开API获取 is64bit = android.os.Process.is64Bit(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // 反射VMRuntime.getRuntime().is64Bit()方法,5.0上有这个方法 Boolean result = null; try { Class<?> clazz = Class.forName("dalvik.system.VMRuntime"); Method getRuntime = clazz.getDeclaredMethod("getRuntime"); getRuntime.setAccessible(true); Object currentRuntime = getRuntime.invoke(null); Method get64Bit = clazz.getDeclaredMethod("is64Bit"); get64Bit.setAccessible(true); result = (Boolean)get64Bit.invoke(currentRuntime); } catch (Exception e) { e.printStackTrace(); } is64bit = result != null ? result : false; } return is64bit; } }