动态加载so文件方案实现
This commit is contained in:
4
lib_so/src/main/AndroidManifest.xml
Normal file
4
lib_so/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
9
lib_so/src/main/java/com/pdlive/lib_so/DynamicLoad.kt
Normal file
9
lib_so/src/main/java/com/pdlive/lib_so/DynamicLoad.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.pdlive.lib_so
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
|
||||
@Keep
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class DynamicLoad()
|
||||
65
lib_so/src/main/java/com/pdlive/lib_so/DynamicSo.java
Normal file
65
lib_so/src/main/java/com/pdlive/lib_so/DynamicSo.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
46
lib_so/src/main/java/com/pdlive/lib_so/DynamicSoLauncher.kt
Normal file
46
lib_so/src/main/java/com/pdlive/lib_so/DynamicSoLauncher.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
64
lib_so/src/main/java/com/pdlive/lib_so/elf/Elf.java
Normal file
64
lib_so/src/main/java/com/pdlive/lib_so/elf/Elf.java
Normal 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
|
||||
}
|
||||
}
|
||||
59
lib_so/src/main/java/com/pdlive/lib_so/elf/Elf32Header.java
Normal file
59
lib_so/src/main/java/com/pdlive/lib_so/elf/Elf32Header.java
Normal 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);
|
||||
}
|
||||
}
|
||||
58
lib_so/src/main/java/com/pdlive/lib_so/elf/Elf64Header.java
Normal file
58
lib_so/src/main/java/com/pdlive/lib_so/elf/Elf64Header.java
Normal 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);
|
||||
}
|
||||
}
|
||||
195
lib_so/src/main/java/com/pdlive/lib_so/elf/ElfParser.java
Normal file
195
lib_so/src/main/java/com/pdlive/lib_so/elf/ElfParser.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user