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())。