2011年7月28日 星期四

Enable Webkit support SVG.

Platform : TI Omap4
Android : 2.3.4

原本預設webkit 不會支援SVG

打開瀏覽器 http://www.croczilla.com/bits_and_pieces/svg/samples/


裡面有不少範例可以驗證,打開範例後被當作文字檔顯示。
要讓webkit 支援SVG需要更改一些檔案

List:
   trunk/mydroid/device/ti/blaze/buildspec.mk.default
   trunk/mydroid/external/webkit/WebCore/Android.derived.jscbindings.mk
   trunk/mydroid/external/webkit/WebCore/Android.derived.v8bindings.mk
   trunk/mydroid/external/webkit/WebCore/bindings/v8/DerivedSourcesAllInOne.cpp
   trunk/mydroid/external/webkit/WebCore/loader/FrameLoaderClient.h



trunk/mydroid/device/ti/blaze/buildspec.mk.default

# To enable SVG in webcore define ENABLE_SVG:=true
 ifndef ENABLE_SVG
 ENABLE_SVG:=true
 ENABLE_SVG_ANIMATION:=true
 endif

打開ENABLE_SVG config & 增加 ENABLE_SVG_ANIMATION


trunk/mydroid/external/webkit/WebCore/Android.derived.jscbindings.mk
trunk/mydroid/external/webkit/WebCore/Android.derived.v8bindings.mk

ifeq ($(ENABLE_SVG_ANIMATION), true)
SVG_FLAGS += ENABLE_SVG_ANIMATION=1
endif

把缺少的SVG_FLAGS補上


trunk/mydroid/external/webkit/WebCore/bindings/v8/DerivedSourcesAllInOne.cpp

//#include "bindings/V8SVGFEMorphologyElement.cpp"

拿掉不需要得include

trunk/mydroid/external/webkit/WebCore/loader/FrameLoaderClient.h

//virtual void dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) = 0;
  virtual void dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) {}

修改會compile err的地方


然後clean build 即可







另外裝其他瀏覽器可以支援SVG 如:Firefox, Opera

2011年5月16日 星期一

Camera key event can't wake up?

首先觀看keylayout.kl檔,每個platform名稱都不一樣,像ti omap4 為omap-keypad.kl
內容如下:
key 399 GRAVE
key 2 1
key 3 2
key 4 3
key 5 4
key 6 5
key 7 6
key 8 7
key 9 8
key 10 9
key 11 0
key 158 BACK WAKE_DROPPED
key 230 SOFT_RIGHT WAKE
key 60 SOFT_RIGHT WAKE
key 107 ENDCALL WAKE_DROPPED
key 62 ENDCALL WAKE_DROPPED
key 229 MENU WAKE_DROPPED
key 139 MENU WAKE_DROPPED
key 59 MENU WAKE_DROPPED
key 127 SEARCH WAKE_DROPPED
key 217 SEARCH WAKE_DROPPED
key 228 POUND
key 227 STAR
key 231 CALL WAKE_DROPPED
key 61 CALL WAKE_DROPPED
key 232 DPAD_CENTER WAKE_DROPPED
key 108 DPAD_DOWN WAKE_DROPPED
key 103 DPAD_UP WAKE_DROPPED
key 102 HOME WAKE
key 105 DPAD_LEFT WAKE_DROPPED
key 106 DPAD_RIGHT WAKE_DROPPED
key 115 VOLUME_UP
key 114 VOLUME_DOWN
key 116 POWER WAKE
key 212 CAMERA
....
key 16 Q
key 17 W
key 18 E
key 19 R
key 20 T
key 21 Y
key 22 U
key 23 I
key 24 O
key 25 P
key 26 LEFT_BRACKET
key 27 RIGHT_BRACKET
key 43 BACKSLASH

首先將CAMERA改成

key 212 CAMERA WAKE_DROPPED

並重開機,但神奇的是進了suspend之後按下CAMERA卻沒有wake up,但kernel卻有resume...
原來在 frameworks/policies/base/phone/com/android/internal/policy/impl/KeyguardViewMediator.java 有一段code,frameworks會阻擋特定key不亮螢幕,並在背景執行該key的動作。

     /**
     * When a key is received when the screen is off and the keyguard is showing,
     * we need to decide whether to actually turn on the screen, and if so, tell
     * the keyguard to prepare itself and poke the wake lock when it is ready.
     *
     * The 'Tq' suffix is per the documentation in {@link WindowManagerPolicy}.
     * Be sure not to take any action that takes a long time; any significant
     * action should be posted to a handler.
     *
     * @param keyCode The keycode of the key that woke the device
     * @return Whether we poked the wake lock (and turned the screen on)
     */
    public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode) {
        if (DEBUG) Log.d(TAG, "onWakeKeyWhenKeyguardShowing(" + keyCode + ")");

        if (isWakeKeyWhenKeyguardShowing(keyCode)) {
            // give the keyguard view manager a chance to adjust the state of the
            // keyguard based on the key that woke the device before poking
            // the wake lock
            wakeWhenReadyLocked(keyCode);
            return true;
        } else {
            return false;
        }
    }

    private boolean isWakeKeyWhenKeyguardShowing(int keyCode) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_MUTE:
            case KeyEvent.KEYCODE_HEADSETHOOK:
            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
            case KeyEvent.KEYCODE_MEDIA_STOP:
            case KeyEvent.KEYCODE_MEDIA_NEXT:
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
            case KeyEvent.KEYCODE_MEDIA_REWIND:
            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
            case KeyEvent.KEYCODE_CAMERA:
                return false;
        }
        return true;
    }

從isWakeKeyWhenKeyguardShowing &onWakeKeyWhenKeyguardShowingTq 可知道符合特定key將不會被wake up。

adb 的serial number要如何修改?

由kernel/drivers/usb/gadget/android.c

static int android_probe(struct platform_device *pdev)
{
        struct android_usb_platform_data *pdata = pdev->dev.platform_data;
        struct android_dev *dev = _android_dev;
        printk(KERN_INFO "android_probe pdata: %p\n", pdata);

        if (pdata) {
                dev->products = pdata->products;
                dev->num_products = pdata->num_products;
                dev->functions = pdata->functions;
                dev->num_functions = pdata->num_functions;
                if (pdata->vendor_id)
                        device_desc.idVendor =
                                __constant_cpu_to_le16(pdata->vendor_id);
                if (pdata->product_id) {    
                        dev->product_id = pdata->product_id;    
                        device_desc.idProduct =    
                                __constant_cpu_to_le16(pdata->product_id);    
                }    
                if (pdata->version)    
                        dev->version = pdata->version;        
                if (pdata->product_name)    
                        strings_dev[STRING_PRODUCT_IDX].s = pdata->product_name;    
                if (pdata->manufacturer_name)    
                        strings_dev[STRING_MANUFACTURER_IDX].s =    
                                        pdata->manufacturer_name;    
                if (pdata->serial_number)    
                        strings_dev[STRING_SERIAL_IDX].s = pdata->serial_number;    
        }        
        return usb_composite_register(&android_usb_driver);    
}

可以知道serial_number是透過pdata->serial_number得到

而pdata則是在arch/arm/mach-omap2/usb-musb.c被註冊

/* standard android USB platform data */
static struct android_usb_platform_data andusb_plat = {
        .vendor_id              = OMAP_VENDOR_ID,
        .product_id             = OMAP_UMS_PRODUCT_ID,    
        .manufacturer_name      = "Texas Instruments Inc.",    
        .product_name           = "OMAP-3/4",    
        .serial_number          = device_serial,    
        .num_products           = ARRAY_SIZE(usb_products),    
        .products               = usb_products,    
        .num_functions          = ARRAY_SIZE(usb_functions_all),    
        .functions              = usb_functions_all,    
};
        
static struct platform_device androidusb_device = {    
        .name           = "android_usb",    
        .id             = -1,    
        .dev            = {    
                .platform_data  = &andusb_plat,    
        },    
};

在andusb_plat的struct中可以發現.serial_number,在深入追下去便發現

static void usb_gadget_init(void)
{
        unsigned int val[4] = { 0 };
        unsigned int reg;
#ifdef CONFIG_USB_ANDROID_RNDIS
        int i;
        char *src;
#endif
        reg = DIE_ID_REG_BASE + DIE_ID_REG_OFFSET;
    
        if (cpu_is_omap44xx()) {
                val[0] = omap_readl(reg);
                val[1] = omap_readl(reg + 0x8);
                val[2] = omap_readl(reg + 0xC);
                val[3] = omap_readl(reg + 0x10);
        } else if (cpu_is_omap34xx()) {
                val[0] = omap_readl(reg);
                val[1] = omap_readl(reg + 0x4);
                val[2] = omap_readl(reg + 0x8);
                val[3] = omap_readl(reg + 0xC);
        }

        snprintf(device_serial, MAX_USB_SERIAL_NUM, "%08X%08X%08X%08X",
                                        val[3], val[2], val[1], val[0]);

#ifdef CONFIG_USB_ANDROID_RNDIS
        /* create a fake MAC address from our serial number.
         * first byte is 0x02 to signify locally administered.
         */
        rndis_pdata.ethaddr[0] = 0x02;
        src = device_serial;
        for (i = 0; *src; i++) {
                /* XOR the USB serial across the remaining bytes */
                rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++;
        }
        platform_device_register(&rndis_device);
#endif
    
#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
        platform_device_register(&usb_mass_storage_device);
#endif
        platform_device_register(&androidusb_device);
}

原來serial_number是讀取CPU暫存器在丟回serial_number。

詳細的source code可以到gitorious

另外因工作的關係發現異想不到的問題,工作上的需求是出貨時每台serial number都要不一樣以便區分每台,而問題在於第一台插上windows XP並安裝adb driver後,第二台在插上結果卻需要在安裝新的adb driver,這問題看似沒什麼,但對於工廠來說時間就是金錢且windows 7不會有這問題。

參考自製USB問題排困解難指南最後面那段
步驟:
  1. regedit 並到[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\UsbFlags]
  2. 確認GlobalDisableSerNumGen的值是否為1
  3. 新增機碼IgnoreHWSerNum[VID][PID]定設為1 
  4. EX: VID=0x451 PID=0xD101 => IgnoreHWSerNum0451D101 = 1
  5. 開機

2011年5月8日 星期日

Custom list view

被交代修改個AP,功能是列出所有sensor還必須有包含還得到的Data,簡單的listactivity是無法應付需要的。

網路上找到一篇文章可以參考Custom ListView,但我還是碰到一些帶解決的問題。

  1. 我希望list是不特定長度,可隨系統讀到的變更。
  2. 按照Custom ListView做出來,sensor又如何動態更新。
接下來直接看程式碼,首先需要兩個layout
首先是sensor_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <ListView android:id="@+id/android:list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

再來是sensor_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical">

     <TextView android:id="@+id/sensorname"
         android:textSize="14px"
         android:textStyle="bold"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"/>
     <TextView android:id="@+id/f1"
         android:textSize="14px"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"/>
     <TextView android:id="@+id/f2"
         android:textSize="14px"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"/>
     <TextView android:id="@+id/f3"
         android:textSize="14px"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"/>

 </LinearLayout>

最後是Java的部份

package otaku_thinking.demo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.app.ListActivity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.SimpleAdapter;

public class sensorlistactivity extends ListActivity implements SensorEventListener {
    static final String LOG_TAG = "ATestingSensors";
    private SensorManager sensorManager;
    private List<Sensor> sensors;
    private ArrayList<HashMap<String, String>> list = new  ArrayList<HashMap<String, String>>();  
    final static String fields[] = { "f1", "f2", "f3"};
    private SimpleAdapter sensorAdapter = null;

    private void AddItem()
    {

        for( int i = 0 ; i < sensors.size() ; ++i )
        {   
            HashMap<String, String> map = new  HashMap<String, String>();  
            map.put( "sensorname" , sensors.get(i).getName());         
            map.put( fields[0] , "0");  
            map.put( fields[1] , "0");
            map.put( fields[2] , "0");
            list.add(map);  
        }
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.sensor_main);
         sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
         sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
         AddItem();
         sensorAdapter = new  SimpleAdapter( this ,  
                 list,
                 R.layout.sensor_list,                              
                 new  String[] { "sensorname" , "f1" , "f2" , "f3" },                     
                 new  int [] {R.id.sensorname,R.id.f1,R.id.f2,R.id.f3}
                 );     
         
         setListAdapter(sensorAdapter);            
    }

    protected void onStart() {
        super.onStart();
        for( int i = 0 ; i < sensors.size() ; ++i )
        {  
            sensorManager.registerListener( 
                    this, 
                    sensors.get(i),
                    SensorManager.SENSOR_DELAY_UI );
        }
    }
    
    protected void onStop() {
        super.onStop();
        if( sensorManager != null )
            sensorManager.unregisterListener( this );
    }
    
    // SensorEventListener
    public void onAccuracyChanged (Sensor sensor, int accuracy) {
    }

    public void onSensorChanged(SensorEvent sensorEvent) {

        HashMap<String, String> map = null;
        for( int i = 0 ; i < sensors.size() ; ++i )
        {   
            
            if(sensors.get(i).getName().endsWith(sensorEvent.sensor.getName()))
            {
                map = list.get(i);  
            }
        }
        
        StringBuilder b = new StringBuilder();
        for( int i = 0 ; i < sensorEvent.values.length ; ++i ) {
            if( i > 0 )
                b.append( " , " );
            b.append( Float.toString( sensorEvent.values[i] ) );
        }

        int count = sensorEvent.values.length < fields.length ?
                    sensorEvent.values.length :
                    fields.length;
        for( int i = 0 ; i < count ; ++i ) {
            map.put(fields[i] , Float.toString( sensorEvent.values[i] ) );  
        }
        sensorAdapter.notifyDataSetChanged();
    }
}

而我的第一個問題在AddItem()得到解決

private void AddItem()
    {

            for( int i = 0 ; i < sensors.size() ; ++i )
            {   
                HashMap<String, String> map = new  HashMap<String, String>();  
                map.put( "sensorname" , sensors.get(i).getName());         
                map.put( fields[0] , "0");  
                map.put( fields[1] , "0");
                map.put( fields[2] , "0");
                list.add(map);  
            }
    }

宣告個HashMap並陸續餵資料,之後加進list裡面,之後new SimpleAdapter將data與listactivity關聯建立起來。

sensorAdapter = new  SimpleAdapter( this ,  
                 list,
                 R.layout.sensor_list,                              
                 new  String[] { "sensorname" , "f1" , "f2" , "f3" },                     
                 new  int [] {R.id.sensorname,R.id.f1,R.id.f2,R.id.f3}
                 );

而第二個問題在onSensorChanged()得到解決
public void onSensorChanged(SensorEvent sensorEvent) {

        HashMap<String, String> map = null;
        for( int i = 0 ; i < sensors.size() ; ++i )
        {   
            
            if(sensors.get(i).getName().endsWith(sensorEvent.sensor.getName()))
            {
                map = list.get(i);  
            }
        }
        
        StringBuilder b = new StringBuilder();
        for( int i = 0 ; i < sensorEvent.values.length ; ++i ) {
            if( i > 0 )
                b.append( " , " );
            b.append( Float.toString( sensorEvent.values[i] ) );
        }

        int count = sensorEvent.values.length < fields.length ?
                    sensorEvent.values.length :
                    fields.length;
        for( int i = 0 ; i < count ; ++i ) {
            map.put(fields[i] , Float.toString( sensorEvent.values[i] ) );  
        }
        sensorAdapter.notifyDataSetChanged();
    }
透過list.get(i)得到原始data,並加以修改之後告知sensorAdapter update date (sensorAdapter.notifyDataSetChanged())。


2011年4月13日 星期三

How to root ?

這問題一直困擾我很久,自己手機明明很簡單就是不知道為什麼自己做的project都無法讓apk root。


在教學之前先了解一下 UID
在安裝應用程式之後Android 會配給每個Application一個獨特UID,而Application的UID是由10000之後往後遞加。系統的詳細配置可以由mydroid/system/core/include/private/android_filesystem_config.h看出一二。
#if (CONFIG_4430SDP_KEYPAD & CONFIG_RECOVERY)
#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 */
#define AID_LOG           1007  /* log devices */
#define AID_COMPASS       1008  /* compass device */
#define AID_MOUNT         1009  /* mountd socket */
#define AID_WIFI          1010  /* wifi subsystem */
#define AID_ADB           1011  /* android debug bridge (adbd) */
#define AID_INSTALL       1012  /* group for installing packages */
#define AID_MEDIA         1013  /* mediaserver process */
#define AID_DHCP          1014  /* dhcp client */
#define AID_SDCARD_RW     1015  /* external storage write access */
#define AID_VPN           1016  /* vpn system */
#define AID_KEYSTORE      1017  /* keystore subsystem */

#define AID_SHELL         2000  /* adb and debug shell user */
#define AID_CACHE         2001  /* cache access */
#define AID_DIAG          2002  /* access to diagnostic resources */

/* The 3000 series are intended for use as supplemental group id's only.
 * They indicate special Android capabilities that the kernel is aware of. */
#define AID_NET_BT_ADMIN  3001  /* bluetooth: create any socket */
#define AID_NET_BT        3002  /* bluetooth: create sco, rfcomm or l2cap sockets */
#define AID_INET          3003  /* can create AF_INET and AF_INET6 sockets */
#define AID_NET_RAW       3004  /* can create raw INET sockets */
#define AID_NET_ADMIN     3005  /* can configure interfaces and routing tables. */

#define AID_MISC          9998  /* access to misc storage */
#define AID_NOBODY        9999

#define AID_APP          10000 /* first app user */

下面是取自鳥哥第十四章、Linux 帳號管理與 ACL 權限設定

id 範圍該 ID 使用者特性
0
(系統管理員)
當 UID 是 0 時,代表這個帳號是『系統管理員』! 所以當你要讓其他的帳號名稱也具有 root 的權限時,將該帳號的 UID 改為 0 即可。 這也就是說,一部系統上面的系統管理員不見得只有 root 喔! 不過,很不建議有多個帳號的 UID 是 0 啦~
1~499
(系統帳號)
保留給系統使用的 ID,其實除了 0 之外,其他的 UID 權限與特性並沒有不一樣。預設 500 以下的數字讓給系統作為保留帳號只是一個習慣。
由於系統上面啟動的服務希望使用較小的權限去運作,因此不希望使用 root 的身份去執行這些服務, 所以我們就得要提供這些運作中程式的擁有者帳號才行。這些系統帳號通常是不可登入的, 所以才會有我們在第十一章提到的 /sbin/nologin 這個特殊的 shell 存在。
根據系統帳號的由來,通常系統帳號又約略被區分為兩種:
1~99:由 distributions 自行建立的系統帳號;
100~499:若使用者有系統帳號需求時,可以使用的帳號 UID。
500~65535
(可登入帳號)
給一般使用者用的。事實上,目前的 linux 核心 (2.6.x 版)已經可以支援到 4294967295 (2^32-1) 這麼大的 UID 號碼喔!
可以看出Android Application在Linux上UID的規劃方式。
而Application可以透過AndroidManifest.xml去請求特殊的權限。
例如
去請求要有vibrate的權限
更多的權限可以去Android SDK尋找。


而當超過這些permission時要怎麼辦?這時就需要讓你的apk具有root的權限。
使用的方式就跟ubuntu一樣透過su將自己的uid變成,但有個問題就是project內所附的su是無法做到的。這是要感謝xda上的人提供 su & Superuser.apk。
su 會去listen有人是否使用,若有人要使用會去通知Superuser.apk ,這時會出現視窗詢問你是否取得root權限。
su & Superuser.apk 可以在xda上抓:[APP] Superuser 2.3.6.1 - Now on the Market

adb push su /system/bin/
adb shell chown root.root /system/bin/su
adb shell chmod 6755 /system/bin/su
adb push Superuser.apk /system/app

這裡又是個學問,讓我卡很久,重點在於 chown & chmod這兩個。
詳細可以參考鳥哥第七章、Linux 檔案與目錄管理
簡單來說當apk使用su時本身UID為root 身份而非Application 的UID。(第七章內的[SUID程式執行的過程示意圖]可以明瞭。)