在Android Gingerbread引入了StorageManager這東西, 似乎是為了OBB(Opaque Binary Blobs)這功能而來的, 不過, 似乎是也還沒把tool含到SDK內, 也沒很詳細的說明文件, 所以看起來現在好像也沒啥人去用這個, 實際上試了一下, 最後還是有問題, 搞不好還有bug, 懶得繼續追下去了, 不過大致上理解怎來利用這東西

OBB是一個含有加過密的檔案系統的檔案, 唸起來頗繞口, 不過如果對Linux有點熟悉的話, 它是一個磁碟映像檔(image file), 最後會以loopback device的方式掛載入Android內(Android底層是Linux, 採用這種方式也不足為奇)

由於是加密過的, 所以, 應用程式可以把需要保密的內容放到這個檔內, 比如說私密的通訊資料庫啦, 或是見不得人的照片…(呃, 我講到哪去了)

要建立一個OBB的檔案需要幾個tool:

mkobb.sh

pdkdf2gen

obbtool

由於目前SDK好像還沒這幾個東西, 所以必須從aosp裡面去找, 前兩者是一組的, 要一起配合, 而且mkobb.sh只能在Linux底下跑, 如果你嘗試在Mac上跑(像我一樣), 是會失敗的 (不過想想, 目前沒支援Mac也很合理)

Linux下, 有幾個kernel module是一定必要被載入的

sudo modprobe cryptoloop

sudo modprobe twofish

sudo modprobe vfat

這樣你才可以建立一個被加密過的loopback file system image

執行的指令如下:

mkobb.sh -d obbdir -k password -o obbfile 

-d 後面那個obbdir可以改成任何一個存在的目錄, 建立好的obb檔會幫你含入所有這目錄裡面的檔案, -k 後面輸入加密的密碼, -o 後面加入輸出的檔名

執行後內容會如下:

最後一行裡的"5f88a3619e6544ef"這個salt很重要, 要抄下來, 之後會用到

接下來就要用obbtool加簽章了, obbtool用法如下:

加簽章: obbtool a -n com.yourcompany.app -v 1 obbtest.obb -s 5f88a3619e6544ef

移除: obbtool r ~/Dropbox/obbtest.obb

-n 後面是package name, 必須要跟你的應用程式的package name相同, -v 後面則是自定的版本, -s 後面接的就是剛剛做mkobb.sh後產生的salt了

這樣這個obb檔就完成了, 可以把它放到手機sdcard或其他地方讓你的程式存取

在程式內掛載obb的話就要用到StorageManager了:

不過目前, 我碰到的狀況就是, 明明state已經變成MOUNTED, 但我就是取不到mounted path, 怪怪的

其實這是一個沒啥用的技巧(至少我還想不出應用), 就是加上一個像是浮水印的東西在畫面上, 不管在哪都會出現的, 像是這樣:

因為這個image要讓他一直不會消失, 所以他並不會是一個Activity, 因為Activity會有他的生命週期, 離開後就沒了, 所以要透過WindowManager去加上這個ImageView, 而且加上View的動作必須要給Background service去處理(這樣也就可以去做一些變化, 或動畫之類的)

首先, 要建立一個Service, 要達成這目的也很簡單, 在onCreate加上:

WindowManager wm = (WindowManager) this

.getSystemService(Service.WINDOW_SERVICE);

ImageView logo = new ImageView(this);

logo.setImageResource(R.drawable.onepieceoverlay);

WindowManager.LayoutParams lparam = new WindowManager.LayoutParams(

WindowManager.LayoutParams.WRAP_CONTENT,

WindowManager.LayoutParams.WRAP_CONTENT,

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 0, PixelFormat.TRANSLUCENT);

lparam.gravity = Gravity.RIGHT | Gravity.TOP;

wm.addView(logo, lparam);

這樣就好了, 其實很簡單, 就是透過WindowManager在system overlay上加個view

但這樣還不會work, 因為少了一個permission, 所以必須在AndroidManifest裡面加上

<uses-permission android:name=“android.permission.SYSTEM_ALERT_WINDOW”></uses-permission>

這樣就好了

不過system overlay沒辦法接touch event, 所以這邊放的view真的只是拿來放好看的

雖然Android可以透過ddms抓screen shot, 不過這樣畢竟沒iOS方便, iOS只要按個熱鍵就可以抓了, 不用接著電腦

要在Android上實現這功能其實也不難(不過,應該只能跑在rooted device吧?本文介紹的是直接跟AOSP一起build用platform certificate)

第一個直覺想到的是直接access framebuffer, 不過這樣實在太醜了, 我想要做的是直接利用Android既有的framework

想到framebuffer在往上一層就是surfaceflinger了, 稍微掃一下code果然發現有captureScreen這種東西, 所以下一步就是grep一下找看看範例囉!!沒想到, 運氣真好, 一下子就在 frameworks/base/services/surfaceflinger/tests/screencap 這邊找到範例, 還是一個standalone的程式

不過我想做的是直接收一個Intent就可以把screen capture到SD卡, 所以這個東西還要做進一步包裝, 於是動手把它包裝成JNI, 並寫了一個簡單的BroadcastReceiver去呼叫他…..花不到一個小時…搞定!!! XD

Source在這邊

不過, 這不是我原本想做的東西….繼續改… :P

抓到Gingerbread之後本來要build一份給Nexus one的, 後來Build break後就懶得管它, 昨天稍微研究一下解法, 果然跟我想的差不多

Builde break的點在於libcameraservice, 主要是某H公司的camera driver並沒open source, 要build必須先從Nexus One取出binary來build, 但偏偏Gingerbread的libcameraservice跟camera driver的interface有變, 像是HAL_getNumberOfCameras, HAL_getCameraInfo在舊版的driver並不存在

解決的方法有兩種:

  1. 用dummy driver
  2. 加個wrapper給它

目前實驗出來的結果很…殘念..還是沒能讓它的camera可以正常使用… orz

第一個方法比較簡單, 只要改makefile: frameworks/base/services/camera/libcameraservice/Android.mk

把USE_CAMERA_STUB:=true變成always的true (就是把一些判斷都mark掉就好)

這樣build出來的就會是用camera stub

第二個方法可以參考這邊

這方法只是加個wrapper把需要的幾個function加上去, 並包裝舊的call

一樣要改frameworks/base/services/camera/libcameraservice/Android.mk, 只是多個目錄放wrapper

Cyanogenmod也是用同一招解決的, 不同的是, 它並沒多生出一個lib來放這warpper, 它是直接改在CameraService.cpp

其實是同一個方法, 由於它多生一個define : BOARD_USE_FROYO_LIBCAMERA來enable/disable這段code(畢竟Cyanogenmod不是只有for N1), 所以要在 device/htc/passion-common/BoardConfigCommon.mk 裡面加上 BOARD_USE_FROYO_LIBCAMERA := true 來把它打開

基本上, 兩種方法N1的Camera都等於廢了, 所以用哪一種都一樣 

 

如果使用過Google MapView你會發現G社會叫你用你的package signature去申請map key(參考這邊)

那MapView本身又是如何取得你的package signature的呢? 

其實很簡單, 可以透過目前的Context用Context.getPackageName()取得package name, 然後再利用package name去PackageManager.getPackageInfo()去查詢package的資訊(PackageInfo), 裡面有項資訊"signatures"就是我們所要的(一個package是可以被sign多次的)

Facebook Android SDK所提供的Single sign on (SSO)也是得先在server端註冊你的package signature, 這樣它可以避免別人偷用你的API key去做single sign on的動作(因為別人的AP可能不會跟你sign同一個signature)

但Facebook的SSO是透過Facebook for Android來做login的動作的, 你的application是透過startActivityForResult去叫Facebook for Android, 這牽涉到兩個不同的package, 如果依上面的例子的話, MapView其實是鑲嵌在你的UI上, 跟你的application是同一個package, 所以可以拿context來取得這資訊

Facebook for Android是如何取得call他的人的signature呢?

其實也是用PackageManager.getPackageInfo() 來取得, 但它如何知道呼叫他的人是哪個package的?

這只要組合兩個API就可以辦到了

第一個是Binder.getCallingUid() , 這可以讓你取得你的Calling process的UID

第二個是PackageManager.getPackagesForUid() , 這可以讓你取得屬於這UID的所有package

或許這邊你可能會有點疑問, 不是不同的package可以share UID嗎? 這樣取出來的package可能有很多個, 我怎知道用哪個? 我們目的只是要取得signature, 所以用哪個都一樣, 因為share UID的必要條件就是這些相同UID的package都是要sign同一把key, 所以只要找出所有package共通的signature就大功告成了