何为隐藏的API?
简单来说就是在源码中被用 @hide
标记的代码块,比如常量,方法等。以常用的 ActivityManager.java
为例,里面就有大量的隐藏API:
@SystemService(Context.ACTIVITY_SERVICE)
public class ActivityManager {
...
/**
* System property to enable task snapshots.
* @hide
*/
public final static boolean ENABLE_TASK_SNAPSHOTS;
...
/**
* Return the default limit on the number of recents that an app can make.
* @hide
*/
static public int getDefaultAppRecentsLimitStatic() {
return getMaxRecentTasksStatic() / 6;
}
...
}
以上代码来自 AOSP
源码,我们下载的 Android SDK
源码中是找不到这些代码的。
而用 Android Studio
进行系统App的开发的我们,有时候难免会调用到系统隐藏的API,那我们怎么办呢?
如果你之前做过功课,你会发现网上有很多通过反射来调用系统API的例子,既然反射能调到,说明这些方法在我们的运行环境中是存在的。那这些方法从哪里加载的呢?这就要提到设备中的一个jar:/system/framework/framework.jar
,他和我们 SDK 中加载的 android.jar
的区别就是他里面包含了这些隐藏的API。到这里我们就得出两种调用系统隐藏API的方法:
反射
这个最常见了,如:
public static boolean silentInstall(PackageManager packageManager, String apkPath) {
Class<?> pmClz = packageManager.getClass();
try {
Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, Class.forName("android.content.pm.IPackageInstallObserver"), int.class, String.class);
method.setAccessible(true);
method.invoke(packageManager, Uri.fromFile(new File(apkPath)), null, 2, null);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
替换 SDK 中的 android.jar
这个就厉害了,反射一次只可以反射一个方法,这种方式所有系统的隐藏API都可以直接调用到。Github上有个一个项目 android-hidden-api ,在这里我们可以下载别人收集的带隐藏API的 android.jar
。
个人不是很喜欢反射,就研究了下第二种方法,但是很遗憾,我没成功(可能是因为我的build工具版本比较高)。那么还有其他方法吗?
由于我们有系统的源码,因此我们可否从源码入手?简单总结了一个思路:创建一个项目,在这个项目中对要调用的系统隐藏API做一些封装,然后在AOSP中编译生成jar包,最后将这个jar包拷贝到 Android Studio
中使用。下面以App的静默安装为例,说明此思路的具体实现。
这里有一个前提,系统隐藏API可以通过各种方法调到,但是能不能调成功却是另外一回事,因为有些隐藏API不是普通权限的App可以调用的。若要升级你的App为系统App,可以按照下面几步操作:
AndroidManifest.xml 中添加
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.your.pkg"
android:sharedUserId="android.uid.system">
...
manifest>
系统签名
使用系统密钥给App签名。一般位于: build/target/product/security/
中。 需要signapk.jar,命令如下:
java -jar signapk.jar platform.x509.pem platform.pk8 in.apk out.apk
安装
直接推到 /system/app/
或者 /system/priv-app/
下面,重启即可。
下面回到我们App静默安装的例子。
bash-3.2$ tree
vendor
|-- ...
|-- SystemHideAPI
| |-- Android.mk
| `-- com
| `-- your
| `-- pkg
| `-- utils
| `-- SystemHideAPI.java
public class SystemHideAPI {
/**
* Silent install
* @param pm
* @param apkPath
* @return
*/
public static boolean silentInstall(PackageManager pm, String apkPath, OnInstallResultListener listener) {
if(null == pm || TextUtils.isEmpty(apkPath)){
return false;
}
pm.installPackage(Uri.fromFile(new File(apkPath)), new PackageInstallObserver() {
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) {
if (null != listener) {
listener.onResult(basePackageName, returnCode, msg);
}
}
}, PackageManager.INSTALL_REPLACE_EXISTING, "");
return true;
}
/**
* App install callback
*/
public interface OnInstallResultListener {
/**
* App install callback
*
* @param pkg
* @param returnCode
* @param msg
*/
void onResult(String pkg, int returnCode, String msg);
}
}
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := SystemHideAPI
LOCAL_MODULE_TAGS := optional
LOCAL_JACK_ENABLED := disabled
include $(BUILD_STATIC_JAVA_LIBRARY)
mma
生成的jar文件位于下面目录:
out/target/common/obj/JAVA_LIBRARIES/SystemAPI_intermediates/
➜ SystemHideAPI_intermediates tree -L 1
.
├── anno
├── classes
├── classes-full-debug.jar
├── classes.jar
└── link_type
2 directories, 3 files
➜ SystemHideAPI_intermediates
最后,将生成的 classes.jar
拷贝到Android Studio项目,使用 SystemHideAPI
中的方法,即可正常编译,运行了。
相比反射,这种方式的灵活性更大,如上面静默安装的例子,如果我想监听安装的回调,还需要去反射 PackageInstallObserver 这个类(这个类也是hide)。如果还需要其他隐藏的API,只需要增加响应的方法即可。给我们在AndroidStuido
中调用系统隐藏API带来很大方便