Firefox OS overview and building emulator

前一陣子看到 Firefox OS 的相關文章,發現有提到 adb 之類的指令,我心想怎麼會用到 adb 呢?就去抓了原始碼下來試試看,順便也 Google 了相關的資料(文章最後的簡報檔)。

  1. 第一步就是要配置 Android 的開發環境,像是 repo、git 等等的。
  2. 接著就是要抓 b2g ,這是 Firefox OS 的 bootstrap 代碼,用這份代碼可以幫你抓真正的原始碼,並且進行編譯:git clone git://github.com/mozilla-b2g/B2G.git
  3. 執行 ./config.sh 可以看到支援的裝置,我是選用 emulator,所以輸入 ./config.sh emulator-x86 ,這個步驟會耗時很久很久,因為會用 repo/git 去下載檔案。
  4. 下載完成,就可以用 ./build.sh 進行編譯,我在編譯的過程中遇到幾個比較麻煩的問題:
    1. 少了 libGL 與 libX11 :這要參考 Firefox OS build prerequisites 的說明,我是用 Ubuntu 12.04,所以要做 ln 的動作。
    2. 編譯 gecko 時,librt 找不到:看了gecko 才知道這邊是用 prebuilt/toolchain/i686-android-linux-4.4.3 在做編譯,而 i686-android-linux-4.4.3 下並沒有提供 librt ,所以就會出現錯誤,我是把 gecko/nsprpub/pr/src/Makefile.in 裡的第 133 行註解掉,也就是避免加上 -lrt 來解決,幸好之後可以順利編譯通過。
    3. 少 package,印象中就只有 yasm,有缺的話,就參考前面提到的 prerequisites 跟錯誤訊息來安裝吧。
  5. 接著就可以執行啦:./run-emulator.sh

看源碼以後,我發現有很大一部分是直接使用 Android 現有的成果,也就是說,在硬體的驅動程式方面,Android 如果有現成的,是可以直接搬過來用的;Build system、音效、繪圖核心、init 等等幾乎都一樣,對於 Android 底層開發者來說,應該是不會陌生才對。接下來應該就是要看硬體廠商對 Firefox OS 的接受度以及消費者的接受度了。

Android螢幕解析度

需要寫一個程式同時支援平版跟手機,是故,螢幕解析度就是一個討厭的問題了。上網 Google 了一下,在 StackOverflow 裡看到一篇:Android : App support for multiple tablet screen resolutions,雖然還沒投票選出最佳答案。但是第一個回覆蠻中肯的,他建議先去看 Android 開發網站的這篇 Using new size qualifiers ,就是一般的就用原來的 layout,那平版就用 layout-sw600dp 或 layout-sw720dp ,甚至乾脆就用 layout-v11。總之,Android 開發網站上 Best practice 裡的 Support multiple screens 很值得一看,有提到設計的原則、圖形怎麼選用與縮放,也提到怎麼測試。

說真的,其實現在也不知道用的平板是那一塊,就先弄一般手機 layout 的就好,等知道了平板的解析度,再來調整也不遲。

 

EditText 的 setOnEditorActionListener

還是紀錄一下好了。

在改 2.3.3 Phone app 裡的 GetPin2Screen 時,很妙的一點,它只有去設置 mPin2Field 的 onClick 事件,這樣的結果是導致輸入完 PIN2,按下 Done 以後,一點反應都沒有。

上網 Google 了一下,看了一堆 EditText 的介紹/密技以後,發現是要用 setOnEditorActionListener 去攔截按下 Done 的動作。所以就老實地實作了 OnEditorActionListener 介面,然後 setOnEditorActionListener()。原本 mClicked 裏面的 code 則被我整理成 function,給 OnEditorActionListener 呼叫。軟體鍵盤右下角的 “Enter” 可以透過改 layout 解決,要幫 EditText 增加一個 attribute:android:imeOptions=”actionDone” 。

主要參考資料:

 

 

資料連線的重試

主要是看資料連線會怎麼個重試法。

從 ConnectivityManagerService 可以看到有追蹤 Mobile data state (MobileDataStateTracker),這裡會呼叫到 Phone 裡的 enableApnType(),這個函式最終是呼叫到 DataConnectionTracker.enableApnType(),而這個 method 其實是 virtual 的,分別由 CdmaDataConnectionTracker 與 GsmDataConnectionTracker 這兩個類別去實作。

在 CdmaDataConnectionTracker 裡可以看到利用 ALARM_SERVICE 進行重試連線的機制。Intent 名稱是 INTENT_RECONNECT_ALARM,而重試時間則是由 nextReconnectDelay 決定。nextReconnectDelay 則是從 mRetryMgr.getRetryTimer() 取得。mRetryMgr.getRetryTimer() 其實是依據 mRetryArray 裡的值來回傳,mRetryArray 則是在 mRetryMgr 被建立時,用 configure() 方法去初始的。configure() 收一個字串,CdmaDataConnectionTracker 裡會先看 ro.cdma.data_retry_config 這個 property,如果沒有,就丟 DataConnectionTracker.DEFAULT_DATA_RETRY_CONFIG 這個字串給 mRetryMgr.configure() 去解析。

字串裡的格式,主要以逗號分隔,可以有這四種:

  • default_randomization 預設亂數時間
  • max_retries 最大重試次數
  • delay_time 延遲時間,亂數時間則是使用 default_randomization
  • delay_time:randomization_time 延遲時間與亂數時間

DEFAULT_DATA_RETRY_CONFIG 裡是這麼寫的:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,320000:5000,640000:5000,1280000:5000,1800000:5000  ,解析以後,會把 5000 到後面的值都放到 mRetryArray 裡,所以 mRetryArray 就有十個元素。

運作時會依據 mRetryArray 的值進行重試 (單位是 ms 亦即除以 1000 就是秒),當連線失敗,會等待 5 秒+亂數2秒,然後重試;如果失敗,接著會等待 10 秒+亂數2秒,然後重試;如果又失敗,接著會等待 20秒+亂數2秒,然後重試;依此類推;當重試超過第十次時,就會一直用第十次的值,繼續去重試。

這個重試的過程會一直持續下去,直到沒訊號、 漫遊或是設定裡的 Data Enabled 被關閉為止。

Android sdk_addon 補充

上次雖然是可以順利 build 出 addon zip 檔案,可是要包進去的 java library 大小卻為 0,真是讓人錯愕。

有人有問跟我一樣的問題,可是沒人回。我接著回,然後問他解了沒。但我的回文一直沒真的上去,真怪。

這篇是說自己加了 <used-library ndroid:name=”com.example.android.platform_library”/> 就可以用,build 的方法就是一樣。

接著找到幾篇有用的文章,解析 Android build system 的文章:

之後是埋頭自己在看,看到了奇妙的用法,長見識了。Makefile 裡 target 的 dependencies 可以寫這樣:aa: bb | cc ,make 執行時,會先試著執行 bb target,如果 bb target 有確實產生檔案出來,就不執行 cc target;若 bb target 沒產生檔案出來,就執行 cc target 。

Android.mk 裡則是可以用 $(info message) 來顯示你想看到的訊息,如果是在 target 之下的指令,則可以善用 echo、$^、$@。

最後是讀了這篇,前面看起來是跟我一樣的錯誤,然後認為是 mkstubs 的問題,所以他們進行實驗,加 PRODUCT_SDK_ADDON_STUB_DEFS 之後就可以了!!

所以,在 sample_addon.mk 加上 PRODUCT_SDK_ADDON_STUB_DEFS := device/sample/products/xyz.defs,而 xyz.defs 裡面則是: +com.example.android.platform_library.* ,打完收工!!

Android sdk_addon

接到任務,說要做 sdk_addon.zip 給客戶使用,客戶有提供一些資訊,基本上就是要參考 device/sample 下的檔案來做,我把 frameworks 跟 products 都依樣建立了,這兩個資料夾下的必要檔案(frameworks/Android.mk跟products/*.ini)也都做了修改,可是怎麼試都失敗。

上網 Google 了一下,找到這篇:Android SDK Add-on Configure, Compile and Release ,一樣也是提到 device/sample ,基本上算是相當清楚,但是在我的環境下仍然失敗。

後來靜下心來看,相對照 sample 以後,發現其實 device/myproduct/Android.mk 裏面已經有去 include 其下所有 Android.mk,這點跟 device/sample/Android.mk 裏面一樣。device/sample 下沒有 AndroidProduct.mk,而是在 device/sample/products 下,裏面則是去 include device/sample/products/sample_addon.mk 。那我的 device/myproduct 下已經有 AndroidProduct.mk 而且寫了好多東西,因此,我就參考 device/sample/products/sample_addon.mk 增加了 PRODUCT_SDK_ADDON_* 相關的變數,然後把 PRODUCT_PACKAGES 從 := 改為 += 。又因為公司合作的開發SDK比較機車,有些 library 都是用複製,而非 build 的,所以再參考之前的 build script ,稍稍修改了一下,這樣就可以 build 出 SDK addon 了,產出的檔案是放在 out/host/linux-x86/sdk_addon 下。

補充:後來編譯 mydroid 時出問題了,發現要在 PRODUCT_SDK_ADDON_* 的前後加上 ifneq ($(filter sdk_addon,$(MAKECMDGOALS)),) 跟 endif 才可以避免無法編譯 mydroid 的窘境。

 

APN的一些筆記

前一陣子看APN的一些筆記。

TelephonyProvider是主要的提供者,從這裡就可以撈到 APN 的列表,這是在第一次開機或 Reset 時,由 /system/etc/apns-conf.xml 載入到 sqlite3 的資料庫裡。

在「設定」那邊的 APN 列表,主要是根據 sim card 上的 mcc, mnc 去查詢符合條件的記錄,然後列出來。查詢 mcc, mnc 的方法是透過 System Property:TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC,這是在開機,Phone 相關服務初使化以後,會存到 System Property 裡。當移除 sim card 時,自然會因為無法取得 mcc, mnc (System Property:TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC 是空的)而查詢不到資料,列表就會是空的。這時候去撈 TelephonyProvider,還是可以撈到完整的 APN 記錄。

 

ping permission何時被更改?

在out目錄下看到的,會是正常的 755,那到了裝置裡為什麼變成 2755??

我是在 mydroid/system/core/include/private/android_filesystem_config.h 裡找到 system/bin/ping,那麼有誰引用呢?相當多程式,像是 make_ext4fs, genext2fs, cpio, mkyaffs2image…等等。換言之,是在製作 .img 時,去變更 ping 權限的。

因此如果要加自己的程式,然後不想在 init.rc 裡改權限的話,就得在 android_filesystem_config.h 裡加,然後記得不要加在 android_files 陣列的後面,加到後面,會因為陣列後面都是 * 的關係,而被忽略掉。

PUK解鎖畫面的開發

基本的思路是想在解鎖畫面的地方,緊鄰緊急電話按鈕的旁邊加上一個按鈕,用以解鎖,並且可以參考緊急電話的程序,來撥打上面提到的特殊電話號碼來解鎖。

依據這篇:Android中PIN和PUK碼解鎖研究的追蹤過程進行追蹤,可以知道:

  1. KeyguardViewMediator.java 的 onSimStateChanged() 裡有對 PUK_REQUIRED 做處理,dokeyguard()會顯示鎖定畫面,而dokeyguard()裡則是先檢查是不是被鎖住,是的話,就送訊息出去,表明該要顯示鎖定的畫面,送出去以後,handleShow()會收到並處理。
  2. handleShow()裡會先播放音效,然後呼叫mKeyguardViewManager.show(),這裡會去建View,並設置View相關Layout參數。從這邊可以追蹤到LockPatternKeyguardView.createLockScreen(),這個函數就直接回傳LockScreen,LockScreen是在frameworks/base/policy/src/com/android/internal/policy/impl/LockScreen.java。
  3. LockScreen則是去解析frameworks/base/core/res/res/layout 下的 keyguard_screen_tab_unlock 與 Keyguard_screen_tab_unlock_land 其中之一(依橫或直來決定)。裡面的emergencyCallButton有註解說在sim card為PUKd時,才顯示。至此,已經知道該改哪個畫面才可以多一個按鈕出來。

那麼多了按鈕之後,能否再帶一個畫面出來輸入PUK與PIN呢?

這邊繼續參考takeEmergency()的部分,takeEmergency()是直接送帶有ACTION_EMERGENCY_DIAL的Intent出去。ACTION_EMERGENCY_DIAL是一個字串’com.android.phone.EmergencyDialer.DIAL’,以這個字串去搜索,可以在packages/apps/Phone/src/com/android/phone/EmergencyDialer.java與Phone app的AndroidManifest.xml中找到,那麼這就表示是由Phone app在處理。

因此,決定仿效這樣的方法,在Settings app裡加上輸入PUK、PIN的畫面,並且在AndroidManifest.xml裡該畫面activity的intent filter裡加上客製的Intent字串。

Framework 層,則先在畫面加上新的按鈕,並且在按鈕的處理裡,仿效takeEmergency(),加上送出客製的Intent字串,這樣就可以串到Settings app的畫面去了。

程式都撰寫好之後,進行測試,首先遇到的問題是,按下按鈕以後,位於Settings app的畫面並沒有顯示出來,利用 adb logcat 進行追蹤,發現的確有送出 Intent,要求 Settings app 裡的 activity 啟動,可是畫面卻出不來,參考EmergencyDialer.java,發現需要在 onCreate() 裡加上 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED),這樣才可以在被鎖定時顯示出來。

第二個遇到的問題是,使用特殊的電話號碼去進行撥號,並沒有反應,也因此無法解鎖。這裡去搜索IccCard.java,發現有supplyPUK()函式,既然supplyPIN()是解鎖PIN,那麼supplyPUK()就應該是解鎖PUK。於是在畫面補上取得IccCard的必要程序,然後去試,這次根本連畫面都出不來了,參考 adb logcat,發現是 activity 啟動失敗,依據啟動失敗的錯誤訊息去搜索,發現是需要為activity加上android:process屬性,於是在 Settings app AndroidManifest.xml 新增的Activity tag上加上android:process=”com.android.phone”,就可以順利啟動並解鎖了。

signalStrength.getGsmBitErrorRate() always return -1

用 PhoneStateListener.onSignalStrengthChanged() 來接signalStrength,就可以呼叫到signalStrength.getGsmBitErrorRate(),試了兩三台手機,都只拿到-1。上網查,很多人回報他的手機也是如此。查Android developer reference,這個要看 27.007 8.5,看了以後知道-1是不正常的值,也知道對Modem那邊是下AT+CSQ。

請同事幫忙看,他說直接對 Modem 下 AT+CSQ 有傳回值:”14,99″,這就怪了,所以就看了 Framework 層這邊,處理回來的 AT+CSQ 的地方是在 GsmServiceStateTracker.onSignalStrengthResult() ,這邊繞了一堆路,簡單的說,就只解析了第一個參數,存到 signalStrength 裡以後,就通知上面了,剛巧,signalStrength 裡 bit error rate 的預設值就是 -1,所以才會一直都拿到 -1。因此這邊加上解析第二個參數的程式以後,就可以順利取得99。

回頭再看 27.007 8.5,99 其實是 not known or not detectable,這還是沒辦法用,再加上 8.5 對 AT+CSQ 的說明是說 optional,不一定要實作完全,因此這就只能回頭問提供 modem 晶片的廠商了。