第十四期 AOSP 编译系统分析《手机就是开发板》

本文深入探讨了AOSP(Android开源项目)的复杂编译过程,包括多种目标文件的生成,如APK、Java库、C/C++应用程序及库等。通过分析Android.mk文件的结构和作用,揭示了模块编译的细节,以及如何使用m、mm、mmm命令执行编译。文章还提供了Zygote进程和Camera应用程序的具体示例。

https://blog.youkuaiyun.com/aggresss/article/details/53976626

AOSP的的编译过程复杂就在于要编译很多种类型的目标文件,不像kernel那样,只需要一种编译工具就能完成,在AOSP中主要包括以下几种目标文件:
1. APK程序,一般的Android程序,编译打包生成apk文件
2. Java库,java类库,编译打包生成jar文件
3. C\C++应用程序,可执行的C\C++应用程序
4. C\C++静态库,编译生成C\C++静态库,并打包成.a文件
5. C\C++共享库, 编译生成共享库(动态链接库),并打包成.so, 有且只有共享库才能被安装/复制到您的应用软件(APK)包中。
大量的源码按照功能通过目录来分类,同一功能的代码通常被编译成一个目标文件,目标文件不仅仅包含可执行C/C++应用程序,还包含动态库、静态库、Java类库、Android应用程序等,在Android编译系统中,每个被编译的目标文件被称为一个模块(module),在每个模块的源码目录中必须创建一个Android.mk文件作为编译规则,这些Android.mk文件在编译时被编译系统中的findleaves.py脚本包含进去。
下面是 build/core/main.mk 中的内容:

#
# Include all of the makefiles in the system
#
 
# Can't use first-makefiles-under here because
# --mindepth=2 makes the prunes not work.
subdir_makefiles := \
    $(shell build/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) $(subdirs) Android.mk)
 
$(foreach mk, $(subdir_makefiles), $(info including $(mk) ...)$(eval include $(mk)))


注:findleaves.py由Python语言编译的脚本,Python是一种执行效率比较高的面向对象的脚本,上述脚本意思是返回subdirs目录下的Android.mk文件,但是会跳过out、.reop、.git目录。
通常编译一个模块时编译器需要知道以下内容:
1. 编译什么文件?(指定源码目录和源码文件)
2.编译器需要哪些编译参数?
3.编译时需要哪些库或头文件?
4.如何编译?(编译成动态库、静态库、二进制程序、Android应用还是Java库?)
5. 编译目标
Android.mk的语法不同于Makefile,Android.mk语法更简洁,用户只需在Android.mk中定义出一些编译变量,Android的编译系统会根据Android.mk文件中变量的值进行编译。
比如Zygote进程app_process模块中的Android.mk如下面代码所示:

@ frameworks/base/cmds/app_process/Android.mk
 LOCAL_PATH:= $(call my-dir)              #指定源码目录
 include $(CLEAR_VARS)                     #包含清除编译变量的mk文件,防止影响本次编译
 
 LOCAL_SRC_FILES:= \                      #指定被编译源码
      app_main.cpp
 
 LOCAL_SHARED_LIBRARIES := \               #指定编译Zygote时用到的其它动态库
      libcutils \
      libutils \
      libbinder \
      libandroid_runtime
 
 LOCAL_MODULE:= app_process                #指定被编译模块的名字
 
 include $(BUILD_EXECUTABLE)                  #指定编译方式,编译成可执行程序


再比如Camera应用程序中的Android.mk:

@ packages/apps/Camera/Android.mk
 LOCAL_PATH:= $(call my-dir)                        #指定源码目录
 include $(CLEAR_VARS)                              #包含清除编译变量的mk文件,防止影响本次编译
 
 LOCAL_MODULE_TAGS := optional                      #指定应用程序标签
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)      #指定被编译源码
 
 LOCAL_PACKAGE_NAME := Camera                   #指定Android应用程序名
 LOCAL_SDK_VERSION := current                        #指定该应用程序依赖的SDK版本
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags    #指定混淆编译配置文件
 
include $(BUILD_PACKAGE)                        #指定模块编译方式,这儿编译成Android应用程序
 
 # Usethe following include to make our test apk.
 include $(call all-makefiles-under,$(LOCAL_PATH))        # 包含当前目录下子目录中的Android.mk文件,向下编译


通过上面两个例子可以看出来,Android.mk文件结构很简单,每个模块的Android.mk文件必须完成以下操作:
1. 指定当前模块的目录
通过调用$(call my-dir)命令包(一些Makefile的集合),来获得当前模块目录。
2.清除所有的LOCAL_XX变量
通过include命令包含clear_vars.mk文件来清除所有的LOCAL_XX变量,防止影响本次编译结果,clear_vars.mk文件由变量CLEAR_VARS来定义
3. 指定源码文件
通过LOCAL_SRC_FILES变量指定源码文件,对于C/C++文件,要将它们全部列出来赋值给LOCAL_SRC_FILES(见上面程序代码),对于Java源码,可以通过调用命令包$(callall-java-files-under, src)来实现,它会在src目录下查找所有的Java文件,将其罗列出来。
4. 指定编译细节
在编译时可能需要修改编译器参数、需要链接其它的库、需要其它路径下的头文件等编译细节。
5. 指定目标模块名
如果是C/C++库、可执行程序或Java类库,通过LOCAL_MODULE指定最终编译出来的模块名,如果是Android应用程序,通过LOCAL_PACKAGE_NAME变量来指定。
6. 指定目标模块类型
模块最终都要进行编译,通过include 命令包含一些预定义好的变量来指定模块最终的类型,这些变量分别对应一个makefile文件,包含了模块类型的编译过程。主要的预定义编译变量如下:

注:上述编译变量的定义在build/core/definitions.mk中。
在Android.mk中,主要编译变量如下表所示:

=========================分割线==========================
搞清楚Android.mk文件后,接下来需要研究我们编译时使用m,mm,mmm命令的执行原理了,AOSP中并没有像Make系统中通过 -f 参数指定Makefile文件,而是采用了自有的一套机制来完成,具体实现也比较复杂,下面这篇文章讲得非常详细:
http://blog.youkuaiyun.com/luoshengyang/article/details/19023609

<think>我们正在为RK3562开发板(基于AOSP Android 13)添加一个悬浮返回按钮。悬浮返回按钮通常是一个系统级的覆盖层,允许用户通过点击屏幕上的一个按钮来模拟返回键操作。 实现思路: 1. 在系统UI(SystemUI)中添加一个悬浮窗服务(似于导航栏的定制)。 2. 创建一个始终位于顶层的视图(View),该视图包含一个返回按钮。 3. 为该按钮设置点击事件,触发返回操作(模拟按下返回键)。 具体步骤: 步骤1:修改SystemUI源码 我们需要在SystemUI模块中创建一个新的服务或修改现有服务来管理悬浮返回按钮。 例如,我们可以创建一个名为`FloatingBackButtonService`的服务,并在其中管理悬浮窗的显示和事件。 步骤2:创建悬浮窗视图 在服务中,使用WindowManager添加一个悬浮窗。注意,需要申请悬浮窗权限,但作为系统应用,我们可以使用系统级权限(TYPE_SYSTEM_OVERLAY或TYPE_APPLICATION_OVERLAY,但要注意Android O之后对悬浮窗的限制)。 由于是系统应用,我们可以使用更高的窗口型,例如: - 对于Android 8.0及以上,使用`TYPE_APPLICATION_OVERLAY` - 注意:在系统应用中,我们也可以使用`TYPE_SYSTEM_ALERT`(在API level 26之前)或`TYPE_SYSTEM_OVERLAY`(但需要SYSTEM_ALERT_WINDOW权限) 但是,在SystemUI中,我们通常使用系统级别的窗口型,例如`TYPE_NAVIGATION_BAR`等,但为了简单,我们可以使用`TYPE_NAVIGATION_BAR_PANEL`(这是一个系统内部使用的型,通常用于系统UI的覆盖层)。 步骤3:实现返回功能 当点击按钮时,我们需要注入一个返回键事件。可以通过`InputManager`注入一个KeyEvent(KEYCODE_BACK)。 步骤4:在SystemUI中启动服务 我们需要在SystemUI的适当位置(例如在状态栏启动时)启动我们的悬浮窗服务。 步骤5:确保在RK3562的编译配置中包含我们的修改 修改SystemUI的Android.mk或Android.bp文件,确保新增的源文件被包含。 详细代码示例: 1. 创建悬浮窗服务(在SystemUI模块中): 新建文件:`packages/SystemUI/src/com/android/systemui/floating/FloatingBackButtonService.kt`(或Java,这里以Kotlin为例,但也可以用Java) ```kotlin package com.android.systemui.floating import android.app.Service import android.content.Context import android.graphics.PixelFormat import android.os.Handler import android.os.IBinder import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.WindowManager import android.view.KeyEvent import android.view.InputEvent import android.view.InputDevice import android.os.SystemClock import com.android.systemui.R class FloatingBackButtonService : Service() { private lateinit var windowManager: WindowManager private lateinit var floatingButton: View override fun onCreate() { super.onCreate() windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager val inflater = LayoutInflater.from(this) floatingButton = inflater.inflate(R.layout.floating_back_button, null) val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, // 使用这个型需要系统权限 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ).apply { gravity = Gravity.BOTTOM or Gravity.END x = 0 y = 100 // 距离底部的距离,可以根据需要调整 } windowManager.addView(floatingButton, params) floatingButton.setOnClickListener { triggerBackEvent() } } private fun triggerBackEvent() { val now = SystemClock.uptimeMillis() val downEvent = KeyEvent(now, now, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD) val upEvent = KeyEvent(now, now, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD) // 注入事件 InputManager.getInstance().injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC) InputManager.getInstance().injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC) } override fun onBind(intent: Intent?): IBinder? { return null } override fun onDestroy() { super.onDestroy() if (::floatingButton.isInitialized) { windowManager.removeView(floatingButton) } } } ``` 2. 创建悬浮按钮的布局文件(在SystemUI的res/layout目录下): 文件:`packages/SystemUI/res/layout/floating_back_button.xml` ```xml <?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/floating_back_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_back_button" <!-- 需要准备一个返回按钮的图标 --> android:contentDescription="@string/accessibility_back_button" /> ``` 3. 在SystemUI的适当位置启动服务: 例如,我们可以在`SystemUIApplication`的`startServicesIfNeeded`方法中启动该服务。 修改文件:`packages/SystemUI/src/com/android/systemui/SystemUIApplication.kt` 在`startServicesIfNeeded`方法中,添加: ```kotlin // 启动悬浮返回按钮服务 startServiceIfNeeded(FloatingBackButtonService::class.java.name) ``` 4. 注意:注入按键事件需要权限。在SystemUI的AndroidManifest.xml中声明服务并添加必要的权限: 在`packages/SystemUI/AndroidManifest.xml`的`application`标签内添加: ```xml <service android:name=".floating.FloatingBackButtonService" android:exported="false" android:permission="android.permission.INJECT_EVENTS" /> ``` 同时,需要确保SystemUI具有`INJECT_EVENTS`权限。该权限是系统级权限,通常只有系统应用才能拥有。在AOSP中,SystemUI本身已经是系统应用,但需要在Android.mk或Android.bp中指定使用系统签名。 5. 编译SystemUI模块: 在编译整个系统镜像时,确保SystemUI模块被重新编译并包含我们的修改。 6. 针对RK3562开发板,我们还需要确保在设备树中启用了SystemUI的修改。通常,SystemUI是AOSP的一部分,只要编译时包含SystemUI即可。 注意事项: - 悬浮窗的位置和样式可以根据需要调整。 - 由于使用了系统级窗口型(TYPE_NAVIGATION_BAR_PANEL),该悬浮窗将显示在其他应用之上。 - 注入按键事件的方式需要系统权限,在SystemUI中使用是合适的。 另外,我们也可以考虑使用辅助功能(AccessibilityService)来实现,但辅助功能需要用户手动开启,而作为系统级功能,我们更希望直接集成在系统UI中。 以上是实现悬浮返回按钮的一种方法。由于涉及系统级修改,需要重新编译SystemUI并刷入设备。 编译步骤(在AOSP环境中): 1. 进入AOSP根目录 2. 设置编译环境:`source build/envsetup.sh` 3. 选择目标(RK3562的设备配置):`lunch`,选择对应的设备 4. 编译SystemUI模块:`make SystemUI` 5. 编译整个系统:`make -j8` 6. 将生成的镜像刷入设备 注意:具体的设备编译配置可能因RK3562开发板而异,需要参考设备供应商提供的文档。 如果开发板使用的是迅为提供的源码,那么编译过程可能参考他们的文档[^2][^3]。 相关问题: 1. 如何调整悬浮返回按钮的位置和大小? 2. 如何为悬浮返回按钮添加动画效果? 3. 如何实现悬浮返回按钮的拖动功能? 4. 在Android 13中,系统UI的定制有哪些新的限制?</think>### 在AOSP安卓13中为RK3562开发板添加悬浮返回按钮 要在基于AOSP Android 13的RK3562开发板上实现悬浮返回按钮,需修改SystemUI模块并添加自定义视图。以下是具体实现步骤: #### 1. **创建悬浮按钮视图** 在SystemUI模块中添加自定义视图组件: ```java // packages/SystemUI/src/com/android/systemui/floating/FloatingBackButton.java public class FloatingBackButton extends ImageView { private final WindowManager mWindowManager; private final WindowManager.LayoutParams mLayoutParams; public FloatingBackButton(Context context) { super(context); setImageResource(R.drawable.ic_back_button); // 返回按钮图标 setClickable(true); // 设置窗口参数 mLayoutParams = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, // 系统级悬浮窗 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); mLayoutParams.gravity = Gravity.BOTTOM | Gravity.END; mLayoutParams.y = 100; // 距底部距离 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mWindowManager.addView(this, mLayoutParams); // 点击事件处理 setOnClickListener(v -> injectBackKeyEvent()); } private void injectBackKeyEvent() { long now = SystemClock.uptimeMillis(); // 注入返回键按下事件 KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0); KeyEvent upEvent = new KeyEvent(now, now, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0); InputManager.getInstance().injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); InputManager.getInstance().injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); } } ``` #### 2. **集成到SystemUI服务** 在SystemUI启动时初始化悬浮按钮: ```java // packages/SystemUI/src/com/android/systemui/SystemUIService.java public class SystemUIService extends Service { @Override public void onCreate() { super.onCreate(); // 添加悬浮按钮初始化 new FloatingBackButton(this); } } ``` #### 3. **添加资源文件** 在SystemUI资源目录中添加按钮图标: ```xml <!-- packages/SystemUI/res/drawable/ic_back_button.xml --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="#FF0000" android:pathData="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/> </vector> ``` #### 4. **配置权限** 在`AndroidManifest.xml`中添加权限声明: ```xml <!-- packages/SystemUI/AndroidManifest.xml --> <uses-permission android:name="android.permission.INJECT_EVENTS" /> ``` #### 5. **编译与部署** 1. 编译SystemUI模块: ```bash source build/envsetup.sh lunch rk3562_<target> # 选择RK3562编译目标 make SystemUI ``` 2. 生成新系统镜像: ```bash make -j8 ``` 3. 刷入设备: ```bash fastboot flash system $OUT/system.img ``` #### 注意事项 1. **窗口型选择**:使用`TYPE_NAVIGATION_BAR_PANEL`确保悬浮窗位于系统层 2. **事件注入权限**:需系统签名(`platform.pk8`和`platform.x509.pem`) 3. **位置调整**:通过`mLayoutParams.x/y`动态调整按钮位置 4. **RK3562适配**:确认uboot支持Android 13启动配置[^2][^3] ### 验证方法 1. 设备启动后观察屏幕右下角是否出现返回按钮 2. 点击按钮测试返回功能是否生效 3. 通过`adb shell dumpsys window`查看窗口层级 ### 优化建议 - 添加拖动功能:重写`onTouchEvent`实现位置拖动 - 持久化位置:使用`SharedPreferences`保存用户调整的位置 - 动态显隐:结合`GestureNavigation`在需要时显示按钮
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值