[[PageOutline]] = Android OS Development = This page provides details about the Gateworks Android BSP which is used to produce the various components of the Android OS on a Gateworks Ventana board. * For those interested in Android Application development please see [wiki:Android/AppDevelopment] * For compiling the actual Android OS, please see here: [wiki:Android/Building] [=#hardware-integration] == Hardware Integration == [=#gpio] [=#led] === LEDs and GPIOs === If you wish to manipulate hardware resources such as LED's and GPIO's you can do so with the Linux sysfs APIs: * [wiki:gpio#gpiolib_sysfs GPIO class] * [wiki:gpio#led LED class] === System voltage and temperature sensors === The standard Linux hwmon sysfs APIs: * /sys/class/hwmon for hardware monitor devices such as the [wiki:gsc GSC temperature and voltage monitor] * /sys/class/thermal for thermal zones [=#button] === Physical Buttons (including Capacitive) === Physical buttons are handled by Linux device drivers that generate Linux input events and get mapped to Android key codes via [https://source.android.com/devices/input/key-layout-files.html Key Layout Files]. Therefore, you can map (or disable) these to Android Key codes in the various .kl files in use on your device. The Android key codes typically tied to buttons are: - POWER - MENU - BACK - HOME - APP_SWITCH - SEARCH For more information on Key Layout Files see: - [https://source.android.com/devices/input/key-layout-files.html Key Layout Files] [=#gsc-pushbutton] ==== GSC User Pushbutton ==== The [wiki:gsc Gateworks System Controller] functions as a pushbutton controller for the front-panel user pushbutton available on most Ventana boards. This particular button is available as gpio-240 on Ventana boards however there is a [wiki:gsc#inputdriver gsc-input driver] that converts GSC related interrupts, such as push-button press-and-release to Linux Input events. By default note that the GSC is configured such that the user pushbutton is a hard reset. To configure it instead to be software controlled you need to set GSC CTRL_0 accordingly depending on the actions you wish to catch. For example: * Configure the pushbutton controller to just catch quick press-and-release events: {{{#!bash i2cset -f -y 0 0x20 0 0x00 # disable hard reset function i2cset -f -y 0 0x20 11 0x01 # enable pushbutton interrupt }}} The [wiki:gsc#inputdriver gsc-input driver] will catch all enabled GSC interrupts and emit a Linux Input event with a key-code cooresponding to a button (defined in Linux input.h). For example the GSC_IRQ_PB (pushbutton press-and-release) interrupt is mapped to BTN_0 which is defined as 0x100 (decimal 256) in [https://lxr.missinglinkelectronics.com/linux/include/uapi/linux/input.h#L481 input.h]. There exists a key layout file that maps the Linux input event keycodes to Android key events in /system/usr/keylayout/gsc_input.kl: {{{#!bash 130|root@ventana:/ # cat /system/usr/keylayout/gsc_input.kl # quick press-and-release (held <700ms) of front-panel pushbutton key 256 POWER WAKE # front panel pushbutton held >700ms (long-press) key 257 HOME WAKE # user-eeprom section erased after 3 quick presses key 258 SEARCH WAKE # tamper event key 259 POWER WAKE }}} * Note that BTN_0 is hex 0x100 which is decimal 256 If for some reason you wish to use the pushbutton to capture events that the GSC does not automatically capture and report as interrupts (for example a button 'press' event instead of a quick press-and-release event, a 'button held for x seconds' event, or a 'button pressed-and-released x times in quick succession' event, you can choose to monitor gpio-240 manually with a simple application which uses the poll(2) stdlib function to block waiting for an interrupt. For more information see: * [wiki:gsc#pushbutton GSC pushbutton] * [wiki:gsc#inputdriver gsc-input kernel driver] * [wiki:gsc#gsc_ctrl_0 GSC_CTRL_0 register] * [wiki:gsc#gsc_interrupt_status GSC_INTERRUPT_STATUS register] * [wiki:gpio#catchingagpiochangefromuserspacewithoutpolling Catching a GPIO event from userspace w/o polling] [=#gpio-pushbutton] ==== GPIO as a Button ==== Any GPIO can be used as a pushbutton by using the gpio-keys or gpio-keys-polled driver. This driver will monitor a gpio's interrupt (or poll it if it has none), debounce it, and emit a Linux input event with a keycode of your choosing. In order to use this driver you need to: 1. add a configuration section for it in the appropriate dtsi file for the board you are using. 2. add a keylayout file to map the Linux input device created by the gpio-keys driver to an Android key For example, the [https://github.com/Gateworks/linux-imx6/blob/gateworks_kk4.4.3_2.0.0-ga/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi gw51xx.dtsi] can have the following section added [https://lxr.missinglinkelectronics.com/linux/include/uapi/linux/input.h input.h]) {{{ /* define linux keyboard gpio-keys driver named 'gpio-button' */ gpio: gpio-button { compatible = "gpio-keys"; #address-cells = <1>; #size-cells = <0>; autorepeat; /* map GW51xx DIO0 IMX6 GPIO1_IO16 to linux keycode 103 (KEY_UP) */ button@1 { label = "GPIO Key Up"; linux,code = <103>; debounce-interval = <100>; gpios = <&gpio1 16 GPIO_ACTIVE_LOW>; }; /* map GW51xx DIO1 IMX6 GPIO1__IO19 to linux keycode 109 (KEY_DOWN) */ button@2 { label = "GPIO Key Down"; linux,code = <108>; debounce-interval = <100>; gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; }; /* map GW51xx DIO2 IMX6 GPIO1__IO17 to linux keycode 106 (KEY_RIGHT) */ button@3 { label = "GPIO Key Right"; linux,code = <106>; debounce-interval = <100>; gpios = <&gpio1 17 GPIO_ACTIVE_LOW>; }; /* map GW51xx DIO3 IMX6 GPIO1__IO18 to linux keycode 105 (KEY_LEFT) */ button@4 { label = "GPIO Key Left"; linux,code = <105>; debounce-interval = <100>; gpios = <&gpio1 18 GPIO_ACTIVE_LOW>; }; }; }}} * The gpio node needs to be at the top level and its placement is arbitrary however its customary to put it in alphabetical order (thus between the '''chosen''' node and the '''leds''' node) A keylayout file (.kl) is added to the live Android system. The keylayout files are matched by device name and in this case, the device-name is taken from the device-tree node name ('''gpio-button''' above) with a unique kernel-defined number appended. To determine the device name you can ls /sys/bus/platform/drivers/gpio-keys/gpio-button*: {{{#!bash root@ventana:/ # ls /sys/bus/platform/drivers/gpio-keys/gpio-button* /sys/bus/platform/drivers/gpio-keys/gpio-button.19 }}} Therefore in the example above where the gpio node was named 'gpio-button' the device name is 'gpio-button.19'. This name is used as the input device name and you can see all names of input devices with: {{{#!bash root@ventana:/ # cat /sys/class/input/input*/name gsc_input gpio-button.19 root@ventana:/ # }}} Now create a keylout file named gpio-button.19.kl with: {{{ # map linux keycode 103 (KEY_UP) to Android DPAD_UP key 103 DPAD_UP WAKE # map linux keycode 108 (KEY_RIGHT) to Android DPAD_RIGHT key 108 DPAD_RIGHT WAKE # map linux keycode 106 (KEY_LEFT) to Android DPAD_LEFT key 106 DPAD_LEFT WAKE # map linux keycode 105 (KEY_DOWN) to Android DPAD_DOWN key 105 DPAD_DOWN WAKE }}} And push it to your system: {{{#!bash adb remount adb push gpio-button.19.kl /system/usr/keylayout/ }}} References: * [https://lxr.missinglinkelectronics.com/linux/include/uapi/linux/input.h Linux keycodes] * [https://lxr.missinglinkelectronics.com/linux/Documentation/devicetree/bindings/input/gpio-keys.txt Linux gpio-keys device-tree bindings] * [https://source.android.com/devices/input/key-layout-files.html Android key-layout files] [=#filesystem-permissions] == Filesystem Permissions == You can read/write files in /sys from your Android app, but only if the Linux filesystem has the correct permissions to do so. Android uses Linux filesystem permissions in the following way: * root user/group used only by init process (as far as we can tell) * system user/group used for core Android OS * user applications get their own unique user/group added at APK install time which will remain constant until uninstalled but will not match the same APK's user/group on another device Therefore, if you want the OS to be able to control something (for instance backlight control) you need to ensure that user/group system has permissions for the resource (for instance /sys/class/backlight/backlight.3/brightness) for the Android Settings app or auto-brightness to adjust LCD brightness. If you want a user application to be able to control something (for instance a GPIO direction/value) you need to ensure that all user/group's have access to the resource (because each Android app has a unique user/group) you need to make sure the resource has the permissions set for 'others' (ie 666 for read/write or 444 for read-only). Permissions and ownership of devices and files are set by the following: * Android init - when processing *.rc files with the {{{chown}}} and {{{chmod}}} directives * Android ueventd - when devices appear dynamically based on configuration in {{{/ueventd.rc}}} and {{{/ueventd.freescale.rc}}} * /system/bin/init.sh (Gateworks Added) via {{{chmod}}} and {{{chown}}} shell commands [=#networking] == Android Networking Support == [=#netd] === Network Daemon (netd) === Most networking operations are performed by the network daemon ([http://androidxref.com/4.4.3_r1.1/xref/system/netd/ netd]) via a socket interface. [=#dhcp] === DHCP === When netlink (kernel) shows a device obtaining a link (NETDEV_CHANGE) the EthernetDataTracker detects the change and dhcpcd is launched. There is a single EthernetDataTracker object (frameworks/base/core/java/android/net/EthernetDataTracker.java) that tracks 'Ethernet' device state changes but by design it only tracks a single device which is controlled by the '''config_ethernet_iface_regex''' string property (frameworks/base/core/res/res/values/config.xml). While this is defaulted to 'eth\d' to keep it generic this does not mean it will catch interface up/down events on 'any' ethernet as expected. Instead it only monitors the last interface from /sys/class/net that matches the setting. The interface change triggers a call to dhcp_do_request (libnetutils/dhcp_utils.c) to start the dhcp client daemon which requires a service for that specific interface be configured in init.rc. For example for eth1 you would need: {{{ # eth1 dhcp support service dhcpcd_eth1 /system/bin/dhcpcd -ABKL class main disabled oneshot service iprenew_eth1 /system/bin/dhcpcd -n class main disabled oneshot }}} If you want to use eth1 with DHCP instead of eth0 you must change this property at build time (device/gateworks/ventana/overlay/frameworks/base/core/res/res/values/config.xml) References: * https://community.freescale.com/docs/DOC-93626 - How to Add Ethernet UI Support in ICS [=#wifi] === !WiFi === See [wiki:Android/wireless] [=#properties] == Android Properties == You may want to use Android properties to abstract resources such as GPIO's, backlights, uart devices etc that may vary per board so that you can use a single firmware image that works across multiple boards. You can see examples of this in the [https://github.com/Gateworks/android_device_gateworks/blob/imx_kk4.4.3_2.0.0-beta/ventana/init.sh device/gateworks/ventana/init.sh] script where we create a symlink to /dev/gpsdevice for the various board-specific tty's. Another example may be to set gpio.dio0, gpio.dio1, gpio.dio2, gpio.dio3 properties to represent the numeric gpio for IMX6_DIO0-1 which vary between GW51xx/GW52xx/GW53xx/GW54xx boards. The Gateworks Android BSP init script sets the following properties that can be used in an app: * gpio.dio0 - the numeric gpio assigned to DIO0 (/sys/class/gpio/gpio) * gpio.dio1 - the numeric gpio assigned to DIO1 (/sys/class/gpio/gpio) * gpio.dio2 - the numeric gpio assigned to DIO2 (/sys/class/gpio/gpio) * gpio.dio3 - the numeric gpio assigned to DIO3 (/sys/class/gpio/gpio) * gpio.can_stby Your android app can use properties to obtain the actual gpio number used to access that function via sysfs: * Android Application Java API: [http://developer.android.com/reference/java/lang/System.html#getProperty(java.lang.String) getProperty] * Native C Code: use property_get from system/core/include/cutils/properties.h Here is a simple example C application showing how to use get_property to find the gpio value for a digital-io (assuming the init script above set this up) and reading the value from /sys/class/gpio: * external/gpio/Android.mk {{{ LOCAL_PATH := $(call my-dir) PRIVATE_LOCAL_CFLAGS := -O2 -g -W -Wall include $(CLEAR_VARS) LOCAL_SRC_FILES := gpio.c LOCAL_MODULE := gpio LOCAL_MODULE_TAGS := optional LOCAL_C_INCLUDES := $(LOCAL_PATH)/include/ LOCAL_CFLAGS := $(PRIVATE_LOCAL_CFLAGS) LOCAL_STATIC_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) }}} * external/gpio/gpio.c {{{#!c /* * gpio.c - Tim Harvey * * Simple example of how to get/set GPIO from a property * */ #include #include #include int main(int argc, char **argv) { char property[PROPERTY_VALUE_MAX]; char buf[256]; FILE *fp; if (argc < 1) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(1); } sprintf(buf, "gpio.dio%d", atoi(argv[1])); if (property_get(buf, property, "") == 0) { fprintf(stderr, "%s property not found\n", buf); exit(1); } printf("%s=gpio%d\n", buf, atoi(property)); sprintf(buf, "/sys/class/gpio/gpio%d/value", atoi(property)); fp = fopen(buf, "r"); if (!fp) { perror("fopen failed"); fprintf(stderr, "likely a permission issue " "or invalid file if gpio was not exported\n"); exit(1); } if (fread(buf, 1, sizeof(buf), fp) > 0) { printf("%d\n", atoi(buf)); } else { fprintf(stderr, "read %s failed\n", buf); } return 0; } }}} [=#boot] == Android Boot Process and Components == [=#bootscript] === Android Bootloader Script === Because the Android kernel needs some additional parameters which are not required by a standard Linux kernel, the default Ventana u-boot scripts are not sufficient for booting Android directly. The default bootscripts however anticipate this requirement and therefore always look for a /boot/6x_bootscript-ventana file on the first partition (ext fs for non-mtd block storage). If this file is present the bootloader will load it and source it (thus executing the script it contains). This file is in device/gateworks/ventana/6x_bootscript.txt and is built by device/gateworks/bootscript.mk which uses the mkimage utility to put a u-boot image header on it. The Android bootscript does the following things in general: * configure GPU memory allocation kernel params (using mem_mb set dynamically by U-Boot) * detect display devices and configure kernel params accordingly * detect boot device and configure kernel params accordingly There are some U-Boot env variables that the bootscript can use for various overrides (mainly because there isn't a foolproof way of detecting display devices and boot devices): * console - device to use as kernel console (you can unset this to disable kernel console which increases boot time) * baudrate - baudrate to use for kernel console device * panel - display (or displays in prioritized order) which can a list of the following: * '''Hannstar-XGA''' (for 10" 1024x768 Freescale MXC-LVDS1 display with a egalax touch controller at i2c 0x04) * '''AUO-G070VW01''' (for 7" 800x480 display with a tsc2007 touch controller at i2c 0x49) * '''HDMI''' (for HDMI display where linux framebuffer mode can be specified in the 'hdmi' env var but defaults to 1080x720M@60. Other options for 'hdmi' var include 1920x1080M@60, 640x480M60 and others your monitor may support from /sys/class/graphics/fb0/modes) * If not specified, the above displays will attempt to be detected by simply seeing if an i2c device responds on their touchscreens slave address. Note that some HDMI monitors may have slave devices that match these * Please see [wiki:ventana/bootloader#Displaysupport setting display in bootloader at this link]. * fs - the filesystem used on the storage device (defaults to ext2) * disk - the device number for the disk type (defaults to 0) * dtype - the storage type which can be one of usb, mmc, sata * this is set by the latest Gateworks U-Boot default scripts to avoid detection * if not specified will be the first device that has a boot/uImage file * bootdev - the boot device passed to Android which can be one of: fsl-ehci.0 (USB OTG), fsl-ehci.1 (USB EHCI), sdhci-esdhc-imx.2, ahci.0 For the best understanding of how the boot script works you can view [https://github.com/Gateworks/android_device_gateworks/blob/imx_kk4.4.3_2.0.0-beta/ventana/6x_bootscript.txt the source here] Because the kernel, bootscript, device-tree files, and initial ramdisk are stored in the BOOT partition you can update the bootscriptmanually as follows via [#adb adb]: {{{#!bash bootable/bootloader/uboot-imx/tools/mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "boot script" -d \ device/gateworks/ventana/6x_bootscript.txt 6x_bootscript-ventana adb remount adb push 6x_bootscript-ventana /boot/boot/ adb reboot }}} [=#init.sh] === Android Init Script === Android has its own init system that differs from the init systems used across various Unix and Linux distributions. The Android init.rc script syntax allows some simple primitives for launching applications (either one-shot or persistent), setting ownership and permissions, and setting properties. This is useful for things that are common to all devices your firmware will run on and things that should be setup once (or defaulted) at boot. For example, if you want your application to be able to control LED brightness you can do either of the following: * init.rc: add the following to device/gateworks/ventana/init.rc 'on boot' section: {{{#!bash # set permissions for user leds (using Android init syntax) chown system system /sys/class/leds/user1/brightness chown system system /sys/class/leds/user2/brightness chown system system /sys/class/leds/user3/brightness chmod 0660 /sys/class/leds/user1/brightness chmod 0660 /sys/class/leds/user2/brightness chmod 0660 /sys/class/leds/user3/brightness }}} The init.rc file for the Gateworks Ventana Android BSP can be found [https://github.com/Gateworks/Android/blob/imx_kk4.4.3_2.0.0-beta/ventana/init.rc here]. Because the Android init application runs from a Linux inital ramdisk (initrd), if you modify init.rc you need to do a toplevel 'make' to rebuild the ramdisk.img, apply a U-Boot header to it with 'mkimage' and update it on your BOOT partition. References: * [https://android.googlesource.com/platform/system/core/+/master/init/README.md] [=#init.rc] === Android Init Shell Script === Because the Android init system does not allow for complex syntax supporting decision making logic (ie if/then/else clauses) we configured [https://github.com/Gateworks/android_device_gateworks/blob/imx_kk4.4.3_2.0.0-beta/ventana/init.rc init.rc] to run a shell script during late boot to handle more complex board-specific tasks. You can find the init.sh script [https://github.com/Gateworks/Android/blob/imx_kk4.4.3_2.0.0-beta/ventana/init.sh here]. Because this script is installed in the SYSTEM partition under /system/bin/init.sh you can update it via [#adb adb] (after making sure to remount /system as read/write): {{{#!bash adb remount adb push device/gateworks/ventana/init.sh /system/bin/init.sh adb shell chmod 0777 /system/bin/init.sh adb reboot }}} Our init script sets some android properties to describe the hardware in a non board-specific way so that you can use properties in your app to allow it to run on a variety of Gateworks boards. See [#properties here] for more details on properties. [=#kernel] === Android Kernel === The Android kernel is a Linux kernel with a few additional patches which have not made it into mainline Linux yet. Because there is no userspace daemon such as udev which monitors Linux hotplug events and loads kernel modules on demand typically the kernel has no modules. This is not a hard rule however and if you want to prune down the kernel to reduce boot time you can remove support that is not required for your application and/or load kernel driver modules from [#init.rc init.rc] [#init.sh init.sh] or some other mechanism after the OS is fully booted. Because the kernel, bootscript, device-tree files, and initial ramdisk are stored in the BOOT partition you can update the kernel and dtbs manually as follows via [#adb adb]: {{{#!bash adb remount adb push kernel_imx/arch/arm/boot/uImage /boot/boot/uImage for i in `ls kernel_imx/arch/arm/boot/dts/imx6*gw*.dtb`; do adb push $i /boot/boot; done adb reboot }}} [=#external_storage] == External Storage == The Gateworks Android BSP supports a number of storage mediums that can be classified as internal or external storage. External storage can be provided by physical media, like an SD card or USB, that is for temporary file storage and data transfer. The physical media may remain with the device for an extended period of time, but is not tied to the device and may be removed. === Android Storage Framework === While linux level external storage support comes built into the kernel packaged with the Android OS, there are a combination of services started at boot time that perform staging operations to prepare the media before exposing it to apps. These framework services as they pertain to external storage are described below. * '''Vold''' - Mounting of physical external storage volumes is handled by Vold, which monitors device node paths and mounts when a device has been inserted and/or detected by the kernel. * '''sdcard Daemon''' - Performs permission fixups to make newly mounted external storage available to Android userspace and applications with the {{{READ/WRITE_EXTERNAL_STORAGE}}} permissions. * '''!MediaScanner''' - Crawls through the various filesystems that the Android OS has mounted successfully and makes the media files on them available to Android apps via special request intents (e.g. Gallery, Wallpaperpicker, etc.) === Adding Support for External Storage === The steps for adding support for a new external storage device as they pertain to the Gateworks Android BSP are defined below: 1. Add Vold device line in {{{fstab_nand}}} and/or {{{fstab_block}}} Depending on whether your boot device was passed as a flash or block device from the bootloader to the kernel command line, either the {{{fstab_nand}}} or {{{fstab_block}}} file will be parsed by Vold to handle how the external storage will be mounted. Adding a new device will require an additional line of the format: {{{ }}} * {{{src}}} - The kernel sysfs path where the device node exists (usually mounted at /sys). The path must start with {{{/}}}. * {{{mnt_point}}} - Filesystem path where the volume should be mounted (usually {{{auto}}}). * {{{type}}} - The type of the filesystem on the volume. For external cards, this is usually {{{vfat}}}. * {{{mnt_flags}}} - Vold ignores this field and it should be set to {{{defaults}}} * {{{fs_mgr_flags}}} - Vold ignores any lines in the unified fstab that do not include the {{{voldmanaged=}}} flag in this field. This flag must be followed by a label describing the card, and a partition number or the word {{{auto}}}. Other possible flags are {{{nonremovable}}}, {{{encryptable=sdcard}}}, {{{noemulatedsd}}}, and {{{encryptable=userdata}}}. An example line for a uSD host controller would be: {{{/devices/soc0/soc.0/2100000.aips-bus/2198000.usdhc/mmc_host* auto vfat defaults voldmanaged=extsd:auto}}} 2. Add storage element to {{{storage_list.xml}}} The device-specific {{{storage_list.xml}}} configuration file (found under {{{$ANDROID_BUILD_TOP/device/gateworks/ventana/overlay/frameworks/base/core/res/res/xml/}}}), defines the attributes and constraints of storage devices. The {{{}}} element contains one or more {{{}}} elements, exactly one of which should be marked primary. {{{}}} attributes include: * {{{mountPoint}}}: filesystem path of this mount. * {{{storageDescription}}}: string resource that describes this mount. * {{{primary}}}: true if this mount is the primary external storage. * {{{removable}}}: true if this mount has removable media, such as a physical SD card. * {{{emulated}}}: true if this mount is emulated and is backed by internal storage, possibly using a FUSE daemon. * {{{mtp-reserve}}}: number of MB of storage that MTP should reserve for free storage. Only used when mount is marked as emulated. * {{{allowMassStorage}}}: true if this mount can be shared via USB mass storage. * {{{maxFileSize}}}: maximum file size in MB. An example {{{}}} element to accompany the fstab line above would be: {{{#!xml }}} 3. Add mkdir calls in {{{init.rc}}} In the {{{init.rc}}} file found under {{{$ANDROID_BUILD_TOP/device/gateworks/ventana/}}} add the following lines in the {{{on init}}} section: {{{#!sh mkdir /mnt/media_rw/extsd 0766 media_rw media_rw mkdir /storage/extsd 0766 root root }}} 4. Append to the {{{SECONDARY_STORAGE}}} var in {{{init.rc}}} Defined in the same {{{init.rc}}} is the {{{SECONDARY_STORAGE}}} variable which is a {{{:}}} delimited list of external paths that the Android framework's !MediaStore will parse. Append your storage device to this declaration line: {{{export SECONDARY_STORAGE /storage/extsd:/storage/extsd1:/storage/udisk:/storage/udisk1:/storage/sata}}} 5. Add sdcard fuse service in {{{init.rc}}} Towards the end of the same {{{init.rc}}} file is a series of service declaration lines. Adding these fuse services will emulate a case-insensitive, permissionless filesystem backed by the storage device regardless of filesystem type, as long as the filesystem is understood by the kernel. Following the same uSD example: {{{#!bash # virtual sdcard daemon running as media_rw (1023) service fuse_extsd /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/extsd /storage/extsd class late_start disabled }}} [=#selinux] == Security-Enhanced Linux (SELinux) == SELinux is a mandatory access control (MAC) system for the Linux OS. Sockets, Files, and Processes all have labels in SELinux. A label takes the form of {{{user:role:type:mls_level}}}. Rules are applied to labels and take the form {{{allow domains types:classes permissions}}}. The {{{file_contexts}}} file assigns labels to files via regular expression matches. Filesystems must be built to include SELinux filesystem attributes. The {{{restorecon}}} userspace application applies policies to a running kernel, which is usually done as one of the first things in system init. Android has been phasing in more robust security measures with each release. [https://en.wikipedia.org/wiki/Security-Enhanced_Linux SELinux] is a Linux kernel security module that provides a mechanism for supporting access control security policies, including the United States Department of Defense-style mandatory access controls (MAC). SELinux started being used for Android 3.x (Jellybean) in a permissive mode, and in enforcing mode for Android 4.x (Kitkat). While troubleshooting it may be useful to disable SELiux which you can do in the following ways: * add {{{selinux=0}}} to the kernel cmdline (requires CONFIG_SECURITY_SELINUX_BOOTPARAM=y in kernel config) * {{{setenforce 0}}} on cmdline SELinux policy files are compiled into the {{{sepolicy}}} which exists in the root of the ramdisk on the BOOT partition. This is created from rules defined in files in paths from BOARD_SEPOLICY_DIRS defined in !BoardConfig.mk. The ventana specific policies are located in [https://github.com/Gateworks/android_device_gateworks/tree/imx_kk4.4.3_2.0.0-ga/ventana/sepolicy device/gateworks/ventana/sepolicy]. Additionally policies can be found in [https://android.googlesource.com/platform/external/sepolicy/+/android-4.4.3_r1 external/sepolicy] which is a collection of common policy files used by Linux distros. SELinux denials errors output as kernel error messages prefixed with "avc: ". For example: {{{#!bash $ adb shell su -c dmesg | grep "avc: " [ 62.658290] type=1400 audit(1443478068.109:5): avc: denied { setgid } for pid=1155 comm="hostapd" capability=6 scontext=u:r:netd:s0 tcontext=u:r:netd:s0 tclass=capability permissive=0 [ 62.675542] type=1400 audit(1443478068.129:6): avc: denied { setgid } for pid=1155 comm="hostapd" capability=6 scontext=u:r:netd:s0 tcontext=u:r:netd:s0 tclass=capability permissive=0 [ 62.692514] type=1400 audit(1443478068.149:7): avc: denied { add_name } for pid=1155 comm="hostapd" name="1010" scontext=u:r:netd:s0 tcontext=u:object_r:cgroup:s0 tclass=dir permissive=0 [ 62.709701] type=1400 audit(1443478068.169:8): avc: denied { setuid } for pid=1155 comm="hostapd" capability=7 scontext=u:r:netd:s0 tcontext=u:r:netd:s0 tclass=capability permissive=0 }}} * above the 'hostapd' process exec'd by netd is trying to setgid/setuid and failed The {{{audit2allow}}} tool from the {{{policycoreutils}}} package can take these denials and converts them into corresponding SELinux policy statements: {{{#!bash sudo apt-get install policycoreutils adb shell su -c dmesg | grep "avc: " | audit2allow # show rules adb shell su -c dmesg | grep "avc: " | audit2allow -p out/target/product/ventana/root/sepolicy # analyze against current policy }}} For example when the above netd/hostapd violations are run through audit2allow this shows the following additions could be made to fix the violations: {{{ #============= netd ============== allow netd cgroup:dir add_name; allow netd self:capability { setuid setgid }; }}} The {{{ls -Z}}} command will show you file contexts and similarly the {{{ps -Z}}} command will show you process contexts. You can find the processed policies with comments detailing their source in $OUTDIR/obj/ETC/sepolicy_intermediates/policy.conf which is built from the {{{external/sepolicy}}} project. During development you can rebuild the {{{sepolicy}}} (which verifies your rules and configuration) via {{{mmm external/sepolicy}}}. You can rebuild and update your ramdisk with a something like: {{{ mmm external/sepolicy && mkbootfs out/target/product/ventana/root | minigzip > out/target/product/ventana/ramdisk.img && \ mkimage -A arm -O linux -T ramdisk -n "RAM Disk" -d out/target/product/ventana/ramdisk.img \ out/target/product/ventana/boot/boot/uramdisk.img && \ adb remount && \ adb push out/target/product/ventana/boot/boot/uramdisk.img /boot/boot/uramdisk.img && \ adb reboot }}} Be aware, that because this does not rebuild the system filesystem, any rule changes will not take effect without a manual {{{restorecon }}}. === Adding New Files === The process for injecting files into a SELinux filesystem includes the following: 1. Properly apply security labels (mentioned [#selinux above]) in {{{device/gateworks/ventana/sepolicy/file_contexts}}} 2. Create a .te policy file under {{{device/gateworks/ventana/sepolicy/}}} 3. Add the .te file to the policy list in {{{device/gateworks/ventana/BoardConfig.mk}}} 5. Apply proper user.group ownership in {{{system/core/include/private/android_file_config.h}}} References: * [https://source.android.com/security/selinux/index.html Security-Enhanced Linux in Android] [=#partitions] == Partitions == The flash storage space on an Android device typically contains the following partitions. * BOOT: /boot - contains bootscript/kernel/ramdisk that boots Android init * RECOVERY: /recovery - contains bootscript/kernel/ramdisk that boots an '''Android recovery image''' who's function is very device/vendor specific * SYSTEM: /system ro - the system '''ROM''' * DATA: /data - user data - this is where your apps and app data are stored and where you want the most space * CACHE: /cache - used for various (unclear) OS caching (including downloading apps from play store) * MISC: /misc - a tiny partition used by recovery to stash some information away about what its doing in case the device is restarted while the OTA package is being applied * VENDOR: /device - for vendor specific files The creation of the partitions is controlled by build/core/Makefile. The mounting of the partitions is controlled by Android's init application by the mount_all command in init.rc which is passed a file containing device and mountpoint information. This is all done in the ramdisk and the mount_all is typically called from the 'on fs' hook as the last step in init. The storage device partitioning is taken care of with whatever script you use to image android onto a specific device: * device/gateworks/ventana/mksdcard.sh - for block storage devices such as USB/mSATA/MMC. The partition sizes are baked into the script * device/gateworks/ventana/ubi/ubinize.ini - for NAND ubi volume creation of ubi image References: - [https://source.android.com/devices/tech/ota/index.html#android-device-layout Android device layout] [=#bootpartition] === BOOT partition === The BOOT partition is very device specific and typically contains a kernel and ramdisk containing the Android init system (/sbin/init, init.rc and its includes) and processes early init until the mounting of the other filesystems. When filesystems are mounted this may be mounted to /boot The files have on our boot partition use are: * 6x_bootscript-ventana - bootscript that sets up the kernel cmdline (including android specific parameters) and loads/executes kernel * uImage - kernel (zImage wrapped in a uboot image header) * imx6*gw*.dtb - kernel devicetree blobs * uramdisk.img - initial ramdisk (compressed cpio archive wrapped in a uboot image header) ==== ramdisk.img / uramdisk.img ==== The boot ramdisk is the initial filesystem loaded by the bootloader into memory and passed to the kernel via a kernel parameter. It is a Linux kernel '''initramfs''' or '''initial ramdisk''' (see Documentation/early-userspace/buffer-format.txt) which is based around the 'newc' or 'crc' CPIO formats and therefore can be created with the cpio utility. It is created by build/core/Makefile using (MKBOOTFS) out/host/linux-x86/bin/mkbootfs (built from system/core/cpio/mkbootfs.c) with the following command: {{{#!bash mkbootfs out/target/product/ventana/root | minigzip > out/target/product/ventana/ramdisk.img }}} It is built from $(OUTPUT_DIR)/boot and deps are setup on all files in that dir other than prebuilt files, copied headers, generated sources, and default installed modules. To install files onto the ramdisk make sure they get copied to the root/ directory of the product output folder: * from ventana.mk or imx6.mk add a file to PRODUCT_COPY_FILES destined for root/: {{{ PRODUCT_COPY_FILES += device/gateworks/ventana/foo:root/foo }}} * from a module Android.mk you can set LOCAL_MODULE_PATH to TARGET_ROOT_OUT to put it in root/: {{{ LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) }}} If changes are made you may need to remove the images to force dependency checks prior to rebuilding: {{{#!bash rm -rf out/target/product/ventana/boot/boot/uramdisk.img \ out/target/product/ventana/ramdisk.img make }}} A typical list of files on a ramdisk: * default.prop * proc - mountpoint * dev - mountpoint * init.rc - config for init * init - android init application * sys - mountpoint * init.goldfish.rc * sbin/adbd - android debug bridge daemon * system - mountpoint * data mountpoint Because its loaded by uboot, a uImage header is wrapped around it by 'mkimage' creating uramdisk.img from ramdisk.img. This is not part of the core/build system but instead done vi device/gateworks/ramdisk.mk To extract a ramdisk.img from a uramdisk.img you need to take off the 64-byte U-Boot header: {{{#!bash dd if=uramdisk.img of=ramdisk.img bs=1 skip=64 }}} To extract a ramdisk.img (which is a gzipped CPIO archive): {{{#!bash mkdir root; cd root; zcat ../ramdisk.img | cpio -i }}} To list the contents of a ramdisk.img (which is a gzipped CPIO archive): {{{#!bash zcat ramdisk.img | cpio -t }}} To create a ramdisk.img from a directory: {{{#!bash find . | cpio -o -H newc -O ../ramdisk.cpio; gzip -f ../ramdisk.cpio; mv ../ramdisk.cpio.gz ../ramdisk.img }}} To add a U-Boot header to a ramdisk.img: {{{#!bash mkimage -A arm -O linux -T ramdisk -n "RAM Disk" -d ramdisk.img uramdisk.img }}} A nice '''one-liner for re-packaging a uramdisk.img from out/target/product/ventana/root and pushing it to a target over adb''': {{{#!bash mkbootfs out/target/product/ventana/root | minigzip > out/target/product/ventana/ramdisk.img && \ mkimage -A arm -O linux -T ramdisk -n "RAM Disk" -d out/target/product/ventana/ramdisk.img \ out/target/product/ventana/boot/boot/uramdisk.img && \ adb remount adb push out/target/product/ventana/boot/boot/uramdisk.img /boot/boot/uramdisk.img && \ adb reboot }}} [=#recoverypartition] === RECOVERY partition === The recovery partition is very device specific and works the same way as the BOOT partition in order to boot a system with some sort of recovery tools or mechanism for restoring a system ROM. Typically this works by booting this instead of BOOT if the user is holding a key down (usually home+power on a mobile device) during powerup and typically (in the case of phones at least) presents the user with a very rudimentary text based menu using vol up/down and power button to move and select menu items allowing you to do things like: * erase cache * create backups * install ROM's A recovery.img is a bootable image with the same layout/format as the boot image (see above). Typically the ramdisk will have a few more files such as graphics resources and a different init script/app. [=#systempartition] === SYSTEM partition === The SYSTEM partition (ext4 for block devs, ubifs for NAND devs) is mounted to /system read-only and contains the base Android OS minus installed apps and data (which go to the DATA partition mounted read-write to /data). This is what is considered the Android '''ROM'''. This is built from core/build/Makefile by creating a ramdisk image of out/target/product//system. The permissions of the files in /system is controlled by system/core/include/private/android_filesystem_config.h ('''do not forget to set permissions here for new files or at least check that they are configured properly through wildcards''') The dependencies on rebuilding system.img are the files in $(PRODUCT_OUT)/installed-files.txt To add files use PRODUCT_COPY_FILES with a destination of system/: {{{ PRODUCT_COPY_FILES += device/gateworks/ventana/foo:system/foo }}} The system partition uses an ext4 filesystem image system.img, created using a helper script 'out/host/linux-x86/bin/mkuserimg.sh ext4 ' which does the following: {{{#!bash make_ext4fs -S out/target/product/ventana/root/file_contexts -l 398458880 -a system out/target/product/ventana/obj/PACKAGING/systemimage_intermediates/system.img out/target/product/ventana/system }}} This in turn uses the out/host/linux-x86/bin/make_ext4fs tool built from system/extras/ext4_utils/make_ext4fs.c {{{#!bash make_ext4fs [ -l ] [ -j ] [ -b ] [ -g ] [ -i ] [ -I ] [ -L