Miracast in Android 小整理

大家都知道 Android 4.2 版支持了以前叫做 WIFI Display 的 Miracast. 看了同事轉寄的網路文章 [1], 才知道 Android 連架構都配合 Miracast 做了修改.

首先考量的是, 使用者需要把手機上的畫面輸出到電視上分享給大家, 卻不能讓大家都看見他/她怎麼在鍵盤上按密碼. 所以 surfaceflinger 就不能把虛擬鍵盤或是密碼框的這一層投射出去. 換言之, 把整個 display planes  (什麼 V1, V2, OSD1, OSD2, SUB1, SUB2) 都 mix 後才輸出的那張圖, 反而不是可以公開的.

因此, surfaceflinger 裡面就多了個 display device 的抽象層.

DISPLAY_PRIMARY:  Android 手機的螢幕

DISPLAY_RXTERNAL: HDMI 設備, 如 TV

DISPLAY_VIRTUAL: WIFI Display, 即 Miracast.

1357478805_4074

對於 virtual 的裝置, Android 並不準備它的 frame buffer.從下面這段 code 中可以看出, 每一個 Display Device 在產生的時候, 都會參考 state.isVirtualDisplay 是否為真? 進而決定 hw 這個 class 的內容. Miracast 顯然就和 HDMI, 和 local 螢幕不同類. 可見得在 HDMI 以 MHL (Mobile High-Definition Link) 接電視的情況下是允許看到輸入密碼視窗的, 但 Miracast 以 WIFI 無線時不可以顯示.

1357478788_3356

其中 static_cast< sp<ISurfaceTexture>> 表示指定型別為 sp<ISurfaceTexture>, 也就是 ISurfaceTexture 的 smart pointer. Smart pointer 也是個 pointer, 它有助於在不同的地方 create 和 destroy, 因此可以靈活地運用並減少記憶體丟失 (或曰"洩漏").它的原文是:

Smart pointers ensure we properly destroy an object even if its creation and destruction are widely separated. They make functions simpler and safer by ensuring that no matter how many different exit paths exist, local objects are always cleaned up correctly. They help enforce that exactly one object owns another object at any given time, preventing both leaks and double-frees.[2]

最後 Android 做 composer 的時候, 會對每一種顯示設備 (共 mDisplays.size 種) 檢查是否 dirty (需要更新)? 並且用 hw->canDraw() 判斷能不能畫? 如果不能畫, 連 dirty 也不畫.在 2011 年的時候, 就有人抱怨過 hw->flip 為什麼在 hw->canDraw() 的前面執行 [3]? 導致 Android suspend 的時候都還在 flip. 看來 Android 是從善如流, 改為優先判斷 canDraw() 了.

1357478824_6904

[REF]

1.Android Wi-Fi Display 介紹

2.Smart Pointer Guidelines

3.Android的Suspend​

HDMI CEC 的一點補充

自從遇見 HDMI (High Definition Multimedia Interface) 的 CEC (Consumer Electronics Control ) 之後, 就被這個又方便又容易出問題的功能發了很多 bug.

有時候它可以用 device (例如 DVD Player, BD Player, Digital Media Player…等等, 簡稱 box 好了) 的搖控器控制電視, 有時候又反過來是用電視的搖控器控制 box. 雖然它們的目的都是減少使用者拿兩支以上搖控器的不便, 但實作起來卻有很大的差異.

有個規格 (protocol) 叫作 RC pass through. RC (remote pass through) 就是搖控器, pass through 就是控制到另外一台裝置. 例如用電視遙控器去播 box. 規格上並沒有說不能用盒子的遙控器去控制電視, 所以兩個方向都有可能. This feature is used to pass remote control commands received by one device (typically the TV) through to another device in the network.

首先我們把 TV 切到 box 所在的 HDMI port, 然後拿電視的遙控器來控制 box 就可以了. 前提是兩台裝置都支援 RC pass through. 當然, 規格不能保證所有的搖控器鍵值都可以傳給別台裝置. 合法的功能寫在 HDMI 1.4 規格的 table 30.

CEC-1.4-Table-30-620x200

其實 HDMI 1.3 講得比較簡單易懂. HDMI 1.4 裡面引進了 <feature abort> 的觀念, 遇到某些不支援的 protocol 或是 command 時, 產生的結果也會不一樣.  因此 HDMI 1.4 還要參考的表格是 table 20, 27 和 27 三張.  HDMI 1.3 裡面只有一個 table 23 則對應到 HDMI 1.4 的 table 30 而已. HDMI 1.4 又定義了久按TV 遙控器按件不放時, 則每隔 450ms (建議值, 可以定在 200~500 ms) 要重送一次鍵值到 box (反向亦然).

雖然 table 30 裡面功能鍵眾多, 其中只有一部分 (0x60~0x6D) 是 deterministic (決定性的) UI function. 這些按鍵的功能不受到其它條件的影響, 按了就要有正確的反應, 例如 power on, power off 時, 毋須考慮什麼事情正做到一半.

另外一個類似的 protocol 是 device menual control. 同樣地, 看了 HDMI 1.3 的規格就會覺得自己很快懂了. 但是看了 HDMI 1.4 就會覺得好複雜. 它的主要功能就是讓 TV 把 box 的控制面板 (control menu) 叫出來, 這樣就可以真正地操作 box. 否則若是 box 上有什麼特異功能, 光靠 TV 的遙控器也無法做到!

舉個誇張一點的例子, 如果搖控器按"上上下下左左右右","next","previous" 可以叫出某個破解功能, 只用 RC pass through 就很難保證哪些 key 按了 400 ms, 哪些是 600 ms. 在 device menu control 假設可以看到畫面上每個鍵被按到後的反白, 應該就能確定自己按對了. 假如 box 支援虛擬小鍵盤, 也是只有 device menu control 可以輕易使用此功能.

另外, 按照規格上的說法, 它只能單向地由 TV 叫出 box 的 control menu.

HDMI-2-CEC-Case

Samba 再舞

原本以為 Samba 的設定對我已經不是問題了, 不過這天拿到一台全新的 server 測試時, 真讓我傷了不少腦筋.

首先不就是去安裝一套 samba 嗎?然後改改 smb.conf, /etc/init.d/smb restart, 從 PC 連過去不就結了?我原本就是這樣想的, 不過 PC 始終連不上這台新 server. 由於別台 server 的 samba 我都能連得上, 所以 PC 的防火牆顯然沒有問題. 還是來關心 smb.conf 吧!

把 passdb backend =tdbsam ? 改到 tdbsam 時,也看到註解上直接說 smbpasswd 那套都過時了, 現在不是 tdbsam 就是 ldapsam. SAM 大概就是指 Samba 了? LDAP 是 Light Directory Access Protocol,設定 ldapsam 帳號密碼會去問 LDAP server,適用於大量的使用者.而 tdbsam (Trivial DataBase)可以直接支援這台 server 上的用戶, 適用於 250 個以下的使用者 [ref 1]. 不過連 security=share 都不靈, 顯然和帳號密碼都無關.

接著很自然就會想到, Google 一下 “Samba 連不上" 這個關鍵字. 呵呵, Google 果然給我們很多建議, 首先就是目錄的權限要設對. 這個容易, 把它們的 mode 都設成 777 就結了, 但這個也不是原因. 其他人遇到和我類似的狀況時, 往往都是孤兒文 – 有人問沒人答.

沒辦法, 我只好去看 log 了 – Windows 的 “診斷". 其實 Windows 的 “疑難排解" 很難用, 這次真的是不得已才去研究它.

原來是 port 445 沒回應. 那麼怎麼把 server 的 port 445 打開呢?首先要去改 iptables [ref 2].

vi ./etc/sysconfig/iptables

加入兩行規則, 把 port 445 的 TCP 和 UDP 都改為接受.

-A INPUT -m state — state NEW -m tcp -p tcp -s 172.26.0.0/16 –dport 445 -J ACCEPT

-A INPUT -m state — state NEW -m udp -p udp -s 172.26.0.0/16 –dport 445 -J ACCEPT

其中, 192.168.0.0/16 的 代表 mask  255:255:0:0, 所以公司裡面不同網段的 IP 都可以連得上. 關於 32~0 各自代表 mask 值多少, 請參照 [ref 3].數字愈小代表接受 IP 的範圍愈大. 
然後重啟防火牆, 
service iptables restart

果然, 現在新增一個網路連線磁碟機的時候, 所有 smb.conf 裡面設定的目錄都自動跑出來讓我選了.

[ref]

1. Howto setup Samba Server (File Server) With tdbsam Backend 

2. 邁向 RHCE 之路 (Day29) – Samba 檔案分享伺服器

3. Classless Inter-Domain Routing

RFC1738,1808,3986 小整理

最近在看直播的問題, 必須看一些 RFC 的文件. 在此把相關的部分都整理出來.

最基礎的一本是 RFC 1738 Uniform Resource Locators (URL), 一個 URL 可以分成兩個部分:

        <scheme>:<scheme-specific-part>

Scheme 可以翻譯成 "系統" 或 "範型", 它包括以下幾種內容, 相當於是大分類. http 或是 ftp 都是常用的 scheme.

scheme 說明
ftp File Transfer protocol
http Hypertext Transfer Protocol
gopher The Gopher protocol
mailto Electronic mail address
news USENET news
nntp USENET news using NNTP access
telnet Reference to interactive sessions
wais Wide Area Information Servers
file Host-specific file names
prospero Prospero Directory Service

至於 scheme-specific-part 的部份, 會依據不同的 scheme 而有不同的格式變化.下面就進入 RFC1808 Relative Uniform Resource Locators 的 URL 格式, 和前面 RFC1738 最大的差異在於:Relative – "相對性".

它基本的長相是這樣. 

<scheme>://<net_loc>/<path>;<params>?<query>#<fragment>

scheme ":"   ::= scheme name.

"//" net_loc ::= network location and login information.

"/" path     ::= URL path.

";" params   ::= object parameters.

"?" query    ::= query information.

"#" fragment ::= fragment identifier.

為了簡化描述, 我們直接看簡化版的例子, scheme, net_loc, params, query (component), fragment 改用 http://, a, p, q, f 代替. Path 的部份因為要多給幾層才能看出端倪, 所以給三個符號 b, c, d. d 可以視為檔名. 此後 URL 變成 <URL:http://a/b/c/d;p?q#f&gt;

於是我們有下列的表達方式:

      g:h        = <URL:g:h>
      g          = <URL:http://a/b/c/g>
      ./g        = <URL:http://a/b/c/g>
      g/         = <URL:http://a/b/c/g/>
      /g         = <URL:http://a/g>
      //g        = <URL:http://g>
      ?y         = <URL:http://a/b/c/d;p?y>
      g?y        = <URL:http://a/b/c/g?y>
      g?y/./x    = <URL:http://a/b/c/g?y/./x>
      #s         = <URL:http://a/b/c/d;p?q#s>
      g#s        = <URL:http://a/b/c/g#s>
      g#s/./x    = <URL:http://a/b/c/g#s/./x>
      g?y#s      = <URL:http://a/b/c/g?y#s>
      ;x         = <URL:http://a/b/c/d;x>
      g;x        = <URL:http://a/b/c/g;x>
      g;x?y#s    = <URL:http://a/b/c/g;x?y#s>
      .          = <URL:http://a/b/c/>
      ./         = <URL:http://a/b/c/>
      ..         = <URL:http://a/b/>
      ../        = <URL:http://a/b/>
      ../g       = <URL:http://a/b/g>
      ../..      = <URL:http://a/>
      ../../     = <URL:http://a/>
      ../../g    = <URL:http://a/g>

由於 a 是 network location, 無論往上幾層, 都不能高過於 http://a/, 其他的都可以直觀地了解. 大致瞭解了比較常聽見的 URL, 接著看看比較陌生的 RFC3896 Uniform Resource Identifier (URI): Generic Syntax.

其實 RFC3896 的內容和 RFC1808 有些重複, 原因是 URI 包含了 URL 和 URN (Uniform Resource Name), 所以 RFC1808 只是 RFC3896 的一部份. 一個 URI 可能是一個 locator (URL), 一個 name (URN), 或是 locator + name. 舉例子來說, 它們會像這樣:

         foo://example.com:8042/over/there?name=ferret#nose
         _/   ______________/_________/ _________/ __/
          |           |            |            |        |
       scheme     authority       path        query   fragment
          |   _____________________|__
         /  /                        
         urn:example:animal:ferret:nose

大家應該很容易就發現, URI 的說明, 簡直就和 URL 一樣嘛! 只不過 URL 用 network location 代替了 URI 的 authority, URL 有 parameters, 而 URI 把它併入了 path. 基本上, URI 著重在 "標識" – identifier, URL 著重在位置 – location, 即使兩者的長相完全一樣.

參考 [ref 4] 的說法, 若是 PPS 把一部 "大話西遊 – 月光寶盒" 的電影, 同時放在伺服器 1, 2, … 和伺服器 100, 以便給不同地區的使用者觀看.  這些片子可以理解為同一個 URI, 但它們的 URL 各自不同. 另外一部同時放在伺服器 1 的電影: "大話西遊 – 仙履奇緣", 就要理解為另外一個 URI 了.

[ref]

1. http://www.ietf.org/rfc/rfc1738.txt

2. http://www.ietf.org/rfc/rfc1808.txt

3. http://www.ietf.org/rfc/rfc3986.txt

4. URI, URL, 和 URN 的區別

USB 充電小註解

目前 USB 的充電規範以 USB應用者論壇(USB-IF) 所制定 BC (Battery Charge Spec.) 1.2 為主, 它定義了每個類型充電器的可用電流上限, 以及充電器的類型.

原先大家的認知, 都是 USB 要支援 500mA 的電流. 但是實際上, 這種電流可能連外接硬碟都推不動. 於是大家可能都 "偷偷" 可以供到 1A. 從 BC 1.1 開始, 規範中就直接讓 USB 可以輸出 1.5 A 了.

在充電類型方面, 一共有三種定義:標準下行埠(Standard Downstream Port, SDP)、充電下行埠(Charging Downstream Port, CDP)及專用充電埠(Dedicated Charging Port, DCP). 

所謂的 SDP 就是標準的 USB HUB 輸出, 像是電腦或是螢幕基座上的 USB 輸出大抵都支援 SDP. 它的充電電流是 500 mA, 所以不致於充得太快! 甚至在被充電裝置完全沒電時, 可以先用 100m A 的小電流輸出, 等到充至 0.5~0.7 V 時才改回 500 mA.

這有什麼好處呢?如果沒有人在管電流限額, 而沒電的手機電壓已經趨近於 0 了, 當輸入阻抗很小, 那麼電流將會很大, 使得溫度過高, 說不定會燒壞什麼電路. 阻抗給很大, 那麼電流就會太小, 導致充得很慢. 因此, 聰明的控制電流是有必要的.

至於 CDP 可以提供 500mA 和 1.5A 兩種電流, 算是有 "兩下子" 的 USB 接口. 一般會先用 500mA, 若是符合 1.5A 的快充, 就會切換過去 [3, 4].

最後的 DCP 只能充電, 不能傳 data.

上面提到的都是 USB 2.0, 而一般 USB 3.0 都是用 USB 2.0 的規範充電.

USB 可持裝置 (portable device, 簡稱 PD) 要怎麼知道連上的是哪一種 USB Port, 並從而決定要抽多少電呢?判斷方式可以參考 ref  3 或 4. 簡單地說:

PD 先把 D+ 設為 0.6V:

1. D- 還在低電位, 表示 USB HOST 沒反應, 那麼這 HOST 是 SDP.

2. 若 D- 變成 0.6V, HOST 可能是 DCP 或 CDP.

PD 再把 D+ 或 D- (只有慢速設備會拉 D-) 拉高到高電位, 然後去看另外一根 D- 或 D+:

1. D- (D+) 還在低電位, 表示這是 CDP.

2. D-  (D+) 也變成高電位, 表示 D+ 和 D- 短路, 一定是 DCP.

因此 PD 有辦法知道它所連接的 port 是哪一種, 再進一步決定抽 0.5A 還是 1.5A.

最後, 一個裝置可能有時支援 CDP, DCP, 或 SDP , 這叫做 multi-role port. 若是不只是能支援充電, 還可以當 OTG 用, 這個叫做 ACA (Accessary Charge Adapter).

PD 只要有一個 micro-ACA, 它的 3 個 port 就同時扮演 device 當別人的 USB Storage (OTG Port), 當 host 外接鍵盤滑鼠 (Accessary Port) , 又被充電 (Charger Port). 一孔多用是未來的趨勢, 因此 ACA 早在 BC 1.1 的時候就被列入規範了.

[REF]

1. USB 快速充電知多少?使用跳線達成 AC 模式充電.

2. 滿足可攜式裝置電源需求 USB電池充電規範角色關鍵

3. USB Battery Charge Spec. 1.2

4.  解读USB-IF电池充电规范