前言

在開發 Android 相關產品時碰到的一個 issue 是有些使用者的 Android 手機連不上我們的產品,而且是在少數一兩家特定廠牌的特定型號的手機發生,QA 測不出來,我們開發端這邊也測不出來,由於公司不是靠開發 Android 賺錢,所以並不像台灣的一些手機廠商公司有各類牌子跟各類型號的手機能測試

最後我們懷疑是跟 Multicast 有關

原因是 Android 的 application 可以選擇讓裝置接收或不接收 Multicast packets,如果選擇接收,會比較耗電,所以有些製造商會 block 掉 Multicast,我們猜測也許是某廠牌的某型號的手機有啟用這個功能讓家裡的 AP 傳送 Multicast packets 給我們的機器,但我們的機器是關閉這個功能的

總之最後是有點死馬當活馬醫,就來嘗試讓我們的機器啟用 Multicast,看會不會讓連線問題漸少

Multicast

顧名思義就是多點傳播

但是跟 Broadcast 不同的是他不是針對全民做廣播服務,是針對特定的團體,在相同時間傳播相同的 Packet

sender / receiver

在 Multicast 中,傳送者並不知道接收者是誰也不知道有幾個接收者,傳送者就是把封包傳給 Multicast group

IANA (Internet Assigned Numbers Authority) 保留了 224.0.0.0 - 239.255.255.255 的 IP 位址給 Multicast groups 使用

接收者要用 IGMP (Internet Group Management Protocol) 來註冊 Multicast group

市面上大多的路由器使用 IGMP snooping 來偵測 IGMP packets,然後根據 IGMP 的資訊讓這些 packet 能被傳送到對應的 Multicast group

在不同的網路連線方式下也有不太一樣的運作細節(Ethernet or Wireless),詳情可看維基百科

Android Multicast

Android 官方文件
https://developer.android.com/reference/android/net/wifi/WifiManager.MulticastLock

前面有提到 Android 可以選擇開啟或關閉這個功能,要讓 application 可以接收 Multicast packets 就要讓他可以獲取 Multicast lock

Manifest

AndroidManifest.xml 內加入 uses-permission

1
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

java file

在你的 java 檔案中 import

1
2
import android.content.Context;
import android.net.wifi.WifiManager;

宣告好變數

1
2
private WifiManager mWifiManager;
private WifiManager.MulticastLock mMulticastLock;

onCreate()

1
2
3
4
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (mWifiManager != null) {
mMulticastLock = mWifiManager.createMulticastLock("My Multicast lock");
}

在需要開啟 Multicast 的地方放入

1
2
3
if (mMulticastLock != null) {
mMulticastLock.acquire();
}

不需要時記得釋放 lock

1
2
3
if (mMulticastLock != null && mMulticastLock.isHeld()) {
mMulticastLock.release();
}

如何檢查裝置有無支援

1
2
adb shell netcfg
adb shell ipconfig eth/wlan

大概會看到如下,找到 MULTICAST 關鍵字

1
2
3
4
5
6
eth0      ...
UP BROADCAST MULTICAST MTU:1500 Metric:1
...
wlan0 ...
UP BROADCAST RUNNING MULTICAST MTU:1500
...

Multicast over WiFi

在有線網路下使用比較沒什麼太大的影響,有線網路是有界限的,所以在大量傳送 Multicast packets 的情況下比較不會發生碰撞 (collision),但在無線網路下就不同了,傳送大量的 Multicast packets 會有一定的負擔

如前面所說有些製造商會刻意 block 掉這個功能,因為怕耗電

原因就是,為了盡可能讓 Group 內的接收者都能收到 Multicast packet,AP 在送出 data frame 時會以符合目前最低需求的 data-rate 來傳輸,對於有 high-bandwidth 要求的應用來說就不是這麼穩定

使用 low data-rate 來傳輸,就代表會使用更多時間來完成傳輸,會造成不必要的 latency,也會耗電

再來就是 Multicast 跟 Broadcast 一樣是在 DTIM interval 傳送,所有接收者都必須從睡眠時期醒來,然後等待特定的 data frame,會讓裝置耗電

另外一個問題就是 Multicast 不會重傳失敗的 data frame,如果失敗率很低的網路環境下是沒什麼,但如果 loss rate 太高就會出現問題

總結問題就是

  1. 低傳輸效率,Low data rate transmitting
  2. 耗電,Battery consumption
  3. 不會重送失敗的訊框,Will not resend corrupt frames

Solution

解法都是在 AP 上,比如增加 minimum data rate

或是將 Multicast packet 轉換成 Unicast packet 送出去(M-to-U),先用 IGMP snooping 確認位址後,就能將轉換後的 packet 以最好的 data rate 送出

後記

可以去看看 WifiManager.java 的原始碼來決定要怎麼寫,當時有看到不同版本的原始碼在 Multicast lock 那邊有不同的寫法…不過最新的官方文件看起來是滿靠譜了

關於 lock 的機制都是跟 multi-threading 有關,Multicast 的啟用在 Android 是 by system,不是 by application,從系統中取用了某項資源,避免多人同時讀寫會發生錯誤,就要有 lock 的機制來確保系統資料的一致性

可以參考 Udacity 的小短片講解關於 mutex lock 機制