Mstar理论及实践篇

理论篇
############################################################################### sp<ITvManager> TvManager::mTvManager;强指针 sp

智能指针
在 Android 的源代码中,经常会看到形如:sp<xxx>、wp<xxx>这样的类型定义,这其实是 Android 中的智能指针。智能指针是 C++中的一个概念,通过基于引用计数的方法,解决对 象的自动释放的问题。在 C++编程中,有两个很让人头痛的问题:一是忘记释放动态申请 的对象从而造成内存泄露;二是对象在一个地方释放后,又在别的地方被使用,从而引起内 存访问错误。程序员往往需要花费很大精力进行精心设计,以避免这些问题的出现。在使用 智能指针后,动态申请的内存将会被自动释放(有点类似 Java 的垃圾回收),不需要再使 用 delete 来释放对象,也不需要考虑一个对象是否已经在其它地方被释放了,从而使程序 编写工作减轻不少,而程序的稳定性大大提高。 Android 的智能指针相关的源代码在下面两个文件中: frameworks/base/include/utils/RefBase.h frameworks/base/libs/utils/RefBase.cpp 涉及的类以及类之间的关系如下图所示: Android 中定义了两种智能指针类型,一种是强指针 sp(strong pointer),一种是弱指 针(weak pointer)。其实成为强引用和弱引用更合适一些。强指针与一般意义的智能指针 概念相同, 通过引用计数来记录有多少使用者在使用一个对象, 如果所有使用者都放弃了对 该对象的引用,则该对象将被自动销毁。 弱指针也指向一个对象,但是弱指针仅仅记录该对象的地址,不能通过弱指针来访问该 对象, 也就是说不能通过弱智真来调用对象的成员函数或访问对象的成员变量。 要想访问弱 指针所指向的对象, 需首先将弱指针升级为强指针 (通过 wp 类所提供的 promote()方法) 。 弱指针所指向的对象是有可能在其它地方被销毁的, 如果对象已经被销毁, wp 的 promote() 方法将返回空指针,这样就能避免出现地址访问错的情况。 是不是很神奇?弱指针是怎么做到这一点的呢?其实说穿了一点也不复杂,原因就在于 每一个可以被智能指针引用的对象都同时被附加了另外一个 weakref_impl 类型的对象,这

个对象中负责记录对象的强指针引用计数和弱指针引用计数。 这个对象是智能指针的实现内 部使用的,智能指针的使用者看不到这个对象。弱指针操作的就是这个对象,只有当强引用 计数和弱引用计数都为 0 时,这个对象才会被销毁。 说了这么多原理, 下面该看看到底智能指针该怎么使用了。 假设现在有一个类 MyClass, 如果要使用智能指针来引用这个类的对象,那么这个类需满足下列两个前提条件: (1)这个类是基类 RefBase 的子类或间接子类; (2)这个类必须定义虚构造函数,即它的构造函数需要这样定义: virtual ~MyClass(); 满足了上述条件的类就可以定义智能指针了,定义方法和普通指针类似。比如普通指针 是这样定义: MyClass* p_obj; 智能指针是这样定义: sp<MyClass> p_obj; 注意不要定义成 sp<MyClass>* p_obj。 初学者容易犯这种错误, 这样实际上相当于定义 了一个指针的指针。尽管在语法上没有问题,但是最好永远不要使用这样的定义。 定义了一个智能指针的变量, 就可以象普通指针那样使用它, 包括赋值、 访问对象成员、 作为函数的返回值、作为函数的参数等。比如: p_obj = new MyClass(); // 注意不要写成 p_obj = new sp<MyClass> sp<MyClass> p_obj2 = p_obj; p_obj->func(); p_obj = create_obj(); some_func(p_obj); 注意不要试图 delete 一个智能指针,即 delete p_obj。不要担心对象的销毁问题,智能 指针的最大作用就是自动销毁不再使用的对象。 不需要再使用一个对象后, 直接将指针赋值 为 NULL 即可:

p_obj = NULL; 上面说的都是强指针,弱指针的定义方法和强指针类似,但是不能通过弱指针来访问对 象的成员。下面是弱指针的示例: wp<MyClass> wp_obj = new MyClass(); p_obj = wp_obj.promote(); // 升级为强指针。不过这里要用.而不是->,真是有负其指针 之名啊 wp_obj = NULL;

智能指针用起来是很方便,在一般情况下最好使用智能指针来代替普通指针。但是需要 知道一个智能指针其实是一个对象, 而不是一个真正的指针, 因此其运行效率是远远比不上 普通指针的。所以在对运行效率敏感的地方,最好还是不要使用智能指针为好。 ###############################################################################

认识理解 Java 中 native 方法
[摘要] Java 不是完美的,Java 的不足除了体现在运行速度上要比传统的 C++慢许多之外,Java 无法直接访 问到操作系统底层(如系统硬件等),为此 Java 使用 native 方法来扩展 Java 程序的功能。 [关键字] Javanative

Java 不是完美的,Java 的不足除了体现在运行速度上要比传统的 C++慢许多之外,Java 无法直接访问到操作系统底层(如系统硬件等),为此 Java 使用 native 方法来扩展 Java 程序 的功能。 可以将 native 方法比作 Java 程序同C程序的接口,其实现步骤: 1、在 Java 中声明 native()方法,然后编译; 2、用 javah 产生一个.h 文件; 3、写一个.cpp 文件实现 native 导出方法,其中需要包含第二步产生的.h 文件(注意其 中又包含了 JDK 带的 jni.h 文件) ; 4、将第三步的.cpp 文件编译成动态链接库文件;

5、 在 Java 中用 System.loadLibrary()方法加载第四步产生的动态链接库文件, 这个 native() 方法就可以在 Java 中被访问了。 JAVA 本地方法适用的情况 1.为了使用底层的主机平台的某个特性,而这个特性不能通过 JAVA API 访问 2.为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用 JAVA 编 写的 3.为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。 首先写好 JAVA 文件 /* * Created on 2005-12-19 Author shaoqi */ package com.hode.hodeframework.modelupdate; public class CheckFile { public native void displayHelloWorld(); static { System.loadLibrary("test"); } public static void main(String[] args) {

new CheckFile().displayHelloWorld(); } } 然后根据写好的文件编译成 CLASS 文件 然 后 在 classes 或 bin 之 类 的 class 根 目 录 下 执 行 javah -jni

com.hode.hodeframework.modelupdate.CheckFile, 就会在根目录下得到一个 com_hode_hodeframework_modelupdate_CheckFile.h 的文件 然后根据头文件的内容编写 com_hode_hodeframework_modelupdate_CheckFile.c 文件 #include "CheckFile.h" #include #include JNIEXPORT jobject obj) { printf("Hello world!\n"); return; } 之后编译生成 DLL 文件如“test.dll”,名称与 System.loadLibrary("test")中的名称一致 vc 的 编 译 方 法 : cl -I%java_home%\include -I%java_home%\include\win32 -LD com_hode_hodeframework_modelupdate_CheckFile.c -Fetest.dll void JNICALL *env,

Java_com_hode_hodeframework_modelupdate_CheckFile_displayHelloWorld(JNIEnv

最后在运行时加参数-Djava.library.path=[dll 存放的路径] ###############################################################################

MStar Android 网络机代码组成
从代码结构上分,MStar Android 网络电视包含 MBoot、PM、Kernel、Supernova、ICS、 TvApp 和 Local MM 这 7 个部分,以 MSD6A801 的代码包为例,如图所示:

上面 7 个部分跟 Android 架构的关系图:

下面针对这 7 个模块进行详细描述, 内容主要涉及启动流程和代码结构等, 最后将对各

模块的相互关联做个总结。

MBoot 概述
MBoot 是 MStar Boot Loader 的缩写,由 SBoot 和 UBoot 两部分组成。MBoot 用于系统的 启动引导,它会初始化硬件,然后从 NAND flash 加载 Linux 内核 (kernel) 和应用程序 (applications)到 DRAM。 (说明:其实标准的 UBoot 程序已经做了 MBoot 的全部工作,MStar 引入 SBoot 的概念,是沿用了 MStar 非网络机的用法) 以下是上面提到的几个名词的对比: Acronym MBoot SBoot UBoot Description MStar Boot loader Small Boot loader Universal Boot loader

MBoot 存储在 flash,该 flash 一般采用 16M/32M bit 容量的 spi flash,里面存放系统的引 导程序及部分系统、用户数据。系统上电后首先通过 Mboot 引导启动,Mboot 完成启动后 再启动系统主程序。系统的主程序存放在 NAND Flash 中。不管是 spi flash 还是 Nand flash, 任何一个有故障,都会导致整机无法启动。 MBoot 主要包含 sboot 和 uboot 两部分:

说明:标准的 GPL UBoot 代码放在目录 u-boot-1.1.6 里面,经过 MStar 移植的 UBoot 代 码放在 u-boot-2011.06。

SBoot 介绍
SBoot 代码结构树
SBoot 是 MBoot 启动系统的入口点,用于初始化 CPU(ARMv7) ,Cache,寄存器等。初 始化工作完成后,MBoot 将跳到 UBoot 的入口点。 SBoot 的目录结构主要包括下面子目录:bin, inc, scripts, out, src。 bin: 存放 PM.bin,关于 PM 在后面会介绍; inc:board 文件,如 MSD6A608 对应的 BD_MST038B_10AHT_EAGLE.h;

scripts:存放脚本文件,用于配置相关参数,即输入 make menuconfig 后弹出的 menu configuration window; out:存放 MBoot 编译生成的 bin; src:SBoot 的源代码.

SBoot 启动流程
启动入口和地址在 sboot.lds(lds 文件是决定一个可执行程序的各个段的存储位置,以 及入口地址,这也是链接定位的作用)文件里描述:

下面以 MSD6A608 为例进行说明: ? 入口点即 ENTRY(_vector),在/sboot/src/eagle/boot.S 有实现代码,boot.S 会生成对应的 boot.o; ? 启动地址:0x00000000;Ram 的地址,这里的 Ram 指的是 DSP Ram; 需要注意的是, 启动代码即 boot.o 应该放在第一个 section 上并且 boot.o 不能大于 8Kbytes。 如下是 SBoot 的 section 定义, 同样在/sboot/src/eagle/sboot.lds 里能找到实现。

入口 Entry 执行完后,执行/sboot/src/eagle/bootrom.S,这一步进行寄存器设置,注 意 一 点 是 , 这 一 步 还 会 进 行 Uart 的 初 始 化 工 作 , 具 体 实 现 在 /sboot/src/eagle/include/Drv_uart.inc

接着执行 /sboot/src/eagle/bootram.S ,在这一步会跳到 UBoot 执行,过程: ? BOOT_CopyBootRAM? BOOTRAM_Entry?UBoot entry. SBoot 完整的流程如下:

Power on Copy BOOTRAM section from FLASH to RAM via DMA which included bootram, bootflash, nandflash and uart16550

Start entry BOOT_Entry in boot.s @0xBFC00000

1. Reset CPU 2. Init MIPS registers 3. Init CPU Cache 4. Init DSP RAM 5. Set SP address at DSP RAM @0x84001000

Jump to BOOTRAM_Entry @0x80200000 in bootram.s

Jump to BOOTROM_Entry in bootrom.c

1. Set SP address @ADDR_STACK_POINT ER (0x80A80000) 2. Copy UBoot bin from FLASH to DRAM @UBOOT_ENTRY_ADDR (0x80100000)

1. PM_Init 2. PAD config 3. Init system H/W register 4. Set CPU clock rate 5. Turn on output pad 6. Enable MSTVTool access ability 7. Disable WDT 8. Init UART 9. Init MIU

Jump to UBoot entry

这个过程简单来说就是:SBoot 从 DSP Ram 的地址 0x1fc00000 开始运行,使用 DSP Ram 进行硬件初始化工作,执行相关 C 代码的工作,并从 SPI Flash 拷贝 UBoot 到 DRam 里面, 最终跳转到 UBoot 入口。

UBoot 介绍
UBoot 代码结构树
MStar UBoot 是基于 u-boot-1.1.6 的,UBoot 自带 CLI(command line interface) ,通过串 口可以跟用户交互。UBoot 能做些啥?有下面几点: ? 通过 CLI 设置环境变量和执行一些操作; ? 初始化系统; ? 通过以太网口加载 kernel 到 DRAM; ? 烧录 kernel 和 application 到 NAND Flash;

? ? ?

设置 kernel 参数; 解压 kernel; 传递启动参数给 kernel;

UBoot 代码包通常包含下面子目录:board, common, cpu, disk, doc, drivers, fs,include, lib_generic, lib_mips, net, pos, rtc, tools.各个包的作用如下: Directory Board common Cpu Disk Doc drivers Fs include lib_generic lib_mips net post rtc Description board define Command and environment setup MIPS cpu start.s Reports device info to the user Documents NAND driver, emac driver, usb driver… ext2, fat, fdos, jffs2, … header file generic lib MIPS lib net, tftp, … Power on self test Real time clock

UBoot 启动流程
启动入口和地址在 u-boot.lds 描述, 然后指向/u-boot-2011.06/arch/arm/cpu/armv7/start.S, 在 start.S 描述了入口地址。

同时, 在 u-boot.lds 还描述了 u_boot_cmd 存放的 section, 关于 u_boot-cmd 在后面做详 细描述。

0x80100000
_start entry

.text .rodata .data .sdata .got
__u_boot_cmd_start

.u_boot_cmd
__u_boot_cmd_end

.bss
UBoot 启动过程:
Reset and critical MIPS register Init Allocation the memory space usage Set GOT pointer @_GLOBAL_OFFSET_TABLE_ relocate_code() Set the SP register address Cache reset

Set SP register pointer @0x80400000

Readjust GP address

Jump to board_init_f()

Copy Uboot code @0x80100000 to relocated address space

Init sequence such as 1.timer, 2. env, 3. baudrate, 4. serial port, 4. console, 5. version and CPU clock rate display 6. ram size

Update GOT offset value

Clear BSS section

Jump to board_init_r()

board_init_r() Get “bootcmd” environment variable Relocates the command table NO Memory malloc Init. bootcmd variable is valid && don’t stop autoboot

YES NAND Flash Init. Execute bootm command Read line string of prompt

Environment variables relocation Uncompress kernel image

Run commands you want

Device(serial port) and console Init

Load kernel image to specific position

Ethernet init.

Pass the kernel arguments to kernel

mtdparts Init. Jump to startup point of kernel

MBoot/UBoot Command 机制
UBoot 中通过宏定义 U_BOOT_CMD 来定义结构体变量,并且把这些同一种结构体的变 量放在一个段中,充分利用了链接器的作用。这样做的好处是,各个模块的研发人员不必去 维护一个全局的结构体数组,而且也不用知道数组中的下标就能定位。 与 UBoot 命令机制相关的定义放在/u-boot-2011.06/include/command.h,下面对关键定 义做分析。 命令结构体:

其中 Struct_Section 的定义如下:

由此可见,被 U_BOOT_CMD 定义过的结构体变量,最终会被链接器放到 u_boot_cmd 段中。链接脚本在 U-boot.lds 定义,如下:

该脚本把所有.u_boot_cmd 放在了一起。 获取所有 U_BOOT_CMD 的命令的 API:

运行 U_BOOT_CMD 定义的 CMD 的 API:

UBoot 环境变量(EnvironmentVariable)
U-Boot 通过环境变量(env)为用户提供一定程度的可配置性,这些环境变量包括启动 kernel 的参数(bootargs) 、本地 IP(ipaddr) 、网卡 MAC 地址(ethaddr) ,波特率(baudrate) 等 等 。 环 境 变 量 可 以 固 化 到 非 易 失 性 存 储 介 质 中 ( 比 如 spi flash ) ,使用 printenv/setenv/saveenv 命令来查看和修改保存。环境变量可以理解为一个键值对,即 name=value,通过 name 找到 value,再对不同的 value 做不同的设置。MBoot 把环境变量分 成 3 类:普通的环境变量,process 环境变量,kernel 环境变量。通过下面代码实现:

几个关键的 API:

查找指定的环境变量:

设置指定环境变量的值:

保存环境变量: 根据不同的硬件配置,环境变量存储的位置不一样,有 spi flash、eeprom、nand flash 等,mstar 的 android 平台一般是用 spi flash 存储环境变量。具体使用哪种存储介质,在 /MBoot/u-boot-2011.06/include/configs/Uboot_module_config.h

PM 概述
PM 即 Power Manager,为待机控制管理模块。待机时有一颗 C51 内核的 MCU 在跑,负 责待机时的电源管理,IR 和按键处理等操作。 进入 PM 的方式有两种,从 MBoot 进入 PM 和从 Supernova 进入 PM。

从 MBoot 进入 PM
1. 定义 Command

2. 注册 Process 类型 Command

3. 执行 Command Main_loop()->MstarProcess()->App_Register_Process()、 run_command(do_if_boot_to_pm)-> If_Boot_To_PM()->msAPI_Power_PowerDown_EXEC()->MDrv_PM_PowerDown(&PmPow erDownCfg) 在 If_Boot_To_PM(void)根据环境变量"factory_poweron_mode"判断是否进入 PM, 可以开机后进入工厂菜单设置,也可以通过 MBoot 的 CLI 改写环境变量的方式。

在 void msAPI_Power_PowerDown_EXEC(void)会传一个 PmWakeCfg 和 PM_PowerDownCfg 的结构体到 PM, 这些结构体定义了开机方法和中断的开关状态。 另 外需要注意,如果 PM 运行状态为 E_PM_STANDBY,则表示 PM 是运行在 Dram 上,所 以进入 PM 后 Dram 不能断电。

从 Supernova 进入 PM
当按 power off 按键或者因为 timer out 而进入待机,将从 Supernova 进入 PM: MSrv_Control_common::EnterSleepMode()->void mapi_system::PowerDown()->MDrv_PM_PowerDown() 过程类似 MBoot 进入 PM

Android 内核(Kernel)概述
内核(kernel)是操作系统最基本的部分,主要负责管理系统资源。内核提供一种硬件 抽象的方法,通过进程间通信机制(IPC)及系统调用(system call) ,应用进程可间接控制 所需的硬件资源。如下图所示: 内核(Kernel) IPC 系统调用 应用软件

Android 内核在整个 Android 架构中的层次图:

Android 内核是基于 Linux 2.6 内核的(MSD6A608 使用 2.6.35.11 版本的内核) ,并且是 Linux 2.6 内核的一个增强版本,除了修改部分 Bug 外,它提供了用于支持 Android 平台的设 备驱动。

内核代码的产生(Zip&unZip Kernel)
Kernel 代码编译连接后产生 kernel 镜像,kernel 镜像分为压缩与非压缩两种。压缩内核 镜像是把非压缩内核镜像作为数据进行压缩打包,并加上了解压缩代码。也就是说,它是一 个自解压的可执行镜像。两种内核镜像的产生过程如下图:

内核入口(Entry of kernel)
Linux 内核编译连接后生成的 ELF 映像文件是 vmlinux,从内核源代码顶层目录下的 Makefile(即顶层 Makefile)中可以找到 vmlinux 的生成规则: vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE 其中$(vmlinux-lds)是编译连接脚本,对于 ARM 平台,就是 arch/arm/kernel/vmlinux-lds 文件。 vmlinux-init 也在顶层 Makefile 中定义: vmlinux-init := $(head-y) $(init-y) head-y 在 arch/arm/Makefile 中定义: head-y:= arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o ? ifeq ($(CONFIG_MMU),) MMUEXT := -nommu endif 对于有 MMU 的处理器,MMUEXT 为空白字符串,所以 arch/arm/kernel/head.O 是第 一个连 接的文件,而这个文件是由 arch/arm/kernel/head.S 编译产生成的。 ARM Linux 启动过程分析 综合以上分析,可以得出结论,非压缩 ARM Linux 内核的入口点在 arch/arm/kernel/head.S 中。

head.S 最后包含了 head-common.S,在 head-common.S 中定义调转到 C 语言入口函数 start_kernel()。

start_kernel()函数
start_kernel()函数是内核初始化 C 语言部分的主体。 。这个函数完成系统底层基本机制, 包括处理器、存储管理系统、进程管理系统、中断机制、定时机制等的初始化工作。 start_kernel()函数的实现在 init/main.c。start_kernel()函数完成基本的初始化工作后,最后调 用了 rest_init()函数。下面来看看 rest_init()函数的实现,同样在 init/main.c 定义: static noinline void __init_refok rest_init(void) __releases(kernel_lock) { int pid; rcu_scheduler_starting(); /* * We need to spawn init first so that it obtains pid 1, however * the init task will end up wanting to create kthreads, which, if * we schedule it before we create kthreadd, will OOPS. */ kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);//创建第一个内核线 程,入口点是 kernel_init()函数 numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//创建第二个内核线 程,入口点是 kthreadd()函数 rcu_read_lock(); kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); rcu_read_unlock(); complete(&kthreadd_done); unlock_kernel();

/* * The boot idle thread must execute schedule() * at least once to get things moving: */ init_idle_bootup_task(current); preempt_enable_no_resched(); schedule(); preempt_disable(); /* Call into cpu_idle with preempt disabled */ cpu_idle(); //进入空闲状态 } 该函数创建了一个入口点 init()函数的内核线程,新创建的内核线程 pid=1,放入了调度 队列中,系统转而执行 kernel_init()函数,kernel_init()函数同样在/init/main.c 中实现,该函 数完成系统更高层次,比如驱动程序,根文件系统等等的初始化工作,其中的关键点为:

do_basic_setup()函数比较重要, 这个函数先调用 driver_init()函数完成驱动程序的初始化, 又通过 do_initcalls()函数依次调用了系统中所有的初始化函数。 接着看 kernel_init()函数, 该函数接着加载了外部程序/init 从而有了自己的用户态空间, 进而变成了一个进程,然后该进程再执行用户态的初始化程序,如创建终端,等待用户登录 等等,系统启动完成。

Android 应用系统
Android 源代码由两部分组成, Android 内核代码和 Android 系统应用部分的代码。 MStar android TV 的系统应用代码放在 android/ics 目录下,顶层目录如下:

Android/abi (abi 相关代码。ABI:applicationbinary interface,应用程序二进制接口) Android/bionic(bionic C 库) Android/bootable(启动引导相关代码) Android/build(存放系统编译规则及 generic 等基础开发配置包) Android/cts(Android 兼容性测试套件标准) Android/dalvik(dalvik JAVA 虚拟机) Android/development(应用程序开发相关) Android/device (设备相关代码) Android/docs (介绍开源的相关文档) Android/external(android 使用的一些开源的模组) Android/frameworks(核心框架——java 及 C++语言,是 Android 应用程序的框架。 ) Android/hardware(主要是硬件适配层 HAL 代码) Android/libcore(核心库相关) Android/ndk (ndk 相关代码。AndroidNDK(Android NativeDevelopment Kit)是一系列的 开发工具,允许程序开发人员在 Android 应用程序中嵌入 C/C++语言编写的非托管代码。) Android/out(编译完成后的代码输出与此目录) Android/packages(应用程序包) Android/prebuilt(x86 和 arm 架构下预编译的一些资源) Android/sdk(sdk 及模拟器) Android/system(文件系统、应用及组件——C 语言) Android/Makefile

Android/v8.log 上面三个红色标示的目录比较常用,建议用 source insight 建立好工程。

Android 应用系统启动流程
上一节提到内核在最后加载根目录下的/init 外部程序,从而离开 kernel 进入 Android 应 用系统,/init 的实现代码在 system/core/init/init.c,入口函数是 main(),这也是 Android 第一 个启动进程和第一个用户进程/init,用它来完成引导。/init 进程会读取 init.rc, init_xxx.rc(跟 制造商有关,MSD6A608 是 init_eagle.rc), 按照脚本引导。引导完毕之后 init 并不退出,继 续负责 property service 的工作。 在脚本文件 init.rc 和 init.eagle.rc 里包含 action 和 service,service 通过 fork()创建为 一个个进程,如下图所示几个重量级进程:

servicemanager
ServiceMananger 从名字上就可以看出来它是用来管理系统中的 service, 比如: tvservice、 ActivityManagerService 等。在 ServiceManager 中有两个比较重要的方法: add_service 、 check_service。 系统的 service 需要通过 add_service 把自己的信息注册到 ServiceManager 中, 当需要使用时,通过 check_service 检查该 service 是否存在。 servicemanager 的代码在目录: ics/frameworks/base/cmds/servicemanager 从它的主函数代码开始:

从 main 函数中可以看出,它主要做了三件事情: 1.打开/dev/binder 设备,并在内存中映射 128K 的空间。 2.通知 Binder 设备,把自己变成 context_manager 3.进入循环,不停的去读 Binder 设备,看是否有对 service 的请求,如果有的话,就去 调用 svcmgr_handler 函数回调处理请求。 再来看看 ServiceManager 中是怎么样去注册服务的。当有对 service 的请求时,调用回 调函数 svcmgr_handler:

在该回调函数中会判断 Service 有什么需要,如果是请求注册 service,那么就执行:

这样在 ServiceManager 中就完成了服务的注册和查找。来看下 ServiceManager 的功能 图: Svcmgr handler

svclist ServiceManager Binder Looper

Binder Open

Binder 块设备

system server
system server 用于启动各种系统服务线程,大部分系统服务都在该进程中运行,常见的 比 如 WindowManagerServer ( Wms )、 ActivityManagerSystemService ( AmS )、 PackageManagerServer(PmS)等,这些系统服务都是以一个线程的方式存在于 SystemServer 进程中。 system server 的代码在目录: \ics\frameworks\base\services\java\com\android\server

从 init2()函数开始:

该函数首先创建了一个 ServerThread 对象,该对象是一个线程,然后直接运行该线程, 从 ServerThread 的 run()方法内部开始真正启动各种服务线程。

Android IR 按键处理流程
按键分为两种,虚拟按键和物理按键。IR 按键属于物理按键的范畴,IR 要作为输入设 备,必须经过 kernel 驱动层注册为输入设别,这里主要是调用到 kernel\drivers\input\input.c 里面的 input_register_device 函数去实现,如下: @kernel\drivers\mstar\ir\mdrv_ir.c

@kernel\drivers\input\input.c

Kernel 负 责 处 理 按 键 , 然 后 通 过 设 备 文 件 的 方 式 提 供 给 framework 。 frameworks/base/services/input 定义各功能模块实现按键处理: EventHub.cpp:实现对设备进行扫描并且判断是哪种设备; InputReader.cpp:对原始数据进行读取; InputDispatcher.cpp:实现数据的派发; InputManager.cpp : 按 键 事 件 处 理 的 核 心 , 通 过 创 建 两 个 线 程 InputReader 和 InputDispatcher,实现管理功能; 如果有输入法存在,则按键交给 pinyinIME.java 的 onKeyDown 处理;否则会判断是否是 system key , 如 果 是 system key , 交 由 PhoneWindwoManager.java 和 MstarTvServiceServerImpl.java 处理,如果不是 system key,交由具体 activity 处理。特别说明 一下,hotkey 的处理在 MstarTvServiceServerImpl.java 的 onKeyDown 函数。

Android Framework 概述
这里顺便讲下 android 开发的类型,有 3 种: 1. 系统移植。 系统移植就是将 android 移植到其他的硬件平台, 需要掌握 Linux 内核层 和系统运行库层。 2. 系统开发。系统开发就是完善 android 系统本身,需要掌握系统运行库层和 framework 层,一般是在本地编写 C/C++代码,然后通过 JNI 向上层提供调用接口。 3. 应用开发。应用开发就是开发应用程序,即利用 framework 层提供的 API 实现应用 的开发。 Framework 为应用开发提供 API,如下框图:

Android 常用的一些 make 命令
1.make -jXX
XX 表示数字,这个命令将编译 Android 系统并生成镜像,XX 表示可以使用到的 CPU 核数,这在配置好的电脑上特别有用,公司的 16 核 ubuntu 服务器执行 make -j16 只要不到 20 分钟,而双核 PC 上需要 4 个小时!

2.make snod

这条命令将重新生成镜像,消耗的时间很少,类似 WinCE 的 makeimg 过程,如果你修 改了一些数据文件(如音乐、视频)等文件时比较有用。

3.make cts
这条命令将编译 CTS 套机,编译出的结果放在 out 目录对应版的 data/app 目录下面。 CTS 测试时有用

4.make installclean
这条命令清除 out 目录下对应板文件夹中的内容,也就是相当于 make clean,通常如果 改变了一些数据文件(如去掉) ,最好执行以下 make installclean,否则残留在 out 目录下的 还会被打包进去。

5.mm/mm -B
开发调试中最喜欢这条命令了, 在修改了的目录下执行这条命令, 就能智能地进行编译, 输出的文件在通过 adb 推送到目标机,可以很方便地调试。

6.make sdk
这条命令可以生成可发布的 SDK,目前还没试过,据说需要 JDK1.5

7.make Setting
可以单独编译 setting 这个模块,目前还没试,猜想是不是可以单独编译 Email、Music 这些模块

8.make bootimage
用这条命令可以生成 boot.img,这个镜像文件中包含 Linux Kernel,Ram disk,生成的 boot.img 只能通过 fastboot 进行烧写,这在只修改了 Linux 内核的时候有用

Supernova 概述
Supernova 设计语言为 C++,位于 Android 程序框架的系统运行库层。Supernova 通过 Android 应用程序框架为 App 提供服务。Supernova 提供 TV 方面的 Service,比如底层的硬件 的操作(Tuner,Backlight) ,以及一些 TV 常用的业务。这里可以思考一个问题:为什么选 用 C/C++作为 Supernova 的开发语言?C/C++跟硬件具有更好的交互性! java 语言需要通过 jvm 解释执行,而 C/C++编译后的直接是 cpu 指令集。系统运行 java 程序在同等条件下,差不多 慢 C++程序一倍。但如果 java 通过 JNI 调用.so 文件内部函数就另当别论了。

Supernova 启动流程
在 Android Platform Flow 里提到, 系统的 1 号进程/init 完成系统应用层的引导, 加载 init.rc 和 init.eagle.rc。在 init.eagle.rc 启动了 Supernova 的 tv service,代码如下: ics\device\mstar\mstareagle\init.eagle.rc

service 结构如下: service <name><pathname> [ <argument> ]* <option> <option> 首先启动了/system/bin/logwrapper service 进程,然后通过/system/bin/logwrapper 进程 创建/lib/ld-linux.so.3 子进程,代码如下: Ics\system\core\logwrapper\logwrapper.c int main(int argc, char* argv[]) { pid = fork(); if (pid < 0) { fatal("Failed to fork\n"); } else if (pid == 0) { child_ptty = open(child_devname, O_RDWR); if (child_ptty < 0) { fatal("Problem with child ptty\n"); } // redirect stdout and stderr close(parent_ptty); dup2(child_ptty, 1); dup2(child_ptty, 2); close(child_ptty);

child(argc - 1, &argv[1]); } else { // switch user and group to "log" // this may fail if we are not root, // but in that case switching user/group is unnecessary 在 child()函数调用 execvp () 函数, execvp () 函数会找到/lib/ld-linux.so.3 文件并执行, 然 后 把 /lib/ld-linux.so.3 --library-path /applications/bin:/vendor/lib:/vendor/lib/utopia:/config /applications/bin/tvos 参数传递给该欲执行的文件。

接着进入 Supernova 的 main 函数入口,在 ics\projects\tvos\main\Main.cpp int main(int argc, char **argv)//Supernova 主进程入口函数 { //Linux 下的进程资源限制描述,这里设置 core dump file 文件的最大值,core dump file 是在一个程序崩溃时,在指定目录下生成一个 core 文件,主要是用来调试的。 struct rlimit limit; int resource; resource = RLIMIT_CORE; limit.rlim_cur = RLIM_INFINITY; limit.rlim_max = RLIM_INFINITY; setrlimit(resource, &limit); …… //详见说明 1 MSrv_Control::Build(); …… //Initializaion of MUF System,详见说明 2 MSystem::Initialize(argc, argv, false); MSystem::InitializeDFBLayer(); …… //创建 Service 实例,并保存在 m_pMSrvList[E_MSRV_MAX]数组 MSrv_Control::GetInstance()->Initialize(); …… //创建 ProcessState 的实例,此实例负责操作 Binder 的一些动作 ProcessState::self();

//创建 Android Service 线程, 并加入到线程池, Android Service 是后台运行的线程, 负责 Service IPC 通信的工作 int ret = pthread_create(&ASthread_id, &ASthr_attr, AndroidServiceInitThread, NULL);//1 创建一个子线程,入口函数是 AndroidServiceInitThread() if (ret) { printf("tvos main, create android service init thread failed\n"); ASSERT(0); } //详见说明 3 StartMsrv(); #if( AUTO_TEST == 1) printf("\033[1;31m sn init-chk#5 \033[0m\n"); #endif ret = pthread_join(ASthread_id, NULL);//1 以阻塞方式等待 Android Service 线程结束 ProcessState::self()->startThreadPool(); //创建管理环境变量类的实例 IEnvManager* pEnvMan = IEnvManager::Instance(); if (pEnvMan) { pEnvMan->SetEnv_Protect("dc_poweroff", "0"); pEnvMan->SaveEnv(); } …… } 说明 1: MSrv_Control::Build(); 主要是初始化硬件配置: ? 创建一个 mapi_system 类的实例,mapi_system 负责:

? ? ? ?

?

加载外部配置文件,主要是 INI 文件,包括 EDID,HDCP,PQ,Volume Curve 等, 详见后面的说明; 加载客制化配置:MSrv_Control_common::LoadCustomizeSystemCfg(); 初始化 I2C,GPIO,Keypad,这里结合 SN_board_XXX.h 里面定义的配置; 通过 mapi_interface::Build()创建信号处理相关类的实例, 如 vdec, video out, demodulator 等等, 这些实例保存在静态成员数组 m_pMapiObj[]里面, 后续会经常 用到这里面实例化的对象; pPcb = new (std::nothrow) device_pcb:定义 device_pcb 类的实例,主要负责 Tuner,Demod,AudioAmp,DTMB Demod 的选择与初始化;

说明 2: MUF:

PS:Android 机 MApplet 部分没有使用到。 说明 3:

Supernova 代码
INI 文件 What is the INI file?
在早期的 windows 桌面系统中主要是用 INI 文件作为系统的配置文件, 从 win95 以后开 始转向使用注册表, 但是还有很多系统配置是使用 INI 文件的。 其实 INI 文件就是简单的 text 文件,只不过这种 txt 文件要遵循一定的 INI 文件格式。现在的 WINCE 系统上也常常用 INI 文件作为配置文件, 这次研究 INI 文件的目的就是为了我的 GPS 定位系统客户端写个系统配 置文件。 “.INI ”就是英文“initialization”的头三个字母的缩写;当然 INI file 的后缀名也不 一定是".ini"也可以是".cfg",".conf ”或者是".txt"。

Why need the INI file?
如果我们程序没有任何配置文件时, 这样的程序对外是全封闭的, 一旦程序需要修改一 些参数必须要修改程序代码本身并重新编译,这样很不好,所以要用配置文件,让程序出厂 后还能根据需要进行必要的配置;配置文件有很多如 INI 配置文件,XML 配置文件,还有就 是可以使用系统注册表等。

INI File Format
INI 文件的格式很简单,最基本的三个要素是:Keys(properties), Sections, Comments Keys (properties):每个 key 对应一个 name 和一个 value。 Sections:把 Key 分组就成为一个 Section。 Comments:注释。

关于 INI File 的更多信息,可参考维基百科:http://en.wikipedia.org/wiki/INI_file

INI File Load&Parse
@/Supernova/projects/msrv/common/src/MSrv_Control_TV.cpp ? BOOL MSrv_Control_TV::Build() @/Supernova/projects/msrv/common/src/MSrv_Control_common.cpp ? void MSrv_Control_common::PreBuildSystem() @/Supernova/projects/systeminfo/src/SystemInfo.cpp ? BOOL SystemInfo::SetSystemInfo(void) 加载 INI file @/Supernova/projects/systeminfo/src/SystemInfo.cpp ? void SystemInfo::LoadIniFile(void) 在 LoadIniFile(void)加载"/config/sys.ini",根据 sys.ini 的[model] Section,获 取 gModelName 这个 name 对应的 value 为 /config/model/Customer_1.ini, 在通过 Customer_1.ini 的 Section 和 name,去获取其他配置,如[panel],[ColorMatrix], [PcModeTable],[HDMI_EDID_1],[VGA_EDID],[KEYPAD],[VolumeCurve]等等。

APP 概述
TvApp 文件结构 TvApp 有四个工程,对应四个 apk 文件:

比较常修改到的是 tvsettinghotkey.apk 和 tvsettingui.apk,其中 tvsettingui.apk 稍微复杂,下 面做个基本剖析: TVapi.jar:封装了 aidl 的实现,后面再详细介绍; framework.jar:framework 包,源码在 ics/frameworks 里面。比如: framework.jar –> com.tvos.common -> ThreeDimensionManager.class 文件 其实现在 ics/frameworks/base/tv/java/com/tvos/common/ThreeDimensionManager.java framework 包的类,可以直接 import 包名,然后使用:

tvsettingui/src 下文件分析:
com.mstar.tvframework:定义公共基类,实现全局方法,如Channel list键的实现,在基 类的onKeyDown方法实现; com.mstar.tvsettingservice:定义接口; com.mstar.tvsrvfunc:调用framework,实现接口; mstar.tvsetting.ui:UI实现;

ManiFest 文件
AndroidManifest.xml 可以理解为 android 的一个注册表文件,在这个文件中,我们可以声明 权限,应用组件和其他配置工作等等。系统启动的时候,由 PackageManagerService 读取和 管 理 Manifest 信 息 ( PackageManagerService 是 Framework 级 的 Service , 源 代 码 在 ics/frameworks/base/cmds/am/src/com/android/commands/pm/PackageManagerService.java) 。 文件结构:

下面是 tvsettinghotkey.apk 对应的 xml 文件:

Activity 的启动
Activity 的启动只有两种方式,一种是用户点击应用程序图标,Launcher 会启动应用程序 的主 Activity,主 Activity 即为在 AndroidManifest.xml 中定义为 LAUNCHER category 的

Activity;另一种是在应用程序起来后通过调用 startActivity(Intent intent)接口启动 新的 Activity,其中 intent 指定启动的 Activity action name,这个 action name 同样在 AndroidManifest.xml 指定,并由应用框架层的 ActivityManagerService 负责维护。第二 种方法可以启动不同 apk 的 Activity。

Activity 的生命周期
主要有两种情况 打开应用时:先后执行了 onCreate()->onStart()->onResume 三个方法 应用程序结束时:先后调用 onPause()->onStop()->onDestory()三个方法 详细介绍一下这几个方法中系统在做什么以及我们应该做什么: onCreate: 在这里创建界面,做一些数据的初始化工作。 onStart: 到这一步变成用户可见不可交互的。 onResume: 变成和用户可交互的,(在 activity 栈系统通过栈的方式管理这些个 Activity 的最上 面,运行完弹出栈,则回到上一个 Activity) 。 onPause: 到这一步是可见但不可交互的, 系统会停止动画等消耗 CPU 的事情从上文的描述已经知 道,应该在这里保存你的一些数据,因为这个时候你的程序的优先级降低,有可能被系统收 回。 在这里保存的数据, 应该在 onResume 里读出来, 注意: 这个方法里做的事情时间要短, 因为下一个 activity 不会等到这个方法完成才启动。 onstop: 变得不可见,被下一个 activity 覆盖了 onDestroy: 这是 activity 被干掉前最后一个被调用方法了, 可能是外面类调用 finish 方法或者是 系统为了节省空间将它暂时性的干掉,可以用 isFinishing() 来判断它,如果你有一个 Progress Dialog 在线程中转动,请在 onDestroy 里把他 cancel 掉,不然等线程结束的时 候,调用 Dialog 的 cancel 方法会抛异常的。

通信使者 Intent
Android 应用的各项组件之间的通讯,通过 Intent(意图)进行交流,这个是 Android 架构的松耦合的精髓部分,大大提高了组件的复用性。当扔出一个 Intent,系统通过什么 来判断谁来接受这个 Intent 呢?有两种方法: 第一种直接指定某个类为接受者,如下: Intent intent = new intent(this. MyActivity.class);

Intent.getExtras().putString(“id”, “1”); startActivity(intent); 第二种是根据 AndroidManiFest.xml 中的 intent-filter 配置。

Handler 机制的原理
Android 提供了 Handler 和 Looper 来满足线程间的通信。 Handler 实行先进先出的原则。 Looper 类用来管理特定线程内对象之间的消息交换(Message Exchange) 。 Looper:一个线程可以产生一个 Looper 对象,由它来管理此线程里的 Message Queue (消息队列) 。 Handler: 可以构造 Handler 对象来与 Looper 沟通, 以便 push 新消息到 Message Queue 里;或者接收 Looper 从 Message Queue 取出所送来的消息。 Message Queue:用来存放线程放入的消息。 线程: UI thread 通常就是 main thread, 而 android 启动程序时会替他建立一个 Message Queue。

定时 Timer 的使用
很多时候,我们需要使用定时器,以便每隔一段时间做相应的事情,定时器其中一种实现: 1. protected Timer timer; protected TimerTask timerTask; 2. timer = new Timer(); timerTask = getTimerTask(); timer.schedule(timerTask, 10, 1000); 3. private TimerTask getTimerTask() { timerTask = new TimerTask() { @Override public void run() { myHandler.sendEmptyMessage(msg_key); } }; return timerTask; }

模块交互 TVOS 万能接口
万能接口可以实现从 App 层传字符串到 Supernova 层, 中间通过 JNI 调用和 Binder 通信 实现。App 发送数据,Supernova 接收数据,并做相应动作。 通用格式: App 端发送:
shortsourcestatus[] = null; try// michael_ktc { Log.i("SourceInfoActivity", "setTvosCommonCommand"); sourcestatus = TvManager .setTvosCommonCommand("SetAc3Info"); } catch (TvCommonException e) { // TODO Auto-generated catch block e.printStackTrace(); }

Supernova 端接收: \Supernova\projects\msrv\dvb\base\src\MSrv_Control_DVB.cpp

发送端跟接收端字符串要相同,才能匹配。

JNI
JNI 是 Java Native Interface 的缩写, 中文为 JAVA 本地调用, 即该方法允许 Java 代码调用 其他语言写的代码。 通用书写步骤: 1. 编写 java 程序,声明 native 方法,并加载 so 本地库; 2. 编译上述 java 程序; 3. 生成头文件,声明本地方法;

4. 编写跟头文件同名的本地方法; 5. 生成动态库,如*.so 文件; 6. 运行程序。

举例:万能接口实现的例子 1. 声明 native 本地方法 @ics\frameworks\base\tv\java\com\tvos\common\TvManager.java

2. 加载 so 库

3. 编译后的 tvos_tvmanager_jni.so 在/android/ics/frameworks/base/tv/jni,整

个 JNI 调用过程完成。 再举一个 Android 的拼音输入法服务进程的例子: 1. 声明 native 本地方法 @ics/packages/inputmethods/pinyinime/src/com/android/inputmethod/pinyin/pin yinDecoderService.java

2. 加载*.so 库

3. So 库的实现代码 @ics/packages/inputmethods/pinyinime/jni/android/com_android_inputmetho d_pinyin_PinyinDecoderService.cpp

Binder
在 Android 系统中,是以进程为单位分配和管理资源的。处于保护机制,一个进程不能 直接访问另一个进程的资源,但为了多个进程完成同一任务,要求进程间能够互相通信。 Android 同时为 Java 环境和 C/C++环境提供了 Binder 机制。Binder 通信是基于 server 与 client 的,Binder 通信是同步而不是异步的。 Binder 机制的组成: 1. service manager:管理 Android 系统中的所有服务; 2. server:Binder 的服务器端; 3. client:Binder 的客户端; 4. 服务代理:服务代理指在客户端生成的服务端代理(proxy) 。服务代理具有 Server 的功能,这样客户端可以通过服务代理去访问服务端的方法。 Binder 的工作流程: 1. 客户端首先获得服务器端的代理对象; 2. 客户端通过调用服务器代理对象的方式向服务器端发送请求; 3. 代理对象将用户请求通过 Binder 驱动发送到服务器进程; 4. 服务器进程处理用户请求,并通过 Binder 驱动返回处理结果给客户端的服务器代 理对象; 5. 客户端收到服务器端的返回结果。 Binder 的几个缩写: p 是 proxy 即代理的意思,Bp 就是 BinderProxy Bn 是 Binder Native 的含义,是和 Bp 相对的,Bp 的 p 是 proxy 代理的意思,那么另 一端一定有一个和代理打交道的东西,这个就是 Bn 下来结合代码看看 Binder 的实现,为了实现进程间的双向通信,Service 跟 Client 的 角色可以灵活变化,这就产生了两种情况: 1. Supernova(Service)<->Framework(Client): App 通过调用 Framework 提供的接口, 然后 Framework 通过 Binder 通信,向 Supernova 请求服务。 a. Supernova 提供的服务即 Binder 的服务器端,服务器端要实现 BnXXX。以便 和客户端的 BpXXX,即客户端的代理对象打交道,首先说说 Supernova 提供哪 些服务?Supernova 的服务源代码定义在 Supernova/projects/tvos, 命名规则 为 XXXservice , 这 些 服 务 对 应 的 BnXXX 定 义 在 Supernova/projects/tvos/main/main.cpp,如下:

这些 BnXXX 对象的初始化在 void * AndroidServiceInitThread(void * pData)函 数,BnXXX 中主要是定义并实现了 onTransact()函数,其作用就是,当在客户端的 代理对象调用了 transact 函数后,这边的 onTransact()函数就会被调用。 然后要向 Service Manager 注册服务, 注册服务在每个 BnXXX 对象的 instantiate() 方法实现,如下:

服务的具体实现在 Msrv_XXX.cpp 里。 BnXXX 的 实 现 , 先 定 义 一 个 继 承 IInterface 的 类 ITimerManager , 再 由 BnTimerManager 继承 ITimerManager 类, 而 TimerManagerService::Client 又继承 于 BnTimerManager , 所 以 ITimerManager 定 义 的 虚 函 数 都 在 TimerManagerService::Client 实现。 至此,服务器端的 Binder 已建立好了。 b. 客户端的 Binder 实现。 首 先 通 过 AIDL 定 义 接 口 , 如 ITvServiceServerTimer , 生 成 ITvServiceServerTimer.Stub 类,再在 TimerBinder 实现方法。 举例说明具体实现过程,如菜单调节 sleep time 为例: ? App 调用 AIDL 提供的接口: Tvapp/tv2d_ui/src/mstar/tvsetting/ui/TimeViewHolder.java

AIDL 的实现在 framework/base/policy/src/com/mstar/tv/service/TimerBinder.java

TimerManager.java

Supernova 作为服务器端的接收: ITimerManager.cpp

2. Framework(Service) --- Supernova(Client):当 Supernova 感知到状态的变化, 需要通知 app 做相应的 UI 显示动作,需要发出一个消息给 Framework,Framework 再通过消息机制把通知给到 app。这里举一个例子,无信号待机时 app 弹出一个倒 计时菜单,这里当有信号接入时,倒计时菜单要隐藏掉。 a. Supernova Client 代码的实现(BpXXX): 首先是实现 IInterface 类, IInterface 类是实现 Binder 接口的基础类。 Supernova/projects/tvos/include/timermanager/ITimerManagerClient.h

b. 调用代理对象的方法(BpXXX 的方法) 。代理对象通过 transact 发送数据出去

Supernova/projects/tvos/timermanager/libtimermanager/ITimerManagerCl ient.cpp

c. 服务器端的实现。 实现 IInterface 基础类并重写 onTransact 方法, onTransact 的 实 现 已 被 MStar 打 包 , 位 置 在 ics\frameworks\base\tv\libtv\libtimermanager.so d. 完整的调用过程是: ? 检测到有信号发送 EV_DESTROY_COUNTDOWN 消息 Supernova/projects/msrv/common/src/MSrv_Control_TV.cpp BOOL MSrv_Control_TV::noSignalCheck_Handler(void) { ?? vtimer->PostEvent(NULL, EV_DESTROY_COUNTDOWN, NULL, NULL); ?? } ? 通过 callback 函数调用 TimerManager 的 PostEvent 函数 Supernova/projects/tvos/common/muf/MSrv.cpp

Supernova/projects/tvos/timermanager/libtimermanagerservice/TimerManage rService.cpp

? 调用 BpXXX 代理对象的方法,方法里通过 Binder 驱动传递数据 Supernova/projects/tvos/timermanager/libtimermanager/ITimerManagerClien t.cpp

? 服务器端通过 onTransact 实现跟代理端的 transact 匹配 Framework/base/include/tv/timermanager/ITimerManagerService.h

Framework/base/tv/java/com/tvos/common/TimerManager.java

? 发送消息 Framework/base/tv/java/com/tvos/common/TimerManager.java

? 发送广播 Framework/base/policy/src/com/mstar/tv/service/TimerBinder.java

Database 操作
通过数据库,也可以实现应用层跟 C++层(Supernova)的通信,这是一种静态的方式, 即一方通过写数据库的数据,另一方读数据库的数据,从而实现共享,下面实现一个完整的 例子,Supernova 获取当前信号源音频是否有 AC3,并共享该信息给 App 层。 1. 通过 sqliteadmin 工具,在 SystemSetting 增加名为 ac3flag 的项。 数据库放在 Supernova\projects\board\INI\misc\user_setting.db,增加过程如 下:

2. Supernova 代码增加 ac3flag 数据,并对数据操作 @Supernova\projects\tvos\include\databasemanager\DatabaseManagerType.h

@Supernova\projects\msrv\common\inc\MSrv_System_Database.h

@Supernova\projects\msrv\common\src\MSrv_System_Database.cpp

@Supernova\projects\msrv\dvb\base\src\MSrv_Control_DVB.cpp

3. App 层增加 ac3flag 数据,并对数据操作 @DataBaseDesk.java

@DataBaseDeskImpl.java

@SourceInfoActivity.java

AIDL
AIDL(AndRoid 接口描述语言)是一个 IDL 语言, 它可以自动产生处理 Binder(IPC)的源代码, 可以使在一个 AndRoid 设备上运行的两个进程使用内部通信进程进行交 互。如果你需要在 一个进程中(例如:在一个 Activity 中)访问另一个进程中(例如:一个 Service)某个对象的方法, 你就可以使用 AIDL 来生成这样的代码来伪装传递各种参数。 实现步骤: 1 创建你的 AIDL 文件。如 TvApp\tvapi\src\com\mstar\tv\service\aidl 里的 aidl 文件。 2 使用 parcelables 进行参数的值传递。 如 TvApp\tvapi\src\com\mstar\tv\service\aidl 里的 java 文件。 3 实 现 接 口 , 生 成 的 接 口 包 括 一 个 名 为 Stub 的 内 部 抽 象 类 。 如 TvApp\tvapi\src\com\mstar\tv\service\interfaces 里的 java 文件。 4 向客户端公开接口。如 TvApp\tvapi\src\com\mstar\tv\service\skin 里的 java 文件。 实现一个 AIDL 接口的 IPC 方法: 1.声明一个接口类型的变量。 2.实现 ServiceConnection。 3.调用 ApplicationContext.bindService(),并在 ServiceConnection 实现中进行传递. 4.在 ServiceConnection.onServiceConnected()实现中,你会接收一个 IBinder 实例(被调用 的 Service). 调 用 YourInterfaceName.Stub.asInterface((IBinder)service) 将 参 数 转 换 为 YourInterface 类型。 5.调用接口中定义的方法。 6.断开连接,调用接口实例中的 ApplicationContext.unbindService()

实践篇
编译、升级
编译请参考“6A608&6A801 Build Code Flow.docx” ; ICS 升级文件一览表: Filename 'auto_update.txt'. Filename 'auto_update_038B.txt'. Filename 'scripts/writecis.txt'. Filename 'NANDINFO.nni'. Filename 'PAIRPAGEMAP_v2.ppm'. Filename 'scripts/set_partition'. Filename 'scripts/[[recovery'. Filename 'recovery.img'. ->ICS 编译的文件 Filename 'scripts/[[boot'. Filename 'boot.img'. ->ICS 编译的文件, boot.img 包含两部分, 分别为 kernel 和 ramdisk Filename 'scripts/[[system'. Filename 'system.img'. ->ICS 编译的文件 Filename 'scripts/[[userdata'. Filename 'userdata.img'. ->ICS 编译的文件 Filename 'scripts/[[cache'. Filename 'cache.img'. Filename 'scripts/[[tvservice_038B'. Filename 'tvservice_038B.img'. Filename 'scripts/[[tvcustomer_038B'. Filename 'tvcustomer_038B.img'. Filename 'scripts/[[tvdatabase_038B'. Filename 'tvdatabase_038B.img'. Filename 'scripts/set_config'. ->Supernova 编译的文件 ->Supernova 编译的文件 ->Supernova 编译的文件

调试 MBoot
使用 SecureCRT.exe 等串口调试工具,开机启动时按”ENTER”进入 MBoot 的 CLI 模式,输 入”help”,得到命令列表:

关注环境变量命令,以修改开机启动方式为例: 打印环境变量:printenv

设置环境变量:

保存环境变量:

当然,还有很多有用的命令,需要我们在实际解问题过程中积累、开发,网上有很多类似文 档。

Kernel
查看打印: 进入 Mboot 命令 Setenv bootargs=console=ttyS0,115200 androidboot.console=ttyS0 root=/dev/ram rw rootwait init=/init LX_MEM=0x15500000 EMAC_MEM=0x100000 DRAM_LEN=0x20000000 LX_MEM2=0x0A2B00000,0x0D400000 LX_MEM3=0x0AFF00000,0x00 quiet CORE_DUMP_PATH=/data/core_dump.%%p.gz BOOTLOGO_IN_MBOOT ENV=SERIAL 将其中的quiet 字串去掉 Sa Reset 就会看到kernel 的打印

Supernova
查看打印: 在 SecureCRT 命令模式下输入 logcat,就可以看 Android 系统运行过程中的打印信息。 (ctrl+z 退出) 。如果只想看 Supernova 的打印信息,可以输入命令:logcat | busybox grep /lib/ld-linux.so.3。 (道符“|”将两个命令隔开,管道符左边命令的输出就会作为管道符右边命 令的输入) 寄存器调试: 1、 串口调试打开方法:按 SOURCE + 2580 进入工厂菜单,在“other options”下, 将光标调至"uart debug" 下,单击一下 OK 键,然后就会如下打印信息:uart1 debug test loopuart1 debug test loop ,这样可以用串口读写寄存器了。 (用 MSTVtool.exe,芯片仍选 MSB2X10) 2、 在串口可以读写的情况下如何打开 I2C,可以用串口读写寄存器,将 0x100B_24 bit0 置高,则可以用 I2C 读写寄存器了。 PQ 在线升级:

通过 USB 升级 PQ 参数,方法: a、 可以通过 USB update 的文件包括:DLC, Gamma table, ColorMatrix, PQ binary files b、 建文件夹命名为"pqbin",然后将生成的 Main.bin, Main_Text.bin 等文件拷到文件夹中。 c、 将"pqbin"COBY 到 U 盘中,再将 U 盘接到要升级的板上,再按工厂菜单,在“other options” 下, 将光标移至"pq update"栏, 单击一下 OK 键, 如果显示 “PQ File? update” = “OK”, 说明升级成功。 d、 重新启动电视机,进工厂菜单,看下“Main PQ version”,“sub PQ version”与你用 的 PQ MAP 中的 EndOfSheet 栏中的 MapGen Ver.后面的版本是否一至,如果一至那 就完全确认你写的 PQ 对了。

ICS&APK
通过 logcat 查看打印: 使用 SecureCRT.exe 的串口功能查看打印, 需要在命令行下输入命令 logcat。 常见的问题 是提示 apk 被强制关闭(force close) ,这时可以通过 logcat 查看并定位到代码中,如下是一 个例子:

通过 adb 调试 apk: db 的全称为 Android Debug Bridge, 就是起到调试桥的作用。 通过 adb 我们可以在 Eclipse 中通过 DDMS 来调试 Android 程序,说白了就是 debug 工具。

Source Insight 工程的建立
整个 android 源码文件较多,且包括*.mk, *.ini, *.lds, *.cfg, *.S, *.inl 等文件,如何建立 source insight 工程,需要特别说明一下(Source Insight 工程文件最好放在本地磁盘上): Ics:建议整个 ics 的源码都包进来,不要进行同步(ics 源码文件多达数万个,同步时间 非常久) ,同时,source insight 做如下设置:

Framework: ics 常改到 framework 代码, 可以建立一个独立的 si 工程, 并进行文件同步, 并做如下设置:

MBoot: 整个 MBoot 源码建立一个工程, 但不要包含 u-boot-1.1.6 文件夹, source insight 做如下设置:

Kernel:整个 kernel 源码建一个工程,source insight 设置:

Supernova:整个 Supernova 源码建立一个 si 工程,并同步文件,si 设置:

OTA 升级方法
详细的说明文档见“How to Build OTA Package in ICS.doc” ,精简下来只有五步:

具体方式如下: (1) 首先指定版本号,见 ics\device\mstar\mstara3\BoardConfig.mk 中的配置: BUILD_NUMBER := V1.0.5.0 按正常流程编译完 ICS,生成了 out\target\product\mstara3 整包文件; (2) 接着,执行 make otapackage。完成后,会在 out\target\product\mstara3 下 生成完整的 OTA 升级包,例如 full_mstara3-ota-V1.0.5.0.zip。 (3) 制 作 出 完 整 升 级 包 后 , 在

ics\out\target\product\mstara3\obj\PACKAGING\target_files_intermediates\ 目 录下会生成 target file 包,例如 full_mstara3-target_files-V1.0.5.0.zip。将其 备份出来,放到其它位置,例如/tmp 目录。这个包制作增量升级包时需要 用到。 接下来的步骤是制作增量升级包需要的:

(4) 修改 ICS 和版本号,假设改为 V1.0.5.1,并更新重新 make 以及 make otapackage , 此 时 会 生 成 新 的 完 整 升 级 包 和 target file 包 , 即 : full_mstara3-ota-V1.0.5.1.zip 和 full_mstara3-target_files-V1.0.5.1.zip (5) 新执行 ICS 的编译流程: source env.sh lunch make incrementalotapackage 说明: 执行 make incrementalotapackage, 会自动判断是 mlc 还是 emmc flash, 两种不同类型的 flash 生成的升级脚本有所不同。

显示系统信息 cat /proc 说明 查看内存使用情况 cat /proc/meminfo

cat /proc/meminfo 查看内存使用情况,会发现 MemFree 值很小。这主要是因为,在 linux 中 有这么一种思想,内存不用白不用,因此它尽可能 cache 和 buffer 一些数据,以方便下次使 用。但实际上这些内存也是可以立即拿来使用的。所以空闲内存=free+buffers+cached

查看 CPU 使用情况 cat /proc/cpuinfo

查看中断 cat /proc/interrupts

查看内核版本 cat /proc/version

查看分区表 cat /proc/mtd

然后可以把分区表的数据存储到 USB 上,如:cat /dev/mtd/mtd3 > /mnt/usb/sda1/mtd3.img 即可备份 boot.img

查看分区使用情况 df 命令
Df 命令用来检查文件系统的磁盘空间使用情况,举例:

频道数据保存在/tvdatabase 分区,通过 df 命令确认分区使用情况:

如上,/tvdatabase 分区的 Free 空间还有很多,说明够用。下面是 DTV 的频道 数据,作为一个.bin 放在/tvdatabase/Database 下面

常用配置 Scan /Supernova/projects/msrv/atv_customer/customer_XXX/middl eware/src/MW_ATV_Scan_Customer.cpp

/Supernova/msrv/common/middleware/src/MW_DTV_Scan.cp p PQ /Supernova/mstarsdk/src/pq/lib/drvPQ.c

PQ 表在 excel 表编译出来后,存放到 Supernova@Supernova\projects\board\eagle\PQ_BIN\NONE_DEFAULT_PQ_BIN\XXX 对应的文 件夹

Database /Supernova/projects/msrv/dvb/base/src/MSrv_System_Databa se_DVB.cpp IR
See “How to customize IR.docx” On line burn *.ko: shell@android:/ $ su //root shell@android:/ # mount -o remount rw /system/ //以 rw 方式挂载/system 目录 shell@android:/ # cd /system/lib/modules/ shell@android:/system/lib/modules # busybox cp /mnt/usb/sda1/mdrv-ir.ko . //调用 busybox 工具的 cp 命令 shell@android:/system/lib/modules # sync //将内存缓冲区内的数据写入磁盘 shell@android:/system/lib/modules # reboot //重启系统 sync 功能说明:将内存缓冲区内的数据写入磁盘。 语 法:sync [--help][--version] 补充说明:在 Linux 系统中,当数据需要存入磁盘时,通常会先放到缓冲区内,等到适当的 时刻再写入磁盘,如此可提高系统的执行效率。

Backlight GPIO
@MBoot/sboot/inc/eagle/board/BD_MST038B_10AHT_EAGLE.h ? #define BALL_E7_IS_GPIO GPIO_OUT_LOW

?

#define mdrv_gpio_set_high( BALL_E7 ) #define mdrv_gpio_set_low( BALL_E7 ) 在 Supernova 也有背光的配置。

Panel_Backlight_VCC_ON() Panel_Backlight_VCC_OFF()

Tuner&Demodulator
@Supernova/projects/devices/Eagle_device.mk ? ifeq ($(BOARD),038B) FRONTEND_HEADER = device_pcb_eagle_038B.h DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/demodulator/device_demodulator_eagle.c pp DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/dish/device_dish_dummy.cpp DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/tuner/device_tuner_mxl601.cpp DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/audioamp/device_audio_amp_MSH9000.cpp DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/pcb/device_pcb_eagle_038B.cpp DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/demodulator/device_demodulator_extend_ atbm885x.cpp #DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/demodulator/device_demodulator_extend_ example.cpp #DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/demodulator/device_demodulator_extend2 _example.cpp ifeq ($(MHL_ENABLE), 1) DEVICE_SRC += $(PHOTOSPHERE_ROOT)/projects/devices/mhl/device_mhl_MSG1200.cpp endif endif

Logo&Music
@Supernova/projects/board/INI/misc/boot1.jpg 显示在 MBoot @MBoot/Sboot/u-boot-1.1.6/drivers/display_logo/Display_main.c

? 具体显示过程,参见 MBoot 宏定义 ENABLE_DISPLAY_LOGO @Supernova/projects/board/INI/misc/boot0.mp3 ? 具体播放过程,参见 MBoot 宏定义 ENABLE_POWER_MUSIC

VD Sensibility
@Supernova/projects/board/INI/VD/VD.ini ATV 搜台漏台,如果是因为 VD 没有 lock 住导致的,可以调这里。

Panel
@/Supernova/projects/board/eagle/mst038b-d01a-s/model/Customer_1.ini ? 选择屏参, 高清屏选择 FullHD_CMO216_H1L01_720P.ini, 选择 ullHD_CMO216_H1L01.ini 进到 android 死机了 @/Supernova/projects/board/INI/panel/ ? 具体屏参数 @/MBoot/sboot/mstarcore/src/api/MsApiPanel.c ? 个别参数在 MBoot 里面写死

Keypad
@/Supernova/projects/systeminfo/inc/Keypad.h

Sleep,Timer
@/Supernova/projects/msrv/common/src/MSrv_Timer.cpp @/Supernova/projects/msrv/common/inc/MSrv_Timer.h

Sleep 包括三种情况:Timer(Timer setting) ,Sleep(Sleep setting) ,Auto Sleep(no signal)

? Sleep Mode:

数组 const static U8 SleepTimeCoef[9] = {0, 1, 2, 3, 6, 9, 12, 18, 24};乘于 SLEEP_TIMER_TIMEBASE, Sleep 模式对应为: off, 10 分钟, 20 分钟, 30 分钟, 60 分钟, 90 分钟,120 分钟,180 分钟,240 分钟。

Default Setting
@/Supernova/projects/msrv/common/src/MSrv_System_Database.cpp

Picture Setting
@/Supernova/projects/msrv/common/src/MSrv_Picture.cpp

Player-各信源的处理
@/Supernova/projects/msrv/common/src/MSrv_AV_Player.cpp @/Supernova/projects/msrv/common/src/MSrv_DTV_Player.cpp @/Supernova/projects/msrv/common/src/MSrv_HDMI_Player.cpp @/Supernova/projects/msrv/common/src/MSrv_SCART_Player.cpp

SSC
@/Supernova/projects/msrv/common/inc/MSrv_System_Database.h

PC mode timming table
@Supernova/projects/board/INI/pcmode/PcModeTimingTable.ini

Overscan
@Supernova/projects/board/eagle/OverScan.h

在 EDID 加入当前编译时间
@/Supernova/project/sw_cfg/compile_option.mk

然后在 Supernova 就可以使用变量 COMPILE_YEAR 和 COMPILE_WEEK 在 EDID 加入当前 日期信息: @/Supernova/projects/systeminfo/src/SystemInfo.cpp BOOL SystemInfo::LoadVGAEDIDInfo(VGA_EDID_Info_t *pVgaEdidInfo, void *pDict)

3D 功能
App 层调用的 3D 功能接口,真正的实现在 Supernova 代码 MSrv_3DManager.cpp,其中通过 BOOL MSrv_3DManager::Enable3D(EN_3D_TYPE en3DType)函数设置 3D 格式。

Supernova 全局宏定义
通过下面两步加入一个宏定义: 定义@Supernova\projects\sw_cfg\XXX\config.mk 加入全局变量@Supernova\projects\sw_cfg\compile_option.mk

补充知识
Nand Flash 类型: Nand-flash 存储器具有容量较大,改写速度快等优点,适用于大量数据的存储。 NOR 的特点是芯片内执行(XIP, eXecute In Place) ,这样应用程序可以直接在 flash 闪存 内运行,不必再把代码读到系统 RAM 中。NOR 的传输效率很高,在 1~4MB 的小容量时具 有很高的成本效益,但是很低的写入和擦除速度大大影响了它的性能。 NAND 结构能提供极高的单元密度,可以达到高存储密度,并且写入和擦除的速度也很 快。应用 NAND 的困难在于 flash 的管理需要特殊的系统接口。 Emmc nand flash:eMMC 的设计概念,就是为了简化内存储器的使用,将 NAND Flash 芯片和控制芯片设计成 1 颗 MCP 芯片,手机客户只需要采购 eMMC 芯片,放进新手机中, 不需处理其它繁复的 NAND Flash 兼容性和管理问题,最大优点是缩短新产品的上市周期和 研发成本,加速产品的推陈出新速度。 MLC nand flash:NAND 闪存可分为三大架构,分别是单层式储存(Single Level Cell) ,即 SLC;多层式储存(Multi Level Cell) ,即 MLC;多位式存储(Multi Bit Cell) ,即 MBC。MLC 技术是今后 NAND Flash 的发展趋势。 Android 版本: Android 操作系统曾有两个内部版本,分别是阿童木(Astro)和发条机器人(Bender) , 在这两个版本之后,Google 为了避免商标问题,将 Android 操作系统的代号由机器人系列转 变为了现在的甜点系列。[67]而现时 Android 系统以差不多每半年一次的步伐进行升级,比 如 1.5 版叫做 Cupcake(纸杯蛋糕) 、1.6 版为 Donut(甜甜圈) 、2.0/2.1 版为 ?clair(闪电泡 芙,一种法式奶油夹心甜点[68]) 、2.2 版为 Froyo(凍酸奶) 、2.3 版为 Gingerbread(姜饼) 、 3.0 版为 Honeycomb(蜂窝) 、4.0 版为 Ice Cream Sandwich(冰激凌三明治) 、4.1/4.2 版称为 Jelly Bean(果冻豆)及 5.0 版称为 Key Lime Pie(墨西哥莱檬派) 。 ###############################################################################


相关文档

SPIN基本理论和实践篇
大客技巧基本理论和实践篇
大客户销售技巧-SPIN基本理论和实践篇
mstar712的原理图
修改病句理论篇和修改病句实践篇
理论和实践相结合地备考建造师执业资格考试房屋建筑工程管理与实务篇
MStar 单层触控方案研讨会-测试篇
引入SPIN基本理论和实践篇的销售技巧
2013计算机信息系统集成项目管理人员继续教育-C组教材习题及答案—管理理论与实践篇-doc
Mstar原理图与BOM
电脑版