Pages

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.xmlconfigs.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.xmlconfig_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