嵌入式安卓编程指南(二)

原文:zh.annas-archive.org/md5/9bc936bbccde26c205e7f8b16cd5450f

译者:飞龙

协议:CC BY-NC-SA 4.0

第五章. 自定义内核和引导序列

在上一章中,我们创建并部署了我们自己的第一个自定义 Android 版本。我们为商业智能手机 Google Nexus 6 创建了一个版本,并为开发板 Udoo Quad 创建了一个更硬核的版本。我们学习了更多开发工具,如 ADB 和 Fastboot。我们专注于调试工具,掌握串行连接和引导序列。

在本章中,我们将深入系统——从内核定制到引导序列。您将学习如何获取谷歌设备的正确源代码,如何设置构建环境,如何构建您的第一个自定义 Linux 内核版本,并将其部署到您的设备上。您将了解:

  • 工具链概述

  • 如何配置主机系统以编译自己的 Linux 内核

  • 如何配置 Linux 内核

  • Linux 内核概述

  • Android 引导序列

  • Init进程

Linux 内核概述

在 Linux 内核及其构建中。选择 Linux 内核的一个原因是其无可置疑的灵活性和无限的可能性,可以将其调整到任何特定场景和需求。正是这些特性使 Linux 成为嵌入式行业中最受欢迎的内核。

Linux 内核附带 GPL 许可证。这个特定的许可证允许谷歌从 Android 的早期阶段开始为项目做出贡献。谷歌提供了错误修复和新功能,帮助 Linux 克服了 2.6 版本的几个障碍和限制。最初,Linux 2.6.32 是 Android 设备市场中最受欢迎的版本。如今,我们看到越来越多的设备配备了新的 3.x 版本。

下面的截图显示了官方谷歌摩托罗拉 Nexus 6 的当前构建,内核版本为 3.10.40:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_73.jpeg

在前几章中,我们创建并部署了我们自己的 Android 版本,该版本配备了 Linux 内核的二进制版本。使用已经编译好的内核版本是标准做法:正如我们所见,AOSP 提供了这种类型的体验。

作为高级用户,我们可以更进一步,为我们的自定义 Android 系统构建一个自定义内核。Nexus 系列提供了进入这个世界的便捷途径,因为我们可以轻松地获取构建自定义版本所需的内核源代码。我们还可以将我们的自定义 Linux 内核装备到我们的自定义 Android 系统中,我们将拥有一个完全定制的 ROM,适用于我们的特定需求。

在这本书中,我们故意使用 Nexus 设备——谷歌是少数几家正式提供内核源代码的公司之一。即使每个生产和销售 Android 设备的公司都受到法律强制要求发布内核源代码,但其中很少有人真正这样做,尽管有 GPL 许可证规则。

获取内核

谷歌为 Nexus 系列中每个设备的每个 Android 版本提供了内核源代码和二进制版本。

以下表格显示了二进制版本和源代码的位置,按设备代码名称排序:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_74.jpeg

正如第四章“转向现实硬件”中所述,我们将使用摩托罗拉 Nexus 6,代码名称Shamu

内核的二进制版本和内核源代码都存储在 git 存储库中。我们只需要编写正确的 URL 并克隆相应的存储库。

获取内核的二进制版本

在本节中,我们将获取内核作为二进制、预构建文件。我们需要的只是之前显示每个设备型号、其代码名称及其二进制位置的表格,我们可以使用这些信息来组成下载 URL。我们针对的是谷歌 Nexus 6,代码名称shamu,二进制位置:

device/moto/shamu-kernel

因此,要获取摩托罗拉 Nexus 6 内核的二进制版本,我们需要以下命令:

$ git clone https://android.googlesource.com/device/moto/shamu-kernel

上一条命令将克隆存储库并将其放置在shamu-kernel文件夹中。这个文件夹包含一个名为zImage-dtb的文件——这个文件是实际可以集成到我们的 ROM 并刷入我们设备的内核镜像。

获取内核镜像后,我们可以使用以下命令获取内核版本:

$ $ dd if=kernel bs=1 skip=$(LC_ALL=C grep -a -b -o $'\x1f\x8b\x08\x00\x00\x00\x00\x00' kernel | cut -d ':' -f 1) | zgrep -a 'Linux version'

输出:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_75.jpeg

上一张截图显示了命令输出:我们的内核镜像版本是 3.10.40,它是在 10 月 22 日 22:49 使用 GCC 版本 4.8 编译的。

获取内核源代码

对于二进制版本,上一张表格对于下载内核源代码也是关键的。针对谷歌 Nexus 6,我们使用设备代码名称shamu的源位置字符串创建下载 URL:

kernel/msm.git

一旦我们有了确切的 URL,我们就可以使用以下命令克隆 GIT 存储库:

$ git clone https://android.googlesource.com/kernel/msm.git

Git 将创建一个msm文件夹。文件夹会奇怪地空着——这是因为文件夹默认跟踪master分支。为了获取我们的 Nexus 6 的内核,我们需要切换到正确的分支。

可用的分支有很多,我们可以使用以下命令查看列表:

$ git branch -a

列表将显示每个分支,针对特定 Nexus 设备的特定 Android 版本。以下截图显示了这些存储库的子集:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_76.jpeg

现在你已经知道了分支名称,对于你的设备和你的 Android 版本,你只需要检出正确的分支:

$ git checkout android-msm-shamu-3.10-lollipop-release

以下截图显示了预期的命令输出:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_77.jpeg

设置工具链

工具链是所有用于有效地将特定软件编译成二进制版本所需工具的集合,使用户能够运行它。在我们的特定领域,工具链允许我们创建一个系统镜像,该镜像可以用于烧录到我们的 Android 设备上。有趣的是,工具链允许我们为与当前架构不同的架构创建系统镜像:我们很可能是使用 x86 系统,但我们想创建一个针对 ARM(高级精简指令集机器)设备的系统镜像。针对与宿主系统架构不同的架构编译软件被称为交叉编译

互联网为这项任务提供了一些方便的解决方案——我们可以使用与 AOSP(Android 开源项目)一起提供的标准工具链,或者我们可以使用一个替代的、非常流行的工具链,即 Linaro 工具链。这两个工具链都能完成工作——为 ARM 架构编译每一个 C/C++文件。

如同往常,工具链既可用预编译的二进制文件提供,也可作为源代码提供,以便编译。对于我们的旅程,我们将使用由 Google 提供的官方工具链,但当你需要更深入地探索这个领域时,你可以尝试下载来自www.linaro.org/download的 Linaro 工具链的二进制版本。Linaro 工具链被认为是市场上最优化和性能最好的工具链,但我们的目标不是比较工具链或固执地使用最好的或最受欢迎的一个。我们的目标是创建尽可能平滑的体验,从整个构建自定义 Android 系统方程中移除不必要的变量。

获取工具链

我们将使用由 Google 提供的官方工具链。我们可以通过 Android 源代码或单独下载来获取它。如果您手头有信任的 Android 源代码文件夹,您可以在以下文件夹中找到工具链:

AOSP/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/

这个文件夹包含构建自定义内核所需的所有内容——编译器、链接器以及一些其他工具,如调试器。

如果由于某些不幸的原因,您缺少 Android 源代码文件夹,您可以使用以下 git 命令下载工具链:

$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8

准备宿主系统

要成功编译我们的自定义内核,我们需要一个配置正确的宿主系统。要求与我们在上一章中构建整个 Android 系统时满足的要求相似:

  • Ubuntu

  • Linux 内核源代码

  • 工具链

  • Fastboot

Ubuntu 需要一点爱来完成任务:我们需要安装ncurses-dev包:

$ sudo apt-get install ncurses-dev

一旦安装了所有必需的工具,我们就可以开始配置所需的环境变量。这些变量在交叉编译过程中使用,并且可以通过控制台进行设置。启动您信任的终端并运行以下命令:

$ export PATH=<toolchain-path>/arm-eabi-4.8/bin:$PATH
$ export ARCH=arm
$ export SUBARCH=arm
$ export CROSS_COMPILE=arm-eabi-

配置内核

在能够编译内核之前,我们需要正确配置它。Android 仓库中的每个设备都有一个特定的分支,带有特定的内核和特定的配置要应用。

第 2 页的表格中有一列包含我们需要的精确信息——构建配置。这些信息代表了我们正确配置内核构建系统所需的参数。让我们为我们的 Google Nexus 6 配置一切。在你的终端中,运行以下命令:

$ make shamu_defconfig

此命令将创建一个针对你的设备的特定内核配置。以下截图显示了命令的运行情况和最终的成功消息:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_78.jpeg

一旦.config文件就位,你就可以使用默认配置来构建内核。作为高级用户,我们想要更多,这就是为什么我们将完全控制系统,深入内核配置。编辑配置可以启用缺失的功能或禁用不需要的硬件支持,以创建完美的自定义内核,满足你的需求。

幸运的是,为了改变内核配置,我们不需要手动编辑.config文件。Linux 内核提供了一个图形工具,它将允许你导航整个配置文件结构,获取关于单个可配置项的文档,并毫不费力地准备一个自定义配置文件。

要访问配置菜单,打开你的终端,导航到kernel文件夹并运行以下命令:

$ make menuconfig

以下截图显示了官方 Linux 内核配置工具——没有花哨的功能,但非常有效:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_79.jpeg

在截图的上半部分,你可以看到我们将要定制的内核版本以及如何导航所有这些菜单项的快速文档:你使用箭头键导航,使用Enter 键进入子菜单,使用Y/N空格键选择或取消选择一个项。

权力越大,责任越大,所以启用和禁用功能时要小心——检查menuconfig中的文档,检查互联网,最重要的是,要有信心。错误的配置可能会在引导过程中导致系统冻结,这将迫使你学习,创建不同的配置并再次尝试。

作为现实世界的例子,我们将启用 FTDI 支持。Future Technology Devices International 或 FTDI 是一家全球知名的半导体公司,以其 RS-232/TTL 到 USB 设备而闻名。这些设备在通过标准 USB 连接与嵌入式设备通信时非常有用。要启用 FTDI 支持,你需要按照以下步骤导航到正确的菜单:

Device Drivers|USB support|USB Serial Converter support

一旦你到达这个部分,你需要启用以下项:

USB FTDI Single Port Serial Driver

以下截图显示了正确选择的项,并给你一个我们可能支持多少设备的想法(此屏幕只显示 USB 串行转换器支持):

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_80.jpeg

一旦一切就绪,只需选择退出并保存配置,如图所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_81.jpeg

使用完全相同的方法,您可以添加您想要的每一个新功能。一个重要的注意事项,我们添加了 FTDI 包,将其合并到内核映像中。Linux 内核给您提供了将功能作为模块提供的可能性。模块是一个外部文件,具有.ko扩展名,可以在运行时注入和加载到内核中。内核模块在您在纯 Linux 系统上工作时是一个伟大且方便的功能,但在 Android 上它们非常不实用。希望有一个模块化内核,您应该自己编写整个模块加载系统,这会给系统增加不必要的复杂性。我们选择将 FTDI 功能包含在内核映像中,从尺寸角度来看可能会受到惩罚,但可以免除对模块本身的手动管理。这就是为什么常见的策略是将我们想要的每一个新功能直接集成到内核核心中。

编译内核

一旦您有一个正确配置的环境和一个全新的配置文件,您只需要一个命令就可以开始构建过程。在您的终端模拟器中,在内核源文件夹中,启动:

$ make

make命令将完成必要的配置并启动编译和汇编过程。这个过程所需的时间很大程度上取决于您系统的性能:可能是一分钟或一个小时。作为一个参考,i5 2.40 GHz CPU,8 GB RAM 的系统完成一个干净的构建需要 5-10 分钟。这比编译整个 AOSP 镜像要快得多,正如您所看到的,这是因为代码库的复杂性和大小不同。

与非谷歌设备一起工作

到目前为止,我们一直使用谷歌设备,享受谷歌开源的心态。作为高级用户,我们经常处理来自谷歌或甚至不是智能手机的设备。作为一个现实世界的例子,我们将再次使用 UDOO 板:一款支持 Ubuntu 或 Android 的单板计算机。目前,UDOO 最流行的版本是 UDOO Quad,这也是我们针对的版本。

对于其他所有设备,标准方法是将制造商的网站视为获取内核源代码和任何有用文档的过程的信任来源:最重要的是,如何正确地将新内核刷入系统。当使用自定义内核时,程序相当规范。您需要源代码、工具链、几个配置步骤,也许还需要在您的宿主系统上安装一些特定的软件包。当涉及到刷写内核时,每个设备都可能有不同的程序。这取决于系统的设计方式和制造团队提供的工具。Google 提供了 fastboot 来将我们的镜像刷入我们的设备。其他制造商通常提供类似或可以轻松完成类似任务的工具。

UDOO 开发团队努力使 UDOO 板完全兼容 fastboot——而不是强迫您适应他们的工具,他们调整了他们的设备以与您已经熟悉的工具一起工作。他们调整了板上的引导加载程序,现在您可以使用 fastboot 来刷写 boot.img,就像您在刷写标准的 Google Android 设备一样。

要获取内核,我们只需要克隆一个 git 仓库。使用您信任的终端,运行以下命令:

$ git clone http://github.com/UDOOBoard/Kernel_Unico kernel

一旦我们有了内核,我们需要在 Ubuntu 系统中安装一些软件包,以便能够与之一起工作。使用以下命令,所有内容都将被安装并就绪:

$ sudo apt-get install build-essential ncurses-dev u-boot-tools

是时候选择工具链了!UDOO 给您提供了一些选择——您可以使用与 Nexus 6 相同的工具链,或者使用 UDOO 团队提供的工具链。如果您决定使用 UDOO 官方工具链,您可以使用几个终端命令下载它。请确保您已经安装了 curl。如果没有,只需使用以下命令安装它:

$ sudo apt-get install curl

一旦您安装了 curl,您可以使用以下命令下载工具链:

$ curl http://download.udoo.org/files/crosscompiler/arm-fsl-linux-gnueabi.tar.gz | tar -xzf

现在,您已经准备好启动构建过程:

$ cd kernel
$ make ARCH=arm UDOO_defconfig

以下是输出结果:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_82.jpeg

上一张截图显示了配置过程的输出。当默认的 .config 文件准备就绪时,您可以使用以下命令启动构建过程:

$ make –j4 CROSS_COMPILE ../arm-fsl-linux-gnueabi/bin/arm-fsl-linux-gnueabi- ARCH=arm uImage modules

当构建过程完成后,您可以在 arch 文件夹中找到内核镜像:

$ arch/arm/boot/uImage

对于 Nexus 6,我们可以使用 menuconfig 来自定义 UDOO 内核。从内核源代码文件夹中,运行以下命令:

$ make ARCH=arm menuconfig

以下截图显示了 UDOO 内核配置菜单。它与 Nexus 6 配置菜单非常相似。我们有相同的按键组合来导航、选择和取消选择功能等:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_83.jpeg

在使用 UDOO 时,与 Nexus 6 一样,我们在移除内核组件时也要小心——其中一些只是为了支持特定硬件而存在,而另一些则是系统启动的关键。像往常一样,您可以自由实验,但请注意不要冒险!

与智能手机相比,这种类型的开发设备使内核调试变得稍微容易一些。UDOO,就像许多其他嵌入式开发板一样,提供了一个串行连接,使您能够监控整个启动序列。如果您正在为某些硬件开发驱动程序并将其集成到内核中,或者您只是想玩一些自定义内核配置,这将非常有用。每个内核和启动相关的消息都将打印到串行控制台,以便捕获和分析。

下一个截图显示了我们的 UDOO Quad 板的启动序列:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_84.jpeg

如您所见,从板子开机到 Android 系统提示,有大量的调试信息。

驾驶员管理

自从 2.6.x 版本以来,Linux 给开发者提供了将内核的部分编译为分离的模块的机会,这些模块可以注入到核心中,以便在运行时添加更多功能。这种方法提供了灵活性和自由度:无需重新启动系统即可享受新功能,如果您只需要更新特定模块,则无需重新构建整个内核。这种方法在 PC 世界中广泛使用,包括路由器、智能电视,甚至是我们熟悉的 UDOO 板。

编写一个新的内核模块并非易事,这远非本书的目的:关于这个主题有很多书籍,而大部分技能都来自经验。在这些页面中,您将了解整体情况、关键点和可能性。

不幸的是,Android 并不使用这种模块化方法:每个必需的功能都构建在一个单一的二进制内核文件中,出于实用性和简单性的原因。在过去的几年里,有一种趋势是将 Wi-Fi 功能所需的逻辑也集成到内核中,这在之前是从分离的模块在启动序列中加载的。

正如我们在上一页的 FTDI 示例中看到的那样,将新驱动程序添加到我们的 Android 内核中最实用的方法是使用 menuconfig 并将功能作为内核的核心部分构建。

在下一章中,我们将更深入地探讨这个主题,并为我们的内核添加一些默认配置中不存在的功能。

修改 CPU 频率

超频 CPU 是高级用户中最受欢迎的话题之一。从您的设备中获取最大性能的想法令人兴奋。论坛和博客充满了关于超频的讨论,在本节中,我们将概述并澄清您在旅程中可能会遇到的几个棘手方面。

每个 CPU 都是设计用来与特定的时钟频率或特定频率范围内工作的。任何现代 CPU 都有可能在需要时调整其时钟频率以最大化性能,在不需要性能时降低功耗,从而在我们的心爱移动设备上节省宝贵的电池。因此,超频表示通过软件改变这个工作时钟频率的可能性,将其提高到高于 CPU 设计频率的水平。

与我们经常在无良论坛帖子或博客上读到的内容相反,CPU 超频可能是一个非常危险的操作:我们正在迫使 CPU 以正式尚未测试过的时钟频率工作。这可能会对我们产生反效果,导致设备自动重启以保护自身,或者在最坏的情况下,我们甚至可能损坏 CPU。

管理 CPU 时钟频率的另一个有趣方面是所谓的降频。利用 CPU 时钟频率调整功能,我们可以根据 CPU 负载和其他方面设计并实施调整策略,以最大化效率。例如,当设备空闲或处于睡眠模式时,我们可以降低频率;当设备处于重负载时,我们可以将时钟频率推至最大,以在每个场景中享受最大的效果。进一步推进 CPU 管理,许多智能手机 CPU 都采用多核架构:如果当前场景不需要,你可以完全关闭一个核心。

降低 CPU 频率的关键概念是在制造商提供的最低频率以下添加一个新的频率。通过软件,我们可以强制设备运行在这个频率上并节省电池。这个过程并非没有风险。我们可能会创建出设备 CPU 频率如此之低,以至于会导致设备无响应或甚至冻结的情况。至于超频,这些是未知的领域,只有谨慎、经验和运气才能让你得到满意的结果。

总体概述

Linux 内核使用称为控制器的特定策略来管理 CPU 频率调整。Linux 内核中已经预建了一些控制器,可以通过menuconfig访问,但你也可以添加定制的控制器,以满足你的特定需求。

以下截图显示了 Google Nexus 6 的menuconfig部分,用于 CPU 频率调整配置:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_85.jpeg

如您所见,有六个预构建的控制器。命名约定非常有用,使得名称具有自解释性:例如,performance控制器旨在始终保持 CPU 在最高频率,以在任何时候实现最高性能,牺牲电池寿命。

在 Android 上最受欢迎的控制器无疑是ondemandinteractive控制器:这些在许多基于 Android 的设备内核中相当常见。我们的参考设备 Google Nexus 6 使用interactive作为默认控制器。

如你所预期,出于安全原因,谷歌禁止直接管理 CPU 频率。在 Android 上没有快速选择特定频率或特定管理器的方法。然而,高级用户可以通过一点努力来满足他们的好奇心或需求。在下一章中,你将了解更多关于 CPU 管理的内容,但现在,让我们定制你的引导镜像。

定制引导镜像

到目前为止,你已经学习了如何获取内核源代码,如何设置系统,如何配置内核,以及如何创建你的第一个自定义内核镜像。下一步是给你的设备配备你的新内核。为了实现这一点,我们将分析每个 Android 设备使用的 boot.img 文件的内部结构。

创建引导镜像

一个自定义固件包含四个 .img 文件,这些文件是创建工作 Android 系统所必需的。其中两个(system.imgdata.img)是兼容 Linux 文件系统的压缩镜像。

剩下的两个文件(boot.imgrecovery.img)不包含标准文件系统。相反,它们是针对 Android 的自定义镜像文件。这些镜像包含一个 2KB 的头部扇区,内核核心,使用 gzip 压缩,ramdisk 以及可选的第二状态加载器。

Android 在 AOSP 源文件夹中 mkbootimg 包含的 boot.img.h 文件中提供了关于镜像文件内部结构的更多信息。

以下截图显示了该文件内容的一个片段:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_86.jpeg

如你所见,该图像包含 boot.img 结构的图形表示。这种 ASCII 艺术包含对大小和页面的更深入解释。

要创建一个有效的 boot.img 文件,你需要你刚刚构建的内核镜像和一个 ramdisk。ramdisk 是一个在引导时挂载到系统 RAM 中的小型文件系统。ramdisk 提供了一组对引导序列至关重要的文件,对于成功的引导序列是必需的。例如,它包含负责启动引导序列中所需所有服务的 init 文件。

生成引导镜像主要有两种方法:

  • 我们可以使用 mkbootimg 工具

  • 我们可以使用 Android 构建系统

使用 mkbootimg 给你提供了很多自由度,但同时也带来了很多复杂性。你需要大量的命令行参数来正确配置生成系统并创建一个有效的镜像。另一方面,Android 构建系统已经包含了整个配置参数集,已经设置好并准备好使用,我们无需做任何努力就可以创建一个有效的镜像。仅为了给你一个关于 mkbootimg 复杂性的大致概念,以下截图显示了所需的参数概览:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_87.jpeg

玩弄如此强大的东西很有吸引力,但正如你所看到的,传递给 mkbootimg 的可能错误参数数量很大。作为务实的开发者,目前处理 mkbootimg 的风险不值得。我们想要完成任务,所以我们将使用 Android 构建系统轻松生成一个有效的引导镜像。

在前面的章节中,你使用 Android 源代码和正确配置的构建系统创建了一个整个系统的自定义版本。我们将利用我们已经完成的所有工作来完成这一新步骤。你所需要做的只是导出一个新的环境变量,指向你几页前创建的内核镜像。使用你信任的终端模拟器,启动:

$ export TARGET_PREBUILT_KERNEL=<kernel_src>/arch/arm/boot/zImage-dtb

一旦你设置了并导出了 TARGET_PREBUILT_KERNEL 环境变量,你可以启动:

$ make bootimage

一个全新的、完全定制的引导镜像将由 Android 构建系统创建,并将放置在以下文件夹中:

$ target/product/<device-name>/boot.img

只需几条命令,我们就有了全新的 boot.img 文件,准备好刷入。使用 Android 构建系统生成引导镜像是所有 Nexus 设备以及那些设计得尽可能接近官方 Google 设备的设备的首选方式。

对于所有符合这一理念的市场上的设备,事情开始变得棘手,但并非不可能。一些制造商利用 Apache v2 许可证,不提供完整的 Android 源代码。你可能会发现自己只有内核源代码,而无法利用 Android 构建系统创建你的引导镜像,甚至无法理解 boot.img 实际的结构。

在这些情况下,一个可能的方法是从一个工作设备中提取 boot.img,提取内容,用你的自定义版本替换默认内核,然后使用 mkbootimg 重新创建 boot.img:说起来容易做起来难。

目前,我们想要关注主要场景,处理一个不与我们抗争的系统。在接下来的章节中,你将学习如何反击并完全控制系统。

升级新的引导镜像

一旦你有了全新的、定制的引导镜像,其中包含你的自定义内核镜像,你只需要将其刷入你的设备。我们正在处理 Google 设备或至少是兼容 Google 的设备,因此你将能够使用 fastboot 将你的 boot.img 文件刷入你的设备。

要能够将镜像刷入设备,你需要将设备置于 fastboot mode,也称为 bootloader mode。每个设备都有进入此模式的方法,因此,根据你使用的设备,你可以查看第四章 迈向现实硬件 中的表格,其中包含了到达 fastboot 模式的所有步骤。

一旦您的设备进入快启模式,您可以通过 USB 将其连接到主机计算机。启动一个终端模拟器并运行升级引导分区的命令:

$ sudo fastboot flash boot boot.img

几秒钟后,fastboot将用您的boot.img文件的内容替换设备引导分区的内容。当烧录过程成功完成后,您可以使用以下命令重启您的设备:

$ sudo fastboot reboot

设备将使用您的新内核重启,多亏了您在前几页添加的新 USB TTL 支持,您将能够使用终端模拟器监控整个引导序列。

Android 引导序列

为了全面理解所有 Android 内部结构,我们将学习整个引导序列是如何工作的:从开机到实际的 Android 系统引导。Android 的引导序列与基于 Linux 的任何其他嵌入式系统类似:在非常抽象的方式下,在开机后,系统初始化硬件,加载内核,并最终加载 Android 框架。任何基于 Linux 的系统在其引导序列中都会经历一个类似的过程:您的 Ubuntu 计算机甚至您的家庭 DSL 路由器。

在接下来的几节中,我们将深入探讨这些步骤,以全面理解我们如此喜爱的操作系统。

内部 ROM – BIOS

当您按下设备上的电源按钮时,系统会加载存储在 ROM 内存中的一小部分代码。您可以将这想象成您 PC 上所拥有的 BIOS 软件的等效物。该软件负责设置 CPU 时钟的所有参数并运行 RAM 内存检查。之后,系统将引导加载程序加载到内存中并启动它。

引导加载程序概述

到目前为止,引导加载程序已经加载到 RAM 内存并启动。引导加载程序负责将系统内核加载到 RAM 内存中并启动它,以继续引导序列。

对于 Android 设备来说,最流行的引导加载程序软件是 U-Boot,即通用引导加载程序。U-Boot 在各种嵌入式系统中得到广泛应用:如 DSL 路由器、智能电视、信息娱乐系统等。U-boot 是开源软件,它可以根据任何设备进行定制,这种灵活性无疑是其受欢迎的原因之一。

U-boot 的主要任务是读取引导分区中的内核镜像,将其加载到 RAM 内存中,并运行它。从这一刻起,内核负责完成引导序列。

您可以将 Android 系统中的 U-boot 想象成 Ubuntu 系统中的 GRUB:它读取内核镜像,解压缩它,将其加载到 RAM 内存中,并执行它。以下图表为您展示了嵌入式 Linux 系统、Android 系统和 Linux PC 上的整个引导序列的图形表示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_88.jpeg

内核

引导加载程序加载内核后,内核的第一个任务是初始化硬件。当所有必要的硬件都正确设置后,内核从boot.img挂载 ramdisk 并启动init

初始化进程

在标准的 Linux 系统中,init 进程负责启动引导系统所需的所有核心服务。最终目标是完成引导序列并启动图形界面或命令行,以便用户可以使用系统。整个过程基于一系列特定的系统脚本,以严格的顺序执行,以确保系统完整性和正确的配置。

Android 遵循相同的理念,但行为不同。在标准的 Android 系统中,ramdisk(包含在 boot.img 中)提供了 init 脚本和引导所需的所有脚本。

Android 初始化进程由两个主要文件组成:

  • init.rc

  • init.${ro.hardware}.rc

init.rc 文件是系统的第一个初始化脚本。它负责初始化所有 Android 系统共有的方面。第二个文件非常特定于硬件。正如你所猜到的,${ro.hardware} 是引导序列发生时特定硬件的占位符。例如,在模拟器引导配置中,${ro.hardware} 被替换为 goldfinsh

在标准的 Linux 系统中,初始化序列执行一组 bash 脚本。这些 bash 脚本启动一组系统服务。Bash 脚本在许多 Linux 系统中是一个常见的解决方案,因为它非常标准化且相当流行。

Android 系统使用不同的语言来处理初始化序列:Android 初始化语言。

Android 初始化语言

Android 团队选择不使用 Bash 来编写 Android 初始化脚本,而是创建了自己的语言来执行配置和服务启动。

Android 初始化语言基于五个语句类别:

  • 动作

  • 命令

  • 服务

  • 选项

  • 导入

每个语句都是面向行的,并且基于特定的标记,由空白字符分隔。注释行以 # 符号开头。

动作

动作是一系列与特定触发器绑定的命令,用于在特定时刻执行特定动作。当发生期望的事件时,动作被放入执行队列,准备执行。

这个片段展示了动作语句的一个示例:

on <trigger> [&& <trigger>]*
  <command>
  <command>
  <command>

动作具有独特的名称。如果在同一文件中创建了具有相同名称的第二个动作,则其命令集将添加到第一个动作的命令集中,作为一个单独的动作进行设置和执行。

服务

服务是初始化序列在引导过程中执行的程序。如果需要这些服务保持运行状态,则可以对其进行监控和重启。以下片段展示了服务语句的一个示例:

service <name> <pathname> [ <argument> ]*
  <option>
  <option>
  ...

服务具有独特的名称。如果在同一文件中存在一个非唯一名称的服务,则只有第一个被视为有效;第二个将被忽略,并且开发者会收到错误消息。

选项

选项语句与服务相关联。它们旨在影响 init 如何以及何时管理特定服务。

Android 提供了相当多的可能选项语句:

  • critical: 这指定了一个设备关键服务。服务将被持续监控,如果它在四分钟内死亡超过四次,设备将在恢复模式下重启。

  • disabled: 此服务将处于默认停止状态。init 不会启动它。禁用的服务只能通过手动启动,指定其名称。

  • setenv <name> <value>: 使用namevalue设置环境变量。

  • socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]: 此命令创建一个 Unix 套接字,指定name/dev/socket/<name>),并为指定的服务提供文件描述符。<type>指定套接字类型:dgramstreamseqpacket。默认的usergroup是 0。seclabel指定创建套接字的 SELinx 安全上下文。

  • user <username>: 在服务执行之前更改用户名。默认用户名是root

  • group <groupname> [ <groupname> ]*: 在服务执行之前更改组名。

  • seclabel <seclabel>: 在启动服务之前更改 SELinux 级别。

  • oneshot: 这将禁用服务监控,当服务终止时,它不会被重启。

  • class <name>: 这指定了一个服务类。可以同时启动或停止服务类。未指定class值的服务的将关联到默认类。

  • onrestart: 当服务重启时执行一个命令。

  • writepid <file...>: 当服务分叉时,此选项将进程 ID(PID)写入指定的文件。

触发器

触发器指定了一个必须满足的条件,以便执行特定的动作。它们可以是事件触发器或属性触发器。事件触发器可以通过触发命令或QueueEventTrigger()函数来触发。示例事件触发器有bootlate-init。属性触发器可以在观察到的属性值改变时触发。每个动作可以有多个属性触发器,但只有一个事件触发器;以下代码为例:

on boot && property:a=b

boot事件被触发且属性a等于b时,此动作将被执行。

命令

命令语句指定了在引导序列中可以执行的命令,将其放置在init.rc文件中。大多数这些命令是常见的 Linux 系统命令。列表相当广泛。让我们详细看看它们:

  • bootchart_init: 如果配置正确,这将启动 bootchart。Bootchart 是一个性能监控器,可以提供有关设备引导性能的见解。

  • chmod <octal-mode-permissions> <filename>: 这更改文件权限。

  • chown <owner> <group> <filename>: 更改指定文件的拥有者和组。

  • class_start <serviceclass>: 这通过类名启动指定的服务。

  • class_stop <serviceclass>: 停止并禁用由其类名指定的服务。

  • class_reset <serviceclass>: 停止由其类名指定的服务。它不会禁用服务。

  • copy <src> <dst>: 将源文件复制到新的目标文件。

  • domainname <name>: 设置域名。

  • enable <servicename>: 通过名称启动一个服务。如果该服务已经排队等待启动,则立即启动该服务。

  • exec [<seclabel>[<user>[<group> ]* ]] -- <command> [ <argument> ]*: 分叉并执行指定的命令。执行是阻塞的:在此期间不能执行其他命令。

  • export <name> <value>: 设置并导出一个环境变量。

  • hostname <name>: 设置主机名。

  • ifup <interface>: 启用指定的网络接口。

  • insmod <path>: 加载指定的内核模块。

  • load_all_props: 加载所有系统属性。

  • load_persist_props: 在成功解密/data分区后,加载持久属性。

  • loglevel <level>: 设置内核日志级别。

  • mkdir <path> [mode] [owner] [group]: 创建具有指定名称、权限、所有者和组的文件夹。默认权限为 755,所有者和组为 root。

  • mount_all <fstab>: 将fstab文件中的所有分区挂载。

  • mount <type> <device> <dir> [ <flag> ]* [<options>]: 在特定文件夹中挂载特定设备。一些挂载标志可用:rwroremountnoatime以及所有常见的 Linux 挂载标志。

  • powerctl: 用于响应sys.powerctl系统参数的变化,对于重启路由的实现至关重要。

  • restart <service>: 重新启动指定的服务。

  • rm <filename>: 删除指定的文件。

  • rmdir <foldername>: 删除指定的文件夹。

  • setpropr <name> <value>: 使用指定的值设置具有指定名称的系统属性。

  • start <service>: 启动一个服务。

  • stop <service>: 停止一个服务。

  • swapon_all <fstab>: 启用fstab文件中指定的交换分区。

  • symlink <target> <path>: 从目标文件到目标路径创建一个符号链接。

  • sysclktz <mins_west_of_gtm>: 设置系统时钟。

  • trigger <event>: 以编程方式触发指定的事件。

  • wait <filename > [ <timeout> ]: 监视路径以等待文件出现。可以指定超时时间。如果没有指定,默认超时值为 5 秒。

  • write <filename> <content>: 将指定的内容写入指定的文件。如果文件不存在,则创建文件。如果文件已存在,则不会追加内容,但会覆盖整个文件。

导入

导入指定了当前文件中需要的所有外部文件并将它们导入:

import <path>

之前的代码片段是一个示例,说明了当前的 init 脚本如何扩展,导入外部 init 脚本。path 可以是一个单个文件,甚至是一个文件夹。如果 path 是一个文件夹,那么该文件夹第一级中的所有文件都将被导入。该命令不会递归地作用于文件夹:嵌套文件夹必须逐个通过程序导入。

摘要

在本章中,你学习了如何获取适用于你的设备的 Linux 内核,如何设置你的主机 PC 以正确构建自定义内核,如何向内核添加新功能,构建它,打包它,并将其闪存到你的设备上。

你学习了 Android 启动序列的工作原理以及如何操作 init 脚本来自定义启动序列。

在下一章中,你将学习如何制作你的第一个自定义 ROM,如何给你的设备 root 权限,以及如何替换恢复分区。

第六章. “制作”你的第一个 ROM

在第五章“定制内核和引导序列”中,我们进行了一次令人惊叹的 Linux 内核之旅——现在你知道了如何为你的设备获取正确的版本以及如何构建它。我们定制和构建你自己的内核版本,针对你的设备——我们添加了新的硬件驱动程序,并移除了那些不必要的。你最终了解了引导序列。

在本章中,我们将进入修改的世界,并将继续前进,制作你的第一个定制 ROM。你将学习如何设置系统和如何创建一个定制 ROM。我们将概述最受欢迎的 ROM,以及你需要用到的所有工具以及如何使用它们。

本章将涵盖以下主题:

  • Android 修改的历史(Cyanogenmod)

  • 定制恢复

  • 根权限

  • 厨房和其他工具

定制 ROM 的历史

首先要明确——什么是“定制 ROM”?

大多数 Android 设备都配备了所谓的NAND 存储器。NAND 存储器是一种特殊的闪存。闪存基于晶体管,而不是像旧硬盘那样的旋转磁盘。这种类型的内存完全由电管理——它可以写入、擦除,并且可以无限期地存储数据(非易失性)。了解这一点后,我们可能会认为在 Android 上可以写入任何东西。好吧,并不完全是这样!

缩写 ROM 代表只读存储器。这种类型的内存常用于嵌入式系统,用于安全地存储所有属于核心系统的文件。为了确保尽可能高的系统完整性,开发者必须确保核心系统在设备重启和可能的故障中保持完整。这就是为什么核心系统存储在只能写入一次的内存中——确切地说,就是只读存储器。随着时间的推移,Android 黑客社区采用了这个缩写并进行了转变。如今,当我们说定制 ROM 时,我们只是在说“这是我针对这个特定设备的自定义 Android 系统”,这就是我们在以下页面中将使用的含义。

对于 Linux 内核来说,Android 是目前开发中最为流行的开源项目之一。免费使用且可定制,被数百万用户使用,Android 是数百个定制操作系统的基本元素——其中大多数是实验性的,一些是为特定场景修复特定错误的定制版本,还有一些是原始系统的优化版本。

在最初,修改社区非常分散——许多孤独的狼,在他们的黑暗房间里进行黑客攻击。随着时间的推移,他们中的大多数人都汇聚到了更社交的环境中,在论坛和社区中结合他们的努力,组建了修改团队,为用户提供更好、更可靠的 ROM。

在第五章“自定义内核和引导序列”中,我们看到了如何使用源代码创建一个自定义版本的 Android。我们能够彻底改变原始系统以创建我们的版本,完美地满足我们的需求,那么关于修改的所有这些炒作是什么?为什么我们不能直接获取源代码并自定义我们的系统?事实是,不幸的是,谷歌就像大海捞针。大多数其他制造商在开源游戏中玩得略有不同,由于缺乏提供的源代码,因此不可能从头开始重建系统。

幸运的是,我们可以通过遵循不同的路径来实现 Android 定制——直接进入系统内存分区,反编译组件,并进行定制,或者所谓的表面修改

在 Linux 内核领域,游戏规则完全不同。正如你可能记得的,Android 和 Linux 内核有不同的许可证——Android 在 Apache 许可证 v2 下分发,而 Linux 内核在 GPL 许可证下分发。GPL 许可证对修改和再分发的限制更严格,制造商很难保持内核的机密性。这就是为什么 Linux 内核总是可用,修改者可以添加、删除和改进他们想要的任何方面——新的驱动程序、改进的电源管理、改进的 CPU 管理,等等。

当你审视整个自定义 ROM 概念时,你会发现你似乎每天都在看到自定义 ROM——制造商的 ROM。如果我们认为真正纯净的 Android 系统是 Nexus 设备上搭载的系统,我们就会意识到制造商是第一个修改者,将原始系统变成了经常完全不同的东西。想想三星或 HTC 的定制 UI。这些都是对 UI 的巨大修改。想想那些带有 AM/FM 收音机的设备——再次,严重的定制。一些制造商在过去的几年中进行了如此多的定制,以至于他们的设备最终甚至与 Google Play 商店不兼容。

在接下来的几页中,我们将概述最受欢迎的自定义 ROM,以了解为什么它们如此受到高级用户的喜爱。

Cyanogenmod

毫无疑问,Cyanogenmod 是最受欢迎的 Android 定制 ROM 之一。它是其中最古老的之一,它带来了在官方 Android 系统中找不到的功能和性能:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_89.jpeg

从一开始,就在 Android 开源代码的第一个公开发布之后,Cyanogen 团队就开始将最新的 Android 版本回溯到旧设备上。他们基本上克服了制造商的商业决策,即让旧设备保留旧的 Android 版本,并努力让所谓的旧设备重获新生。

在这些年中,Cyanogenmod 团队添加和调整了大量的功能,这种方法吸引了成千上万的用户。这些改进如此之好,以至于官方的 Google Android 团队经常将它们合并到官方的 Android 源代码库中,体现了真正的开源精神。

如前所述,Cyanogenmod 团队并非从零开始这个项目。他们使用了 Android 开源项目并对其进行了增强。与其他许多定制者采用不同的方法,他们决定整个项目必须以开源代码的形式提供,让每个人都能享受所有功能,从源代码中学习,并为项目本身做出贡献。多年来,社区显著增长,大量的博客文章、教程和实用指南涌入网络空间,使 Cyanogenmod 成为目前最受欢迎的自定义 ROM 之一。

这是 Cyanogenmod 目前提供的最受欢迎的功能列表:

  • 主题支持:整个系统 UI 可以使用用户创建的主题进行自定义,这些主题可以在系统运行时应用

  • FLAC 支持:无损音频编解码器(Free Lossless Audio Codec)是系统上可用的许多音频编解码器之一

  • 更大的 APN(接入点网络)列表:随着时间的推移,添加了大量的不同 APN,使得在众多设备上快速设置互联网连接变得容易

  • OpenVPN 客户端:流行的 VPN 软件可用并准备好使用

  • 丰富的关机菜单:关机菜单包含新的操作,如重启、恢复模式重启等

其他一些功能包括:

  • 支持 Wi-Fi、蓝牙和 USB 共享网络连接

  • CPU 超频管理和系统级性能增强

  • 软按钮的高级管理

  • 系统通知菜单中的新切换按钮,如 GPS、蓝牙和 Wi-Fi

  • 高级应用程序权限管理,为系统提供细致的安全保障

  • 系统级图形增强

  • 与任何其他从官方 Google 纯 Android 系统派生的 Android 系统相比,团队表示性能和可靠性有所提高

2013 年 4 月,Cyanogenmod 从社区项目转变为一家真正的公司。尽管如此,开源的本质仍然是公司的主要核心价值观之一。到目前为止,它有 17 名全职员工在项目上工作。在过去三年中,他们从第三方合作伙伴那里收到了一些捐赠,如 Benchmark Capital 和 Redpoint Ventures,推动了更简单的 Cyanogenmod 安装过程的开发。

2014 年,Cyanogenmod 宣布与智能手机制造商 OnePlus 建立合作伙伴关系,将预装 Cyanogenmod 的设备进行分发。根据他们的分析,Cyanogenmod 目前被 5000 万台设备使用。

构建 Cyanogenmod

受 Google AOSP 的启发,Cyanogenmod 提供了一个官方网站,您可以从该网站下载项目源代码并访问支持论坛:www.cyanogenmod.org

网站还提供了一个支持的所有设备的完整列表。与仅正式支持 Nexus 设备的 Google AOSP 不同,Cyanogenmod 适用于数十种不同的设备。

Cyanogenmod 构建系统与你在前几章中已经掌握的完全相同。了解这一点后,我们将它留作练习,下载并构建你自己的 Cyanogenmod 版本,以完全理解 Android AOSP 可以定制和改进到何种程度。

安装预构建版本

作为开源项目,你可以从源代码构建 Cyanogen。如果你想找到一个更快的解决方案,Cyanogenmod 为众多设备提供了预构建的系统安装版本。只需检查网站,寻找你的设备——很可能它就在支持设备列表中。

一旦你发现你的设备受到支持,你可以选择许多版本中的一个。发布周期与 Google 的非常不同。整个 Cyanogenmod 世界中最冒险的特性之一是夜间构建——每晚,一个自动系统都会使用源代码仓库的最新贡献启动一个新的构建。这些版本很复杂,必须被视为不稳定,但将包含开发团队每天添加到系统中的所有新功能——仅限勇敢者!

除了不同的发布周期外,Cyanogenmod 还使用不同的版本命名约定。团队使用标签来指定 ROM 的不同版本:

  • 夜间版:如前所述。

  • 实验版:这是目前正在测试的版本。

  • M 快照,或里程碑快照:这比夜间版更稳定,但仍被视为不稳定。

  • 发布候选版:这是达到稳定状态前的最后一步。这是第一个建议在日常设备上使用的版本。

  • 稳定版:这是最终状态,面向所有用户。

Android Open Kang Project

Android Open Kang Project,简称 AOKP,是一个于 2011 年诞生的开源项目,旨在为智能手机和平板电脑提供官方 Google Android 的替代品:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_90.jpeg

如你所想,Kang 团队并没有从头开始创建系统。他们像 Cyanogenmod 一样,以 Google 的 Android 开源项目作为起点。这个特定的 Android 版本针对高端智能手机和平板电脑,并改进了一些方面,使系统更高效和可定制。以下是它的主要优点,这也是越来越多的用户决定切换到 AOKP 的原因。

用户喜爱的一个方面是 AOKP 团队专注于使系统尽可能轻量。他们移除了所有不必要的应用,基本上只留下了官方的 Google 应用,以创建尽可能小的系统。

如今,大多数智能手机和平板电脑都包含大量的美学

这些功能可能会减慢系统速度,并且对视觉影响较大。这类应用程序被称为冗余软件,通常是预装的系统应用程序,无法从系统中移除。AOKP 将消除这些无用应用程序作为其主要目标之一。

Kang 团队非常努力地工作,以确保用户系统的最大定制化水平。AOKP 提供了一个ROM 控制菜单来定制系统的许多方面,从 UI 定制到行为定制。在手势管理领域投入了大量精力,其中最酷的功能之一是可以通过手势启动任何所需的程序,而不是点击图标。

与 Cyanogenmod 一样,AOKP 也在他们的网站上提供了大量的文档和下载,网址为aokp.co。同样,你可以查看源代码自行编译,或者尝试已经编译好的版本。

这里是一个快速列表,列出了你可以在 AOKP 中找到的亮点:

  • 振动模式:每个联系人都可以关联到特定的振动模式。

  • 导航环:Android 锁屏可以通过用户选择的程序进行定制,以便在设备锁定的情况下快速访问。

  • LED 控制:可以根据颜色、闪烁和持续时间来定制系统 LED 的行为,以创建适用于自定义场景的通知。

  • 自定义开关:通知区域可以通过不同的切换按钮进行定制,以创建适合你需求的完美设置。

下面的图片显示了实际系统中的两个截图:

安装 AOKP

AOKP 的版本与 Google 和 Cyanogenmod 不同。AOKP 只提供两个版本:

  • 夜间版本

  • 里程碑

夜间版本相当于 Cyanogenmod 的夜间构建。实际上,这只是 AOKP 构建系统每天自动生成的一个构建。这应该被视为高度不稳定,仅用于测试目的。

相比之下,里程碑是稳定的构建版本,旨在用于稳定的日常使用。

为了保持社区的参与度,Kang 团队创建了AOKP PUSH应用程序,该应用程序可以保持手机更新到新构建,并且还包括安装系统更新的功能。最后值得一提的是,与 Cyanogenmod 一样,AOKP 是完全免费且开放接受贡献的。

小型 ROM

在前面的章节中,我们看到了目前市场上最流行的两种定制 ROM 的概述,这些 ROM 适用于 Android 智能手机和平板电脑。正如你可以想象的那样,这仅仅是冰山一角——多年来,已经开发并发布了数十种不同的定制 ROM。其中许多针对特定场景,以解决特定问题或满足用户的具体需求,以他们自己的方式改进 Android 系统。大多数不是从头开始构建的,而是基于已经可用的系统,这些系统经过定制和重新分发。

大多数可用的定制 ROM 针对特定设备,以解决特定设备的问题并提高可用性和性能。DroniX(由本书作者创建的项目,针对特定设备,华为 Ideos U8150,当时一款非常流行的低端设备。开发团队专注于性能,从 Ideos CPU 中榨取了每一个可用的兆赫兹。由于有内核源代码可用,我们能够提高 CPU 频率和控制器。更好的电源管理意味着更好的电池管理,性能提升并增加了电池寿命。

总是小心尝试定制 ROM。其中一些可能非常极端,可能对您的设备造成危险。这是不幸的,但这是一个真实的情况。没有魔法可以烹饪定制 ROM,而且有很多事情可能会出错。例如,极端超频是危险的,明智的用户应该不相信试图销售这些功能的 ROM。在 Android 上进行实验可能很有趣、令人满意和具有挑战性,但必须具备知识和智慧。

我们无法列出野外所有可用的定制 ROM。我们能做的是指明正确的方向:www.xda-developers.com/。这可能是获取最新消息和最新疯狂事物的最著名论坛。

OEM 定制概述

即使它们通常不被认为是定制 ROM,制造商分发的所有 Android 变体都可以被认为是进行了大量定制。我们每天都在见证这些——每次你看到三星设备时,你就知道它不是纯 Android。

从系统启动器到设置菜单,这些系统的每一个组件都被 OEM 大量定制,与官方谷歌版本相去甚远。在某些情况下,系统差异如此之大,以至于普通用户甚至不知道他正在使用相同的 Android 5 系统,例如。

这是一个最受欢迎的 OEM 定制列表,以展示系统如何被修改,以及不同的制造商如何使相同的 Android 版本在设备上看起来如此不同。

三星 – TouchWiz

TouchWiz 是一个针对触摸界面的图形界面,由三星及其技术合作伙伴开发。通常,它被错误地定义为“定制操作系统”,但从技术角度来说,它只是对 Android UI 的重度定制。

TouchWiz 的第一个版本于 2010 年发布,针对 Android 2.1 和三星为其智能手机和平板电脑创建的操作系统 BADA。当前版本是 TouchWiz 5,我们可以在多年中找到许多改进。最初,TouchWiz 只是一个不同的 UI。今天,它是一系列定制系统应用、自定义 UI 小部件以及许多新的设置和功能,如声音配置文件、电源管理、开关等。以下截图展示了主屏幕和应用程序抽屉:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_92.jpeg

华为 EMUI

在三星工作的启发下,华为也为其设备提供了自己的 Android UI 版本。与三星一样,他们从定制 UI 开始,并添加了许多功能,如主题定制——图标、颜色、字体和锁屏。通知区域也进行了定制和改进。

最有用的新功能之一无疑是高级电源管理。它提供了三种可能的设置:超长续航、智能和普通。超长续航是极端的设置——一键操作,你可以关闭除了最基本的传感器之外的所有传感器,旨在实现尽可能长的电池寿命。智能尝试尽可能自动管理电力使用。普通模式则专注于性能——电池寿命不会很长,但设备将以全速运行。

下图展示了华为 EMUI 的主屏幕:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_93.jpeg

HTC Sense

2009 年,HTC 发布了其智能手机定制的第一个 UI 版本。它针对 Android 和 Windows Mobile,提供共享的图形用户界面,以避免用户混淆。

在 HTC 中最受欢迎的功能是一大堆主屏幕小部件,但还有其他同样有趣的功能,例如用于设备被盗时使用的追踪系统。该系统允许用户远程操作设备以定位它或擦除内存,或者简单地锁定它。甚至可以在锁屏上显示自定义消息,包括地址或奖励以重新获得设备。

下图展示了 HTC Sense 7 的主屏幕:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_94.jpeg

LG Optimus UI

LG,和其他厂商一样,提供定制的用户界面——用户可以选择系统图标、颜色和一些自定义设置。一个有趣的功能是语音命令拍照以及从连拍中选择最佳照片的能力。

下图展示了主屏幕和自定义通知区域:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_95.jpeg

小米 MIUI

这绝对是最重量级的定制系统,它有一个之前版本所没有的特定功能——它是开源的!小米从 Android 2.3.7 和 Cyanogenmod 7 开始着手开发 MIUI——这两个是系统的核心。多年来,他们创建了一个定制的 ROM,它不仅仅是一个定制的用户界面,还添加了越来越多的功能。

2011 年,小米进入市场,从系统定制者转变为设备制造商,推出了高端、低成本的设备,并配备了其 MIUI 系统。

下图展示了 MIUI 主屏幕和应用程序商店:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_94.jpeg

很不幸,这已经成为一种流行趋势——一种简单的方法用于品牌建设和确保客户忠诚度,但这并不总是推荐的做法。

有些制造商更喜欢在设备上预装纯净的 Android 系统——例如摩托罗拉。摩托罗拉的品牌策略是仅添加几款 由摩托罗拉提供 的应用程序。这些通常是实用程序应用程序,旨在丰富用户体验同时保持系统简洁。

摩托罗拉的策略还有一个很大的优点——与谷歌原始系统非常接近的系统意味着更新更快。每次谷歌发布一个新的 Android 版本,摩托罗拉设备在几天内也会收到系统更新。这对大多数其他制造商来说非常不寻常,某种程度上注定要停留在旧的 Android 版本上,因为更新这样一个高度定制的系统需要大量的工作。

Android 恢复的概述

整个 Android 架构中最重要的部分之一是 Recovery 分区。在嵌入式系统中,Recovery 分区非常常见,我们已经在之前的章节中对其进行了概述。正如我们所知,所谓的 Recovery 是一个最小运行时系统,完全与主 Android 系统解耦,并且完全自给自足。其主要目标是保证系统完整性,并提供必要的工具来解决常见的轻微问题,并恢复一个正常工作的系统。

使用 Android 纯净 Recovery,我们可以:

  • 更新 Android 系统

  • 擦除数据分区和缓存分区

如果我们想要将设备恢复到出厂默认设置,例如为了拥有一个干净的系统来开始实验特定的事情,或者如果我们只是想出售它,擦除数据和缓存分区是一种常见的做法。

深入 Android 恢复

Android Recovery 系统是完全独立的。这意味着无论主 Android 系统发生什么,recovery 总是能够恢复一个正常工作的系统。

为了达到这种级别的弹性,recovery 包含了自己的 Linux 内核和自己的 rootfs。以下截图显示了 recovery 实际上位于 Android 系统附近,但完全独立:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_97.jpeg

之前的截图显示了如何通过BootLoader访问recoveryBootLoader无法决定当前的引导序列将以运行recovery还是运行 Android 系统结束。

关闭设备时,可以通过按键组合进入恢复模式。以我们的参考设备 Google Nexus 6 为例,您可以采取以下步骤:

  1. 同时按下音量下音量上电源按钮

  2. Fastboot Mode菜单出现时,释放所有按钮。

  3. 使用音量按钮,直到屏幕上方的部分显示Recovery Mode文本。

  4. 电源键选择Recovery Mode——之后您将看到一个倒置的 Android 图标。

  5. 按住电源按钮,然后按一次音量上按钮。

一旦进入恢复主屏幕,您可以使用音量按钮进行导航,并使用电源按钮确认您的选择。

您在恢复菜单中找到的选项可能会有所不同,但 Android 原生的recovery肯定会提供以下选项:

  • 立即重启系统:此选项将重启系统。

  • 从 ADB 应用更新:Android 调试桥接可以从主机计算机上传官方 Google 系统更新。由于恢复中实施的安全措施以保证系统完整性,只能上传和通过这种方式应用经过认证的更新。

  • 擦除缓存分区:此选项将擦除缓存分区。此分区通常包含系统的临时数据和应用程序缓存数据。删除此文件将释放相当多的磁盘空间,而不会丢失用户数据或应用。

  • 擦除数据/恢复出厂设置:此选项将擦除易失性内存并恢复原始出厂系统。所有严格与系统无关的内容都将被删除:视频、音乐、文档、用户应用等。缓存分区也将被清除。

以下截图显示了原装 Android recovery

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_98.jpeg

安装替代恢复

就像整个 Android 系统一样,recovery源代码也是可供研究和修改的,而且多年来,Android 社区已经开发了可以替代 Android 原装recovery的替代方案。

所有这些替代方案旨在改进和添加更多功能到原装恢复。最常见的特点是:

  • 保存和恢复系统备份的能力:NANDroid 对于实验自定义系统和冒险配置极为有用

  • 安装自定义 ROM 的能力:从自定义 ROM 开发者的角度来看,这可能是添加功能中最重要的一个

  • 增强的 UI 和 UXD:这些自定义恢复中的一些提供了对触摸屏的支持,而不是默认的音量/电源按钮导航

最受欢迎的恢复替代方案有:

  • Clockworkmod

  • 4EXT

  • Amon Ra Recovery

  • Team Win Recovery Project (TWRP)

它们中的每一个在某种程度上都是不同的——外观、高级功能等,但它们都为高级用户提供了安装定制 ROM 的明确方式。

Clockworkmod

这无疑是游戏中最受欢迎的定制恢复之一。它通常被称为CWM,由 Koushik “Koush” Dutta 开发。他从古老的 Android 2.1 恢复源代码开始,从那时起,他一直在不断添加功能。

其中一个主要功能是 NANDroid 备份,它允许用户安全地保存和恢复整个系统结构。另一个有趣的功能是能够通过 ADB 从计算机连接到恢复 shell。一个至关重要的功能是使用非官方更新包更新系统的能力。与原装恢复不同,Clockworkmod 忽略了所有签名证书,因为它知道只有高级用户才会尝试刷写定制的更新包。

可以通过 Google Play Store 分发的特定应用轻松安装 Clockworkmod 恢复,或者像我们将要看到的那样手动安装。

要在您信任的 Nexus 设备上手动安装它,您可以使用fastboot。按照以下步骤安装 Clockworkmod 恢复:

  1. 首先要做的事情是下载它。Clockworkmod 网站列出了所有支持的设备和特定的下载文件:www.clockworkmod.com/rommanager

  2. 一旦您有了文件,解压它,您将得到一个.img文件。

  3. 现在,将您的设备置于fastboot模式,正如我们在前面的章节中看到的,打开一个终端,并使用以下命令将.img文件刷入恢复分区:

    $~: sudo fastboot flash recovery recovery.img
    
    
  4. 一旦安装了全新的恢复,您可以使用以下命令直接重启设备到恢复模式:

    $~: sudo fastboot reboot recovery
    
    

从现在开始,我们可以安装定制 ROM 或执行完整系统备份。

由于该项目是开源的,您也可以从头开始重新编译 Clockworkmod 源代码。您还可以在之前段落中讨论过的定制 Cyanogenmod ROM 中找到自定义恢复源代码。从源代码构建 Cyanogenmod 遵循您已经遵循的构建官方 Android 的相同步骤:构建系统和构建设置是相同的。应用与纯 Android 相同的知识,您可以轻松创建 Cyanogenmod 系统镜像和 Clockworkmod 定制恢复。

下面是 Clockworkmod 用户界面的截图:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_99.jpeg

TWRP – Team Win Recovery Project

Clockworkmod 恢复的一个替代方案是 TWRP,代表 Team Win Recovery Project。这个恢复模式最重要的功能之一是支持触摸屏。

以这种方式,您可以直接通过屏幕与恢复模式进行交互,就像您通常使用 Android 一样,这非常方便,尤其是如果我们将其与其他所有恢复模式中使用的音量键进行比较。图形界面相当可用,有大按钮显示所有各种选项(它们与 Clockwork 模式中的按钮非常相似)。使用 TWRP,您可以安装非官方的 ROM,也可以执行完整的系统备份。

该项目始于 2011 年 7 月 30 日,是一个开源项目——在这里,您可以为您的设备下载二进制文件,或者从源代码重新编译。

您可以在官方网站上找到更多信息:teamw.in/

这里是 TWRP 的一些截图:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_100.jpeg

使用 ADB 连接到恢复 shell

自定义恢复可以使用它们的标准 UI 操作,就像我们看到的,以及使用 ADB 连接。这个功能在标准恢复模式中不可用,在我们的实验中将会非常有用。

一旦安装了自定义恢复模式,启动终端并运行以下命令:

~$: adb devices

ADB 将列出所有可用的设备,如下面的截图所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_101.jpeg

知道只有一个设备,我们可以简单地使用以下命令来连接到恢复 shell:

~$: adb shell

您将看到一个#符号,这表明您作为root用户拥有管理员权限。作为root用户,您有机会执行高级任务,例如以读/写模式挂载system/目录,并添加或删除任何您想要的文件,而无需启动整个 Android 系统。

设备权限

正如我们在前面的章节中看到的,Android 基于 Linux,因此它也继承了与用户权限相关的部分。与标准的 Linux 系统一样,Android 也通过组和用户来管理一切。在默认配置中,无法获得管理员(root)访问权限,以防止对系统进行篡改。此外,拥有对整个操作系统的访问权限,很容易意外或故意地损坏系统本身(例如,使用病毒窃取用户数据)。

每个 Android 应用在系统上安装时,都会生成一个新的用户和组,并且应用间的通信根据 Android SDK 的约束和协议进行。然而,有时拥有对设备的完全控制权是有用的,例如,当安装管理 CPU 频率和 CPU 管理器的应用时。

现在我们来看看如何获得 root 访问权限以及 root 设备的影响。

Root 访问

Root 访问权限允许安装了 Android OS 的智能手机、平板电脑和其他设备的用户获得对整个 Android 操作系统的特权访问,也称为 root 访问权限。正如我们之前提到的,Android 使用 Linux 内核,因此获得 root 访问权限与获得常规 Linux 或类 Unix 操作系统(如 FreeBSD 或 Mac OS X)的管理员(超级用户)访问非常相似。

通常,获得 root 访问权限的原因是为了克服硬件制造商对设备施加的限制。作为 root 用户,你有能力修改或替换系统应用并更改设置。此外,你可以使用需要 root 权限的应用程序,这使你能够执行普通 Android 用户无法访问的操作。对设备进行 root 操作,即获得 root 访问权限,如果你想要完全删除设备操作系统并替换为另一个,可能更近期的版本,这也有帮助。

在接下来的段落中,我们将看到如何获得 root 访问权限,这是安装自定义 ROM 的关键前提条件。

SuperSu

要在 Android 应用程序中使用 root 权限,一位名叫 Chainfir Jorrit Jongma 的独立开发者开发了一个库,它允许你在应用程序中使用这些权限,因此可以执行 root 级别的操作。所有这些都是开源的,你可以在开发者的官方网站上探索有关 API 的文档:su.chainfire.eu

如果你想查看库源代码,你可以在以下位置找到它(并贡献):github.com/Chainfire/libsuperuser

获得 root 访问权限

现在是时候看看如何在我们的设备上实际获得 root 权限了。不幸的是,这并不简单,获得设备 root 权限的方法有很多种。每种设备都有其独特之处,因此需要执行不同的步骤来获得 root 权限。一般来说,我们可以这样说,如果有可能安装恢复模式,那么也有可能安装成为 root 所需的所有必要软件。我们只需要将正确的文件复制到默认情况下以只读方式挂载的系统分区,这样我们就可以通过创建一个临时的系统分区使用源文件来访问它,或者——在我们没有 Android 源代码的情况下——通过使用我们之前描述的某个自定义恢复模式将分区挂载为读写模式。

到目前为止,我们还没有讨论过修改设备上现有软件的法律问题。一般来说,将自定义 ROM 安装到我们的设备上并不违法,但可能会使设备保修失效。至于 Nexus 设备,没有任何问题;它们是为了软件开发目的而销售的,因此产品保修不是与软件相关,而是与硬件相关。

Chef 工具包

本书的主要目标之一是帮助您实现自己版本的 ROM 定制。在修改者的词典中,为了制作自己的 ROM 定制而修改 Android 版本的行为通常用动词“to cook”和名词“kitchen”来描述。

“制作自己的 ROM”意味着修改设备上安装的原始 Android 版本,目的是创建一个新的版本。

因此,所有可能帮助简化 ROM 定制开发的工具都被称为“Chef toolkit”。

如前几章所述,从源代码创建自己的 ROM 版本确实是可能的,但并非总是如此,因为一些设备制造商没有发布他们的源代码。在所有这些情况下,我们需要在系统分区上操作,通常直接在构建内部核心的二进制文件上操作,包括应用程序框架和文件系统实用工具。

在接下来的段落中,我们将学习如何从二进制镜像开始制作 ROM,从环境分析开始,了解将帮助我们完成第一个 ROM 定制的开发工具。

准备环境

在我们开始开发 ROM 之前,我们肯定需要在电脑上准备一个合适的环境。Android 基本上可以与所有最新的操作系统一起使用,从 Windows 到 Linux,再到 OS X。

我们总是提到 Ubuntu,就像我们在前几章中处理从源代码编译 Android 时做的那样。因此,您只需要一台装有最新版 Ubuntu 的电脑即可开始。除此之外,我们还建议安装一个好的开发者文本编辑器——它可以是命令行的 VIM,或者图形编辑器,如 ATOM、SublimeText 等。我们将主要在控制台工作,使用不同的脚本和工具来完成我们的第一个定制 ROM。

Android kitchen

烹饪大师最重要的工具无疑是“Kitchen”。虽然我们借鉴了烹饪世界的类比,但实际上我们关注的是我们第一个 Android 定制的准备工作——第一步是获取系统二进制镜像。

我们将通常使用的工具集称为“Android Kitchen”,例如在 shell 中使用的脚本,这些工具帮助开发者执行自动化任务,如解压缩和编辑构建 ROM 的系统镜像,反编译 APK 包,有时向 ROM 添加 root 权限等。

当然,网上存在许多不同的厨房,每个都有其独特的特点。我们将研究其中的一些,并尝试执行简单的操作,以便将我们的第一个定制 ROM 准备好,以便将其刷入我们的设备。

最受欢迎的Android 厨房之一是dsixda。该项目正式“退役”,但它已被许多用户分叉,并且开发仍在继续。它是开源的,你可以从github.com/dsixda/Android-Kitchen下载它或分叉它并为项目做出贡献。

dsixda 的厨房基于一系列Bash脚本和工具,提供了一种简单的方法来执行最常见的烹饪操作:

  • 添加 Busybox

  • 添加 root 权限

  • 自定义启动屏幕

这些只是其控制台菜单中可用的可能操作中的一些。这个厨房与 Windows、Linux 和 OS X 兼容。我们将使用我们信任的 Ubuntu。一旦你下载了厨房(github.com/dsixda/Android-Kitchen/archive/0.224.zip),将其解压缩到一个文件夹中,进入该文件夹,并运行以下命令:

$: ./menu

此命令将启动主菜单,如下面的截图所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_102.jpeg

dsixda 厨房操作两个特定的分区——系统和 boot,分别压缩在system.imgboot.img文件中。在以下章节中,我们将深入了解提取这些分区并对其进行自定义。

其他开发者的工具

当然,对于开发者来说,许多其他不同的工具可能会很有用,这完全取决于个人的具体需求。一个十六进制编辑器对于二进制图像的分析肯定非常有用,而简单的图形编辑软件在修改图标或其他图形方面以及为编译 Linux 内核和可能添加到 ROM 的 Android 应用程序准备整个环境时也会有所帮助。

我们通常准备环境,就像我们需要从源代码编译 Android 和 Linux 内核一样,这样我们肯定有所有必要的工具来构建我们的自定义 ROM。

使用 APKTool 操作 DEX 文件

在与 Android 系统一起工作时,需要操作 DEX 文件是很常见的。DEX 代表Dalvik 可执行文件,这些文件由 Android 虚拟机使用。为了轻松操作这些文件,你可以使用 Ryszard Wiśniewski 和 Connor Tumbleson 的 APKTool。这些工具是开源的,你可以在ibotpeaches.github.io/Apktool/下载它们。

APKTool 是用 Java 编写的,因此你需要一个 JVM 来使用它。一旦你有了 APKTool 的jar文件,打开一个终端并运行以下命令:

$: java –jar apktool_2.0.3.jar

如果需要,将版本替换为你的版本。以下截图显示了工具的初始帮助菜单:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_103.jpeg

APKTool 基于另外两个工具——smalibaksmali,用于文件的汇编和反汇编。它需要一个初始设置来正常工作:framework-res.apk位置。你必须指定 APKTool 必须查找以获取此文件的位置。framework-res.apk是 Android 系统的一部分,可以从正在运行的 Android 设备中提取,使用我们信任的 ADB:

~$ adb pull /system/framework/framework-res.apk .

之前的命令将 APK 从 Android 设备复制到当前文件夹。一旦我们有了文件,我们就可以告诉 APKTool 在哪里找到它:

~$ apktool if {path to framework-res.apk}

现在一切配置就绪,我们可以尝试使用以下命令反编译和定制一个 APK:

~$ apktool d myapk.apk path_destination_decompilation

APK 内容将被放置在我们指定的目标文件夹中,我们可以编辑我们想要的任何文件。在我们所有的修改完成后,我们可以使用以下命令将文件夹重新压缩成一个 APK 文件:

~$ apktool b path_decompiled_files new_apk_mod.apk

一旦新的 APK 准备好,我们可以使用文件传输应用或使用我们在前几章中看到的ADB push将其复制到设备上。

烹饪我们的第一个 ROM

到目前为止,我们已经看到了从二进制系统镜像创建自定义 ROM 所需工具套件的概述。其中最重要的是“厨房”,它需要system.imgboot.img分区文件来正确地完成其工作。

如果你针对的是谷歌设备,这是一个简单的游戏。谷歌为其设备提供系统源代码,因此我们可以始终从源代码构建.img文件,就像我们在前几章中学到的那样。我们还可以从谷歌为 Android 系统每个新版本提供的官方系统安装包中获取.img文件。

如果你针对的不是 Nexus 设备,事情就会变得更加冒险。大多数情况下,你不会有系统源代码;通常甚至没有可下载的系统镜像。正如你将在下一节中看到的那样,总有办法获取拼图中最后一块,以创建我们的自定义 ROM。

收集原料

列表相当简短。你所需要的只是:

  • 内核源代码,如果你想在核心级别定制系统

  • system.img

  • boot.img

这两个.img文件可能由制造商提供,就像谷歌那样,或者可以从运行中的设备系统内存中手动转储。第一种情况是幸运的;第二种情况更高级,需要一点创造力。这是我们将要深入探讨的情况,因为,如果你足够幸运,有制造商的系统恢复文件,你只需要将其解压缩到一个文件夹中,你就可以得到你想要的.img文件。

转储系统分区

要创建系统内存的转储,你需要以 root 权限访问系统。正如我们已经知道的,有几种方法可以获得 root 权限——设备特定的 rooting、安装自定义恢复等。选择你喜欢的技术。

一旦你有了 root 权限,打开一个终端,使用以下命令连接到你的设备 shell:

~$: adb shell

系统将以 # 符号欢迎我们。我们现在可以继续分区转储。要获取分区结构的概览,你可以使用以下命令:

~ # cat /proc/partitions

下面的截图显示了标准 Google Nexus 6 设备的输出:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_104.jpeg

分区的数量几乎令人难以置信,但我们只需要关注系统分区和引导分区。我们知道我们感兴趣的分区就在所有列出的分区中。现在,我们必须找出这些分区中哪个实际上是 system/,哪个是 boot/

使用以下命令显示物理分区与其在 Android 架构中角色的关系:

~ # ls /dev/block/platform/msm_sdcc.1/by-name

之前的命令将显示类似以下内容:

~ # . . .
~ # … recovery -> /dev/block/mmcblk0p35
~ # … system   -> /dev/block/mmcblk0p41
~ # … boot     -> /dev/block/mmcblk0p37
~ # … userdata -> /dev/block/mmcblk0p42
~ # . . .

如你所见,它显示了每个相关的分区及其角色。我们可以很容易地确定物理 mccblk0p41 将成为我们的 system.img,而 mmcblk0p37 将成为我们的 boot.img 文件。

我们将利用 /sdcard 分区来存储转储,并使用 dd 工具创建转储:

~ # dd if=/dev/block/mmcblk0p41 of=/sdcard/system.img

使用之前的命令,你正在将整个系统分区复制到 SD 卡上的一个单独的文件中。这个过程可能需要一些时间——请耐心等待。一旦你有了 system.img 文件,你就可以继续创建 boot.img 文件,使用以下命令:

~ # dd if=/dev/block/mmcblk0p37 of=/sdcard/boot.img

你现在有了创建自定义 ROM 的两个最重要的文件。让我们开始定制它们。

修改 Android 系统二进制镜像

按照以下步骤修改 Android 系统二进制镜像:

  1. 让我们从 system.img 开始。首先,你需要将它复制到你的主机计算机上:

    ~$ adb pull /sdcard/system.img .
    
    
  2. 然后,你需要创建一个挂载点来挂载镜像到其中:

    ~$ mkdir system_mount_point
    
    
  3. 现在,你可以将其挂载为一个常见的镜像文件:

    ~$ mount –o loop system.img system_mount_point
    
    
注意

在旧设备上,system.img 使用的文件系统是 yaffs。多年来,Android 系统迁移到了 ext4 文件系统,这在许多 Linux 系统中也非常常见。你很可能会现在使用 ext4 文件系统。

使用 cd 进入挂载点,并用 ls 列出文件,你将看到一个类似于下一张图片中的文件夹结构:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_105.jpeg

现在,你可以导航文件夹树并研究结构,移除或添加你想要的文件。一个值得研究的有意思的文件是 build.prop。这个文件包含有关系统和其配置的详细信息。它是一个非常硬件特定的文件,由于定制 Android 系统的无限可能性,但大多数变体都共享一些共同细节,例如内存堆大小、显示密度、设备代码名称、制造商名称、Android 框架 SDK 版本、Android 系统版本等等。甚至还有关于系统构建时间和通知及电话的默认铃声的信息。有许多小定制可以让你玩耍和实验。对于更重的修改,请继续阅读并准备好下一章即将到来的内容。

修改 Android 二进制引导镜像

如您从前面的章节中学到的,引导镜像与系统镜像略有不同。首先,它不包含我们可以在主机系统上挂载的文件系统:引导镜像必须被解压缩

要解压缩引导镜像,您将使用上一页中Android Kitchen的特定菜单项。引导镜像是自定义 ROM 的关键组件:那里是内核所在,也是init脚本所在。这是放置必须在 Android 系统启动前应用的系统定制化的完美位置,例如 CPU 管理器的设置。

要开始与引导镜像工作,只需将文件复制到Kitchen文件夹中,打开菜单,并从菜单中选择您想要的选项:

  • 修改 ROM 名称可以是完美的第一步

  • 添加 root 权限

  • 对 APK 文件进行zipalign以加快读取和加载速度

  • 使用deodexk对 APK 文件进行解密,以便于文件操作,但代价是加载速度较慢

一旦您对修改满意,使用kitchen生成更新文件。这是一个.zip文件,可以通过自定义恢复刷写到设备上,代表您的第一个自定义 ROM——恭喜!

刷写我们的自定义 ROM

您已经有了.zip文件和您定制的系统分区,并且您非常兴奋地将它们刷写到您的设备上。

要刷写系统分区,我们可以使用fastboot。首先,您必须使用以下命令卸载分区本身:

~$ umount system_mount_point

在我们开始对系统分区进行实验之前,总是明智的做法是进行系统备份:

“做好准备。你永远不知道。”

现在,您可以将设备置于 Fastboot 模式,根据您设备的特定序列。以我们的参考设备 Google Nexus 6 为例,序列如下:

  1. 关机

  2. 同时按下音量增加音量减少电源按钮

  3. 当出现Fastboot菜单时释放

设备现在已准备好接收新的系统分区。使用以下命令进行刷写:

~ $ fastboot flash system system.img

您全新的系统分区已就位!如果您的修改非常极端且具有冒险性,您可能会遇到引导循环——系统持续重启且永远不会结束引导序列。制造商分发的库存系统镜像或您自己的备份在这种情况下非常有用。

注意

如果您正在使用三星设备并且您拥有 Windows 系统,您可以查看Samsung Odin,这是一个用于刷写 ROM 和获取设备 root 权限的图形界面工具。

最后一步是刷写您使用kitchen生成的.zip文件。该文件根据特定的文件结构生成,并准备好传递到您的自定义recoveryrecovery将其视为“系统更新”,即使它是一个全新的、定制的系统。

首先,以恢复模式重启您的系统。您可以通过按键序列或使用 ADB,使用以下命令来完成:

$: adb reboot recovery

一旦设备进入恢复模式,使用音量按钮进行导航,并选择从 ADB 应用更新。这将使设备进入等待模式。返回您的终端,并导航到使用kitchen生成的.zip文件。最后,将文件加载到设备上:

$: adb sideload filename.zip

恭喜!您的第一个定制 ROM 已经在您的设备上激活了。现在,返回去进一步定制它吧!

摘要

本章向我们介绍了什么是定制 ROM。我们从对目前存在的、最相关的项目的描述开始,并深入探讨了细节。我们还审视了一些非常重要的组件,例如Android Recovery,包括原生的和经过修改的。最后,就像我们在前面的章节中所做的那样,我们采取了一种实用方法,学习如何为 Android 定制准备合适的环境。我们还研究了通常用于执行此任务的不同工具,最后,我们通过创建一个定制 ROM 的简单示例来应用我们刚刚学到的概念。在下一章中,我们将更深入地探讨 ROM 的每一个方面,使用实际示例来展示如何定制和提升 ROM 的性能。

第七章. 定制你的个人安卓系统

在上一章中,你学习了最流行的自定义安卓系统。我们开始深入分析系统在修改过程中的各个部分,以有效地了解如何进行定制以及如何操作,掌握安卓修改工具集。

在本章中,我们将更进一步,深入到 ROM 的每一个细节,通过实际案例展示如何定制和提升 ROM 的性能。

本章的主要内容包括:

  • 黑客安卓框架

  • 将新的安卓应用程序添加到构建系统中

  • 使用安卓源代码添加新的 Linux 原生应用程序,或编辑现有的二进制 ROM 镜像

  • 优化系统以更好地支持自定义硬件,重点关注应用层和内核层。

接收空中更新 – OTA

每个安卓设备按设计都能在一段时间内接收更新。这些可以是系统更新——当新的安卓版本发布时,或者安全更新——当某些关键漏洞被修复且谷歌正在分发补丁时。一旦收到更新,每个设备都能按照所需程序解压缩并应用此更新。

这类更新被称为 OTA,或空中更新,因为它们可以通过安卓设备本身下载和应用,无需主机 PC 的支持。这些更新通常用于修复操作系统功能,在所谓的只读部分工作。没有任何用户应用会受到这些更新的影响——通过谷歌应用商店安装的应用完全安全。

当新的 OTA 可用时,安卓会异步通知你。大多数情况下,如果你连接到 Wi-Fi 网络且电池电量高于 50%,你将收到通知,以确保可能的快速下载和安全的更新过程。当有更新可用时,状态栏通知区域将出现新的系统通知。一旦点击通知,安卓将显示有关更新的详细信息,如下面的图片所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_106.jpeg

OTA 更新可以分为以下三类:

  • 完整系统更新

  • 增量系统更新

  • 单个更新包

更新整个系统

如你所猜,这一系列的更新将整个系统提升到新版本。它们包含整个系统镜像,包括systembootrecovery分区。

要安装这些更新,系统需要能够正确引导恢复系统,并简单地读取和应用更新文件。

即使是完整的系统更新,用户分区也不会受到影响,且不会删除任何应用或用户数据。

增量更新系统

这些更新比完整的系统更新要小一些,它们的目的是应用特定系统组件的补丁。由于针对特定版本的操作系统和特定版本的文件进行定制,因此这些更新不能随机应用于可用的设备。

为了强制执行此约束,在安装此类更新文件之前,系统会检查正确的文件版本以及更新所需的任何其他可能的要求。如果某些要求未得到满足,Android 会通过错误图标通知用户,并中止更新过程。

应用单个组件更新

OTA 更新包是一个标准的.zip文件,包含一个META-INF/com/Google/Android/update-binary文件。一旦 Android 验证了 ZIP 文件的签名,它就会在/tmp中解压缩文件并执行它。一些参数传递给命令行。这些是:

  • 更新二进制 API 版本号

  • 命令行文件描述符,用于与命令行通信,向 UI 发送进度更新

  • 文件名

update-binary文件所在的同一文件夹中,还有一个有趣的文件——updater-binary。此文件包含执行更新的操作序列。所有这些操作都使用 Google 为这项任务创建的定制领域特定语言DSLEdify来表示。正如开源世界中的惯例,Google 记录了有关此语言的所有信息,您可以在/bootable/recovery/edify中找到文档。

事实是,Recovery 可以执行所有名为update-library的静态链接二进制文件。利用这个机会,许多开发者更愿意使用他们更熟悉的语言来执行所有必要的操作以应用更新。

在接下来的几页中,我们将看到使用 Google 的 Edify 或自定义解决方案的两种可能场景的示例。

创建空中更新

Google 提供了大量的开发者工具来生成不同类型的 OTA。如果您想生成一个完整更新 OTA,需要以下两个步骤:

  1. 生成包含完整更新文件的 ZIP 文件

  2. 生成包含更新所需所有工具集的 OTA 包

要生成包含所选目标文件的zip文件,请导航到 AOSP 源代码的root文件夹并运行以下命令:

. build/envsetup.sh && lunch aosp-shamu
mkdir dist_output
make dist DIST_DIR=dist_output

如果过程成功,我们应该在dist_output目录中有一个包含目标文件的zip文件。例如,让我们尝试使用以下命令列出文件夹内容:

ls -l dist_output/*target_files*

现在我们应该看到一个.zip文件,其名称也将包含我们正在编译的目标名称。

到目前为止,您只需要生成包含更新所需所有文件的 OTA 包。在可用的工具中,有一个实用程序可以帮助我们通过以下命令完成此操作:

./build/tools/releasetools/ota_from_target_files \
 dist_output/aosp_shamu-target_files-eng.esteban.zip ota_update.zip

如此所示,您将找到包含生成的 OTA 包和命令输出的屏幕:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_107.jpeg

现在我们已经有了准备安装到 开发设备 上的 OTA 包,因为默认的 OTA 是用 测试密钥 签名的。如果你想为用户提供可安装的 OTA 包,你需要使用 OTA 生成工具提供的特定选项,用你自己的 私有密钥 签名 OTA。

为了生成 增量 OTA,过程几乎相同,只是你还需要指出包含上一个 OTA 版本的 ZIP 文件。命令可能如下所示:

./build/tools/releasetools/ota_from_target_files \
 -i PREVIOUS-aosp-shamu-target_files.zip \ 
 dist_output/aosp-shamu-target_files.zip incremental_ota_update.zip

就像我们之前的例子一样,你会得到一个包含增量备份的 ZIP 文件。

最后,没有预定义的工具用于组成 Update OTA 包,因为这取决于我们决定通过更新脚本安装/更新什么,我们将在稍后详细研究。

OTA 内部结构

如前节所预期,OTA 包在其文件夹树中包含一个二进制文件:

META-INF/com/google/android/update-binary

这个二进制文件是由 Android 的构建系统生成的,位于 bootable/recovery/updater 文件夹中,并用于正确执行更新。

二进制文件包含内部例程和 Edify 脚本语言的解释器。这种语言支持一组特定命令,以便在不影响系统本身完整性的情况下正确执行系统更新。你可以在你刚刚生成的 OTA ZIP 文件中的一个找到 Edify 脚本的示例,在以下位置:

META-INF/com/google/android/updater-script

这里展示了一个 Edify 脚本的示例截图:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_108.jpeg

通常,我们不需要手动编写任何 Edify 代码,因为在标准场景中,有自动化工具可以生成包含所有必要文件的正确 OTA 包,但在调试时手动修改它们可能很有用,或者在我们从二进制文件构建自定义 ROM 并需要定制闪存上的安装时。

让我们看看下一节中的 Edify 语法。

Edify 语法

首先要知道的是,Edify 将每个表达式评估为所有字符串类型值。在布尔上下文中,空字符串被视为 false,而任何其他值都视为 true。为了总结,Edify 支持以下所有表达式类型:

(expr )
 expr + expr  # string concatenation, not integer addition
 expr == expr
 expr != expr
 expr && expr
 expr || expr
 ! expr
 if expr then expr endif
 if expr then expr else expr endif
 function_name(expr, expr,...)
 expr; expr

包含以下类型字符的每个字符串,当然这些不是保留字,都被视为字符串字面量:

a-z, A-Z, 0-9, _, :, /, .

对于保留字,我们指的是像 if elseendif 这样的词。

可以使用 双引号 来编写常量字符串,以便创建包含空格或其他字符的字符串,这些字符在之前的示例中没有列出,例如以下内容:

\n, \t,

对于新行和制表符,也可以分别写成以下内容:

\", \\ 

作为转义字符,我们在用 双引号 编写的字符串中使用 "\

运算符是简单的短路运算,也就是说,如果逻辑结果由表达式的左侧确定,则甚至不会考虑右侧。语法可以非常简洁,如下面的代码片段所示;两行是等价的:

a1 && a2
if a1 then a2 endif

; 字符是一个序列点,意味着其左侧的内容先被考虑,而右侧的内容后被考虑。

让我们看看一个更丰富的例子:

show_progress(0.750000, 0);
ui_print("Android Shamu");
mount("ext4", "EMMC", "/dev/block/…/system", "system");
unmount("/system");

解释器包含完成正确更新所需的所有函数。除非另有说明,否则函数在成功时通常返回 true,在出错时返回 false

语言提供了控制流程和管理边缘情况的有用方法。例如,如果我们想触发错误以阻止安装,我们可以使用以下函数:

abort();
assert();

如你所料,如果你想添加新功能,你可以通过修改源代码来实现,但在那之前,让我们看看一些已经可用且非常有用的函数:

  • abort([msg]): 此方法允许你中断当前运行的脚本。它还接受一个字符串参数 msg,可以显示给用户作为中断的进一步信息。

  • assert(expr[, expr, ...]): 此方法接受一个表达式列表作为参数,并逐个评估它们。如果其中任何表达式失败或返回 false,则整个脚本执行停止。系统还会显示一个“断言失败”消息和刚刚失败的断言文本。

  • apply_patch(src_file, tgt_file, tgt_sha1, tgt_size, patch1_sha1, patch1_blob, [...]): 此方法接受一个 patch1_blob 文件,并将其作为二进制补丁应用到源文件 src_file 上,以生成目标 tgt_file

  • delete_recursive([dirname, ...]): 此函数接受一个文件夹名称列表作为参数,并删除它们,同时删除它们包含的每个单个文件。

  • file_getprop(filename, key): 此方法可以被视为属性文件检查器。它接受一些参数,包括一个文件名和一个键,并像属性文件一样扫描文件,寻找提供的键。如果找到键,则返回其值。

  • format(fs_type, partition_type, location, fs_size, mount_point): 此方法提供了一种强大的方式来格式化分区。

  • ifelse(cond, e1[, e2]): 此方法表示常见的 if-then-else 计算机科学语句。

  • is_mounted(mount_point): 此方法有助于检测挂载的分区。

  • mount(fs_type, partition_type, name, mount_point): 此方法在 mount_point 处挂载 fs_type 文件系统。

  • rename(src_filename, tgt_filename): 此方法接受两个参数,用于将 src_filename 重命名为 tgt_filename

  • run_program(path[, arg, ...]): 此方法执行路径处的二进制文件,传递 args,并返回程序的退出状态。

  • sleep(secs): 此方法接受一个整数 secs 作为参数,并暂停执行 secs 秒。

  • symlink(target[, source, ...]): 此方法接受一个target文件和一个sources列表,并将所有来源创建为指向目标的符号链接

  • unmount(mount_point): 这是mount的对应方法。此方法用于卸载在mount_point上挂载的文件系统。

  • 这只是所有可用命令的一个子集。如果你对整个列表感兴趣,可以查看官方 Google 文档source.android.com/devices/tech/ota/inside_packages.html

我们现在能够修改——或者从头开始创建——用于更新安装的Edify脚本。这种知识将证明在自定义 ROM 中非常有用,尤其是在源代码不可用的情况下,如果你想通过自定义恢复修改系统,在只读系统分区中安装特定文件。

自定义 ROM 的 OTA

如预期的那样,从 OTA 概念中,我们得到了一个方便的系统用于自定义 ROM 的安装。原因是大多数自定义 ROM 都是以更新 ZIP 包的形式分发,用于提供给自定义恢复,然后由恢复程序负责在系统中安装这些包。通过分析 OTA 结构——正如我们在上一节所做的那样——我们可以直观地理解如何组织一个特定的包来安装修改版的 Android。实际上,通过一个临时的 Edify 脚本,可以格式化和重新安装任何系统分区的所有文件,以便分发你自己的修改版 Android。

这个任务留给读者作为练习,因为它可以用到目前为止获得的知识来完成。

高级 ROM 定制

在前面的章节中,你在自定义 ROM 的世界中迈出了第一步;我们已经发现了在线上已有的内容,并详细分析了最典型的方面。在本章中,我们将深入探讨,学习如何修改 Android 框架的最内部部分。

自定义 ROM 通常与那些添加最意想不到功能并在线分享一切的"黑客"联系在一起,但并不总是如此。

如前几章所述,许多设备制造商提出了他们自己的修改版 Android,这实际上就是 Android 自定义 ROM。

这是一个非常重要的方面,因为这本书既面向前面提到的"黑客",也面向那些在日常工作中使用这些知识的人——一个"黑客"通常会处理二进制 ROM,而很少处理源代码,而专业人士肯定会有源代码,以及所有相关的工具,以便实现额外功能的发展。

在下一节中,我们将尝试以简单的方式解释两种不同的定制方法。这些是:从源代码和从二进制。

二进制 ROM 定制

从二进制文件开始修改 ROM,我们遗憾地只有很少的选择。由于我们没有源代码来生成不同的镜像,我们只能修改文件系统,添加实用工具和新应用程序,或者从框架二进制文件开始对颜色和图标进行美学上的更改。

我们可以使用上一章中看到的工具并应用所有必要的更改,然后,当我们完成时,我们可以使用正确的 Edify 脚本生成一个 update.zip 包,该包允许安装新功能。

此外,我们还可以添加新的应用程序,无论是 Java 还是 C,或者通过添加 BASH 环境来增强系统镜像,或者复制更新后的应用程序到 /system 分区,如 GmailMaps,这些可能会占用 /data 分区的空间。

即使在这种场景下可能性有限,从二进制镜像开始,我们也可以尝试一些优化和调整,正如我们将在接下来的章节中看到的。

从源代码定制 ROM

如果您有源代码,您几乎可以做什么,但如您所知,

“能力越大,责任越大”—本叔叔,蜘蛛侠。

第一步是确定我们想要修改的部分及其仓库。让我们以 Android 的 Settings 菜单为例,我们将将其作为一个主示例来修改我们的 ROM。Settings.apk 的源代码在以下路径:

packages/apps/Settings

一旦确定了源代码路径,也就是仓库,开始您定制的最佳方式是将仓库镜像到您的服务器上,然后您将对代码进行更改。

为了确保您的仓库是 Android 系统的一部分,您需要更新 manifest.xml,这样当您再次与 “repo” 同步时,您将克隆自己的 Settings 版本,而不是 Android 的。

之后,您需要创建另一个个人仓库,在那里您将保存您的清单,并修改以下行:

<project path="packages/apps/Settings" name="platform/packages/apps/Settings" groups="pdk-fs" />

这里您可以看到代码将本地下载的位置:

project path="packages/apps/Settings"

而在这里,它的远程位置:

name="platform/packages/apps/Settings"

您会注意到远程位置没有链接,因为我们将使用默认的,如下所示定义在顶部:

<remote name="aosp" fetch=".." />
<default revision="refs/tags/android-6.0.0_r6" remote="aosp" sync-j="4" />

如您所见,fetch 指的是父文件夹 “..” 而不是绝对路径。为了简化我们的工作,最好的做法是添加一个远程仓库,如下所示:

<remote  name="my_repo-github" fetch="git://github.com/my_personal_repo/" />

以这种方式,我们已经定义了我们的远程仓库,我们只需要修复 Settings 行,如下所示:

<project path="packages/apps/Settings" name="my_repo_Settings" remote="my_repo-github" />

现在我们已经准备好所有必要的配置来继续开发:我们有一个单独的仓库,我们可以在其中开发代码,但最重要的是,由于清单中的修改,我们不必触摸由 Google 管理的剩余系统部分,这样其他 Google 组件的更新就变得简单且顺畅。

将新包添加到 Android 的构建系统中

第一步是将包添加到 Android 的构建系统中,这样,当我们执行构建时,它将自动编译并添加到 ROM 中,就像其他应用程序发生的那样。我们可以在两个层面上工作:将系统应用程序作为编译的二进制应用程序添加,用C编写,或将系统应用程序作为应用层添加,该层在 Android Dalvik 虚拟机上运行,并以 APK 的形式分发。

为了创建 Android 应用程序,首先要做的是为编写代码准备环境并生成将被 Android 内部虚拟机执行的 APK 文件。我们将使用 Java、Android Studio 和 Android SDK 开发一个标准的 Android 应用程序。

通过二进制添加包

在开发自定义 ROM 时,您可能需要添加二进制可执行文件或应用程序,您没有源代码。例如,您可能希望将特定应用程序作为特定任务的默认应用程序添加,这样当用户启动 ROM 时,该应用程序就已经安装到系统中。我们可以将Facebook应用程序作为此类示例。

要成功将新应用程序添加到您的系统镜像中,您只需获取APK文件并将其复制到正确的 ROM 目录中。您可以使用update.zip文件,添加正确的 Edify 脚本,这将安装新的 APK——我们将在稍后更详细地看到——或者,如前几章中已预料的,您可以通过 Android 的构建系统执行整个操作。

第一步是编写正确的Android.mk;让我们假设我们的 APK 文件位于以下路径:

<aosp-root>/package/app/myapkfolder/

一旦您的 APK 已经就位,您需要创建一个Android.mk文件并添加以下片段:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := < your app folder name >
LOCAL_SRC_FILES := < app apk filename >
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)

分析代码片段,您将注意到几个占位符,您需要用实际值替换它们。之后,您需要在commons.mk文件中创建一个新的条目,该文件位于:

build/target/product

添加新的 APK 安装相关行,如下所示:

PRODUCT_PACKAGES += < what you have defined in LOCAL_MODULE >

到目前为止,您只需重新编译 AOSP,以在系统中找到新的 APK,它将与其他系统应用程序一起预装。

另一种非常常见且方便的方法是将预编译的应用程序添加到我们的 ROM 中,即通过 Android 更新系统的帮助。假设您已经安装了自定义恢复镜像——这将使所有操作都更容易——要向 Android 的/system/xbin目录添加新的二进制文件,您只需创建一个包含 Edify 脚本的update.zip文件以执行正确的操作。

在这里,您将看到一个 Edify 脚本,它在目标文件夹/system/xbin中执行预编译的应用程序安装。脚本包含在:

META-INF/com/google/android/

脚本包含以下代码:

ui_print("Edify Script for binary installation");
ui_print("Flashing a binary");
show_progress(0.700000, 0);
ui_print("mounting /system");
mount("ext4", "EMMC", "/dev/block/system", "/system");
ui_print("");
ui_print("Installing binary");
package_extract_dir("system", "/system");
ui_print("unmounting system");
unmount("/system");
ui_print("unmounted system");
ui_print("Operations completed!");

update.zip文件的内部结构将如下所示:

update.zip
---> META-INF/com/google/android/update-script
---> META-INF/com/google/android/updater-script
---> system/xbin/mybinary

一旦创建好更新包,您只需通过设备上安装的恢复自定义应用来应用它。如您所注意到的,相同的做法,“edify 脚本 + update.zip + 恢复”被反复使用,这显示了 Android 更新系统的稳固和灵活,并且对于大量任务和场景非常有用;但我们还可以更进一步。

还有另一种程序,我们可以将其定义为“”,它允许更复杂的安装。您仍然会使用更新包的程序,但不是使用 Edify 语法,这在许多情况下可能不方便,并且对于高级场景来说并不那么强大,您将重新定义 update-script 二进制文件的内容。

如您所知,这个二进制文件默认包含执行 Edify 脚本的解释器,由系统启动。这种“”技术包括用执行所需操作的 shell 脚本替换这个二进制文件。使用这种替代方法,您有了非常强大的 shell 脚本语言,并且假设某些恢复自定义包括 Bash——作为 shell——因此它将作为解释器工作。

以下是在 Android 系统中使用 ad hoc update.zip 和操作安装的 shell 脚本进行 busybox 安装的示例:

#!/sbin/sh

FD=$2

ui_print() {
  echo -n -e "ui_print $1\n" > /proc/self/fd/$FD
  echo -n -e "ui_print\n" > /proc/self/fd/$FD
}

set_perm() {
  chown $1:$2 $4
  chmod $3 $4
}

ui_print "- Mounting /system"
mount /system

ui_print "- Installing BusyBox"
unzip -o "$3" busybox -d /system/xbin

ui_print "- Setting right permissions -"
set_perm 0 2000 0755 /system/xbin/busybox

ui_print "- Symlinking BB applets"
for i in $(/system/xbin/busybox --list); do
  busybox ln -sf busybox "/system/xbin/$i"
done

ui_print "- Unmounting /system"
umount /system

ui_print "- BusyBox Installation complete -"

此脚本将替换我们的 update-script 并执行二进制安装。结果,更新包将具有以下结构:

update.zip
---> META-INF/com/google/android/update-script
---> busybox

因此,我们可以执行最复杂的安装,并且这实际上是 Android ROM “modders”最常用的方法之一。

通过源代码添加软件包

在系统源代码中。在本节中,我们将通过使用 Android Studio 创建一个Hello World应用程序来做一个真正的例子,我们将将其导入并与整个 Android 系统一起编译。

首先,我们需要使用 Android Studio 创建一个基础应用程序。

对于安装说明,请阅读以下链接:developer.android.com/sdk/index.html

当您的系统准备就绪时,启动 IDE 并创建一个新的项目:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_109.jpeg

上一张图片显示了如何指定应用程序名称、域名和 Android 项目的路径文件夹。一旦输入所有数据,您就可以点击下一步并转到 API 级别选择,如这里所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_110.jpeg

如前一张图片所示,默认情况下 Android Studio 将针对 API 16,以覆盖超过 95% 的市场。在这个场景中,这个值并不重要,因为此应用将仅安装在我们的定制 ROM 中,这可能是 Android 6。让我们转到下一个屏幕——活动选择器:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_111.jpeg

上一张图片展示了我们可以轻松添加到我们的应用中的众多可能的活动。在这个例子中,我们将只使用一个空活动,以保持事情简单:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_112.jpeg

上一张图片显示了如何重命名我们全新的活动——MainActivity将完美地完成这项工作。只需点击完成,Android Studio 就会带你进入编辑器屏幕,以便向你的Hello, World应用添加一些代码:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_113.jpeg

上一张图片显示了当我们的应用启动时如何显示Toast消息;没有太多花哨的东西,但足以让你了解如何通过适当的工具集和知识使事情变得简单。

当你的应用准备就绪时,只需点击运行按钮,开始构建你的 APK 文件。尽可能多地测试它,当你对结果满意时,将源代码复制到 AOSP 源代码文件夹:

<aosp>/package/apps/Myapp

根据你目前的知识,你可以更新清单文件,将此应用添加到安卓构建系统中。

最后一步是Android.mk文件。对于这个Hello, World示例,只需创建一个新文件,如下所示:

<aosp>/package/apps/Myapp/Android.mk

添加以下片段:

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)

  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)

  # Name of the APK to build
  LOCAL_PACKAGE_NAME := LocalPackage

  # Tell it to build an APK
  include $(BUILD_PACKAGE)

使用安卓的构建系统,你现在能够构建和打包你自己的安卓应用到你的定制 ROM 中。

破解安卓框架

这些定制与用户界面相关。由于涉及个人品味因素,UI 定制是一个棘手的话题:许多用户喜欢“纯安卓”界面,许多其他用户喜欢“不同安卓”界面的想法,远离主流的 UI 体验。

在本节中,我们给你自由选择,在纯安卓和定制安卓之间进行选择。你将学习如何进行小范围的定制,比如对状态栏或颜色进行定制,或者进行大范围的定制,比如向设置菜单添加新项目,以正确设置你定制 ROM 的定制功能。

定制引导序列 UI

¾¦+引导序列的图形外观无疑是您最想做的定制之一,通常是用户会要求并喜爱的。

在引导过程中,标准的安卓设备将显示:

  • 启动画面

  • 引导动画

启动画面是系统在开机后前几秒显示的静态图像。在谷歌 Nexus 设备上,启动画面看起来如下所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_114.jpeg

图片显示了谷歌品牌和一个锁。正如我们之前所学的,锁代表引导加载程序的状态——锁定或解锁。启动画面与引导的初始阶段相关联——通常,系统从开机到引导加载程序和 Linux 内核设置序列完成期间显示启动画面。

定制¾¦+启动画面并不容易,因为即使从理论上讲它只是一个图像,或者一系列图像,存储在 NAND 内存中,每个制造商都使用定制的方案来完成这个目标,并且他们非常不愿意记录我们如何恢复他们的工作。对他们来说非常容易的事情,拥有大量的工具和关于他们系统的知识,对我们来说却变成了几个小时的反向工程,结果和效果对整个系统的稳定性都是不可预测的。

将注意力转向启动动画,我们可以看到启动动画是一系列图像,大多数情况下是动画的,任何 Android 设备在启动序列中都会显示这些图像,紧随启动画面之后,直到 Android 系统完成启动。许多制造商定制这个动画来强化他们的品牌,你也会用你的自己的品牌来做同样的事情。从技术角度来看,当你看到启动动画时,内核已经加载,分区已经挂载,Android 开始启动。

与启动画面相比,这个图像序列更容易定制。这是因为即使大多数设备都有定制的启动动画,每一个都严格遵守非常严格的已知要求——这意味着我们为此有文档!

就像许多 Android 组件一样,启动动画以标准的.zip文件形式提供,并放置在/system/media/文件夹或/data/local/中。我们只需要抓取它,按照我们的喜好编辑它,然后放回——小菜一碟!

要检索文件,我们可以使用我们信任的adb。打开你的终端并运行以下命令:

adb pull /system/media/bootanimation.zip .

当然,如果文件不存在,就尝试我们之前提到的第二个可能的位置。一旦你在你的主机计算机上有了这个文件,你就可以解压缩它,你将看到以下图像中显示的相同的文件夹结构:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_115.jpeg

所有那些part*文件夹包含创建动画的图像,而desc.txt文件包含正确执行动画的说明。

使用你喜欢的文本编辑器打开desc.txt文件,你将看到以下图像类似的内容:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_116.jpeg

第一行指定了动画将显示的分辨率和帧率。行26指定了如何显示动画的不同部分。

第一个字母,“c”,代表“继续”,指示系统即使在启动序列完成后也要继续播放序列。

第一个数字指定了部分需要重复的次数。在示例中,只重复一次,或者无限时间(使用 0 作为值来表示无限循环)。第二个数字指定了在开始下一个部分之前将等待多少秒。行尾的最后标记指定了包含要显示的图像的文件夹。

现在你已经了解了bootanimation.zip文件的内部结构和如何设置序列,是时候发挥创意,替换所有那些无聊的图像,创建你自己的精彩动画了!

一旦你满意,就是时候创建一个新的bootanimation.zip文件了。打开你的终端并运行以下命令:

zip -r -0 bootanimation.zip part0 part1 partX desc.txt

仔细地将partx替换为你动画序列中正确的文件夹序列。要尝试你全新的启动动画,只需使用adbzip文件上传到/data/local/文件夹。你甚至可以创建一个自定义的update.zip并将其通过 Recovery 刷入你的设备。这取决于你。

注意

FFMPEG 是一个方便的工具,可以从视频中提取图像以创建你的动画序列。打开终端并运行以下命令:

ffmpeg -i "path_file" -r 1 -s 1024x768 -f image2 "path_images-=.jpg"

之前的命令指定了一些有趣的参数:-r 1用于每秒捕获一帧,-s用于指定最终图像的分辨率,-f image2用于实际捕获一帧并将其保存为图像。一如既往,你可以通过-h来获取更多文档信息。

定制安卓设置菜单

安卓的一个显著特点是模块化:大部分的系统功能实际上都是独立的安卓应用,分别开发和维护。例如,安卓的设置菜单本身就是一个安卓应用,称为Settings.apk,作为 AOSP 的一部分,可以根据我们的需求自由定制。在下一页,你将学习如何对Settings.apk进行操作以添加自定义菜单项。

打开你的终端模拟器,从包含安卓源代码的WORKING_DIRECTORY开始,导航到:

WORKING_DIRECTORY/packages/apps/Settings

这个文件夹包含了原始设置菜单的源代码;这是你定制的起点。

这是一个关键示例,因为当你在一个自定义 ROM 上工作时,你正在改进系统,添加新功能或增强现有功能。你的新功能可能需要一定程度的设置,并将所有可能的配置选项放置在用户期望的位置,即设置菜单,这是良好用户体验的基本点。

以下图像显示了原始安卓设置菜单,这是我们定制的对象:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_117.jpeg

一旦你进入了设置菜单应用文件夹,packages/apps/Settings,你就可以开始编辑文件以添加你的新菜单项。让我们从添加一些字符串开始。使用你喜欢的编辑器——Android Studio、Atom、SublimeText 等——编辑res/values/strings.xml并添加以下行:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_118.jpeg

strings.xml文件包含了设置应用中使用的所有文本字符串列表;它是你定制的完美起点,并为你提供了关于命名约定和结构的想法。

一旦你对字符串文件满意,创建一个名为 CustomSettings.java 的新 .java 文件,并将其放置在 src/com/android/settings 文件夹中。这将包含我们需要的所有逻辑。以下图片显示了你可以创建的自定义 PreferenceFragment 的一个片段:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_119.jpeg

这个 Fragment 将加载你需要创建的特定布局文件。让我们称它为 custom_settings.xml,并按照下一张图片所示进行填充:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_120.jpeg

现在,你需要向 AndroidManifest.xml 文件中添加几行。导航到 root 文件夹并按照以下方式编辑 AndroidManifest.xml 文件:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_121.jpeg

导航到主 src/ 文件夹并打开 Settings.java 文件。这个文件包含了 Settings 菜单中可用的所有 Activity。在这里,你可以添加自己的 Activity,如下一张图片所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_122.jpeg

src/ 目录下包含一个 SettingsActivity.java 文件。在这文件的开始部分,你会找到一个名为 ENTRY_FRAGMENTSString 数组。这些都是可以被 Settings 菜单中的 Activity 文件加载的 Fragment。这个列表相当令人印象深刻,在 Android Marshmallow 系统中,它包含大约 70 个 Fragment;在你的 Android 版本中,它将包含一个额外的条目:你的。按照以下截图所示,将你的 CustomSettings 类添加到数组中:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_123.jpeg

我们几乎完成了。接下来我们需要做的是使用以下命令编译新的包:

:$ mm

一旦我们创建了新的包,我们就可以创建一个新的更新文件,并使用 Recovery 来刷写它。在下次启动时,我们将在 设置 屏幕中看到我们全新的菜单项,如下一张截图所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_124.jpeg

提升系统性能

你可以在网上找到的许多自定义 ROM 都带来了性能提升、电池寿命延长以及许多小调整。这些增强中的大多数都可以通过 build.prop 文件的手术式调整来实现。

自定义系统属性文件

Android 的 build.prop 文件包含在启动序列中应用于系统的各种系统设置的详细信息。在深入其定制之前,我们需要对其内部结构有一个概述。

打开终端并使用以下命令连接到你的设备:

:$ adb shell

导航到 /system 文件夹并打开 build.prop 文件。内容将类似于以下片段:

ro.product.model=Nexus 6
ro.product.brand=google
ro.product.name=shamu
ro.product.device=shamu
ro.product.board=shamu
[]

如你所猜,这些说明中的部分是针对每个设备特定的,但其中一些相当常见。我们确实有设备型号名称、品牌、产品、设备和板子的代号等等。

这些常见值可以轻松编辑,以在我们的系统中获得有趣的行为变化。例如,你可能已经注意到了在接收到电话时,智能手机开始响铃前的那一小段,但可以感知到的延迟。通过仅编辑build.prop文件中的几行,就可以移除这个延迟。扫描文件并查找以下两行:

ro.telephony.call_ring.delay=0
ring.delay=0

简单地将分配给它们的任何值替换为漂亮的0(零),然后你可以告别延迟。

你是否曾经想过为什么当手机显示锁屏或应用启动器时,你无法旋转屏幕?不再需要疑惑。查找这两行,并将现有的属性替换为新的属性:

log.tag.launcher_force_rotate=VERBOSE
lockscreen.rot_override=true

你是否想要将设备旋转超过 180 度?通过以下行启用 270 度旋转:

windowsmgr.support_rotation_270=true

我们可以通过单行编辑实现另一个 UI 技巧,即更改 LCD 密度值。搜索以下行:

ro.sf.lcd_density=XXX

XXX替换为你想要尝试的值。更改此值将产生系统图标的大小调整和屏幕空间的增加:你设置的值越小,你得到的空闲空间就越多。不幸的是,这里没有精确的科学方法,一点点的试错是不可避免的,所以尝试用几个值进行实验,直到找到你喜欢的设置。

每天 Android 设备都在变得更强大,但,在那些日子里,可用的 CPU 功率非常有限。为了保证令人满意的性能和用户体验,Android 使用了智能调整,如下所示:

ro.media.enc.jpeg.quality=xxx

之前的值改变了 JPEG 文件的渲染质量。即使它在过去很有用,我们也可以认为在上一代智能手机上它是多余的,我们可以安全地将它设置为100并享受 100%原始质量的图片。

如果你的智能手机有物理导航按钮,你可以通过以下方式设置下一个属性来增加屏幕空间,移除屏幕底部的导航软键:

qemu.hw.mainkeys=1

如果你的设备没有物理按键,你仍然可以移除软键并使用手势导航;在 Google Play Store 中查找手势应用,如All in one Gestures。继续“屏幕空间”主题,你可以通过以下属性移除系统通知栏中的debug mode图标:

persist.adb.notify=0

这最后两个调整指的是网络设置。第一个如下:

wifi.supplicant_scan_interval=300

这行配置了每次自动 Wi-Fi 扫描之间的秒数。Android 默认执行自动 Wi-Fi 扫描,寻找可连接的开放网络或仅为了提高导航系统的精度。你可以增加或减少这些扫描的频率,试图在导航的更高精度和更长的电池寿命之间找到完美的平衡。第二个网络调整给你提供了设置默认 DNS 服务器的机会:

net.dns1=8.8.8.8
net.dns2=8.8.4.4

这在政府根据 IP 地址过滤互联网网站的国家中非常有用。使用前面片段中显示的 DNS IP,Google 的 DNS 服务器,你将能够绕过这种审查。

添加自定义初始化序列

Linux 的传统在 Android 架构的几个关键方面仍然很强。其中最有趣的一个是在初始化期间执行自定义脚本的可能性。如果你熟悉 Linux 系统,你了解 /etc/init.d 文件夹。这个系统文件夹包含了一组在系统启动时可以执行的脚本。为了在 Android 上实现相同的行为,我们可以使用 busybox 和它的 run-parts 工具。这个工具接受一个文件夹作为参数,并执行该文件夹中包含的每个脚本。例如,以下命令将执行 /system/etc/init.d 文件夹中包含的每个脚本:

run-parts /system/etc/init.d

为了正确复制 Linux init.d 的行为,我们希望能够以严格的顺序执行脚本。你可以通过巧妙的文件命名来实现这一点。只需重命名你的脚本,并在前面加上一个数字,如下例所示:

01settings
02optimizations

在前面的例子中,01settings 脚本将在 02optimizations 脚本之前执行,依此类推。现在你已经有了一组有序的脚本,并且知道如何逐个执行它们,你需要编辑我们在前几章中看到的 install-recovery.sh 文件,并添加以下行:

run-parts /system/etc/init.d

高级 Linux 内核修改

当你想到定制 Android 系统的核心时,你立刻会想到定制 Linux 内核。它管理 CPU、传感器、无线电和显示,并且是每个系统定制的起点。正如我们之前看到的,修改内核并不是一件容易的事情,但有了正确的思维方式、知识和工具集,它可以是一次令人满意的体验。

每个嵌入式系统都有其自定义的可能性,当涉及到 Android 时,大部分努力都集中在以下方面的定制:

  • 管理器

  • I/O 调度器

  • CPU 超频/降频

深入 CPU 频率管理

在工作中,以及如何根据不同的场景选择不同的管理器。在本节中,你将学习如何自定义现有的管理器以及如何将新的管理器添加到你的系统中。

注意

一个 管理器,或 CPU 频率管理器,描述了 CPU 根据特定的环境因素如何表现。

一个典型的通用管理器会在系统负载低时减少活跃核心的数量和它们的工作频率,当系统需要高性能时,将 CPU 推向全功率和全速。

标准的 Linux 内核提供了以下管理器:

  • 按需: 这是市场上大多数内核的默认调速器。它被认为是一个平衡的调速器,因为它可以保证系统具有反应性,在需要时快速增加 CPU 频率。事实是,由于如此渴望增加 CPU 频率,这个调速器并没有对实际所需的 CPU 功率进行任何实际评估。按需调速器不考虑实际系统负载;相反,当它被触发时,它会将 CPU 频率增加到最大值,如果不需要,它会缓慢降低。正如你所看到的,这不适合“省电”场景:每次系统认为需要更多电力时,它都会推到最高速度,而不进行更深入的分析。这种方法肯定会保证设备具有反应性,但肯定会迅速耗尽电池。

  • 省电: 这无疑是节省电池寿命最有效的方法之一。这个调速器将 CPU 的最高频率设置为最低可能值。电池的续航时间肯定会“无限”,但设备将无法使用:一个 2 GHz 四核 CPU 可以轻松降低到 200 MHz,如果它始终保持在那里,那就毫无意义。

  • 性能: 这个调速器与省电调速器的行为正好相反:它将 CPU 的最小频率设置为最大可能值以实现最佳性能。从电池的角度来看,这会迅速耗尽电池:一个始终以全功率运行的 2 GHz 四核 CPU 当然表现良好,但智能手机的续航时间不会很长。

  • 交互式: 这是按需调速器的更智能版本。其目标是提供一种反应性 CPU 缩放,而不会陷入按需陷阱。按需调速器根据预设值更改 CPU 频率,而不进行任何具体分析。相反,交互式调速器会持续评估系统负载,并根据需要调整 CPU 频率,具有更线性的 CPU 缩放曲线:这绝对是一个优点。整个 CPU 缩放分析不是基于原始工作量,而是根据请求的时间进行。这种方法保证了系统流畅性和在多媒体场景中的更好性能,因为 CPU 不会在频率上上下跳动,而是在整个必要的时间内保持稳定,在需要时提供恒定的帧率。

  • 保守: 这个调速器是按需调速器的平滑版本。与按需调速器不同,保守调速器不会每次都将 CPU 推到最高频率,而是会根据 CPU 负载逐步调整 CPU 频率。

  • 用户空间: 这是最可定制的也是最“非自动”的调速器。它为用户提供手动选择所需频率的可能性。

添加自定义 CPU 频率调速器

如果你需要特定的 CPU 行为,或者你只是想更深入地了解内核定制,你可以创建自己的 CPU 调速器。

对于这个任务,您需要内核源代码并导航到:

<root-source>/drivers/cpufreq

此文件夹包含我们在上一节中看到的每个调度器以及您将要添加或设备制造商已经添加的每个可能的自定义调度器。

让我们在该文件夹中创建一个新的调度器,例如创建一个.c文件:

<root-source>/drivers/cpufreq/cpufreq_mygovernor.c

将文件放置好之后,您需要将其添加到以下提到的文件中:

 <root-source>/drivers/cpufreq/Kconfig 

我们按照以下片段所示进行更改:

config CPU_FREQ_GOV_MYGOVERNOR
 tristate "'mygovernor' cpufreq governor"
 depends on CPU_FREQ
 help
 'mygovernor' - my optimized governor!

config CPU_FREQ_DEFAULT_GOV_ MYGOVERNOR
 bool "mygovernor"
 select CPU_FREQ_GOV_MYGOVERNOR
 help
 Use the CPUFreq governor 'mygovernor' as default.

完成对Kconfig的编辑后,编辑Makefile并添加以下行:

obj-$(CONFIG_CPU_FREQ_GOV_ MYGOVERNOR) += cpufreq_mygovernor.o

作为最后一步,编辑以下文件:

<root-source>/include/linux/cpufreq.h

大约在第 400 行,有一个当前可用的调度器列表,如下所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_125.jpeg

按照相同的模式,让我们添加您的新调度器引用,使用以下片段:

#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_MYGOVERNOR)
extern struct cpufreq_governor cpufreq_gov_mygovernor;
#define CPUFREQ_DEFAULT_GOVERNOR (&amp;cpufreq_gov_mygovernor)

任务完成:您的新调度器现在可用并准备好集成到您的下一个内核构建中。尝试运行menuconfig并导航到调度器屏幕;您将能够启用它并将其设置为默认调度器。

探索 I/O 调度器

I/O 调度器指定了 I/O 密集型操作必须如何执行以及在 CPU 核心之间如何平衡。Android 自带一组默认的 I/O 调度器:

  • Noop: 这几乎可以被认为是一个调度器。实际上,它对任务列表没有影响:它只是按顺序排队。

  • SIO: 这是第一个真正的调度器。即使它不进行任务重排序,它也能保证从任务入队到执行的最小延迟。

  • CFQ: 此调度器根据特定类别将任务排序到独立的队列中,并为每个队列分配一个执行时间窗口。窗口大小取决于分配给相关任务的优先级。

  • BFQ: 此调度器与 CFQ 调度器类似,但它使用磁盘带宽窗口而不是时间窗口来分组和调度任务。

  • Anticipatory: 此调度器使用预测技术来分组和调度任务,暂停执行一段时间并等待可能的新任务被添加到特定队列。

  • ROW: 此调度器基于“读优先于写”规则:每个读取任务都比写入任务具有更高的优先级。

  • Deadline: 此调度器保证入队任务的终止,试图避免“饥饿”场景。饥饿是计算机科学中的一个知名概念,适用于资源管理。想象一下,N个进程想要使用相同的共享资源。共享资源一次只能由一个进程使用,进程根据它们的优先级交替使用。如果一个低优先级进程请求资源,但由于其他高优先级进程正在使用它,资源永远不会变得可用,会发生什么?低优先级进程将永远等待资源,永远不会得到使用。在计算机科学的术语中,它将饿死

每个可用的调度器都存储在以下文件夹中:

<root-source>/block

创建 I/O 调度器可能具有挑战性,这超出了本书的目的。我们能做的是为你指明正确的方向,并激发你对这个主题的兴趣。

展望未来

在撰写这本书的过程中,我们很幸运地提前看到了即将到来的 Android N。新版本可能于 2016 年底作为稳定版发布,在几个月的公开开发者预览之后。

Android N 引入了一些有趣的功能,如下一张图片所示的多窗口模式:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_126.jpeg

在谷歌,他们非常注重用户反馈,并在三星测试了几个月后决定将此功能引入官方版本。我们大多数人都会从三星已经提供的 Android ROM 中认出多窗口模式。在 Android N 中,这项功能将面向所有人,适用于所有 Android 设备,并完全支持两种方向,即纵向和横向,甚至可以通过拖动“分隔线”来调整分割窗口的大小。

根据许多博客文章,Google Play Store 应用中最受欢迎的类别之一是来电显示过滤器。在 Android N 中,这个功能将作为系统功能提供,就像新的“移动数据节省”功能一样,旨在减少特定应用的背景数据消耗。

Android N 带来的新 UI 增强之一是可以在滚动下拉的快速设置菜单中添加和删除操作图标,如下一张图片所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_127.jpeg

此外,下拉的通知菜单还带来了新的通知设计,它允许更丰富的交互,更快地访问常用操作,如下一张图片所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_128.jpeg

设置部分也得到了一些关注,新增了就地通知,如下一张图片所示,这让你有机会在不导航到特定位置的情况下禁用或启用设置:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_129.jpeg

下一个图片还展示了新添加到设置部分的导航抽屉,以便更快地导航到更深的菜单层级:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_130.jpeg

新版本中将提供大量的小修复,许多改进旨在提高性能和电池寿命,如最受欢迎的移动中的省电模式,它承诺将为 Android 设备带来变革。

摘要

在本章中,你学习了如何通过实际案例在不同的级别上有效地自定义 Android,你现在知道如何从源代码中程序性地创建一个自定义 ROM,准备一个带有每个部件就绪的自定义文件夹结构,以便由 Android 的构建系统组装。你还知道如果你有一个已经组装的系统镜像,如何处理自定义任务,以及如何自定义和重新组装二进制镜像。

下一章将带你走出纯智能手机体验,并展示 Android 如何有效地成为我们生活中的无处不在:物联网、Android Auto 和 Android Wear、智能家居和娱乐只是我们目前能找到的绿色机器人的几个场景。

第八章. 超越智能手机

在第七章,定制您的个人 Android 系统中,你学习了如何给你的定制 Android 系统添加最后的个人风格。你定制了应用层和系统层:新的菜单、新的应用和新的守护进程。

在本章中,我们将更进一步:我们将超出智能手机,连接到外部微控制器、传感器和不同的设备。我们将看到整个世界如何与 Android 连接和互动。

你将了解 Android ADK 和 Arduino,以及谷歌如何用 Android 导向的设备丰富我们的生活:从 Chromecast 设备到 Android Auto,从智能手表到物联网。

认识 Arduino

十多年前,在一个小意大利酒吧里,一群学生和研究人员创造了一个低成本微控制器,它将彻底改变 DIY(Do It Yourself)的世界——Arduino,如下一张图片所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_131.jpeg

Arduino(或 Genuino,非美国市场)的最新版本被称为 Arduino UNOUno在意大利语中意味着一个,这个代号是为了庆祝板子自带的 IDE(集成开发环境)的第一个稳定版本。这个板子基于 Atmel 的 ATmega328P,提供了一套可控制的输入/输出引脚。一旦被正确编程,它可以作为一个独立的微控制器工作,并且可以通过其 USB 连接使用。

Arduino 最伟大的特点是它的开放性:从硬件原理图到开发 IDE,一切自第一天起就都是开源的。这种开放性和板子的可扩展设计允许制造商和高级用户创建无限数量的所谓扩展板

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_132.jpeg

Arduino 扩展板是一个可以附加到 Arduino 上以增强其功能和添加新功能的独立组件。上一张图片展示了如何堆叠 Arduino 扩展板以创建一个完全新定制的设备。Arduino 扩展板的常见例子包括:

  • Ethernet Shield,它使 Arduino 能够通过互联网连接与外界通信。

  • Proto Shield,它可以用来制作你用面包板创建的原型的永久版本。

  • Relay Shield,它使 Arduino 能够控制高压电路。这在家庭自动化中至关重要,当你需要开关灯或电器时。

  • Display Shield,它为 Arduino 提供了一种与外界进行视觉通信的方式。

自从 Arduino 问世以来,它获得了越来越多的粉丝和热情的开发者,这得益于其易于使用的界面和极低的学习曲线。今天,没有硬件或电子知识的软件开发者也可以创建超出他们电脑的项目,并能与外部世界互动。为了利用这些可能性,2012 年谷歌创建了 Android ADK。

Android ADK

Android Accessory Development Kit 是 Android Open Accessory 设备的参考实现。在 2012 年的 Google I/O 大会上,谷歌向开发者提供了 Android Accessory Development Kits,并为制造商提供了创建他们自己的套件、为 Android 创建外部配件设备的明确规范。这些认证设备之一就是 Arduino 本身,但多亏了整个项目的开放性,你自己也可以构建一个兼容设备。

不幸的是,Android ADK 在开发者中从未真正火爆。当然,你可以在网上找到许多关于将 Android 智能手机连接到 Arduino 的有趣项目,比如 TCRobotics 在blog.bricogeek.com/noticias/arduino/el-adk-de-google-en-un-arduino-uno上的项目。这肯定是我们最喜欢的之一;它展示了巨大的潜力,但也揭示了保持 Android 智能手机有线连接到整个过程的巨大牺牲:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_133.jpeg

幸运的是,对于 Android ADK 的使用,有更多酷炫的方式来与传感器和电子设备互动。

使用 UDOO 作为一体化的 ADK 设备

如你所知,UDOO 可以运行 Android。你可能不知道的是,它上面还内置了 Arduino。是的,Android 和 Arduino 都在同一块板上!当你想到你可以将触摸屏,甚至鼠标和键盘连接到 UDOO 时,你很快就会开始幻想你那些极客项目的实现。

准备工作

要开始使用 Arduino,你只需要设置 UDOO 并将 Android 部分连接到 SAM3X(Arduino 兼容)部分。以下图片展示了从上方看到的 UDOO,左侧高亮显示的是跳线 18。这个跳线必须拔掉才能启用 SAM3X。右侧也高亮显示了你要连接的 USB 端口:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_134.jpeg

一旦板子准备就绪,你就可以进入软件部分。

刷写 Arduino 板

在这个快速示例中,我们将控制连接到 UDOO 的 LED。LED 将连接到 UDOO 板的输入 13。每个 LED 都有两个引脚;较长的那个是阳极,必须连接到输入 13,较短的则是阴极,必须连接到地,即输入 13 左侧的无编号输入:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_135.jpeg

电子设备设置已经就绪。让我们从www.udoo.org/other-resources/下载 Arduino IDE。

第一次运行 Arduino IDE 时,你会看到一个空的项目文件:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_136.jpeg

这个空的 Arduino 草图为你提供了我们的 Arduino 程序的骨架结构:

  • 一个只运行一次并设置第二方法所需一切的setup方法

  • 一个不断重复运行的loop方法,直到板子被关闭

为了正确连接和编程我们的 Arduino,我们需要选择板型和端口。从 Arduino IDE 工具菜单中选择 | Arduino Due (编程端口)

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_137.jpeg

上一张图片显示了目前市场上可用的不同 Arduino 板数量。UDOO 与 Arduino Due 兼容,因此我们选择了该板型。一旦我们选定了合适的板型,我们需要选择用于连接到 UDOO 的端口

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_138.jpeg

如你在图中所见,端口名在不同的电脑上可能会有所不同。上一张图片显示了苹果 MacBook Pro 的常见配置。

一旦 IDE 配置正确,我们就可以开始编写源代码,如下所示:

#include "variant.h"
#include <stdio.h>
#include <adk.h>

#define  LED_PIN  13

// Accessory descriptor. It's how Arduino identifies itself to Android.
char descriptionName[] = "ArduinoADK"; 
char modelName[] = "UDOO_ADK";           // Arduino Accessory name (Need to be the same defined in the Android App)
char manufacturerName[] = "Packt";     // Manufacturer (Need to be the same defined in the Android App)

char versionNumber[] = "1.0";            // version (Need to be the same defined in the Android App)
char serialNumber[] = "1";
char url[] = "http://www.packtpub.com";      // If there isn't any compatible app installed, Android suggest to visit this url

USBHost Usb;
ADK adk(&Usb, manufacturerName, modelName, descriptionName, versionNumber, url, serialNumber);

#define RCVSIZE 128
uint8_t buf[RCVSIZE];
uint32_t bytesRead = 0;

void setup() {
    Serial.begin(115200);   
    pinMode(LED_PIN, OUTPUT);
    delay(500);
    Serial.println("Starting...");
}

void loop() {
    Usb.Task();

    if (adk.isReady()) {
      adk.read(&bytesRead, RCVSIZE, buf);// read data into buf variable
      if (bytesRead > 0) {
        if (parseCommand(buf[0]) == 1) {// compare received data
          // Received "1" - turn on LED
          digitalWrite(LED_PIN, HIGH);
        } else if (parseCommand(buf[0]) == 0) {
          // Received "0" - turn off LED
          digitalWrite(LED_PIN, LOW); 
        }  
      }
    } else {
      digitalWrite(LED_PIN , LOW); // turn off light
    }  

    delay(10);
}

// the characters sent to Arduino are interpreted as ASCII, we decrease 48 to return to ASCII range.
uint8_t parseCommand(uint8_t received) {
  return received - 48;
}

我们可以快速分析源代码,并找出:

  • 我们指定了引脚号 13

  • 我们指定了模型名称、制造商名称和版本号,这些信息将识别我们连接到 Android 的板型。

  • 我们正在配置串行连接

  • 我们正在监听串行连接上的传入数据,并相应地做出反应:

    • 如果我们收到 1,则打开 LED

    • 如果我们收到 0,则关闭 LED

一旦源代码就绪,你可以使用 IDE 的文件 | 上传菜单将其烧录到 Arduino 上。

创建 Android 应用

Android 应用将非常简单:一个切换按钮来打开和关闭 LED。你可以使用 Android Studio 向导创建初始应用,创建一个空的Activity以开始。一旦有了框架,你需要在build.gradle中添加一个新的依赖项:

dependencies {
    compile 'me.palazzetti:adktoolkit:0.3.0'
}

Emanuele Palazzetti,Packt Publishing 出版的《入门 UDOO》一书的作者,发布了一个实用的 Android 库,ADK Toolkit (github.com/palazzem/adk-toolkit),用于简化你的 Android 应用和 Android ADK 设备之间的通信,我们将充分利用这个库。

你需要在你的 AndroidManifest 中添加一些特定的配置。在你的<activity>标签中,添加以下行:

<intent-filter>
  <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
</intent-filter>
<meta-data
  android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
  android:resource="@xml/accessory_filter"/>

如你所注意到的,<meta-data>标签引用了一个名为accessory_filter的 XML 资源。目前它缺失。让我们在src/res/xml/文件夹中创建一个accessory_filter.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <usb-accessory
    manufacturer="Packt"
    model="UDOO_ADK"
    version="1.0"/>
</resources>

这是我们在 Arduino 草图中所添加的确切信息,将允许 Android 正确识别我们的板型。

设置完成。让我们继续到我们应用的 UI。遵循向导后,你现在有一个带有自己布局的单个Activity;很可能是它的名字是main.xml,位于src/res/layout。一旦你找到了布局,我们可以添加我们的按钮:

<ToggleButton 
 android:id="@+id/toggleButtonLED"
 android:textOn="Turn OFF" 
 android:textOff="Turn ON" 
 android:layout_width="500dp" 
 android:layout_height="200dp" 
 android:layout_centerVertical="true" 
 android:layout_centerHorizontal="true"
 android:textSize="50sp"
 android:onClick="blinkLED"/>

这非常直接:一个 ID,几个标签,以及一个当按钮被点击时触发的onClick方法。

onClick引用的方法将被放置在我们的Activity中:

public void blinkLED(View v) {
    if (buttonLED.isChecked()) {
        adkManager.write("1");
    } else {
        adkManager.write("0");
    }
}

当按钮被点击时,如果是开启状态,我们发送1;如果是关闭状态,我们发送0。这很公平,但我们把数据发送到哪里?那个adkManager是什么?

adkManager模块随 ADK Toolkit 提供。我们在Activity中创建它并设置它。这是最终的结果:

public class UDOOBlinkLEDActivity extends Activity {

    private ToggleButton buttonLED;

    private AdkManager adkManager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        buttonLED = (ToggleButton) findViewById(R.id.toggleButtonLED);

        adkManager = new AdkManager(this);
        registerReceiver(adkManager.getUsbReceiver(), adkManager.getDetachedFilter());
    }

    @Override
    public void onResume() {
        super.onResume();
        adkManager.open();
    }

    @Override
    public void onPause() {
        super.onPause();
        adkManager.close();
    }

    public void blinkLED(View v) {
        if (buttonLED.isChecked()) {
            adkManager.write("1");
        } else {
            adkManager.write("0");
        }
    }
}

最后,我们的应用完成了。只需将其上传到我们的 UDOO,您将有一个巨大的按钮来打开和关闭 LED:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_139.jpeg

探索物联网的可能性

知道您最喜欢的操作系统可以在数千种设备上运行,在数百种不同的定制中,并且可以与任何类型的设备通信,无论是有线还是无线,这开辟了令人难以置信的可能性。

Android Auto

2014 年,谷歌推出了 Android Auto,这是一个旨在使用我们汽车中已有的控制命令 Android 系统的创新项目:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_140.jpeg

2015 年,Android Auto 的第一个版本发布,开发者社区开始真正关注它。2016 年,数十家汽车制造商将推出支持集成 Android Auto 的车型。

Android Auto 背后的理念是支持驾驶安全,并在驾驶时为用户提供一种访问其设备的替代方式。为了实现这一目标,谷歌工程师与汽车制造商合作,在 Android 设备和汽车仪表盘之间建立了一座桥梁。

汽车仪表盘和控制代表了我们驾驶时可能拥有的用户体验和交互的顶端。一切都被放置得恰到好处,以便于访问,一切都被设计得易于使用,一切都被创造得既有效又强大,但又不分散注意力。

这些限制迫使谷歌重新思考他们为这个新挑战而流行的应用。当您将 Android 智能手机连接到 Android Auto 兼容的汽车时,您可以享受一个针对特定场景定制的不同操作系统用户界面。下一张图片显示了 Android Auto 的 Google Maps 界面:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_141.jpeg

下一张图片显示了连接我们的设备到 Android Auto 兼容汽车后,Google Play Music 的用户界面:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_142.jpeg

如 Google Maps 或 Google Play Music 等流行应用演变成更有效的设计,充分利用了仪表盘更大的屏幕和方向盘控制。

从开发者的角度来看,Android Auto 带来一个明显的问题:

我需要一辆车来开发和测试我的应用吗?

幸运的是,谷歌为那些想要接触 Android Auto 的人提供了测试工具:桌面车载单元DHU)。它随 Android SDK 一起提供,在您的计算机上运行,并允许您的计算机充当汽车仪表盘。以下图片显示了智能手机如何切换到 Android Auto 模式,以及用户界面如何切换到 DHU:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_143.jpeg

上一张图片展示了将智能手机连接到汽车后,智能手机显示屏的外观——它变黑并显示了 Android Auto 标志。下一张图片展示了将智能手机连接后,汽车仪表盘如何变得活跃。汽车仪表盘变成了 Android Auto 用户界面,在这个例子中,显示了几个 Google Now 卡片,包括交通和天气信息:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_144.jpeg

Android Wear

当我们等待 Android Auto 功能的车款进入我们的生活时,我们可以将注意力转向 Android Wear。

2014 年,谷歌宣布了一种特定的 Android 版本,专门为智能手表设计和开发。Android Wear 最初是基于 Android 5.0 Lollipop 的定制版本,目前基于 Android 6.0.1 Marshmallow。

Android Wear 旨在增强用户每天与世界互动的方式。一款 Android Wear 智能手表连接到 Android 智能手机,并提供更快地访问通知、消息以及所有可能的、在不与智能手机本身交互的情况下以更好的方式享受的内容:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_145.jpeg

像上一张图片中的那样的智能手表提供了与数十个服务的集成,例如 Google Fit、Endomondo 和 IFTTT。它们具有蓝牙和 Wi-Fi 连接性、GPS 和加速度计。这一巨大的可能性推动 Android 社区进行实验,并为数十种场景创造解决方案。

在 Android SDK 和 Android 社区的支持下,在过去两年里,我们看到为智能手表量身定制的应用程序数量不断增加——我们可以用我们的手表打开我们的飞利浦 Hue 灯,我们可以用我们的手表关闭我们的 Google Nest,我们可以通过 Parrot Flower Power 了解我们植物的状态。

沿着这条道路走下去,将直接带我们进入下一节。

智能家居

我们生活在一个许多设备、家电和“事物”原本是未连接的世界,现在却成为了不断增长的互联设备生态系统中的一部分。我们来自一个过去,计算只能在我们的电脑中发生——我们现在生活在这样一个当下,计算发生在我们的口袋里,通过我们的智能手机。我们正朝着这样一个未来迈进,计算将在任何地方发生:手表、汽车、无人机、房屋、花园等等。

我们曾经有需要手动控制的恒温器,现在我们有智能恒温器,例如 Google Nest,它们会从我们的习惯中学习并相应地做出反应,以创造更好的用户体验:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_146.jpeg

我们以前需要用墙上的开关来打开和关闭灯光,现在我们有智能灯泡,比如飞利浦 Hue,可以通过智能手机甚至智能手表来控制。这些灯泡在我们接近家的时候可以自动打开,利用地理围栏等概念。我们有可以与其他设备互联的灯光,比如智能门铃,可以为听力受损的用户创建视觉触发器:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_147.jpeg

我们有植物传感器,比如 Parrot Flower Power,可以在我们的智能手机上显示通知,告诉我们我们的植物需要浇水。知道了这一点,即使我们在数千公里外的某个偏远海滩上,享受着假期,我们也可以远程命令 Belkin WeMo 开关打开我们的灌溉系统并给植物浇水。

我们有智能冰箱,比如三星 Family Hub,它们连接到互联网,并允许你实际上看到冰箱内部,检查明天早餐的橙汁是否缺少。它们变得越来越互联,韩国版本将知道你最喜欢的产品的可能折扣,并建议你检查哪个特定的超市以节省一些钱。

我们有智能镜子,比如汉娜·米特尔施泰特(Hannah Mittelstaedt)制作的那个(github.com/HannahMitt/HomeMirror),可以用你的一部旧安卓设备轻松创建。给你的旧平板电脑赋予新的生命和新的用途。它可以提供天气预报、最新新闻、植物状态、交通信息,或者在你早上刷牙时你想拥有的任何有用信息。

我们有智能咖啡机,比如Nespresso Prodigio,它可以告诉我们水位、剩余咖啡胶囊和必要的维护情况。咖啡机可以远程控制,从沙发上操作,而且在我们历史上,这个经典的笑话不再是笑话了;我们的安卓手机实际上可以为我们煮咖啡!

一个绿色的机器人能让你娱乐吗?

一旦人类满足了所有基本需求,就开始与无聊作斗争了!

好吧,可能有点夸张了,但我们正在进入娱乐部分,所以让我们来谈谈一些有趣的事情吧!

多媒体

娱乐是一个巨大的市场,谷歌很快就跳了进来,推出了它的 Nexus Player 和 Chromecast 设备:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_148.jpeg

上一张图片展示了谷歌 Chromecast 的最新型号。当谷歌进入这个市场时,他们决定为用户提供一个尽可能容易设置的设备。谷歌 Chromecast 有一个 HDMI 接口和一个 USB 电源线;仅此而已。你将 HDMI 连接到电视上,连接电源,现在你的电视就可以连接到你的智能手机了。

你的智能手机变成了遥控器,只需几点击,你就可以开始流式传输你想要的任何多媒体内容,直接到你的电视:你偏好的 YouTube 频道,你从 Google Play Store 偏好的电影,你从 Google Play Music 的音乐,以及数百个第三方应用都可以成为内容来源。

如果你不喜欢看电视,而你是一个音乐迷,谷歌的 Chromecast Audio 可以满足你的需求:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_149.jpeg

至于 Chromecast,Chromecast Audio 连接到你的 Hi-Fi 系统非常简单,并且可以通过你可以在 Android 智能手机上安装的 Chromecast 应用轻松设置。

其中一个关键特性是它可以通过家庭 Wi-Fi 系统建立独立的 Wi-Fi 连接,因此它可以被指令播放你的音乐,而不是要求你的手机将音乐流式传输到 Chromecast Audio。你可以使用智能手机控制它,但不会耗电,因为你的手机和 Chromecast Audio 之间没有持久的 Wi-Fi 或蓝牙连接。

玩具

现在,当我们想到由 Android 供电的玩具时,我们只能想到无人机!

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_150.jpeg

最受欢迎的,如图中所示的 Parrot ARDrone,设定了标准并推动了市场的发展。随着时间的推移,出现了许多商业替代品,但就像智能镜子一样,遥控玩具社区也完全转向了 DIY。

在 2015 年都灵 Droidcon 黑客马拉松期间,我们展示了如何在 24 小时内使用 Android 设备通过 Wi-Fi 控制由 UDOO 供电的遥控车……www.hackster.io/team-dronix-alter-ego/dronixcar-37b81a?f=1#

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/lrn-emb-andr-n-prog/img/epub_36702041_151.jpeg

这款遥控车配备了视频摄像头,实时流媒体传输到智能手机。智能手机充当视频消费者和遥控器。

整个项目以开源的形式发布,遵循经典的 Android/Linux 传统。

摘要

我们的旅程结束了!这是一段相当刺激的过山车之旅,从操作系统的历史到它如何让设备准备好与外部世界通信。你学习了如何获取设备的源代码,如何导航源文件夹树,以及如何创建完美的设置来正确构建纯净的 Android 系统。

你开始升级系统,添加自定义功能,以丰富用户体验,提高性能,并添加对您自己的硬件的支持。你深入到引导序列的内部结构,以进一步自定义系统。然后你回到表面,自定义架构金字塔的最高部分,即用户界面,为用户提供终极定制的 Android 系统。

最后,你看到了如何轻松地摆脱 Android 设备本身,发现一个由 Android 平台驱动的设备世界,这些设备正等待通信和交互。

我们的旅程已经结束,但你的旅程才刚刚开始!掌握你所学的知识,进行实验,尝试你的想法,失败,学习更多,再次尝试,最终成功!

Android 是一个强大的工具;你可以用它将你疯狂的想法变成现实!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值