Pages

顯示具有 Android 標籤的文章。 顯示所有文章
顯示具有 Android 標籤的文章。 顯示所有文章

2011年11月15日 星期二

[Cupcake] Multi-touch Part2

以下是在 Androdi 1.5 Platform 實現,當初的 MotionEvent 並沒有 Multi-Touch 相關欄位,所以是拿 size(4 bytes) 欄 來替代,現在 2.x 版已經有這些欄位。

[Cupcake] Multi-touch Part1

  由於 Android 1.5 還沒支援多點觸控, 對於我們這種看人臉色吃飯的系統廠, 一種方式就是坐以待斃, "要等 Google 之後的版本支援才能做, 目前辦不到"。但 touch panel 明明可以支援到兩點觸控, 所以, 秉持身為 RD 的堅持, 在稍微了解相關的程式碼後, 我們只要稍微修改一下 driver 和 package, 就讓 Android 1.5 的應用程式也可以做到 multi-touch, 底下這張圖顯示底層收到的封包隔式、driver/framework在單指以及雙指的數值表現:

AnalogClock - Matrix Rotation

旋轉
         這個範例直接對 Canvas 做旋轉,然後把圖(Drawable)畫上去,就可以達到圖片旋轉的效果。##ReadMore##

廣播
          AnalogClock 在類別中宣告一個 BroadcastReceiver 變數,並實作 onReceive,而 IntentFilter 的描述直接在程式碼中,非 AndroidManifest.xml。
IntentFilter filter = new IntentFilter(); 
filter.addAction(Intent.ACTION_TIME_TICK); 
filter.addAction(Intent.ACTION_TIME_CHANGED); 
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 
getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);

參考資料

Calculate Time Consuming

Java Layer
  • Timer
    • System.nanoTime .. (Link)
    • SystemClock.uptimeMillis .. (Link)
    • SystemClock.elapsedRealtime .. (Link)
  • System Time (?)
    • Data Class .. (Link) 
##ReadMore##
C++ Layer
(#include <utils/Timers.h>)
  • Timer
    • DurationTimer
      • DurationTimer::start()
      • DurationTimer::stop()
      • DurationTimer::durationUsecs

[Froyo] Compatibility Test Suite

       Android 是一個 Open Source Project,每個人都可以自由修改,也因此市面上有許多 Andorid Device,Google 為了確保 Android 程式可以順利在這些裝置上運行,提供 CTS 這個工具。這在 Android 2.1 版之前是無法自由取得,不過從這個版本開始,可以從這個網頁取得;另外還有簡介。##ReadMore##甚至在 Android Source Code 都可以從 source root 看到 cts/ 這個資料夾,不過我編譯後,雖然成功在 ASOP/out/host/bin 產生 cts 的腳本,但是相關的xml 與 apk 都沒有在 ASOP/out/host/linux-x86/cts 產生。另外,還要下載 SDK,因為會用到 adb 這個工具。

使用方式暫時參考底下的連結,因為還沒有裝置可以親自測試。已經在 NAZ10 上面測試,目前還在跑,我是下載 2.1 版的 sdk 與 cts,然後記得設定 SDK_ROOT,因為會用到 adb,然後執行方式,例如:
  1. 啟動CTS:./startcts
  2. 列舉有多少plan可以跑:ls --plan (擺在android-cts/repository/plans)
  3. 執行某個plan:start --plan VM   (執行的對象在android-cts/repository/testcases)
        其他用法,就參考底下連結吧!執行後的結果會放在 android-cts/repository/results,但我沒去研究 result 都寫些甚麼。

        保險起見,須先確認 SDK 版本,因為就算是給 2.2 是用,r6 或者 r7 跑出來的相容性測試結果就可能不同。至於要如何判斷正確的 SDK 版本,目前想到的方式是,直接使用 Android source code 編譯出來的 CTS 與 SDK tools,然後去執行它,就會出現 SDK 版本資訊,只是說,可能需要處理一下擺放路徑所造成的錯誤訊息。

參考資料:

Intent Sample

        在同一個 package 中,利用 Intent 啟動另一個 Activity,並透過 Bundle 物件帶參數給他 (下面的例子是送一個字串給接收端的 TextView) ##ReadMore##

<<發送端>>
Intent intent = new Intent();
intent.setClass(this, IntentReceiving.class);

Bundle bundle = new Bundle();
bundle.putString("Receive", "Intent Receiving");  // (Key - Value)
intent.putExtras(bundle);      

startActivity(intent);

<<接收端>>
CharSequence cs;
Bundle bundle = this.getIntent().getExtras();
cs = bundle.getString("Receive");
textview.setText(cs);

        也可以不透過 Bundle,直接 intent.setString() 或 intent.setInt()。另外,在宣告 IntentFilter 時,除了在 AndroidManifest.xml 中定義;也可直接在程式碼中描述。

參考資料

System Resource

        在 framework/base/core/res 有許多已經定義好的資源可以使用,但自己寫得應用程式,通常還是在自己的 res/。##ReadMore##

        假設現在我要讓 Settings->Display->Brightness 的 Preference Dialog 出現 Auto Brightness 的 checkbox 和 text。只要在 framework/base/core/res/res/values/config.xml 做修改:
<bool name="config_automatic_brightness_available">false</bool>

        這樣一來,這個 checkbox 和 text 就跑出來了。至於說,為甚麼這樣就會出現,是因為在 BrightnessPreference.java 在建構函式讀取這個變數值,來決定這個 UI 要不要秀出來。
mAutomaticAvailable = context.getResources().getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);

Thread & Handler

   如果在某個 Activity/Service/Class 中使用執行緒,然後,需透過該執行緒來變更/通知某個事件,就可以使用 Handler。##ReadMore##
private static final int LIGHTSENSOR_LUX = 0x101;   // 自訂常數

Thread t = new Thread() {
    public void run() {
        while (true) {
            int getLuxValue = 0;

            Message msg = new Message();
            msg.what = LIGHTSENSOR_LUX;
            msg.arg1 = getLuxValue;
            handler.sendMessage(msg); 

            try {   
               Thread.sleep(500);    
            } catch (InterruptedException e) {   
               Thread.currentThread().interrupt();   
            }
        }
    }
};
t.start();

private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {   
        case LIGHTSENSOR_LUX:
            Log.e("Test", "Lux Value = "+msg.arg1);
            break; 
    }   
    super.handleMessage(msg);
}

   Message 提供三個公開變數可以傳資料:arg1、arg2 是傳整數,obj 是傳 object type 資料;還有 setData(Bundle) 可以使用,但還沒用過。

參考網址

  • 利用 Handler 來更新 Android UI .. (Link)
  • Andorid Developer Messge .. (Link)
  • Andorid Developer Handler .. (Link)
  • Why isn't the thread stopping .. (Link)
  • 背景執行服務(Service) .. (Link)

2011年11月10日 星期四

[Froyo] Airplane Mode

        Airplane Mode 開啟就會斷掉所有通訊 (BT、WiFi、3G)。在 Android 上,這個功能要能夠正常運作,必須有 3G module。過程如下:

2011年7月25日 星期一

偵測有線耳機的插拔

這個功能也是 UEvent 的應用,繼承 UEventObserver,並且實作 onUEvent callback function,參考這份文件,有挺完整的介紹。不過,有幾點需要注意:##ReadMore##
  • 實作的類別改名為 WiredAccessoryObserver,這代表著,它會偵測不同的有線配件,如:耳機、HDMI。
  • 在 boot complete 之後,先到 /sys 底下去讀取耳機起始狀態,然後,開始監督 DEVPATH 這個子字串。

  原本是叫做 HeadsetObserver,看了 Android 3.1 mr2,發現已經被整合在一起。不過架構是不變的,如果偵測到插拔的動作,就會發 intent 通知 Audio Service。

  之前有發現,系統進入 suspend 之後,再去做插拔的動作,然後 resume,系統並不知道有做這個動作,原因是,kernel module 並沒有實作 resume 的函式,只要補上去就可以啦!

2011年5月20日 星期五

Battery Power Update

這是 UEvent 的應用之一,底層會定期送 battery information 給 Android,而上層負責處理的是 Battery Service。

路徑
   framework/base/services/java/com/android/server/BatteryService.java
說明
  • BatteryService 會註冊一個 UEventObserver,用來觀察是否有符合 SUBSYSTEM=power_supply 子字串的 uevent 發上來,假如有,就會觸發 onUEvent。
    mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
  • 當 onUEvent 觸發,會做兩件事,第一,呼叫 update,這個函式會叫用 JNI native_update,去 /sys 底下讀取 battery information,並利用 setIntField、setBooleanField 等函式,設定給 battery service。

  • 在 update 結束後,執行 processValues,處理從底下得到的數值,接著,發 ACTION_BATTERY_CHANGED Intent 給 SystemUI[註] ,更新電量資訊、圖示,如果電量過低,還會發 alert dialog。
  UEvent 是定期發出來,即使電量不變也會收到。另外,在 SystemUI[註] 會用到 framework/base/core/res/res/values/string.xml 和 configs.xml 定義的數值。例如:alert message、battery criteria 等等。

[註] Froyo 是由 StatusBarPolicy 處理 ACTION_BATTERY_CHANGED
參考資料


2011年5月18日 星期三

UEvent Simple Introduction

Android 有蠻多服務透過收 UEvent 的方式,取得從底層送上來的訊息,例如:Rotation、Battery、StatusBar 等等,可以在 framework/base/service 看到這些足跡。那麼,它的 code flow 是怎麼跑的,底下咱們來瞧一瞧。

Java Framework
路徑:
framework/base/core/java/android/os/UEventObserver.java
說明:
abstract class UEventObserver
  • UEventObserver 為系統監控是否有 uevent 從 kernel 發出來,它是一個抽象類別,程式設計師可以去繼承它,並且實作
    abstract void onUEvent(UEvent event);
  • 呼叫 startObserving,就會把需要觀察的裝置路徑,儲存在列表中(路徑長得像:"DEVPATH=/devices/...." or "SUBSYSTEM=..."),以便之後和 uevent 字串比對用。
  • 因為 UEventObserver 的運作是透過執行緒不斷地去讀取,所以第二步的 startObserving 就會去檢查執行緒起動了沒。另外,這一條執行緒是系統中僅有的一條執行緒,會透過以下變數判斷執行緒是不是已經生成。
    static boolean sThreadStarted
  • 若執行緒讀到的 uevent 字串,經比對後符合列表中的裝置路徑,就會觸發 onUEvent 這個函式。
class UEvent
  • UEvent 這個類別提供一個 HashMap 資料結構,會把透過 socket 收到的 uevent 字串做拆解,把「=」前當成 key、「=」後當成 value,以便 onUEvent 函式需要的時候使用。uevent 的長相如下:(怪怪的字元應該是結束字元)
    change@/devices/platform/tegra_i2c.1/i2c-1/1-0058/power_supply/battery�� ACTION=change�� DEVPATH=/devices/platform/tegra_i2c.1/i2c-1/1-0058/power_supply/battery�� SUBSYSTEM=power_supply�� POWER_SUPPLY_NAME=battery�� POWER_SUPPLY_TYPE=Battery�� POWER_SUPPLY_STATUS=Unknown��POWER_SUPPLY_HEALTH=Good�� POWER_SUPPLY_PRESENT=0�� POWER_SUPPLY_TECHNOLOGY=Li-ion��POWER_SUPPLY_VOLTAGE_NOW=0�� POWER_SUPPLY_CAPACITY=0�� POWER_SUPPLY_TEMP=-2731�� SEQNUM=721��

private static native void native_setup();
private static native int next_event(byte[] buffer);
  • 這兩個是叫用 JNI 介面,第一個用來做是初始化;第二個是用來讀取 uevent,都在 UEventThread.run() 中使用。


JNI
路徑:
framework/base/core/jni/android_os_UEventObserver.cpp
說明:
android_os_UEventObserver_native_setup
  • 這就是 UEventObserver.java 的 native_setup 所對應的 JNI 接口,這個函式會呼叫 HAL 中的 uevent_init。
android_os_UEventObserver_next_event
  • 這就是 UEventObserver.java 的 next_event 所對應的 JNI 接口,這個函式會呼叫 HAL 中的 uevent_next_event。


HAL(hardware)
路徑:
hardware/libhardware_legacy/uevent/uevent.c
     hardware/libhardware_legacy/include/hardware_legacy/uevent.h
說明:
uevent_init()
  • 這個函式會建立 AF_NETLINK 類型的 socket,然後 bind。

uevent_next_event(char* buffer, int buffer_length)
  • 利用 poll 的方式,對這個 socket fd 作輪詢,如果有資料,就用 recv 去接收,並放在 buffer。poll 的 timeout 等於 -1,代表「Infinitely」。


備註

  • Driver 必須要發出 uevent,語法是 kobject_uevent(&kobj, KOBJ_CHANGE),KOBJ_CHANGE 也可以是 KOBJ_ADD、KOBJ_REMOVE。

參考資料

2011年5月6日 星期五

config.gz

想了解 kernel config 的設定,可以在開機進入系統後,利用 adb tool 輸入以下指令:##ReadMore##
./adb pull /proc/config.gz

  解壓縮這個檔案,就可以得知 kernel configuration。例如:我想要把 VPN enable,Android OS 的部分已經完成,只須將底層設置打開,這時候就可以把 Nexus One 的 config.gz 拿出來比較一下。不過,想在開機後看到這個檔案,必須在編譯核心時,把 CONFIG_IKCONFIG_PROC=y 才能看到。

[Froyo] Sensor Notes

  我所實作的部分是 light sensor,因為它比較單純,至於其他類型的感測器,有各自的 know how 需要了解,所以這裡就不討論。<參考>

  Sensor HAL 的標頭檔位於 hardware/libhardware/include/hardware/sensors.h,有一個結構 sensor_module_t,它繼承自 hardware_module_t,這個結構是在 hardware.h。sensor_module_t 的變數宣告已在 hardware.h 規定成「HAL_MODULE_INFO_SYM」,這裡頭會定義一個 open 的函式指標,用來初始化 sensor_data_device_t 和 sensor_control_device_t。
const struct sensors_module_t HAL_MODULE_INFO_SYM
結構 sensor_data_device_t 定義裝置的 open、close、poll,這些東西會在 SensorManager 叫用;sensor_control_device_t 定義裝置的 activate、delay、wake,這些東西會在 SensorService 叫用。

  同一種類型的感測器可能多個,所以是一個 list,不過現在 Froyo 是直接回傳串列的第一個。如果有些函式在底層沒有實作,那 HAL 會直接 return,像 poll function 在 light sensor driver 就沒有實作。所以想要讀 light sensor 的值,只能被動地一直去讀 /sys 下的節點。

  /sys 下的檔案是一般類型的檔案,所以 read 之後,需要做 lseek(fd, 0, SEEK_SET); 移回開頭,否則會讀不到東西,第二個參數是指移動多少 byte。另外, lseek(int fildes,0,SEEK_END) 移動到結尾;lseek(int fildes,0,SEEK_CUR) 移動到目前位置 .. <參考>。

  PowerManagerService 有處理 proximate sensor 和 light sensor,針對 light sensor 的部分,它會去檢查 framework/base/core/res/res/values/config.xml 的 config_automatic_brightness_available 設定值,如果為 true,它就認為有 light sensor 存在,就會嘗試跟 SensorManager 註冊,並處理 auto brightness 的內容。不過,目前 HAL 吐上來的數值範圍 落在 [10,1000],如果給這個 service 處理會發生問題,需要做轉換。

  當系統發生出 ACTION_BOOT_COMPLETED,就去啟動 sensor service,這部分實作 sensor initialization & event callback function。

  假設把 source code 放在 hardware/libhardware 底下,需要將 hardware/libhardware/Android.mk 做以下修改,才能在 clean build 編譯到 sensor 的部分。

   include $(addsuffix /Android.mk, $(addprefix $(LOCAL_PATH)/, \
     modules/gralloc \
     modules/sensors \
   ))

  HAL library 名稱有其規則,可以是 sensors.tegra 或者 sensors.default。另外,sensor library 會呼叫 hw_get_module load() -> dlsym 載入 library。

  /* This goes first so that it can pick up a different file on the emulator. */
  "ro.hardware",
  "ro.product.board",
  "ro.board.platform",
  "ro.arch"

2011年5月5日 星期四

System Property

「System Property」是儲存資料常用的方式,如同「Windows Registry」。它的 APIs 散佈在 Android codebase 的各個角落:.mk、.rc、.java、.c.cpp 都找的到。簡單來說,System Property 分為「get」和「set」兩種方法,「get」相對而言就容易許多,因為不需要去檢查權限,code flow 只要呼叫到 libcutils 的 property_get 即可。所以底下只討論「set」。

  
  由上圖得知,send_prop_msg 會透過 socket 將 cmd message 送給 INIT,當 INIT 接收到後,會呼叫 handle_property_set_fd 來做處理,該函式就會呼叫 check_perms 檢查權限,這也是為什麼有時候設定屬性會失敗的原因。check_perms 參考底下的 ACL (Access Control List) 做判斷:

<PATH>
system/core/init/property_service.c
struct {
const char *prefix;
unsigned int uid;
unsigned int gid;
} property_perms[] = {
{ "net.rmnet0.",      AID_RADIO,    0 },
{ "net.usb",          AID_RADIO,    0 },
{ "net.gprs.",        AID_RADIO,    0 },
{ "net.ppp",          AID_RADIO,    0 },
{ "ril.",             AID_RADIO,    0 },
{ "gsm.",             AID_RADIO,    0 },
{ "persist.radio",    AID_RADIO,    0 },
{ "net.dns",          AID_RADIO,    0 },
{ "net.",             AID_SYSTEM,   0 },
{ "dev.",             AID_SYSTEM,   0 },
{ "runtime.",         AID_SYSTEM,   0 },
{ "hw.",              AID_SYSTEM,   0 },
{ "sys.",             AID_SYSTEM,   0 },
{ "service.",         AID_SYSTEM,   0 },
{ "wlan.",            AID_SYSTEM,   0 },
{ "dhcp.",            AID_SYSTEM,   0 },
{ "dhcp.",            AID_DHCP,     0 },
{ "vpn.",             AID_SYSTEM,   0 },
{ "vpn.",             AID_VPN,      0 },
{ "debug.",           AID_SHELL,    0 },
{ "log.",             AID_SHELL,    0 },
{ "service.adb.root", AID_SHELL,    0 },
{ "service.adb.tcp.port", AID_SHELL,    0 },
{ "persist.sys.",     AID_SYSTEM,   0 },
{ "persist.service.", AID_SYSTEM,   0 },
{ "persist.security.", AID_SYSTEM,   0 },
{ NULL, 0, 0 }
};
「check_perms」檢查的流程為:
  1. 如果 UID 為 ROOT .. PASS。
  2. 如果屬性為 ro 開頭,就接續判斷 ro. 之後的字串。
  3. 和 ACL 一一比對,prefix 要相同、且 uid/gid 有一個相同 .. PASS。
底下是 uid/gid 定義的地方
<PATH>

system/core/include/private/android_filesystem_config.h

#define AID_ROOT             0  /* traditional unix root user */

#define AID_SYSTEM        1000  /* system server */

#define AID_RADIO         1001  /* telephony subsystem, RIL */
#define AID_BLUETOOTH     1002  /* bluetooth subsystem */
#define AID_GRAPHICS      1003  /* graphics devices */
#define AID_INPUT         1004  /* input devices */
#define AID_AUDIO         1005  /* audio devices */
#define AID_CAMERA        1006  /* camera devices */
...
...
static const struct android_id_info android_ids[] = {
{ "root",      AID_ROOT, },
{ "system",    AID_SYSTEM, },
{ "radio",     AID_RADIO, },
{ "bluetooth", AID_BLUETOOTH, },
{ "graphics",  AID_GRAPHICS, },
{ "input",     AID_INPUT, },
{ "audio",     AID_AUDIO, },
{ "camera",    AID_CAMERA, },
...
};

之前為了在 BluetoothService.java (UID/GID 皆為 system) 設定屬性,定義了 persist.bluetooth.a2dp 和 audioflinger.bluetooth.a2dp,但怎麼設定都設定不動,後來胡亂地嘗試 persist.service.bluetooth.a2dp 就成功了,回過頭來看,就是因為滿足 ACL 的要求啦!

如果自行定義的 prefix 可以正常存取,要嘛 UID/GID 為 ROOT;不然就只能唯讀。最後補充一點,在 /default.prop 這個檔案內看到的屬性,是 makefile 中由 ADDITIONAL_DEFAULT_PROPERTIES 這個變數產生;而 /system/build.prop 的是 build/tools/buildinfo.sh 和以下這段所產生。
ADDITIONAL_BUILD_PROPERTIES :=
$(ADDITIONAL_BUILD_PROPERTIES) \
$(PROPERTY_PROPERTY_OVERRIDES)

參考資料

Audio Reference

參考資料

Dalvik Heap Size Adjustment

調整這個數值,原則上,只要在 system/ 或者 framework/base/ 底下搜尋 dalvik.vm.heapsize 即可。這個東西可以在 init.c 用 property_set;可以在 init.rc 用 setprop;也可以像底下那樣,改在 makefile...##ReadMore##

PRODUCT_PROPERTY_OVERRIDES += \
   dalvik.vm.heapstartsize=5m \
   dalvik.vm.heapgrowthlimit=48m \
   dalvik.vm.heapsize=256m

系統啟動時,可以透過 adb shell getprop 來查看 dalvik.vm.heapsize 數值為何。

Monkey

之前為了檢驗修改執行緒優先權的應用程式,是否不易出現 ANR,所以用這個指令做重複性測試。##ReadMore##


<指令> 檢測 browser
./adb shell monkey -p  com.android.browser -v 50000

2011年5月4日 星期三

Low Memory Killer

當系統記憶體不足時,會依優先權來殺掉某些行程,以釋放記憶體。但是,Android 預設並沒有開啟這項功能,所以需要去啟用它。

<step1> Enable Kernel Module
CONFIG_ANDROID_LOW_MEMORY_KILLER=y

<step2> Setup Recycle Memory Criteria in init.rc
# Define the oom_adj values for the classes of processes that can be
# killed by the kernel.  These are used in ActivityManagerService.
setprop ro.FOREGROUND_APP_ADJ 0
setprop ro.VISIBLE_APP_ADJ 1
setprop ro.PERCEPTIBLE_APP_ADJ 2
setprop ro.HEAVY_WEIGHT_APP_ADJ 3
setprop ro.SECONDARY_SERVER_ADJ 4
setprop ro.BACKUP_APP_ADJ 5
setprop ro.HOME_APP_ADJ 6
setprop ro.HIDDEN_APP_MIN_ADJ 7
setprop ro.EMPTY_APP_ADJ 15

# Define the memory thresholds at which the above process classes will
# be killed.  These numbers are in pages (4k).
# These are currently tuned for tablets with approx 1GB RAM.
setprop ro.FOREGROUND_APP_MEM 8192
setprop ro.VISIBLE_APP_MEM 10240
setprop ro.PERCEPTIBLE_APP_MEM 12288
setprop ro.HEAVY_WEIGHT_APP_MEM 12288
setprop ro.SECONDARY_SERVER_MEM 14336
setprop ro.BACKUP_APP_MEM 14336
setprop ro.HOME_APP_MEM 14336
setprop ro.HIDDEN_APP_MEM 16384
setprop ro.EMPTY_APP_MEM 20480

# Write value must be consistent with the above properties.
# Note that the driver only supports 6 slots, so we have combined some of
# the classes into the same memory level; the associated processes of higher
# classes will still be killed first.
write /sys/module/lowmemorykiller/parameters/adj 0,1,2,4,7,15

write /proc/sys/vm/overcommit_memory 1
write /proc/sys/vm/min_free_order_shift 4
write /sys/module/lowmemorykiller/parameters/minfree 8192,10240,12288,14336,16384,20480

# Set init its forked children's oom_adj.
write /proc/1/oom_adj -16

# Tweak background writeout
write /proc/sys/vm/dirty_expire_centisecs 200
write /proc/sys/vm/dirty_background_ratio  5

  在 system/core/init/init.c 中會去讀取 init.rc,並將以上這些參數設定給 low memory killer。另外,上層還有一支 ActivityManagerService,但不知道他和底層 low memory killer 如何溝通。

 
Blogger Templates