Android系统定制开发 | 如何调用系统方法?

1,046 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情

当开发System App时,除了需要获取较高的系统权限外,有时还需要访问系统级的API,以自定义地实现一些系统级的功能,例如:Settings应用访问 SystemProperties 的方法获取系统的属性。

我们可以作这么一个验证:

  • 在Android Studio中任意打开一个代码文件,输入SystemProperties,你会发现IDE提示无法找到该类。
  • 当全局搜索 SystemProperties 时,会发现它存在于Android SDK中,但加上了 @SystemApi 的注解。

这两个现象正好对应,使用Android Studio开发可访问系统方法的应用时需要解决的两个问题:

  • 使用Android Studio 开发构建工程时 能够访问到系统方法;
  • 使用Android Studio构建出的apk,在 目标设备上运行 时能够访问到系统方法;

这篇文章首先将从介绍 什么是framework.jar 开始,然后说明如何 引用frameworkwork.jar 来解决构建时访问系统方法,以及如何 获取调用系统方法的权限 来解决运行时访问系统方法。

一、什么是framework.jar

在介绍 framework.jar之前,我们先聊聊 android.jar。任意打开我们的一个工程,在它的 External Libraries 里,我们都可以看到 android.jarimage.png 这个 android.jar 是什么,又有什么用呢?在 stack overflow 上有这样一段回答:

The first one is the android.jar in the computer. This is a JAR file. It is provided by gradle in the classpath. It contain the Android framework classes. It does not contain the Android framework code but only stubs for the type signatures, methods, types, etc. The android.jar JAR file is only used for the Java compiler before deployment on an Android device. It is not bundled with your application. By default, all calls to the provided Android.jar throw exceptions, this allows you to write tests that do not depend on any Android platform behavior.

Once you application is deployed on the device it will use the android.jar JAR file on the Android device (The second one). I couldn't find any source that said android.jar is here. I think android.jar fit better in the "Java API Framework" layer.

简单说,我们在Android Studio中看到的 android.jar 就是下载至电脑本地的Android SDK,它并不包含完整的framework代码,仅用于编译器构建apk文件。

当apk安装至设备上运行时,它使用的是设备上的 android.jar。这个 android.jar 就是我们说的 framework.jar,它 包含了完整的framework代码

如果我们的工程直接引用framework.jar,而不是android.jar,就可以使编译器直接访问到全量的、不加限制的代码。

二、引用framework.jar

1. 获取framework.jar

这一步需要你编译系统源码,或者让系统团队的同事提供一下。对于不同版本的Android系统,编译输出位置的不一样:

  • Android N/O : out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar

  • Android P/Q : out/soong/.intermediates/frameworks/base/framework/android_common/combined/framework.jar

  • Android R : out/soong/.intermediates/frameworks/base/framework-minus-apex/android_common/combined/framework-minus-apex.jar

2. 导入framework.jar

在需用访问系统方法的模块的 build.gradle 文件中添加依赖。

dependencies {
    ...
    // 目的是让代码能够使用这个jar里面的方法和类,只在编译的时候使用jar,不会把jar编译进apk
    compileOnly files('libs/framework.jar')
    ...
}

全局build.gradle 文件的 allprojects 中添加编译配置,提高framework.jar的优先级,高于Android SDK中的android.jar,告诉编译器优先在framework.jar中寻找Java类实现。

allprojects {
    ...
    gradle.projectsEvaluated {
        tasks.withType(JavaCompile) {
            // Xbootclasspath/p: 是Java编译的寻址优先设置,插入内容到-bootclasspath的内容之前
            // Xbootclasspath/p: 之后的路径请使用「绝对路径」!!!
            options.compilerArgs.add("-Xbootclasspath/p:${rootDir.path}/app/libs/framework.jar")
        }
    }
	...
}

三、获取调用系统方法的权限

上面的步骤只是帮助我们通过Android Studio顺利编译出apk,而当apk运行时,绝大部分的SystemApi都会作一层权限校验,当发现当前应用不具备系统权限时,会立马抛出异常。

因此,我们还需要对应用做一些配置,以获取调用系统方法的权限。

1. 设置sharedUserId

在主模块 AndroidManifest.xml 的manifest节点中添加属性 android:sharedUserId="android.uid.system" 

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sevenhoo.sample"
    android:sharedUserId="android.uid.system">

2. 配置系统签名

具体方法可以参考我的另一篇文章 Android系统应用开发 | 三种系统签名的方法