网站建设淘宝店铺模板,ajax瀑布流网站模板,wordpress 屏蔽升级,班级网页设计模板html本期视频地址#xff1a;https://www.bilibili.com/video/BV1NY411z7TK/ 前言
Hello#xff0c;大家好#xff0c;我是林栩。
开发车载应用#xff0c;其实主要都是在Android系统中编写各种系统应用#xff0c;所以上期视频先介绍了Android系统源码的下载和编译流程https://www.bilibili.com/video/BV1NY411z7TK/ 前言
Hello大家好我是林栩。
开发车载应用其实主要都是在Android系统中编写各种系统应用所以上期视频先介绍了Android系统源码的下载和编译流程本期视频我们开始介绍Android系统应用是如何开发的。
系统应用简介
我们第一次启动Android系统的手机时会发现手机中已经预先安装了很多应用例如系统设置、桌面等等。这些应用并不是通过普通的方法安装到系统上的而是直接嵌入在Android ROM中直接刷写到硬件里的。通过这种方式安装的应用无法使用通常的方法卸载。只有在获取root权限后删除对应目录下的的apk文件或者刷机否则无法移除这些系统应用。
除此以外我们还会发现系统应用拥有远超普通的应用的权限以系统设置为例它可以切换当前系统的用户类型设置其它应用的通知权限甚至于可以卸载Android系统上的普通应用这些功能都是普通应用无法实现的原因就在于Android SDK中有很多没有公开的API这些API只允许系统应用调用。
所以我们可以总结系统应用具有以下特点
可以调用Android SDK未公开的私有API。拥有更高的系统权限。直接嵌入到Android ROM中普通方法无法卸载。
系统应用准备条件
接下来我们演示如何编写一个 Android 系统应用不过在此之前我们还需要做以下的准备
第 1 步制作 API 包
系统应用的特点决定了它的开发方式与普通的Android应用并不完全一样。首先系统应用可以调用Android SDK隐藏的API这需要我们引入包含被隐藏API的jar包。当然如果不需要调用隐藏API这一步可以跳过。在实际项目中这一步会由负责framework开发的同事协助完成因为farmework层一般都有新增的接口需要一起打包。
1编译Android framework
我们可以使用make framework指令编译 framework 的源码或者使用mmm frameworks/base以及在/framework/base目录下执行mm都可以。 但是要注意 make 指令后跟的是 module name 而不是模块的路径所以这里不能写成 frameworks。 编译 Android 11和以后版本编译指令有所调整使用make framework-minus-apex
编译成功后进入/out/target/common/obj/JAVA_LIBRARIES/framework-minus-apex_intermediates/目录该目录下的classes-header.jar就是我们需要的jar包。
classes-header.jar中包含了Android SDK中没有公开的API例如用于启用RRO机制的OverlayManager。
如果没有下载AOSP源码上述编译好的framework.jar可以去本视频的github仓库中下载github地址[https://github.com/linxu-link/CarAndroidCourse]可以在本视频的简介中查看。
2导入 Android Studio
生成 framework.jar 后我们把它导入到 Android studio中并在工程目录的 build.gradle中加入以下代码。
allprojects{gradle.projectsEvaluated {//Arctic Foxtasks.withType(JavaCompile) {SetFile fileSet options.bootstrapClasspath.getFiles()ListFile newFileList new ArrayList();newFileList.add(new File(./app/libs/framework_header.jar))newFileList.addAll(fileSet)options.bootstrapClasspath files(newFileList.toArray())}}
}在App目录的build.gradle中以compileOnly的形式引入jar包。
compileOnly files(libs/framework_header.jar)第 2 步制作系统签名
Android系统会识别应用的签名类型并根据签名类型赋予应用相应的权限等级将普通应用提升为系统应用的重要条件就是应用需要使用系统签名。所以在这一步我们要先制作一份系统签名方便我们在开发时调试应用。
1 控制台进入AOSP的build目录
cd build/target/product/security2制作系统签名
openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out [platform.pem]0 -nocryptopenssl pkcs12 -export -in platform.x509.pem -inkey [platform.pem] -out [platform.pk12] -name [key的别名] -password pass:[key的密码]keytool -importkeystore -deststorepass [key的密码] -destkeypass [key的密码] -destkeystore platform.jks -srckeystore platform.pk12 -srcstoretype PKCS12 -srcstorepass [key的密码] -alias [android]制作完成后会在当前目录下载生成一个platform.jks的签名文件将它导入到android studio中即可对应用进行签名。
3导入 Android Studio
将platform.jks放置在App目录下并build.gradle中加入以下代码。
signingConfigs {sign {storeFile file(platform.jks)storePassword 123456keyAlias androidkeyPassword 123456}
}buildTypes {release {minifyEnabled falsesigningConfig signingConfigs.sign}debug {minifyEnabled falsesigningConfig signingConfigs.sign}
}将系统签名引入android studio后app工程就可以直接在Android模拟器中调用系统API同时也可以获取更高等级的权限了。 注意基于AOSP源码制作的test key文件一般无法使用在真实环境中例如手机车载项目则较为复杂有的项目在开发阶段就会使用较为严格的签名校验那么AOSP的签名文件也是无法使用的。不过也有项目会在最后的量产阶段更换签名那么在此之前AOSP中test key依然可以使用。 有关签名文件补充资料如下
在Android源码的build/target/product/security/目录下有如下5对常见的KEY media.pk8与media.x509.pem 适用于媒体/下载系统所包含的 apk 包的测试密钥。 platform.pk8与platform.x509.pem 适用于核心平台所包含的 apk 包的测试密钥。 shared.pk8与shared.x509.pem 适用于家庭/联系人进程中的共享内容的测试密钥。 testkey.pk8与testkey.x509.pem 适用于未另外指定密钥的 apk 包的通用默认密钥。 networkstack.pk8与networkstack.x509.pem 适用于网络系统所包含的 apk 包的测试密钥。
其中“.pk8”文件为私钥“.x509.pem”文件为公钥。注意此目录中的测试密钥仅用于开发不得用于在公开发布的映像中签署包。
有关密钥的更多内容可以阅读官方的文档https://source.android.com/docs/core/ota/sign_builds?hlzh-cn
而这些密钥如何与被签名的APK对应上呢在APK源码目录下的Android.bp文件中有certificate字段用于指定签名时使用的KEY如果不指定默认使用testkey。系统应用对应的certificate可设定为如下的值。
certificate: platform
certificate: shared
certificate: media而在Android.bp中的这些配置需要在APK源码的AndroidManifest.xml文件中的manifest节点添加如下内容
android:sharedUserIdandroid.uid.system
android:sharedUserIdandroid.uid.shared
android:sharedUserIdandroid.media实践系统应用
第 1 步定义需求
为了让各位能直观的感受到『系统应用』与『普通应用』的区别我们要求『系统应用』完成以下的功能
应用在系统开机后自行启动即开机自启开机后覆盖一个 View 在屏幕上且不需要授权『显示在其它应用的上层』应用被杀死后自动拉起即进程保活
这些功能都是在『普通应用』上难以实现的需求我们演示一下在『系统应用』上是如何实现的。
第 2 步修改AndroidManifest.xml
开机自启与进程保活两项功能Android系统本身已经提供了相应的机制来实现我们只需要在manifest.xml中进行配置即可。
persistent
设定应用是否保持常驻状态。默认值为false设定为true为开启常驻模式常驻模式仅适用于系统应用。
开启常驻模式后应用会在Android系统开机动画播放完毕之前就会完成启动同时应用会常驻后台即使被杀死后也会立即拉起。
applicationandroid:persistenttrue除此以外系统应用中还有一些可能较为常用的属性可以配置我们逐一介绍。
android:sharedUserId
设定不同用户间共享数据。 默认情况下Android 会为每个应用分配其唯一用户 ID。如果两个或多个应用将此属性设置为相同的值则这些应用都将共享相同的 ID前提是这些应用的签名完全相同。具有相同用户 ID 的应用可以访问彼此的数据如果需要的话还可以在同一进程中运行。 API 级别 29 中已弃用此属性。 注意由于现有应用无法移除此值这类应用应添加 android:sharedUserMaxSdkVersion“32” 以免在新用户安装时使用共享用户 ID。 manifest xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:sharedUserIdandroid.uid.systemandroid:sharedUserMaxSdkVersion32directBootAware
直接启动模式。直接启动模式是在Android7.0之后出现的当设备已正常开机但尚未解锁时称设备处于DirectBoot模式。默认情况下应用不会在DirectBoot模式下启动即使是系统应用。
如果应用需要在DirectBoot模式下启动可以在manifext.xml将directBootAware属性设定为true。
application android:directBootAwaretrue 需要在“直接启动”模式下运行的一些常见应用用例包括
已安排通知的应用如闹钟应用提供重要用户通知的应用如短信应用提供无障碍服务的应用如 Talkback关键的系统服务如CarService等。
注意对应用程序而言存储空间分为以下两种
Credential encrypted storage凭据加密存储区。默认存储数据的地方仅在用户解锁手机后可用。Device encrypted storage设备加密存储区。主要对应的就是DirectBoot时使用的存储空间。该存储空间在DirectBoot模式下和用户解锁手机后都可以使用。
0-当Android系统开机后首先进入一个DirectBoot模式如果应用在DirectBoot模式下运行时需要访问本地数据可以通过调用Context.createDeviceProtectedStorageContext()创建一个特殊的Context实例。通过此实例发出的所有存储类 API 调用均可以访问设备的加密存储。如下所示 Context directBootContext appContext.createDeviceProtectedStorageContext();// Access appDataFilename that lives in device encrypted storageFileInputStream inStream directBootContext.openFileInput(appDataFilename);// Use inStream to read content...如果需要监听屏幕解锁的时机可以注册下面的广播 receiverandroid:directBootAwaretrue ...intent-filteraction android:nameandroid.intent.action.LOCKED_BOOT_COMPLETED //intent-filter/receiver
一些关键的系统应用或服务需要在Android屏幕解锁前完成启动并开始运行这种情况就可以配置为直接启动模式。此时必仔细阅读官方文档防止出现意外的bug官方文档https://developer.android.google.cn/training/articles/direct-boot?hlzh-cn
uses-library
指定应用必须与之关联的共享库。 该标签会告知系统将库的代码添加到软件包的类加载器中。 车载应用项目中可能会它用来加载一些framework自定义的共享库。 uses-libraryandroid:namestringandroid:required[true | false] /android:name库的名称。此名称由您使用的软件包的文档提供。例如“android.test.runner”是一个包含 Android 测试类的软件包。
android:required指示应用是否需要 android:name 指定的库
true如果没有此库则应用将无法正常运行。系统不允许在没有此库的设备上安装应用。false应用可以使用此库如果存在但专门在没有此库的情况下运行如果有必要。系统允许安装应用即使不存在此库也是如此。如果您使用 false则需要在运行时检查有没有此库 完整的androidmanifest.xml配置如下
?xml version1.0 encodingutf-8?
manifest xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:sharedUserIdandroid.uid.systemandroid:sharedUserMaxSdkVersion32xmlns:toolshttp://schemas.android.com/toolsuses-permission android:nameandroid.permission.SYSTEM_ALERT_WINDOW /applicationandroid:name.SystemApplicationandroid:iconmipmap/ic_launcherandroid:labelstring/app_nameandroid:persistenttrueandroid:supportsRtltrueandroid:themestyle/Theme.SystemAppactivityandroid:name.MainActivityandroid:exportedtrueintent-filteraction android:nameandroid.intent.action.MAIN /category android:nameandroid.intent.category.LAUNCHER //intent-filter/activityserviceandroid:name.SystemServiceandroid:enabledtrueandroid:exportedtrueintent-filteraction android:namecom.android.systemapp.action //intent-filter/service/application/manifest第 3 步编写逻辑代码
本应用中只有一个Service在Application中启动该Service。
class SystemApplication : Application() {override fun onCreate() {super.onCreate()Log.e(System, System APP started)val intent Intent()intent.setPackage(com.android.systemapp)intent.setAction(com.android.systemapp.action)startService(intent)}
}在Service中我们通过WindowManager绘制一个View系统动画没有播放完毕之前该View是无法进行绘制和显示的。换句话说当这个View可以绘制时系统动画已经播放完毕且SystemUI已经显示出来了。
// 创建用于 window 显示的context
val dm getSystemService(DisplayManager::class.java)
val defaultDisplay dm.getDisplay(Display.DEFAULT_DISPLAY)
val defaultDisplayContext createDisplayContext(defaultDisplay)
val ctx defaultDisplayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);// 在屏幕上绘制一个像素的view用于监控开机动画是否播放完毕
val mWindowManager ctx.getSystemService(WindowManager::class.java)
val bounds mWindowManager.getCurrentWindowMetrics().getBounds();val windowSizeTest: View object : View(ctx) {override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {Log.e(TAG, system launch)}
}Service 完整代码如下
class SystemService : Service() {private val TAG SystemService::class.java.simpleName;override fun onBind(intent: Intent): IBinder? {return null}override fun onCreate() {super.onCreate()// 创建用于 window 显示的contextval dm getSystemService(DisplayManager::class.java)val defaultDisplay dm.getDisplay(Display.DEFAULT_DISPLAY)val defaultDisplayContext createDisplayContext(defaultDisplay)val ctx defaultDisplayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);// 在屏幕上绘制一个像素的view用于监控开机动画是否播放完毕val mWindowManager ctx.getSystemService(WindowManager::class.java)val bounds mWindowManager.getCurrentWindowMetrics().getBounds();val windowSizeTest: View object : View(ctx) {override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {// 暂停5秒后移除该ViewThread{sleep(5_000)mWindowManager.removeView(this)}.start()}}val testParams: WindowManager.LayoutParams WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLEand WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ONand WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)testParams.width bounds.width() / 2testParams.height bounds.height()/2testParams.gravity Gravity.CENTERtestParams.title TAGmWindowManager.addView(windowSizeTest, testParams)}
}第 4 步验证
在这一步中我们通过Android Studio中的模拟器来验证系统应用的运行方式是否符合我们的预期。
将编写好的系统应用 push 到System/app/下不过由于模拟器的 System 分区不开放写入权限在此之前我们需要先获取 System 分区的写入权限。
1修改模拟器写入权限
首先进入Android SDK 模拟器目录执行如下指令控制台出现 remount succeeded 的信息即表示修改写入权限成功了。
./emulator -list-avds
./emulator -writable-system -avd [10.1_WXGA_Tablet_API_31] -no-snapshot-load -qemu // 修改分区写入权限吧
adb root
adb remount
adb reboot // 重启模拟器
// 等待模拟器重启后
adb root
adb remount2将应用 apk push到 system/app/xxx 目录
在system/app目录下新建一个SystemApp名称任意然后将 apk push到该目录下。
3重启模拟器查看效果
模拟器重启后SystemApp进程会自动启动并在屏幕上覆盖一个黑色View整个过程中 SystemApp 没有弹出权限申请的窗口。
如果我们使用adb kill [进程号]杀死 SystemApp系统会立即将 SystemApp 进程拉起。普通应用上难以实现的进程保活在『系统应用』上轻而易举的就可以达成了而且进入系统设置中查看 SystemApp 发现 SystemApp 实际上也无法被卸载。
总结
本期视频我们介绍了Android系统应用的开发方式车载 Android 应用开发说到底都是在做系统应用开发了解系统应用的开发方式是我们入门车载 Android 应用开发最基本的技术要求。
好以上就是本视频的全部内容了。本视频的文字内容发布在我的个人微信公众号-『车载 Android』和我的个人博客中视频中使用的 PPT 文件和源码发布在我的Github[https://github.com/linxu-link/CarAndroidCourse]中在本视频的简介里可以找到相应的地址。
感谢您的观看我们下期视频再见拜拜。 参考资料 https://developer.android.google.cn/guide/topics/manifest/application-element?hlzh-cn#persistent https://developer.android.google.cn/guide/topics/manifest/manifest-element?hlzh-cn#uid