动态加载so文件方案实现

This commit is contained in:
gongduoxiang 2024-07-30 13:39:37 +08:00
parent 3a12d848e1
commit 1f66204031
31 changed files with 1612 additions and 19 deletions

View File

@ -75,6 +75,99 @@ android {
exclude 'lib/arm64-v8a/libfuai.so'
}
// rtc
exclude 'lib/arm64-v8a/libagora_ai_echo_cancellation_extension.so'
exclude 'lib/arm64-v8a/libagora_ai_noise_suppression_extension.so'
exclude 'lib/arm64-v8a/libagora_audio_beauty_extension.so'
exclude 'lib/arm64-v8a/libagora_clear_vision_extension.so'
exclude 'lib/arm64-v8a/libagora_content_inspect_extension.so'
exclude 'lib/arm64-v8a/libagora_face_capture_extension.so'
exclude 'lib/arm64-v8a/libagora_face_detection_extension.so'
exclude 'lib/arm64-v8a/libagora_lip_sync_extension.so'
exclude 'lib/arm64-v8a/libagora_screen_capture_extension.so'
exclude 'lib/arm64-v8a/libagora_segmentation_extension.so'
exclude 'lib/arm64-v8a/libagora_spatial_audio_extension.so'
exclude 'lib/arm64-v8a/libagora_video_av1_decoder_extension.so'
exclude 'lib/arm64-v8a/libagora_video_decoder_extension.so'
exclude 'lib/arm64-v8a/libagora_video_encoder_extension.so'
exclude 'lib/arm64-v8a/libagora_video_quality_analyzer_extension.so'
exclude 'lib/arm64-v8a/libagora-core.so'
exclude 'lib/arm64-v8a/libagora-fdkaac.so'
exclude 'lib/arm64-v8a/libagora-ffmpeg.so'
exclude 'lib/arm64-v8a/libagora-rtc-sdk.so'
exclude 'lib/arm64-v8a/libagora-soundtouch.so'
exclude 'lib/arm64-v8a/libvideo_dec.so'
exclude 'lib/arm64-v8a/libvideo_enc.so'
exclude 'lib/armeabi-v7a/libagora_ai_echo_cancellation_extension.so'
exclude 'lib/armeabi-v7a/libagora_ai_noise_suppression_extension.so'
exclude 'lib/armeabi-v7a/libagora_audio_beauty_extension.so'
exclude 'lib/armeabi-v7a/libagora_clear_vision_extension.so'
exclude 'lib/armeabi-v7a/libagora_content_inspect_extension.so'
exclude 'lib/armeabi-v7a/libagora_face_capture_extension.so'
exclude 'lib/armeabi-v7a/libagora_face_detection_extension.so'
exclude 'lib/armeabi-v7a/libagora_lip_sync_extension.so'
exclude 'lib/armeabi-v7a/libagora_screen_capture_extension.so'
exclude 'lib/armeabi-v7a/libagora_segmentation_extension.so'
exclude 'lib/armeabi-v7a/libagora_spatial_audio_extension.so'
exclude 'lib/armeabi-v7a/libagora_video_av1_decoder_extension.so'
exclude 'lib/armeabi-v7a/libagora_video_decoder_extension.so'
exclude 'lib/armeabi-v7a/libagora_video_encoder_extension.so'
exclude 'lib/armeabi-v7a/libagora_video_quality_analyzer_extension.so'
exclude 'lib/armeabi-v7a/libagora-core.so'
exclude 'lib/armeabi-v7a/libagora-fdkaac.so'
exclude 'lib/armeabi-v7a/libagora-ffmpeg.so'
exclude 'lib/armeabi-v7a/libagora-rtc-sdk.so'
exclude 'lib/armeabi-v7a/libagora-soundtouch.so'
exclude 'lib/armeabi-v7a/libvideo_dec.so'
exclude 'lib/armeabi-v7a/libvideo_enc.so'
exclude 'lib/x86/libagora_ai_echo_cancellation_extension.so'
exclude 'lib/x86/libagora_ai_noise_suppression_extension.so'
exclude 'lib/x86/libagora_audio_beauty_extension.so'
exclude 'lib/x86/libagora_clear_vision_extension.so'
exclude 'lib/x86/libagora_content_inspect_extension.so'
exclude 'lib/x86/libagora_face_capture_extension.so'
exclude 'lib/x86/libagora_face_detection_extension.so'
exclude 'lib/x86/libagora_lip_sync_extension.so'
exclude 'lib/x86/libagora_screen_capture_extension.so'
exclude 'lib/x86/libagora_segmentation_extension.so'
exclude 'lib/x86/libagora_spatial_audio_extension.so'
exclude 'lib/x86/libagora_video_av1_decoder_extension.so'
exclude 'lib/x86/libagora_video_decoder_extension.so'
exclude 'lib/x86/libagora_video_encoder_extension.so'
exclude 'lib/x86/libagora_video_quality_analyzer_extension.so'
exclude 'lib/x86/libagora-core.so'
exclude 'lib/x86/libagora-fdkaac.so'
exclude 'lib/x86/libagora-ffmpeg.so'
exclude 'lib/x86/libagora-rtc-sdk.so'
exclude 'lib/x86/libagora-soundtouch.so'
exclude 'lib/x86/libvideo_dec.so'
exclude 'lib/x86/libvideo_enc.so'
exclude 'lib/x86_64/libagora_ai_echo_cancellation_extension.so'
exclude 'lib/x86_64/libagora_ai_noise_suppression_extension.so'
exclude 'lib/x86_64/libagora_audio_beauty_extension.so'
exclude 'lib/x86_64/libagora_clear_vision_extension.so'
exclude 'lib/x86_64/libagora_content_inspect_extension.so'
exclude 'lib/x86_64/libagora_face_capture_extension.so'
exclude 'lib/x86_64/libagora_face_detection_extension.so'
exclude 'lib/x86_64/libagora_lip_sync_extension.so'
exclude 'lib/x86_64/libagora_screen_capture_extension.so'
exclude 'lib/x86_64/libagora_segmentation_extension.so'
exclude 'lib/x86_64/libagora_spatial_audio_extension.so'
exclude 'lib/x86_64/libagora_video_av1_decoder_extension.so'
exclude 'lib/x86_64/libagora_video_decoder_extension.so'
exclude 'lib/x86_64/libagora_video_encoder_extension.so'
exclude 'lib/x86_64/libagora_video_quality_analyzer_extension.so'
exclude 'lib/x86_64/libagora-core.so'
exclude 'lib/x86_64/libagora-fdkaac.so'
exclude 'lib/x86_64/libagora-ffmpeg.so'
exclude 'lib/x86_64/libagora-rtc-sdk.so'
exclude 'lib/x86_64/libagora-soundtouch.so'
exclude 'lib/x86_64/libvideo_dec.so'
exclude 'lib/x86_64/libvideo_enc.so'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_18
@ -288,7 +381,6 @@ android {
}
}
repositories {
flatDir {
dirs 'libs', '../libs'
@ -306,6 +398,7 @@ dependencies {
api project(':main')
//
api project(':video')
implementation project(path: ':lib_so')
annotationProcessor rootProject.ext.dependencies["arouter-compiler"]

View File

@ -25,6 +25,7 @@ import com.blankj.utilcode.util.Utils;
import com.facebook.appevents.AppEventsLogger;
import com.fm.openinstall.OpenInstall;
import com.google.gson.Gson;
import com.pdlive.lib_so.DynamicSoLauncher;
import com.yunbao.common.utils.LogUtils;
import com.tencent.imsdk.v2.V2TIMGroupMemberInfo;
import com.tencent.imsdk.v2.V2TIMManager;
@ -144,6 +145,17 @@ public class AppContext extends CommonAppContext {
if (!isMainProcess()) {
return;
}
String path = getFilesDir().getAbsolutePath() + "/dynamic_so/";
File file = new File(path);
if (!file.exists()) {
boolean b= file.mkdir();
Log.i("mLog","创建文件 "+b);
}
// 在合适的时候将自定义路径插入so检索路径 需要使用者自己负责在这个路径上有写入权限
DynamicSoLauncher.INSTANCE.initDynamicSoConfig(this, path, s -> {
// 处理一些自定义逻辑
return true;
});
CrashSaveBean.getInstance().setStartTime(System.currentTimeMillis());
//注册全局异常捕获
registerError();

View File

@ -1,7 +1,6 @@
package com.shayu.phonelive.activity;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@ -22,7 +21,6 @@ import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.app.NotificationManagerCompat;
@ -50,14 +48,12 @@ import com.yunbao.common.interfaces.CommonCallback;
import com.yunbao.common.manager.IMLoginManager;
import com.yunbao.common.manager.imrongcloud.RongcloudIMManager;
import com.yunbao.common.utils.DownloadUtil;
import com.yunbao.common.utils.DpUtil;
import com.yunbao.common.utils.L;
import com.yunbao.common.utils.LogUtil;
import com.yunbao.common.utils.MD5Util;
import com.yunbao.common.utils.RouteUtil;
import com.yunbao.common.utils.SpUtil;
import com.yunbao.common.utils.ToastUtil;
import com.yunbao.common.utils.WordUtil;
import com.yunbao.live.views.LauncherAdViewHolder;
import com.yunbao.main.activity.EntryActivity;
import com.yunbao.main.activity.MainActivity;
@ -592,4 +588,5 @@ public class LauncherActivity extends AppCompatActivity implements View.OnClickL
this.finish();
}
}
}

View File

@ -56,7 +56,7 @@ allprojects {
ext {
IS_PUBLISH_LOCAL=true
LIB_VERSION="1.0.6"
AGORA_RTC_SDK= 'io.agora.rtc:agora-special-full:4.2.6.12'
AGORA_RTC_SDK= 'io.agora.rtc:agora-special-full:4.2.6.14'
// AGORA_RTC_SDK= "${rootProject.rootDir.absolutePath}/sdk"
// AGORA_RTC_SDK="io.agora.rtc:full-sdk:4.2.6"
}

View File

@ -0,0 +1,240 @@
package com.yunbao.common.utils;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class LoadSoUtil {
public static void loadSo(Context context, onLoadSoListener listener) {
try {
String path = context.getFilesDir().getAbsolutePath() + "/dynamic_so";
File file = new File(path);
AssetManager assetManager = context.getAssets();
String[] files = assetManager.list("libso");
if (!file.exists()) {
boolean b = file.mkdir();
Log.i("mLog", "创建文件 " + b);
}
if (file.listFiles().length != files.length) {//说明之前已经保存了
file.delete();
file.mkdir();
Log.i("mLog", "需要拷贝so文件 ");
// 获取AssetManager对象
for (int i = 0; i < files.length; i++) {
loadLibrary(context, "libso/" + files[i], files[i]);
}
}
listener.ok();
Log.i("mLog", "拷贝so成功------------------------");
} catch (IOException e) {
Log.e("mLog", "拷贝so错误++++++++++++++++++++");
e.printStackTrace();
listener.error();
}
}
public static void loadSo2(Context context, onLoadSoListener listener) {
String path = context.getFilesDir().getAbsolutePath() + "/so_zip";
File file = new File(path);
if (!file.exists()) {
file.mkdir();
} else if (isLoadSo(context)) {
listener.ok();
return;
}
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10 * 5, TimeUnit.SECONDS)
.cookieJar(new CookieJar() {
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(@NotNull HttpUrl httpUrl, @NotNull List<Cookie> list) {
cookieStore.put(httpUrl.host(), list);
}
@NotNull
@Override
public List<Cookie> loadForRequest(@NotNull HttpUrl httpUrl) {
List<Cookie> cookies = cookieStore.get(httpUrl.host());
return cookies != null ? cookies : new ArrayList<>();
}
});
builder.hostnameVerifier((hostname, session) -> true);
//开始下载文件
downloadFile(builder.build(), "https://downs.yaoulive.com/x86.zip", new File(path + "/" + "libSo.zip"), new onLoadSoListener() {
@Override
public void ok() {
//下载完成开始解压 解压完成之后 区初始化声网数据
zip(context, path + "/" + "libSo.zip", listener);
}
@Override
public void error() {
listener.error();
}
});
}
private static void zip(Context context, String path, onLoadSoListener listener) {
ZipInputStream zis = null;
FileOutputStream fos = null;
try {
zis = new ZipInputStream(new FileInputStream(path));
ZipEntry ze;
String soPath = context.getFilesDir().getAbsolutePath() + "/dynamic_so";
File file = new File(soPath);
if (!file.exists()) {
file.mkdir();
}
while ((ze = zis.getNextEntry()) != null) {
String fileName = ze.getName();
File newFile = new File(soPath + File.separator + fileName);
fos = new FileOutputStream(newFile);
byte[] buffer = new byte[1024];
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
zis.closeEntry();
}
listener.ok();
} catch (IOException e) {
e.printStackTrace();
listener.error();
} finally {
try {
if (zis != null) zis.close();
if (fos != null) fos.close();
} catch (Exception e) {
}
}
}
private static void downloadFile(OkHttpClient mOkHttpClient, String url, File file, onLoadSoListener listener) {
Request request = new Request.Builder()
.get()
.url(url)
.build();
mOkHttpClient.newCall(request)
.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
if (listener != null) listener.error();
}
@Override
public void onResponse(Call call, Response response) {
if (response.isSuccessful()) {
InputStream is = null;
BufferedOutputStream bos = null;
try {
is = response.body().byteStream();
bos = new BufferedOutputStream(new FileOutputStream(file));
byte[] bytes = new byte[10240];
int len;
while ((len = is.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.flush();
if (listener != null) listener.ok();
} catch (Exception e) {
e.printStackTrace();
if (listener != null) listener.error();
} finally {
try {
if (bos != null) bos.close();
if (is != null) is.close();
} catch (Exception e) {
}
}
}
}
});
}
public static boolean isLoadSo(Context context) {
String pathSo = context.getFilesDir().getAbsolutePath() + "/dynamic_so";
File fileSo = new File(pathSo);
if (fileSo.listFiles().length == 22) {//说明之前已经保存了
Log.i("mLog", "已经下载了so 所有直接加载就行 ");
return true;
}
return false;
}
public static boolean loadLibrary(Context context, String oldFileName, String libName) throws IOException {
// 获取应用的私有模式的libs目录真实的目录是app-libs
String path = context.getFilesDir().getAbsolutePath() + "/dynamic_so/";
// 可以获取到assets资源文件的数据流
InputStream open = context.getAssets().open(oldFileName);
String new_file_name = path + libName;
// 把资源文件的数据流写入到app-libs目录下
if (!copyLibrary(open, new_file_name)) {
return false;
}
return true;
}
//从assets资源目录下复制到app-libs目录下
public static boolean copyLibrary(InputStream fileInputStream, String new_file) {
FileOutputStream fos = null;
try {
File file = new File(new_file);
fos = new FileOutputStream(file);
int dataSize;
byte[] dataBuffer = new byte[2048];
while ((dataSize = fileInputStream.read(dataBuffer)) != -1) {
fos.write(dataBuffer, 0, dataSize);
}
fos.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
Log.e("mLog", e.getMessage());
} finally {
try {
if (fos != null) fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
public interface onLoadSoListener {
void ok();
void error();
}
}

View File

@ -30,6 +30,7 @@ import com.yunbao.common.utils.L;
import com.yunbao.common.utils.StringUtil;
import com.yunbao.common.utils.ToastUtil;
import java.util.ArrayList;
import java.util.List;
import io.agora.rtc2.ChannelMediaOptions;
@ -62,6 +63,7 @@ public class SWAuManager extends BaseCacheManager {
private FrameLayout pkContainer2; //pk主播视图2
private FrameLayout pkContainer3; //pk主播视图3
private FrameLayout linkUserContainer;//连麦用户视图
private List<LiveBean> mDatas=new ArrayList<>();//保存数据 如果还没初始化的时候就把这个数据保存起来等加载完so再来添加
private int liveMicUid;
@ -113,6 +115,7 @@ public class SWAuManager extends BaseCacheManager {
config.mContext = mContext;
config.mAppId = CommonAppConfig.getSwAppId();
config.mEventHandler = mRtcEventHandler;
config.mNativeLibPath=mContext.getFilesDir().getAbsolutePath() + "/dynamic_so/";
// 创建并初始化 RtcEngine
mRtcEngine = (RtcEngineEx) RtcEngineEx.create(config);
} catch (Exception e) {
@ -441,9 +444,21 @@ public class SWAuManager extends BaseCacheManager {
}
public void preloadChannel(List<LiveBean> uids) {
if (mRtcEngine==null){
mDatas.addAll(uids);
}else {
for (int i = 0; i < uids.size(); i++) {
int code = mRtcEngine.preloadChannel(CommonAppConfig.SWToken, getChannelName(uids.get(i).getUid()), Integer.parseInt(CommonAppConfig.getInstance().getUid()));
L.eSw("设置秒开数据 uid" + uids.get(i).getUid() + " --- userName:" + uids.get(i).getUserNiceName() + " code " + code);
}
}
}
public void preloadChannel() {
if (mRtcEngine!=null && mDatas.size()>0){
for (int i = 0; i < mDatas.size(); i++) {
int code = mRtcEngine.preloadChannel(CommonAppConfig.SWToken, getChannelName(mDatas.get(i).getUid()), Integer.parseInt(CommonAppConfig.getInstance().getUid()));
L.eSw("设置秒开数据 uid" + mDatas.get(i).getUid() + " --- userName:" + mDatas.get(i).getUserNiceName() + " code " + code);
}
}
}
}

1
lib_so/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

31
lib_so/build.gradle Normal file
View File

@ -0,0 +1,31 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.pdlive.lib_so'
compileSdk rootProject.ext.android.compileSdkVersion
defaultConfig {
minSdk rootProject.ext.android.minSdkVersion
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_18
targetCompatibility JavaVersion.VERSION_18
}
}
dependencies {
api rootProject.ext.dependencies["appcompat-androidx"]
}

View File

21
lib_so/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@ -0,0 +1,9 @@
package com.pdlive.lib_so
import androidx.annotation.Keep
@Keep
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class DynamicLoad()

View File

@ -0,0 +1,65 @@
package com.pdlive.lib_so;
import android.content.Context;
import android.util.Log;
import com.pdlive.lib_so.elf.ElfParser;
import com.pdlive.lib_so.pathinsert.LoadLibraryUtils;
import java.io.File;
import java.io.IOException;
import java.util.List;
class DynamicSo {
public static void loadSoDynamically(File soFIle, String path) {
try {
ElfParser parser = null;
final List<String> dependencies;
try {
parser = new ElfParser(soFIle);
dependencies = parser.parseNeededDependencies();
} finally {
if (parser != null) {
parser.close();
}
}
//如果nativecpp3->nativecpptwo->nativecpp 则先加载 DynamicSo.loadStaticSo(nativecpptwo)此时nativecpp作为nativecpptwo的直接依赖被加载了
//不能直接加载nativecpp3导致加载直接依赖nativetwo的时候nativecpp没加载导致错误 这个可以优化比如递归
for (final String dependency : dependencies) {
try {
File file = new File(path + dependency);
if (file.exists()) {
//递归查找
loadSoDynamically(file, path);
} else {
// so文件不存在这个文件夹代表是ndk中的so如liblog.so则直接加载
// 把本来lib前缀和.so后缀去掉即可
String dependencySo = dependency.substring(3, dependency.length() - 3);
//在application已经注入了路径DynamicSo.insertPathToNativeSystem(this,file) 所以采用系统的加载就行
System.loadLibrary(dependencySo);
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException ignored) {
}
// 先把依赖项加载完再加载本身
// System.loadLibrary(soFIle.getName().substring(3, soFIle.getName().length() - 3));
Log.i("mLog","通过库加载so "+soFIle.getAbsolutePath());
System.load(soFIle.getAbsolutePath());
}
public static void insertPathToNativeSystem(Context context, File file) {
try {
LoadLibraryUtils.installNativeLibraryPath(context.getClassLoader(), file);
} catch (Throwable e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,46 @@
package com.pdlive.lib_so
import android.content.Context
import android.util.Log
import androidx.annotation.Keep
import java.io.File
@Keep
object DynamicSoLauncher {
private var soPath = ""
private var beforeSoLoadListener: ((soName: String) -> Boolean)? = null
fun initDynamicSoConfig(context: Context, soPath: String,beforeLoadListener: ((soName: String) -> Boolean)?=null) {
DynamicSoLauncher.soPath = soPath
beforeSoLoadListener = beforeLoadListener
DynamicSo.insertPathToNativeSystem(context, File(soPath))
}
fun loadSoDynamically(file: File) {
if (soPath.isEmpty()) {
throw RuntimeException("you must call initDynamicSoConfig first. The soPath is empty")
}
if(beforeSoLoadListener?.invoke(file.name) == false){
return
}
DynamicSo.loadSoDynamically(file, soPath)
}
fun loadSoDynamically(fileName: String) {
if (soPath.isEmpty()) {
throw RuntimeException("you must call initDynamicSoConfig first. The soPath is empty")
}
if(beforeSoLoadListener?.invoke(fileName) == false){
return
}
DynamicSo.loadSoDynamically(File(soPath + fileName), soPath)
}
// 插件调用
@JvmStatic
fun loadLibrary(soName: String) {
Log.e("hello", soName)
val wrapSoName = "lib${soName}.so"
loadSoDynamically(wrapSoName)
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Dynamic32Structure extends Elf.DynamicStructure {
public Dynamic32Structure(final ElfParser parser, final Elf.Header header,
long baseOffset, final int index) throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(header.bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
baseOffset = baseOffset + (index * 8);
tag = parser.readWord(buffer, baseOffset);
val = parser.readWord(buffer, baseOffset + 0x4);
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Dynamic64Structure extends Elf.DynamicStructure {
public Dynamic64Structure(final ElfParser parser, final Elf.Header header,
long baseOffset, final int index) throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(header.bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
baseOffset = baseOffset + (index * 16);
tag = parser.readLong(buffer, baseOffset);
val = parser.readLong(buffer, baseOffset + 0x8);
}
}

View File

@ -0,0 +1,64 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
public interface Elf {
abstract class Header {
public static final int ELFCLASS32 = 1; // 32 Bit ELF
public static final int ELFCLASS64 = 2; // 64 Bit ELF
public static final int ELFDATA2MSB = 2; // Big Endian, 2s complement
public boolean bigEndian;
public int type;
public long phoff;
public long shoff;
public int phentsize;
public int phnum;
public int shentsize;
public int shnum;
public int shstrndx;
abstract public SectionHeader getSectionHeader(int index) throws IOException;
abstract public ProgramHeader getProgramHeader(long index) throws IOException;
abstract public DynamicStructure getDynamicStructure(long baseOffset, int index)
throws IOException;
}
abstract class ProgramHeader {
public static final int PT_LOAD = 1; // Loadable segment
public static final int PT_DYNAMIC = 2; // Dynamic linking information
public long type;
public long offset;
public long vaddr;
public long memsz;
}
abstract class SectionHeader {
public long info;
}
abstract class DynamicStructure {
public static final int DT_NULL = 0; // Marks end of structure list
public static final int DT_NEEDED = 1; // Needed library
public static final int DT_STRTAB = 5; // String table
public long tag;
public long val; // Union with d_ptr
}
}

View File

@ -0,0 +1,59 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Elf32Header extends Elf.Header {
private final ElfParser parser;
public Elf32Header(final boolean bigEndian, final ElfParser parser) throws IOException {
this.bigEndian = bigEndian;
this.parser = parser;
final ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
type = parser.readHalf(buffer, 0x10);
phoff = parser.readWord(buffer, 0x1C);
shoff = parser.readWord(buffer, 0x20);
phentsize = parser.readHalf(buffer, 0x2A);
phnum = parser.readHalf(buffer, 0x2C);
shentsize = parser.readHalf(buffer, 0x2E);
shnum = parser.readHalf(buffer, 0x30);
shstrndx = parser.readHalf(buffer, 0x32);
}
@Override
public Elf.SectionHeader getSectionHeader(final int index) throws IOException {
return new Section32Header(parser, this, index);
}
@Override
public Elf.ProgramHeader getProgramHeader(final long index) throws IOException {
return new Program32Header(parser, this, index);
}
@Override
public Elf.DynamicStructure getDynamicStructure(final long baseOffset, final int index)
throws IOException {
return new Dynamic32Structure(parser, this, baseOffset, index);
}
}

View File

@ -0,0 +1,58 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Elf64Header extends Elf.Header {
private final ElfParser parser;
public Elf64Header(final boolean bigEndian, final ElfParser parser) throws IOException {
this.bigEndian = bigEndian;
this.parser = parser;
final ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
type = parser.readHalf(buffer, 0x10);
phoff = parser.readLong(buffer, 0x20);
shoff = parser.readLong(buffer, 0x28);
phentsize = parser.readHalf(buffer, 0x36);
phnum = parser.readHalf(buffer, 0x38);
shentsize = parser.readHalf(buffer, 0x3A);
shnum = parser.readHalf(buffer, 0x3C);
shstrndx = parser.readHalf(buffer, 0x3E);
}
@Override
public Elf.SectionHeader getSectionHeader(final int index) throws IOException {
return new Section64Header(parser, this, index);
}
@Override
public Elf.ProgramHeader getProgramHeader(final long index) throws IOException {
return new Program64Header(parser, this, index);
}
@Override
public Elf.DynamicStructure getDynamicStructure(final long baseOffset, final int index)
throws IOException {
return new Dynamic64Structure(parser, this, baseOffset, index);
}
}

View File

@ -0,0 +1,195 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.Closeable;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ElfParser implements Closeable, Elf {
private final int MAGIC = 0x464C457F;
private final FileChannel channel;
public ElfParser(File file) throws FileNotFoundException {
if (file == null || !file.exists()) {
throw new IllegalArgumentException("File is null or does not exist "+file.getAbsolutePath());
}
final FileInputStream inputStream = new FileInputStream(file);
this.channel = inputStream.getChannel();
}
public Header parseHeader() throws IOException {
channel.position(0L);
// Read in ELF identification to determine file class and endianness
final ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(ByteOrder.LITTLE_ENDIAN);
if (readWord(buffer, 0) != MAGIC) {
throw new IllegalArgumentException("Invalid ELF Magic!");
}
final short fileClass = readByte(buffer, 0x4);
final boolean bigEndian = (readByte(buffer, 0x5) == Header.ELFDATA2MSB);
if (fileClass == Header.ELFCLASS32) {
return new Elf32Header(bigEndian, this);
} else if (fileClass == Header.ELFCLASS64) {
return new Elf64Header(bigEndian, this);
}
throw new IllegalStateException("Invalid class type!");
}
public List<String> parseNeededDependencies() throws IOException {
channel.position(0);
final List<String> dependencies = new ArrayList<String>();
final Header header = parseHeader();
final ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(header.bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
long numProgramHeaderEntries = header.phnum;
if (numProgramHeaderEntries == 0xFFFF) {
/**
* Extended Numbering
*
* If the real number of program header table entries is larger than
* or equal to PN_XNUM(0xffff), it is set to sh_info field of the
* section header at index 0, and PN_XNUM is set to e_phnum
* field. Otherwise, the section header at index 0 is zero
* initialized, if it exists.
**/
final SectionHeader sectionHeader = header.getSectionHeader(0);
numProgramHeaderEntries = sectionHeader.info;
}
long dynamicSectionOff = 0;
for (long i = 0; i < numProgramHeaderEntries; ++i) {
final ProgramHeader programHeader = header.getProgramHeader(i);
if (programHeader.type == ProgramHeader.PT_DYNAMIC) {
dynamicSectionOff = programHeader.offset;
break;
}
}
if (dynamicSectionOff == 0) {
// No dynamic linking info, nothing to load
return Collections.unmodifiableList(dependencies);
}
int i = 0;
final List<Long> neededOffsets = new ArrayList<Long>();
long vStringTableOff = 0;
DynamicStructure dynStructure;
do {
dynStructure = header.getDynamicStructure(dynamicSectionOff, i);
if (dynStructure.tag == DynamicStructure.DT_NEEDED) {
neededOffsets.add(dynStructure.val);
} else if (dynStructure.tag == DynamicStructure.DT_STRTAB) {
vStringTableOff = dynStructure.val; // d_ptr union
}
++i;
} while (dynStructure.tag != DynamicStructure.DT_NULL);
if (vStringTableOff == 0) {
throw new IllegalStateException("String table offset not found!");
}
// Map to file offset
final long stringTableOff = offsetFromVma(header, numProgramHeaderEntries, vStringTableOff);
for (final Long strOff : neededOffsets) {
dependencies.add(readString(buffer, stringTableOff + strOff));
}
return dependencies;
}
private long offsetFromVma(final Header header, final long numEntries, final long vma)
throws IOException {
for (long i = 0; i < numEntries; ++i) {
final ProgramHeader programHeader = header.getProgramHeader(i);
if (programHeader.type == ProgramHeader.PT_LOAD) {
// Within memsz instead of filesz to be more tolerant
if (programHeader.vaddr <= vma
&& vma <= programHeader.vaddr + programHeader.memsz) {
return vma - programHeader.vaddr + programHeader.offset;
}
}
}
throw new IllegalStateException("Could not map vma to file offset!");
}
@Override
public void close() throws IOException {
this.channel.close();
}
protected String readString(final ByteBuffer buffer, long offset) throws IOException {
final StringBuilder builder = new StringBuilder();
short c;
while ((c = readByte(buffer, offset++)) != 0) {
builder.append((char) c);
}
return builder.toString();
}
protected long readLong(final ByteBuffer buffer, final long offset) throws IOException {
read(buffer, offset, 8);
return buffer.getLong();
}
protected long readWord(final ByteBuffer buffer, final long offset) throws IOException {
read(buffer, offset, 4);
return buffer.getInt() & 0xFFFFFFFFL;
}
protected int readHalf(final ByteBuffer buffer, final long offset) throws IOException {
read(buffer, offset, 2);
return buffer.getShort() & 0xFFFF;
}
protected short readByte(final ByteBuffer buffer, final long offset) throws IOException {
read(buffer, offset, 1);
return (short) (buffer.get() & 0xFF);
}
protected void read(final ByteBuffer buffer, long offset, final int length) throws IOException {
buffer.position(0);
buffer.limit(length);
long bytesRead = 0;
while (bytesRead < length) {
final int read = channel.read(buffer, offset + bytesRead);
if (read == -1) {
throw new EOFException();
}
bytesRead += read;
}
buffer.position(0);
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Program32Header extends Elf.ProgramHeader {
public Program32Header(final ElfParser parser, final Elf.Header header, final long index)
throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(header.bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
final long baseOffset = header.phoff + (index * header.phentsize);
type = parser.readWord(buffer, baseOffset);
offset = parser.readWord(buffer, baseOffset + 0x4);
vaddr = parser.readWord(buffer, baseOffset + 0x8);
memsz = parser.readWord(buffer, baseOffset + 0x14);
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Program64Header extends Elf.ProgramHeader {
public Program64Header(final ElfParser parser, final Elf.Header header, final long index)
throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(header.bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
final long baseOffset = header.phoff + (index * header.phentsize);
type = parser.readWord(buffer, baseOffset);
offset = parser.readLong(buffer, baseOffset + 0x8);
vaddr = parser.readLong(buffer, baseOffset + 0x10);
memsz = parser.readLong(buffer, baseOffset + 0x28);
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Section32Header extends Elf.SectionHeader {
public Section32Header(final ElfParser parser, final Elf.Header header, final int index)
throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(header.bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
info = parser.readWord(buffer, header.shoff + (index * header.shentsize) + 0x1C);
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright 2015 - 2016 KeepSafe Software, Inc.
*
* 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 com.pdlive.lib_so.elf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Section64Header extends Elf.SectionHeader {
public Section64Header(final ElfParser parser, final Elf.Header header, final int index)
throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(header.bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
info = parser.readWord(buffer, header.shoff + (index * header.shentsize) + 0x2C);
}
}

View File

@ -0,0 +1,202 @@
/*
* Tencent is pleased to support the open source community by making Tinker available.
*
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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 com.pdlive.lib_so.pathinsert;
import android.os.Build;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class LoadLibraryUtils {
private static final String TAG = "LoadLibrary";
public static void installNativeLibraryPath(ClassLoader classLoader, File folder)
throws Throwable {
if (folder == null || !folder.exists()) {
Log.e(TAG, "installNativeLibraryPath, folder is illegal");
return;
}
// android o sdk_int 26
// for android o preview sdk_int 25
if ((Build.VERSION.SDK_INT == 25 && Build.VERSION.PREVIEW_SDK_INT != 0)
|| Build.VERSION.SDK_INT > 25) {
try {
V25.install(classLoader, folder);
} catch (Throwable throwable) {
// install fail, try to treat it as v23
// some preview N version may go here
Log.e(TAG, String.format("installNativeLibraryPath, v25 fail, sdk: %d, error: %s, try to fallback to V23",
Build.VERSION.SDK_INT, throwable.getMessage()));
V23.install(classLoader, folder);
}
} else if (Build.VERSION.SDK_INT >= 23) {
try {
V23.install(classLoader, folder);
} catch (Throwable throwable) {
// install fail, try to treat it as v14
Log.e(TAG, String.format("installNativeLibraryPath, v23 fail, sdk: %d, error: %s, try to fallback to V14",
Build.VERSION.SDK_INT, throwable.getMessage()));
V14.install(classLoader, folder);
}
} else if (Build.VERSION.SDK_INT >= 14) {
V14.install(classLoader, folder);
} else {
V4.install(classLoader, folder);
}
}
private static final class V4 {
private static void install(ClassLoader classLoader, File folder) throws Throwable {
String addPath = folder.getPath();
Field pathField = ShareReflectUtil.findField(classLoader, "libPath");
final String origLibPaths = (String) pathField.get(classLoader);
final String[] origLibPathSplit = origLibPaths.split(":");
final StringBuilder newLibPaths = new StringBuilder(addPath);
for (String origLibPath : origLibPathSplit) {
if (origLibPath == null || addPath.equals(origLibPath)) {
continue;
}
newLibPaths.append(':').append(origLibPath);
}
pathField.set(classLoader, newLibPaths.toString());
final Field libraryPathElementsFiled = ShareReflectUtil.findField(classLoader, "libraryPathElements");
final List<String> libraryPathElements = (List<String>) libraryPathElementsFiled.get(classLoader);
final Iterator<String> libPathElementIt = libraryPathElements.iterator();
while (libPathElementIt.hasNext()) {
final String libPath = libPathElementIt.next();
if (addPath.equals(libPath)) {
libPathElementIt.remove();
break;
}
}
libraryPathElements.add(0, addPath);
libraryPathElementsFiled.set(classLoader, libraryPathElements);
}
}
private static final class V14 {
private static void install(ClassLoader classLoader, File folder) throws Throwable {
final Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
final Object dexPathList = pathListField.get(classLoader);
final Field nativeLibDirField = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories");
final File[] origNativeLibDirs = (File[]) nativeLibDirField.get(dexPathList);
final List<File> newNativeLibDirList = new ArrayList<>(origNativeLibDirs.length + 1);
newNativeLibDirList.add(folder);
for (File origNativeLibDir : origNativeLibDirs) {
if (!folder.equals(origNativeLibDir)) {
newNativeLibDirList.add(origNativeLibDir);
}
}
nativeLibDirField.set(dexPathList, newNativeLibDirList.toArray(new File[0]));
}
}
private static final class V23 {
private static void install(ClassLoader classLoader, File folder) throws Throwable {
final Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
final Object dexPathList = pathListField.get(classLoader);
final Field nativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories");
List<File> origLibDirs = (List<File>) nativeLibraryDirectories.get(dexPathList);
if (origLibDirs == null) {
origLibDirs = new ArrayList<>(2);
}
final Iterator<File> libDirIt = origLibDirs.iterator();
while (libDirIt.hasNext()) {
final File libDir = libDirIt.next();
if (folder.equals(libDir)) {
libDirIt.remove();
break;
}
}
origLibDirs.add(0, folder);
final Field systemNativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "systemNativeLibraryDirectories");
List<File> origSystemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList);
if (origSystemLibDirs == null) {
origSystemLibDirs = new ArrayList<>(2);
}
final List<File> newLibDirs = new ArrayList<>(origLibDirs.size() + origSystemLibDirs.size() + 1);
newLibDirs.addAll(origLibDirs);
newLibDirs.addAll(origSystemLibDirs);
final Method makeElements = ShareReflectUtil.findMethod(dexPathList,
"makePathElements", List.class, File.class, List.class);
final ArrayList<IOException> suppressedExceptions = new ArrayList<>();
final Object[] elements = (Object[]) makeElements.invoke(dexPathList, newLibDirs, null, suppressedExceptions);
final Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements");
nativeLibraryPathElements.set(dexPathList, elements);
}
}
private static final class V25 {
private static void install(ClassLoader classLoader, File folder) throws Throwable {
final Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
final Object dexPathList = pathListField.get(classLoader);
final Field nativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories");
List<File> origLibDirs = (List<File>) nativeLibraryDirectories.get(dexPathList);
if (origLibDirs == null) {
origLibDirs = new ArrayList<>(2);
}
final Iterator<File> libDirIt = origLibDirs.iterator();
while (libDirIt.hasNext()) {
final File libDir = libDirIt.next();
if (folder.equals(libDir)) {
libDirIt.remove();
break;
}
}
origLibDirs.add(0, folder);
final Field systemNativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "systemNativeLibraryDirectories");
List<File> origSystemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList);
if (origSystemLibDirs == null) {
origSystemLibDirs = new ArrayList<>(2);
}
final List<File> newLibDirs = new ArrayList<>(origLibDirs.size() + origSystemLibDirs.size() + 1);
newLibDirs.addAll(origLibDirs);
newLibDirs.addAll(origSystemLibDirs);
final Method makeElements = ShareReflectUtil.findMethod(dexPathList, "makePathElements", List.class);
final Object[] elements = (Object[]) makeElements.invoke(dexPathList, newLibDirs);
final Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements");
nativeLibraryPathElements.set(dexPathList, elements);
}
}
}

View File

@ -0,0 +1,244 @@
package com.pdlive.lib_so.pathinsert;
import android.content.Context;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class ShareReflectUtil {
/**
* Locates a given field anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the field into.
* @param name field name
* @return a field object
* @throws NoSuchFieldException if the field cannot be located
*/
public static Field findField(Object instance, String name) throws NoSuchFieldException {
for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
try {
Field field = clazz.getDeclaredField(name);
if (!field.isAccessible()) {
field.setAccessible(true);
}
return field;
} catch (NoSuchFieldException e) {
// ignore and search next
}
}
throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
}
public static Field findField(Class<?> originClazz, String name) throws NoSuchFieldException {
for (Class<?> clazz = originClazz; clazz != null; clazz = clazz.getSuperclass()) {
try {
Field field = clazz.getDeclaredField(name);
if (!field.isAccessible()) {
field.setAccessible(true);
}
return field;
} catch (NoSuchFieldException e) {
// ignore and search next
}
}
throw new NoSuchFieldException("Field " + name + " not found in " + originClazz);
}
/**
* Locates a given method anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the method into.
* @param name method name
* @param parameterTypes method parameter types
* @return a method object
* @throws NoSuchMethodException if the method cannot be located
*/
public static Method findMethod(Object instance, String name, Class<?>... parameterTypes)
throws NoSuchMethodException {
for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
try {
Method method = clazz.getDeclaredMethod(name, parameterTypes);
if (!method.isAccessible()) {
method.setAccessible(true);
}
return method;
} catch (NoSuchMethodException e) {
// ignore and search next
}
}
throw new NoSuchMethodException("Method "
+ name
+ " with parameters "
+ Arrays.asList(parameterTypes)
+ " not found in " + instance.getClass());
}
/**
* Locates a given method anywhere in the class inheritance hierarchy.
*
* @param clazz a class to search the method into.
* @param name method name
* @param parameterTypes method parameter types
* @return a method object
* @throws NoSuchMethodException if the method cannot be located
*/
public static Method findMethod(Class<?> clazz, String name, Class<?>... parameterTypes)
throws NoSuchMethodException {
for (; clazz != null; clazz = clazz.getSuperclass()) {
try {
Method method = clazz.getDeclaredMethod(name, parameterTypes);
if (!method.isAccessible()) {
method.setAccessible(true);
}
return method;
} catch (NoSuchMethodException e) {
// ignore and search next
}
}
throw new NoSuchMethodException("Method "
+ name
+ " with parameters "
+ Arrays.asList(parameterTypes)
+ " not found in " + clazz);
}
/**
* Locates a given constructor anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the constructor into.
* @param parameterTypes constructor parameter types
* @return a constructor object
* @throws NoSuchMethodException if the constructor cannot be located
*/
public static Constructor<?> findConstructor(Object instance, Class<?>... parameterTypes)
throws NoSuchMethodException {
for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
try {
Constructor<?> ctor = clazz.getDeclaredConstructor(parameterTypes);
if (!ctor.isAccessible()) {
ctor.setAccessible(true);
}
return ctor;
} catch (NoSuchMethodException e) {
// ignore and search next
}
}
throw new NoSuchMethodException("Constructor"
+ " with parameters "
+ Arrays.asList(parameterTypes)
+ " not found in " + instance.getClass());
}
/**
* Replace the value of a field containing a non null array, by a new array containing the
* elements of the original array plus the elements of extraElements.
*
* @param instance the instance whose field is to be modified.
* @param fieldName the field to modify.
* @param extraElements elements to append at the end of the array.
*/
public static void expandFieldArray(Object instance, String fieldName, Object[] extraElements)
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field jlrField = findField(instance, fieldName);
Object[] original = (Object[]) jlrField.get(instance);
Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), original.length + extraElements.length);
// NOTE: changed to copy extraElements first, for patch load first
System.arraycopy(extraElements, 0, combined, 0, extraElements.length);
System.arraycopy(original, 0, combined, extraElements.length, original.length);
jlrField.set(instance, combined);
}
/**
* Replace the value of a field containing a non null array, by a new array containing the
* elements of the original array plus the elements of extraElements.
*
* @param instance the instance whose field is to be modified.
* @param fieldName the field to modify.
*/
public static void reduceFieldArray(Object instance, String fieldName, int reduceSize)
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
if (reduceSize <= 0) {
return;
}
Field jlrField = findField(instance, fieldName);
Object[] original = (Object[]) jlrField.get(instance);
int finalLength = original.length - reduceSize;
if (finalLength <= 0) {
return;
}
Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), finalLength);
System.arraycopy(original, reduceSize, combined, 0, finalLength);
jlrField.set(instance, combined);
}
public static Object getActivityThread(Context context,
Class<?> activityThread) {
try {
if (activityThread == null) {
activityThread = Class.forName("android.app.ActivityThread");
}
Method m = activityThread.getMethod("currentActivityThread");
m.setAccessible(true);
Object currentActivityThread = m.invoke(null);
if (currentActivityThread == null && context != null) {
// In older versions of Android (prior to frameworks/base 66a017b63461a22842)
// the currentActivityThread was built on thread locals, so we'll need to try
// even harder
Field mLoadedApk = context.getClass().getField("mLoadedApk");
mLoadedApk.setAccessible(true);
Object apk = mLoadedApk.get(context);
Field mActivityThreadField = apk.getClass().getDeclaredField("mActivityThread");
mActivityThreadField.setAccessible(true);
currentActivityThread = mActivityThreadField.get(apk);
}
return currentActivityThread;
} catch (Throwable ignore) {
return null;
}
}
/**
* Handy method for fetching hidden integer constant value in system classes.
*
* @param clazz
* @param fieldName
* @return
*/
public static int getValueOfStaticIntField(Class<?> clazz, String fieldName, int defVal) {
try {
final Field field = findField(clazz, fieldName);
return field.getInt(null);
} catch (Throwable thr) {
return defVal;
}
}
}

BIN
log.txt Normal file

Binary file not shown.

View File

@ -225,10 +225,18 @@ public class MainActivity extends AbsActivity implements MainAppBarLayoutListene
OpenAdManager.getInstance().dismiss();
}
public static boolean isLoadSo=false;
@Override
protected void main() {
//初始化声网
String path = getFilesDir().getAbsolutePath() + "/dynamic_so";
File file = new File(path);
if (file.listFiles().length==22){
//说明已经有了可以直接初始化
isLoadSo=true;
SWAuManager.get().initRtcEngine(this);
}
ActivityCompat.postponeEnterTransition(this);
ConversationIMListManager.get(this);
//在请求一下这个接口给我后台版本号

View File

@ -12,6 +12,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
@ -29,7 +30,6 @@ import com.bumptech.glide.request.target.DrawableImageViewTarget;
import com.bumptech.glide.request.target.Target;
import com.ms.banner.Banner;
import com.ms.banner.listener.OnBannerClickListener;
import com.yunbao.common.utils.MobclickAgent;
import com.yunbao.common.CommonAppConfig;
import com.yunbao.common.Constants;
import com.yunbao.common.activity.WebViewActivity;
@ -46,14 +46,14 @@ import com.yunbao.common.http.HttpCallback;
import com.yunbao.common.http.LiveHttpUtil;
import com.yunbao.common.interfaces.OnItemClickListener;
import com.yunbao.common.manager.LiveClassManager;
import com.yunbao.common.manager.OpenAdManager;
import com.yunbao.common.utils.DialogUitl;
import com.yunbao.common.utils.LiveRoomCheckLivePresenter;
import com.yunbao.common.utils.LoadSoUtil;
import com.yunbao.common.utils.MicStatusManager;
import com.yunbao.common.utils.MobclickAgent;
import com.yunbao.common.utils.RouteUtil;
import com.yunbao.common.utils.StringUtil;
import com.yunbao.common.utils.ToastUtil;
import com.yunbao.common.utils.WordUtil;
import com.yunbao.common.views.CustomViewHolder;
import com.yunbao.live.utils.LiveStorge;
import com.yunbao.live.views.LiveRoomViewHolder;
@ -488,6 +488,35 @@ public class MainHomeLiveViewHolder extends AbsMainHomeChildViewHolder implement
@Override
public void onItemClick(LiveBean bean, int position) {
if (MainActivity.isLoadSo) {
onItemClick2(bean, position);
} else {
Toast.makeText(mContext, "正在下载so文件请稍后。。。", Toast.LENGTH_LONG).show();
LoadSoUtil.loadSo2(mContext, new LoadSoUtil.onLoadSoListener() {
@Override
public void ok() {
MainActivity.isLoadSo = true;
SWAuManager.get().initRtcEngine((MainActivity) mContext);
SWAuManager.get().preloadChannel();
onItemClick2(bean, position);
}
@Override
public void error() {
mRefreshView.post(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "下载so文件失败了", Toast.LENGTH_SHORT).show();
}
});
Log.e("mLog", "加载so文件的时候错误了 ****************** ");
}
});
}
}
public void onItemClick2(LiveBean bean, int position) {
//到这里判断是否需要加载so
if ("1".equals(bean.getIslive())) {
intoIndex = 1;
watchLive(bean, Constants.LIVE_HOME, position);
@ -537,7 +566,6 @@ public class MainHomeLiveViewHolder extends AbsMainHomeChildViewHolder implement
}
}
@Override
public void loadData() {
if (mAdapter != null) {

View File

@ -82,4 +82,6 @@ android{
}
}
// flavor
defaultPublishConfig "link_test"
}

View File

@ -8,3 +8,5 @@ include ':lib_huawei'
include ':lib_google'
include ':IAP6Helper'
include ':lib_faceunity'
include ':lib_so'
include ':lib_so'