如何调用android 系统隐藏类和方法
我们在做有些特殊功能的开发,公开的android-sdk里的api不能满足要求,就需要调用android系统中的隐藏类和里面的隐藏方法,今天我们就用Android中的PackageParser类 调用里面的parsePackageLite静态方法为例子来实现
我们先来看看这个类
这个类是在android.content.pm 这个包下面
我们看到这个类是隐藏的,不对外提供sdk的调用,所以我们直接去实例化是不行的
上面这个就是我们要调用的方法,这里还是android7.1.2的源码
我们看下android 10以上是什么样子的
我们发现android10(包括10)这个方法都有@UnsupportedAppUsage 注解,这个注解就是不支持用户app去调用了,经过我测试,有了这个注解,通过反射的反射是获取不到这个方法的,系统底层的虚拟器会读取这个注解,然后过滤调它,接下来我们测试下
1、反射
private void testReflect()
{
try {
Class<?> cls=Class.forName("android.content.pm.PackageParser");
Method[] methods=cls.getMethods();
for (int i = 0; i < methods.length; i++) {
Log.i("RR",methods[i].getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
我在iqoo5 上测试测,是android11的版本
结果
28991-28991/com.lihua.txtest I/RR: collectCertificates
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: equals
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generateActivityInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generateApplicationInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generateApplicationInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generateInstrumentationInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generatePackageInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generatePackageInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generatePermissionGroupInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generatePermissionInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generateProviderInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: generateServiceInfo
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: getClass
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: hashCode
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: notify
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: notifyAll
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: parseMonolithicPackage
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: parsePackage
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: parsePackage
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: parsePackageLite
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: setCompatibilityModeEnabled
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: setSeparateProcesses
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: toString
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: wait
2021-12-06 10:21:49.381 28991-28991/com.lihua.txtest I/RR: wait
我们看到这些方法中并没有我们要调用的那个方法,parsePackageLite
我们来看下android10一下,就用api28来测试下,开个模拟器
结果是可以获得到这个方法的,我们最终是可以调用的里面的这个方法
那么我们就没有办法在android10 上调用这个方法了吗?
2、声明系统的类,空实现,抛异常
我们经常在查看类的时候,可以看到这样的代码,这个是编译通过,在最终调用的时候,会去找最终的系统实现类
那么我们就参照系统的写法来试试
我们创建的类需要和系统的报名一样,里面需要引用的其他类,也要声明,写上
PackageParser:
package android.content.pm;
import java.io.File;
import java.security.cert.Certificate;
import java.util.List;
public class PackageParser {
public static PackageLite parsePackageLite(File packageFile, int flags)
throws PackageParserException {
throw new RuntimeException("Stub!");
}
public static class PackageLite {
public final String packageName;
public final int versionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
/** Names of any split APKs, ordered by parsed splitName */
public final String[] splitNames;
/**
* Path where this package was found on disk. For monolithic packages
* this is path to single base APK file; for cluster packages this is
* path to the cluster directory.
*/
public final String codePath;
/** Path of base APK */
public final String baseCodePath;
/** Paths of any split APKs, ordered by parsed splitName */
public final String[] splitCodePaths;
/** Revision code of base APK */
public final int baseRevisionCode;
/** Revision codes of any split APKs, ordered by parsed splitName */
public final int[] splitRevisionCodes;
public final boolean coreApp;
public final boolean multiArch;
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
String[] splitCodePaths, int[] splitRevisionCodes) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
this.installLocation = baseApk.installLocation;
this.verifiers = baseApk.verifiers;
this.splitNames = splitNames;
this.codePath = codePath;
this.baseCodePath = baseApk.codePath;
this.splitCodePaths = splitCodePaths;
this.baseRevisionCode = baseApk.revisionCode;
this.splitRevisionCodes = splitRevisionCodes;
this.coreApp = baseApk.coreApp;
this.multiArch = baseApk.multiArch;
this.use32bitAbi = baseApk.use32bitAbi;
this.extractNativeLibs = baseApk.extractNativeLibs;
}
public List<String> getAllCodePaths() {
throw new RuntimeException("Stub!");
}
}
public static class ApkLite {
public final String codePath;
public final String packageName;
public final String splitName;
public final int versionCode;
public final int revisionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
public final Certificate[][] certificates;
public final boolean coreApp;
public final boolean multiArch;
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
Signature[] signatures, Certificate[][] certificates, boolean coreApp,
boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs) {
throw new RuntimeException("Stub!");
}
}
public static class PackageParserException extends Exception {
public final int error;
public PackageParserException(int error, String detailMessage) {
super(detailMessage);
throw new RuntimeException("Stub!");
}
public PackageParserException(int error, String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
throw new RuntimeException("Stub!");
}
}
}
我们看到实现的部分都用throw new RuntimeException(“Stub!”); 这个代替
属性都要全部写上,我都是自己把源码拷贝过来,然后把实现都throw new RuntimeException(“Stub!”);
VerifierInfo:
package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
import java.security.PublicKey;
public class VerifierInfo implements Parcelable {
/** Package name of the verifier. */
public final String packageName;
/** Signatures used to sign the package verifier's package. */
public final PublicKey publicKey;
/**
* Creates an object that represents a verifier info object.
*
* @param packageName the package name in Java-style. Must not be {@code
* null} or empty.
* @param publicKey the public key for the signer encoded in Base64. Must
* not be {@code null} or empty.
* @throws IllegalArgumentException if either argument is null or empty.
*/
public VerifierInfo(String packageName, PublicKey publicKey) {
throw new RuntimeException("Stub!");
}
private VerifierInfo(Parcel source) {
throw new RuntimeException("Stub!");
}
@Override
public int describeContents() {
throw new RuntimeException("Stub!");
}
@Override
public void writeToParcel(Parcel dest, int flags) {
throw new RuntimeException("Stub!");
}
public static final Parcelable.Creator<VerifierInfo> CREATOR
= new Parcelable.Creator<VerifierInfo>() {
public VerifierInfo createFromParcel(Parcel source) {
throw new RuntimeException("Stub!");
}
public VerifierInfo[] newArray(int size) {
throw new RuntimeException("Stub!");
}
};
}
接下来我们来调用下试试看Demo
private void testRuntimeExp()
{
File file=new File(Environment.getExternalStorageDirectory().getPath()+"/mm/app-debug.apk");
if (!file.exists())
{
Toast.makeText(this, "file is not exist", Toast.LENGTH_SHORT).show();
return;
}
try {
PackageParser.PackageLite packageLite= PackageParser.parsePackageLite(file,0);
Log.i("RR","packageName:"+packageLite.packageName);
Log.i("RR","versionCode:"+packageLite.versionCode);
Log.i("RR","installLocation:"+packageLite.installLocation);
Log.i("RR","codePath:"+packageLite.codePath);
Log.i("RR","baseCodePath:"+packageLite.baseCodePath);
Log.i("RR","coreApp:"+packageLite.coreApp);
String s="";
} catch (PackageParser.PackageParserException e) {
e.printStackTrace();
}
}
结果:
看到结果是正确的,可以获得信息
VirtualApk 这个框架就大量运用了这种技术,想了解更多详情的同学可以到GitHub上看下