Android的Surface系统定义了一个DisplayType的枚举,其中有代表手机屏幕的DISPLAY_PRIMARY和代表HDMI等外接设备的DISPLAY_EXTERNAL,还有用于Wi-Fi Display的虚拟显示设备DISPLAY_VIRTUAL。本文就讲解HDMI的输出制式的设置流程。
Android设备通过HDMI线把内容显示到电视机上,当我们开关电视、拔插HDMI线,设置输出制式时,Android系统都做了什么来实现我们的操作?如果由你来设计这个流程,你要怎么实现?看过笔者前面写过的关于Android的网络系统的同仁,熟悉Android系统的代码的人估计很快就会想出Google的工程师应该会怎么实现。首先代码里要有一个HDMI service,这个service用来设置和管理HDMI输出制式,这个service可以是Java的,也可以是C++的,如果是C++说明这个service需要硬件配合实现。当HDMI cable插入后,底层HDMI的驱动检测到(HPD)后,需要通过本地Socket或者其他通信手段把uevent传给HDMI daemon,daemon再把event发送给HDMI service,HDMI daemon和HDMI service的通信不用多说,自然应该是通过Binder机制来实现。HDMI service收到event后做相应的处理来实现用户的操作。
/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
Android系统启动的时候有调用PhoneWindowManager对象的init方法,而该方法里有调用initializeHdmiState(),我们就来分析一下这个
initializeHdmiState()
{
//这里会创建出一个UEventThread线程来,接收Native层传上来的uevent
//就是说接收和/devices/virtual/switch/hdmi相关的uevent
mHDMIObserver.startObserving("DEVPATH=/devices/virtual/switch/hdmi");
}
显然这个UEventThread线程就是我们要找的HDMI daemon。
在UEventThread线程的主函数里
/frameworks/base/core/java/android/os/UEventObserver.java
public void run() {
nativeSetup();
while (true) {
//不停的接受从Native层传上来的uevent
Stringmessage = nativeWaitForNextEvent();
if (message!= null) {
//把Message发送给相对应的UEventObserver对象
sendEvent(message);
}
}
}
nativeWaitForNextEvent()函数是jni接口函数
/frameworks/base/core/jni/android_os_UEventObserver.cpp
static jstring nativeWaitForNextEvent(JNIEnv *env, jclassclazz) {
for (;;) {
int length =uevent_next_event(buffer, sizeof(buffer) - 1);
}
}
/hardware/libhardware_legacy/uevent/uevent.c
int uevent_init()
{
//在明显不过通过Socket和底层驱动进行通信
s =socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
setsockopt(s,SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
if(bind(s, (structsockaddr *) &addr, sizeof(addr)) < 0) {
}
}
int uevent_next_event(char* buffer, int buffer_length)
{
while (1) {
//recv接收来自底层驱动的UEvent。
int count =recv(fd, buffer, buffer_length, 0);
}
}
从上面两个函数来看的话,HDMI daemon其实就是通过Socket和底层驱动进行通信的。
而HDMI的UEventObserver对象就是mHDMIObserver
/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
private UEventObserver mHDMIObserver = new UEventObserver() {
//处理来自于HDMI daemon的消息
public voidonUEvent(UEventObserver.UEvent event) {
setHdmiHwPlugged("1".equals(event.get("SWITCH_STATE")));
}
};
void setHdmiHwPlugged(boolean plugged) {
if(SystemProperties.getBoolean("ro.platform.has.mbxuimode", false)) {
if (plugged)
mMboxOutputModeManager.setHdmiPlugged();
else
mMboxOutputModeManager.setHdmiUnPlugged();
}
}
/frameworks/base/core/java/android/app/MboxOutputModeManager.java
public void setHdmiPlugged(){
mService.setHdmiPlugged();
}
其中mService就是MboxOutputModeService对象
MboxOutputModeService是SystemServer里的服务,显然MboxOutputModeService就是我们苦苦寻找的HDMI service。
该服务专门设置盒子的输出制式
/frameworks/base/services/java/com/android/server/MboxOutputModeService.java
public void setHdmiPlugged() {
setOutputMode()
}
public void setOutputModeNowLocked(final String mode){
//把新制式保存到文件/sys/class/display/mode里
//发送广播
}
common/drivers/xxxxx/hdmi/hdmi_tx/hdmi_tx.c
static int amhdmitx_probe(struct platform_device *pdev)
{
//显然有个线程在不停的处理HDMI相关的事情
hdmitx_device.task= kthread_run(hdmi_task_handle, &hdmitx_device, "kthread_hdmi");
}
show_disp_cap()
{
//获取edid
const char*native_disp_mode = hdmitx_edid_get_native_VIC(&hdmitx_device);
if(hdmitx_device.tv_no_edid){
//获取不到edid的话,添加"nulledid"
pos +=snprintf(buf+pos, PAGE_SIZE, "null edid\n");
} else {
//正常流程
}
}