Chrome OS上的Android系统

Posted on Sep 20, 2019


Chrome OS是Google公司开发的基于Chrome浏览器的操作系统。本文会讲解Chrome OS对于Android系统支持方面的内容。

前言

Chrome OS与Android都是Google公司开发的操作系统。前者发布于2011年,后者发布于2008年。

当然,大家可能还知道Google的另外一个操作系统,叫做Fuchsia。在今后的文章中,我们也会谈到。

距离Android发布已经超过10年时间,它在移动互联网时代取得了巨大的成就。与此同时,Chrome浏览器也一样非常的成功。相较而言,Chrome OS却仍然不温不火。

目前,Chrome OS主要在以下几种设备上运行:

  • Chromebooks
  • Chromeboxes
  • Chromebits

本文不想对比Chrome OS或者Android系统那个更好,也不会深入讨论Chrome浏览器或者 Chrome OS的具体实现。只将重心发在Chrome OS对于Android App的支持上。因为对于很多人来说,要么是Android平台的开发者,要么是Android系统的用户。

阅读本文需要对Android系统实现有一定的了解,你可以访问Android官方网站获取相应的知识:

或者阅读我之前写过的一些文章:AndroidAnatomy

获取Chrome OS源码

通过下面的命令可以获取Chrome OS的源码:

mkdir chromeos
cd chromeos
repo init -u https://chromium.googlesource.com/chromiumos/manifest.git
repo sync

上面的命令会将Chrome OS的源码下载在chromeos目录中。完整的源码大小超过20个G,因此可能需要比较长的时间才能完成下载。

如果你想要编译Chrome OS或者对Chrome OS系统开发有兴趣,下面两个文档是一个比较好的开始。

Chrome上的Android应用

Chrome上对于Android应用的支持经历了三个项目和阶段。

项目名称 开发时间 状态 特点 其他说明
ARC 2014年 已废弃 支持各桌面平台的Chrome浏览器 需要Android开发者对接,并存在安全问题
ARC++ 2016年 正在使用 Chrome OS专用,通过Linux容器运行 安全性有所提升,但仍然不够
ARCVM 2019年 开发中 独立虚拟机,更加安全 不确定是否会与ARC++项目合并

ARC是 App/Android Runtime for Chrome 的缩写。

ARCVM 是 ARC Virtual Machine 的缩写。

由于ARC项目已经废弃,因此本文不再讨论。

下面仅对另外两个项目做一些介绍。

ARC++ 项目

前面已经看到,ARC项目是为Chrome浏览器设计的。由于浏览器的安全模型与原生系统有很大差异,因此支持Chrome浏览器的应用需要开发者进行特定的修改。这对应用开发者来说,无疑是增加了负担。

因此ARC++项目的设计目标包括:

  • 能够直接访问Google Play商店上的应用
  • 尽可能少的修改 Android Framework
  • 不破坏Chrome OS安全性
  • 维持Chrome的更新模型

在Chrome OS工程中,ARC++项目的源码并非集中在一起,它们分散在多处。

Android container

整体来说,ARC++项目的实现机理是:将Android系统运行在基于Linux namespace的容器中。以此达到与宿主系统(Chrome OS)相隔离的目的。

Linux namespace 是内核提供的一个特性。它通过命名空间的形式将资源隔离,使得不同集合的进程看到不同集合的资源。这项技术也是Linux上容器(例如:lxc 和 docker)实现的基础。

Linux 提供了 7 种类型的命名空间:

  • Cgroup
  • IPC
  • Network
  • Mount
  • PID
  • User
  • UTS

关于Linux namespace,读者可以通过下面两个链接了解更多信息:

run_oci 与 config.json

在 Chrome OS 中,有一个名称为 run_oci 的模块,它是一个小巧的容器运行时,它会读取名称为 config.json 的配置文件来完成Android容器的初始化工作。

config.json 中的描述了需要挂载的文件系统,命名空间,设备节点,cgroup配置以及继承的能力。你可以点击链接查看这个文件中的内容。

Android container的启动与退出

config.json 文件中包含了下面这段配置。

"process": {
	"terminal": true,
	"user": {
		"uid": 0,
		"gid": 0,
		"additionalGids": [5005]
	},
	"umask": 0,
	"args": [
		"/system/bin/init",
		"second_stage"
	],
	"env": [
		"INIT_SELINUX_TOOK=1500",
		"INIT_STARTED_AT=1000",
		"PATH=/sbin:/system/sbin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"
	],
	...

对Android有所了解的读者在看到/system/bin/init应该会觉得很熟悉。

在Android系统上,init进程负责了整个系统的启动逻辑。它会读取 /init.rc 文件完成系统的初始化工作,这其中包含启动 Android 系统中最重要的一些进程,例如:zygotesystem_serversurfaceflinger等。

如果你不理解这里的内容,请熟悉一下 Android Init Language,或者阅读我之前写过的文章:Android系统启动:init进程与init语言

Chrome OS 上 Android container 启动和退出的流程如下图所示:

借助于run_oci,可以很方便的控制Android容器的入口。这样无论是在开发阶段还是运行阶段,Android系统和Chrome OS都很好的解耦和分离开了,这也就非常的便于维护。这种做法是我们在做软件设计(尤其是大型软件系统)时非常值得借鉴学习的。

图形系统

在Linux容器的基础上,Android的相关进程都已经启动。接下来还需要对接用户界面的图形系统。

图形系统的内容包括:

  • 负责应用窗口的创建
  • 负责应用窗口的状态变化(例如:移动,改变尺寸)
  • 负责图形内存的分配
  • 负责图形的合成
  • 负责图形的渲染
  • 负责输入事件处理

对于 Android 图形系统不熟悉的读者,可以先看这里的文章:AOSP: Android Graphics Overview

Android的图形系统结构如下:

在Android系统上,图形系统的主要组件包括:

  • Gralloc:负责分配图形缓冲。
  • HWComposer:通过硬件确定缓冲合成的最有效方法。
  • SurfaceFlinger:接受来自多个源的数据缓冲,将它们合成并将它们发送到显示器。
  • WindowManager:负责创建和管理窗口。

从Android 4.0开始,Android 应用使用的Canvas API通常都会通过硬件加速。另外,还有一些应用会使用 OpenGL ES,甚至使用更新的 Vulkan 接口。

Android系统上的所有界面都通过 Surface 渲染出来。应用程序借助于 Surface 生成应用的界面,并将图形缓冲放入队列中,这些缓冲队列由SurfaceFlinger处理。在底层,Surface的图形缓冲通过gralloc分配。

对于窗口管理来说,Android N(7.0)已经支持多窗口。在《Android 7.0中的多窗口实现解析》一文中,我们已经了解到,Android 的多窗口支持下面三种模式:

  • 分屏模式
  • 画中画模式
  • Freeform 模式

有了这些背景知识之后便可以继续了解ChromeOS对于Android系统的对接逻辑了。

ARC++ 这篇文章中,Chromium 项目的开发者 David Revenman 详细介绍了 ARC++ 的项目实现。

这里是他的演讲资料:《Arc++ Graphics: Rendering, Compositing and Window Management》。

ARC++ 项目中图形系统的整体结构如下图所示:

在 Chrome OS 上,grallocGLES 驱动使用 DRM(Direct Rendering Manager)来渲染。对于合成,HWComposer先处理所有的Android Surface,然后再交由Chrome OS来与Chrome OS的其他界面一起合成。

对于窗口管理,位置变更和尺寸改变由Android完成,而对于最大化,最小化和全屏由 Chrome OS 完成。

Chrome OS中的输入事件由 Ozone 抽象层完成。它使用 GpuMemoryBuffer 对象来保存在Android端使用gralloc 或在Chrome OS端使用DRM分配的DRM缓冲区。

Exosphere是Chrome OS中的一个组件,它允许其他客户端连接到用户界面。它通过验证所请求的操作来保护Chrome OS免受潜在恶意客户端(如Android应用)的侵害。

Android和Chrome OS借助于Wayland协议来完成窗口管理和输入事件的通信。

Chrome OS 中的相关项目见下面这些链接:

ARCVM 项目

虚拟机和容器

在虚拟化技术中,虚拟机和容器是很常见的概念。它们非常相似,但不完全一样。

简单来说,虚拟机是对机器的抽象,而容器是对操作系统的抽象。在实际的运行过程中,每个虚拟机会有独立的操作系统内核。而容器互相之间共享同一个内核。

因此:

  • 虚拟机通常更”重“,因为它要占用更多的系统资源。不过好处是每个虚拟机之间隔离得更好,运行在同一个物理机的多个虚拟机,如果其中某一个出现了问题,不会影响其他虚拟机和宿主操作系统。
  • 容器通常更”轻“,因为它不需要独立的内核(大家所熟悉的Docker就是一个容器)。但容器的缺点也正是因为它是共享同一个内核:如果某个容器中的进程触及了内核的某个Bug,导致内核出现问题,这将影响整个系统,当然也包括所有运行在这个系统上的容器。

下图是虚拟机(左)和容器(右)的对比图:

Crostini项目

从2018年秋天发布的Chrome OS 69版本开始,用户可以在Chrome OS上面运行Debian Linux,这对于很多技术工作者或者软件开发者来说无疑是极大的方便,有了Linux环境,很多笔记上的工作都可以完成了。

支持这个功能的是Crostini项目。

这个项目的机理是:在Chrome OS的系统上,会运行一个称之为crosvm的虚拟机管理器(类似于QEMU),在这之上来运行Linux虚拟机。

其架构如下图所示:

这个项目包含了很多的术语,如果你浏览Chrome OS的文档你可能会经常看到这些术语:

  • Cicerone:Chrome OS中的守护进程,用来和容器通信。
  • Concierge:Chrome OS中的守护进程,管理虚拟机和容器的生命周期。
  • crosh:全称是Chrome OS shell,这是Chrome OS上的命令行工具。
  • Crostini:在Chrome OS上提供Linux环境的项目名称。
  • crosvm:Chrome OS上的虚拟机管理器。
  • KVM:全称是Kernel Virtual Machine,这是Linux提供的虚拟机机制。
  • Termina:自定义虚拟机的代码名称。
  • Terminal:获取完整的Linux命令行环境和运行Crostini的公开名称。
  • vsh:虚拟机中的shell。

关于这个项目的更多内容,读者可以阅读下面两个文档:

在这个项目的基础上,出于对系统稳定性和安全性的考虑,Chrome OS的维护者便倾向于将Android的运行环境从容器向虚拟机方向过渡。于是乎有ARCVM(Android Runtime for Chrome OS Virtual Machine)的想法就很自然了。

目前该项目还处于早期的开发阶段,你可以通过Gerrit上的patch了解相关内容:message:arcvm Chromium Gerrit

另外,ARC++和ARCVM项目今后会怎么样,会不会废除前者合并到后者,目前还不确定

获取 Chrome OS 模拟器

对于大部分人来说,可能并没有运行 Chrome OS 的物理设备。不过通过 Android Studio 可以获取 Chrome OS 的模拟器镜像。借此来熟悉 Chrome OS 的环境。

目前(2019年7月) Google 只提供了预览版的镜像。也许很快就会更新,想要获取最新信息,请访问: Android Developers: Apps for Chrome OS overview

在安装好 Android Studio 之后,通过下面的方法可以获取 Chrome OS 的模拟器。

  1. 打开 Android Studio,选择 Tools > SDK Manager
  2. 点击 SDK Update Sites tab。
  3. 点击 + 号,添加 URL 为: https://storage.googleapis.com/chrome_os_emulator/addon2-1.xml,名称随意也可以不填。
  4. 再次点击 + 号,添加 URL 为 https://storage.googleapis.com/chrome_os_emulator/sys-img2-1.xml,名称随意也可以不填。
  5. 点击 Apply
  6. 点击 SDK Tools tab然后在列表中选择 Chrome OS device
  7. 点击 OK 就会安装 Chrome OS 的虚拟设备了。
  8. 重启 Android Studio。
  9. 在Android Studio中,选择 Tools > AVD Manager
  10. 点击 Create Virtual Device 便可以在Tablet分类下面看到 Chrome OS 的虚拟机设备:Pixelbook。如下图所示:

当前的 Chrome OS 系统镜像以Android 7.1.1版本为基础,因此还需要下载相应版本的Android Image才能运行。

下载完成之后启动需要通过Google账号进行登录,如下图所示:

登录之后可以上拉任务栏可以看见完整的应用列表。从列表中可以看出,系统中内置了Google的GMS。

当然,这也包括了 Google Play 应用商店。

Chrome OS 中的进程

Chrome OS 的虚拟设备启动之后,我们可以用熟悉的 adb 命令来了解其环境。

首先通过adb shell 进入的系统中。

在登录Google账号之前,系统只启动了部分进程,如下所示:

novato_cheets:/ $ ps
USER      PID   PPID  VSIZE  RSS   WCHAN            PC  NAME
root      1     0     8176   2020           0 00000000 S /init
system    2     1     5116   1888           0 00000000 S /system/bin/boot_latch
logd      4     1     11864  3076           0 00000000 S /system/bin/logd
root      5     1     7120   2708           0 00000000 S /system/bin/debuggerd
root      6     1     20060  7964           0 00000000 S /system/bin/vold
root      12    5     6864   412            0 00000000 S debuggerd:signaller
root      19    1     3444   576            0 00000000 S /sbin/healthd
shell     20    1     5064   1872           0 00000000 S /system/bin/bugreportd
system    21    1     6460   2292           0 00000000 S /system/bin/servicemanager
system    22    1     80392  28368          0 00000000 S /system/bin/surfaceflinger
root      23    1     1604804 124292          0 00000000 S zygote
shell     24    1     30528  1260           0 00000000 R /sbin/adbd
shell     279   24    5020   2564  sigsuspend ef676a69 S /system/bin/sh
shell     284   279   6416   2408           0 edd48a69 R ps

一旦登录系统之后,再次通过ps命令便可以看到更多的Andorid进程被启动了。

novato_cheets:/ $ ps
USER      PID   PPID  VSIZE  RSS   WCHAN            PC  NAME
root      1     0     8184   2036           0 00000000 S /init
logd      4     1     12376  3056           0 00000000 S /system/bin/logd
root      5     1     7120   2680           0 00000000 S /system/bin/debuggerd
root      6     1     24416  8060           0 00000000 S /system/bin/vold
root      12    5     6864   536            0 00000000 S debuggerd:signaller
root      19    1     3444   572            0 00000000 S /sbin/healthd
shell     20    1     5064   1856           0 00000000 S /system/bin/bugreportd
system    21    1     6460   2400           0 00000000 S /system/bin/servicemanager
system    22    1     83508  28660          0 00000000 S /system/bin/surfaceflinger
root      23    1     1606816 121436          0 00000000 S zygote
arc-tracing 87    1     17120  6160           0 00000000 S /system/bin/arctraceservice
system    88    1     19212  6204           0 00000000 S /system/bin/arcbridgeservice
audioserver 89    1     33740  10176          0 00000000 S /system/bin/audioserver
cameraserver 90    1     29256  11952          0 00000000 S /system/bin/cameraserver
drm       91    1     18572  7932           0 00000000 S /system/bin/drmserver
root      92    1     7020   3640           0 00000000 S /system/bin/installd
keystore  93    1     15400  6600           0 00000000 S /system/bin/keystore
mediacodec 94    1     36464  13688          0 00000000 S media.codec
media     95    1     29224  11092          0 00000000 S /system/bin/mediadrmserver
mediaex   96    1     51584  13404          0 00000000 S media.extractor
media     97    1     55480  14684          0 00000000 S /system/bin/mediaserver
root      100   1     25992  5236           0 00000000 S /system/bin/netd
system    101   1     15084  6448           0 00000000 S /system/bin/gatekeeperd
root      104   1     5600   2176           0 00000000 S /system/xbin/perfprofd
system    107   23    1810244 189396          0 00000000 S system_server
root      112   100   6420   2900           0 00000000 S /system/bin/iptables-restore
root      123   100   6432   2916           0 00000000 S /system/bin/ip6tables-restore
shell     228   1     12096  1124           0 00000000 R /sbin/adbd
u0_a16    275   23    1047744 79796          0 00000000 S com.android.systemui
radio     335   23    1045676 94992          0 00000000 S com.android.phone
u0_a40    351   23    1083856 94600          0 00000000 S org.chromium.arc.home
u0_a28    447   23    1023400 65240          0 00000000 S android.ext.services
u0_a47    500   23    1028860 67876          0 00000000 S org.chromium.arc.backup_settings
u0_a26    511   23    1030168 70180          0 00000000 S org.chromium.arc.crash_collector
u0_a49    532   23    1028964 68004          0 00000000 S org.chromium.arc.cast_receiver
u0_a23    545   23    1033920 70136          0 00000000 S org.chromium.arc.gms
u0_a56    553   23    1026796 62364          0 00000000 S org.chromium.arc.ime
u0_a46    566   23    1028868 68484          0 00000000 S org.chromium.arc.tts
system    586   23    1069980 93040          0 00000000 S org.chromium.arc.applauncher
u0_a30    600   23    1030132 78984          0 00000000 S org.chromium.arc.intent_helper
u0_a66    634   23    1025280 68548          0 00000000 S com.android.printspooler
u0_a13    655   23    1035140 85384          0 00000000 S android.process.acore
...

当然了,启动了更多的进程需要消耗更多的内存。在 Chrome OS 完全启动之后,其消耗的内存还是比较大的,你可以通过 free -h 或者 dumpsys meminfo 命令来查看。

novato_cheets:/ $ free -h
		total        used        free      shared     buffers
Mem:             3.8G        2.3G        1.5G        3.1M        302M
-/+ buffers/cache:           2.0G        1.8G
Swap:            5.6G           0        5.6G

借助 Android Studio 和 Android 模拟器还有adb命令,我们几乎可以把 Chrome OS 当成与 Android 系统一样来开发应用。

为Chrome OS开发Android应用

虽说运行Chrome OS的设备可以直接访问Google Play商店下载Andorid应用。但是,毕竟运行Chrome OS的设备(更像笔记本电脑)和手机存在较大的差异,因此如果希望应用在Chrome OS上能获得更好的体验,还需要开发者进行一定的优化。

这些优化主要在下面四个方面:

  • 更大的屏幕:手机设备的屏幕尺寸通常是 4 ~ 7英寸。而运行Chrome OS的设备通常是10 ~ 15英寸。直接将手机的应用界面放大到大屏幕上使用通常体验都不太好,因为控件的留白和字体的大小通常是不合适的。
  • 横屏模式:除了游戏和视屏应用之外,手机上的应用大部分都是竖屏的,而运行Chrome OS的设备通常是类似笔记本电脑的横屏。只支持竖屏将浪费很大的屏幕空间。
  • 窗口控制:Android N开始支持多窗口。多窗口中提供了Freeform模式,这使得用户可以自由调整窗口的大小和位置。在桌面电脑上,这是几乎必备的功能。
  • 外设支持:对于Chrome OS设备来说,除了触摸屏之外,键盘,鼠标和手写笔是很常见的输入设备。对于一些应用来说(例如:绘图应用),这些硬件的支持将极大的提升用户的使用效率。

关于这部分内容不再继续展开,有兴趣的读者可以查看下面几个链接:

参考资料与推荐读物


原文地址:《Chrome OS上的Android系统》 by 保罗的酒吧
 Contents