1 Android system will try to reconnect paired A2DP device automatically after BT enable.
123456789
04-2501:56:31.080D/BluetoothAdapterService(2093):AutoConnectingA2DPProfilewithdevice50:C9:71:0D:D2:D9packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cppstaticjbooleanconnectA2dpNative(JNIEnv*env,jobjectobject,jbyteArrayaddress)constbt_interface_t*btInf=getBluetoothInterface();constbtav_interface_t*sBluetoothA2dpInterface=(btav_interface_t*)btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);status=sBluetoothA2dpInterface->connect((bt_bdaddr_t*)addr)external/bluetooth/bluedroid/btif/src/btif_av.cbtif_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE,bd_addr,connect_int);//This will trigger an event in btu_task, now the caller thread returned.GKI_send_msg(BTIF_TASK,BTU_BTIF_MBOX,p_msg);
2 This will later trigger a A2DP server event API_CONNECT_REQ_EVT in state CCB_IDLE_ST:
3 A2DP server handle this event in bellow two actions:
123456789101112
external/bluetooth/bluedroid/stack/avdt/avdt_ccb.c129constUINT8avdt_ccb_st_idle[][AVDT_CCB_NUM_COLS]={130/* Event Action 1 Action 2 Next state */139/* API_CONNECT_REQ_EVT */{AVDT_CCB_SET_CONN,AVDT_CCB_CHAN_OPEN,AVDT_CCB_OPENING_ST},//3.1 Set CCB variables associated with AVDT_ConnectReq().996voidavdt_ccb_set_conn(tAVDT_CCB*p_ccb,tAVDT_CCB_EVT*p_data)BTM_SetSecurityLevel(TRUE,"",BTM_SEC_SERVICE_AVDTP,p_data->connect.sec_mask,AVDT_PSM,BTM_SEC_PROTO_AVDT,AVDT_CHAN_SIG);//3.2 initiate a signaling channel connection.87voidavdt_ccb_chan_open(tAVDT_CCB*p_ccb,tAVDT_CCB_EVT*p_data)BTM_SetOutService(p_ccb->peer_addr,BTM_SEC_SERVICE_AVDTP,AVDT_CHAN_SIG);avdt_ad_open_req(AVDT_CHAN_SIG,p_ccb,NULL,AVDT_INT);
4 How L2CAP handle this channel connection:
12345678910111213
external/bluetooth/bluedroid/stack/l2cap/l2c_api.c229UINT16L2CA_ErtmConnectReq(UINT16psm,BD_ADDRp_bd_addr,tL2CAP_ERTM_INFO*p_ertm_info)p_lcb=l2cu_allocate_lcb(p_bd_addr,FALSE);l2cu_create_conn(p_lcb);//This function initiates an acl connection via HCI2180BOOLEANl2cu_create_conn_after_switch(tL2C_LCB*p_lcb)//external/bluetooth/bluedroid/stack/hcic/hcicmds.cbtsnd_hcic_create_conn(p_lcb->remote_bd_addr,HCI_PKT_TYPES_MASK_DM1+HCI_PKT_TYPES_MASK_DH1,page_scan_rep_mode,page_scan_mode,clock_offset,allow_switch));HCI_CMD_TO_LOWER(p_buf);//external/bluetooth/bluedroid/main/bte_main.cbt_hc_if->transmit_buf((TRANSAC)p_msg,\(char*)(p_msg+1),\p_msg->len);utils_enqueue(&tx_q,(void*)transac);bthc_signal_event(HC_EVENT_TX);btu_start_timer(&p_lcb->timer_entry,BTU_TTYPE_L2CAP_LINK,L2CAP_LINK_CONNECT_TOUT);
5 This command will trigger connection establish process between local and remote device, accomplished by a event-driven state machine in BT stack.
Item B. a2dp_write data path:
1 A2DP client writes to A2DP data socket will trigger API_WRITE_REQ_EVT in SCB_STREAM_ST state:
12345
05-0201:14:03.134I/bt-avp(2139):SCBhdl=1event=1/API_WRITE_REQ_EVTstate=SCB_STREAM_ST394/* state table for streaming state */395constUINT8avdt_scb_st_stream[][AVDT_SCB_NUM_COLS]={396/* Event Action 1 Action 2 Next state */398/* API_WRITE_REQ_EVT */{AVDT_SCB_HDL_WRITE_REQ,AVDT_SCB_CHK_SND_PKT,AVDT_SCB_STREAM_ST},
2 A2DP server handle this with bellow two actions:
12345678
//2.1 build a new media packet and stores it in the SCB.external/bluetooth/bluedroid/stack/avdt/avdt_scb_act.c1320voidavdt_scb_hdl_write_req(tAVDT_SCB*p_scb,tAVDT_SCB_EVT*p_data)//2.2 send this stored media packet to L2CAP layer.1921voidavdt_scb_chk_snd_pkt(tAVDT_SCB*p_scb,tAVDT_SCB_EVT*p_data)avdt_ad_write_req(AVDT_CHAN_MEDIA,p_scb->p_ccb,p_scb,p_pkt);L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid,p_buf);
3 L2CAP to HCI layer
12345678910111213141516171819202122
bluedroid/stack/l2cap/l2c_api.c1633UINT8L2CA_DataWrite(UINT16cid,BT_HDR*p_data)1636returnl2c_data_write(cid,p_data,L2CAP_FLUSHABLE_CH_BASED);p_ccb=l2cu_find_ccb_by_cid(NULL,cid);l2c_csm_execute(p_ccb,L2CEVT_L2CA_DATA_WRITE,p_data);935staticvoidl2c_csm_open(tL2C_CCB*p_ccb,UINT16event,void*p_data)//Just consider channel connected state1050caseL2CEVT_L2CA_DATA_WRITE:/* Upper layer data to send */1051l2c_enqueue_peer_data(p_ccb,(BT_HDR*)p_data);1052l2c_link_check_send_pkts(p_ccb->p_lcb,NULL,NULL);1053break;l2c_link_check_send_pkts(p_lcb,NULL,NULL);l2c_link_send_to_lower(p_lcb,p_buf);1341#ifBLE_INCLUDED==TRUE1342if(p_lcb->is_ble_link)1344L2C_LINK_SEND_BLE_ACL_DATA(p_buf);1346else1349L2C_LINK_SEND_ACL_DATA(p_buf);bte_main_hci_send((BT_HDR*)(p),BT_EVT_TO_LM_HCI_ACL);bt_hc_if->transmit_buf((TRANSAC)p_msg,\(char*)(p_msg+1),p_msg->len);bluedroid/hci/src/bt_hci_bdroid.c:249staticinttransmit_buf(TRANSACtransac,char*p_buf,intlen)utils_enqueue(&tx_q,(void*)transac);bthc_signal_event(HC_EVENT_TX);
bluedroid/hci/src/bt_hci_bdroid.c339staticvoid*bt_hc_worker_thread(void*arg)if(events&HC_EVENT_RX)p_hci_if->rcv();uint16_thci_h4_receive_msg(void)//Construct HCI EVENT/ACL packets and send them to stack957if(p_cb->p_rcv_msg->event!=MSG_HC_TO_STACK_HCI_ACL)958btsnoop_capture(p_cb->p_rcv_msg,TRUE);//dump to HCI trace file/socket.960if(p_cb->p_rcv_msg->event==MSG_HC_TO_STACK_HCI_EVT)965bt_hc_cbacks->data_ind((TRANSAC)p_cb->p_rcv_msg,(char*)(p_cb->p_rcv_msg+1),p_cb->p_rcv_msg->len+BT_HC_HDR_SIZE);bluedroid/main/bte_main.c:504staticintdata_ind(TRANSACtransac,char*p_buf,intlen)GKI_send_msg(BTU_TASK,BTU_HCI_RCV_MBOX,transac);//handle in btu_task.
1 Get Android defined A2DP interface btav_interface_t from bt_interface_t get_bluetooth_interface().
12345678910
04-2501:56:30.530I/BluetoothA2dpServiceJni(2093):classInitNative:succeedspackages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp137staticvoidinitNative(JNIEnv*env,jobjectobject)GLOBALconstbtav_interface_t*sBluetoothA2dpInterface=(btav_interface_t*)btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks);external/bluetooth/bluedroid/btif/src/btif_av.c725staticbt_status_tinit(btav_callbacks_t*callbacks)686intbtif_a2dp_start_media_task(void)GKI_create_task((TASKPTR)btif_media_task,A2DP_MEDIA_TASK,btif_enable_service(BTA_A2DP_SERVICE_ID);//Upon BT enable, BTIF core shall invoke the BTA APIs to enable the profiles
2 Init A2DP service by btav_interface_t->init().
* Start a btif_media_task as main loop for A2DP service.
* Open a socket to listen on client's connect request from control channel.
123456789101112131415161718192021
external/bluetooth/bluedroid/btif/src/btif_media_task.c//Task for SBC encoder. This task receives an event when the waveIn interface has a pcm data buffer ready. On receiving the event, handle all ready pcm data buffers. If stream is started, run the SBC encoder on each chunk of pcm samples and build an output packet consisting of one or more encoded SBC frames.1066intbtif_media_task(void*p)1073btif_media_task_init();1044UIPC_Init(NULL);606UDRV_APIvoidUIPC_Init(void*p_data)569intuipc_start_main_server_thread(void)pthread_create(&uipc_main.tid,(constpthread_attr_t*)NULL,(void*)uipc_read_task,NULL)1047UIPC_Open(UIPC_CH_ID_AV_CTRL,btif_a2dp_ctrl_cb);uipc_setup_server_locked(ch_id,A2DP_CTRL_PATH,p_cback);//This is the control socket that listen on A2DP client.1079while(1)//A2DP event loop 1080{1085if(event&BTIF_MEDIA_TASK_CMD)1090btif_media_task_handle_cmd(p_msg);10931094if(event&BTIF_MEDIA_TASK_DATA)1099btif_media_task_handle_media(p_msg);11021103if(event&BTIF_MEDIA_AA_TASK_TIMER)1105/* advance audio timer expiration */1106btif_media_task_aa_handle_timer();
Android defined one interface bt_interface_t to control a Bluetooth Adapter, Bluedroid implemented this interface as “static const bt_interface_t bluetoothInterface” in external/bluetooth/bluedroid/btif/src/bluetooth.c.
Android framework defined two hardware interfaces for operating audio output devices, they are audio_hw_device_t and audio_stream_out_t, AudioFlinger is the only user of these interfaces. Bluedroid implemented these two interface in external/bluetooth/bluedroid/audio_a2dp_hw/audio_a2dp_hw.c, AudioFlinger can output audio sample to a connected A2DP sink device if this implementation has been registered to Android.
In Bluedroid’s A2DP hardware implementation, it will use two sockets to communicate with A2DP server.
A Bluetooth SDIO card driver talks with hardware through SDIO interface, providing R/W method for Bluetooth Adapter layer, here’s the class diagram for this relationship:
The outbound Bluetooth data path:
Upper layer use HCI interface send() to send data/command packet, implemented as btmrvl_send_frame() in this driver.
Put this packet in adapter’s tx queue, wakeup the main data processing thread (like NAPI in a network driver, thread function is btmrvl_service_main_thread()).
In main data procssing thread, re-organize skb data payload for DMA transfer (in btmrvl_tx_pkt()).
Call sdio_writesb() to write data to hardware (in btmrvl_sdio_host_to_card()).
The incoming Bluetooth data path:
SDIO card received a data packet, triggered a interrupt to host.
The SDIO ISR triggred the main data processing thread.
In this thread, allocate a skb with DAM aligned, call sdio_readsb() to read the data from SDIO interface (in btmrvl_sdio_card_to_host()).
Call hci_recv_frame(skb) to send this data packet to upper layer Bluetooth stack.
Appendix: How to register a driver specific ISR to SDIO’s ISR:
In driver module init, hook up a device ISR to SDIO’s ISR.
sdio_claim_host(card->func);//Get firmware from user space.request_firmware(&fw_firmware,card->firmware,//name of firmware file, = "mrvl/sd8787_uapsta.bin",&card->func->dev);//Write firmware into SDIO device, check firmware status.tmpfwbufsz=ALIGN_SZ(BTM_UPLD_SIZE,BTSDIO_DMA_ALIGN);fwbuf=(u8*)ALIGN_ADDR(tmpfwbuf,BTSDIO_DMA_ALIGN);memcpy(fwbuf,&firmware[offset],txlen);sdio_writesb(card->func,card->ioport,fwbuf,tx_blocks*blksz_dl);//Release firmware related resource in kernel.release_firmware(fw_firmware);sdio_release_host(card->func);
Alternatively, you can use request_firmware_nowait() if current thread is not allowed to sleep for a long time.
card->func means an independent function residues in same card (there maybe different functions implemented in same card simultaneously, like BT and Wifi in MRVL 8787 module. The device field in struct sdio_device_id is used as function id to distinguish these functions, in this driver, the device field in driver is 0x911B for MRVL_BT_SD8787, it reflected as:
0x911b in /sys/class/mmc_host/mmc1/mmc1\:0001/mmc1\:0001\:3/device.
sdio_claim_host() is acting like a lock, I guess this will serialize access to same SD device between different functions, and also between different threads inside same function.
stdlib will try to handle it internally, if can’t satisfy this request, then,
Call sbrk() to let kernel enlarge current process’s heap space (by adjust process’s VMAs).
What happens next when try to access the memory:
CPU’s MMU use this virtual address to look up in current CPU’s TLB, not found.
Then MMU switch to look up this address in process’s Page Table, try to do the virtual-to-physical address translation.
Step 2 will fail, a Page Fault exception happens.
In Page Fault exception handler, check if current process has write permission to this address, that’s done by check process’s VMA list.
Allocate a physical page, update process’s Page Table for this page.
After the exception handler returned, and the process get scheduled to execute again, it will retry the instruction that caused the Page Fault, this time will get correct physical address pointed to the new page. Update TLB entry for this new map.
Select proper line in Cache for this physical address, write the new value to Cache, then hardware will write back this new value from Cache to real memory at proper time.
In E-DDC, a special I²C addressing scheme was introduced, in which multiple 256-byte segments could be selected. To do this, a single 8-bit segment index is passed to the display via the I²C address 30h. (Because this access is always a write, the first I²C octet will always be 60h.).
In my MHL project, this first write will always get an I2C NO_ACK error(E-DDC need tolerate with NO_ACK error for this write, to be back-compatible with legacy DDC, so we are using GPIO to simulate I2C, instead of using standard I2C controller).
From below signal, we found the ACK actually came, but master did not acquire it at that time, that’s exactly what clock stretching targeted at.
Add clock stretching logic in our GPIO simulation function, then can successfully read all e-EDID segments.
+#include<pthread.h>+#include<semaphore.h>+#include<signal.h>+staticsem_tevent_sem;+staticvolatilesig_atomic_tintrested_event=0;++voidsig_handler_event1(intsig)+{+interested_event=1;+sem_post(&event_sem);+}+staticvoid*event_handler_thread_func()+{+while(1){+sem_wait(&event_sem);+if(intrested_event){+LOGD("%s,%d, received intrested_event signal.\n",__func__,__LINE__);+intrested_event=0;+}+}+pthread_exit(NULL);+}intmain(intargc,char*argv[])+pthread_tevent_thread;+if(pthread_create(&event_thread,NULL,event_handler_thread_func,NULL)!=0){printf("Thread create failed%s.\n",strerror(errno));+exit(1);+}+sem_init(&event_sem,0,0);+structsigactionusr_action;+sigset_tblock_mask;+sigfillset(&block_mask);+usr_action.sa_handler=sig_handler_event1;+usr_action.sa_mask=block_mask;//block all signal inside signal handler.+usr_action.sa_flags=SA_NODEFER;//do not block SIGUSR1 within sig_handler_int.+sigaction(SIGUSR1,&usr_action,NULL);+intfd=open("/dev/target_device_name",O_RDWR);+intmy_pid=getpid();+ioctl(fd,0x111,&my_pid);+close(fd);
A signal could interrupt below primitive calls:
close, fcntl (operation F_SETLK), open, read, recv, recvfrom, select, send, sendto, tcdrain, waitpid, wait, and write,
POXIS and BSD will handle this situation differently. POXIS will fail these primitive call with EINTR, caller need to use macro TEMP_FAILURE_RETRY (expression) to retry.
Programmer need to take care of this issue.
Reference:
Sending Signal Across Processes
In receiver process, create a share memory, write receiver’s pid to it, then wait for the signal.
+#include<signal.h>+#include<sys/ipc.h>+#include<sys/shm.h>+#include<pthread.h>+#include<semaphore.h>+#defineMY_IPC_KEY_PATH"/data/misc/bluedroid"+staticsem_tevent_sem;+staticvolatilesig_atomic_tintrested_event=0;++staticvoidsig_handler_int(intsig)+{+interested_event=1;+sem_post(&event_sem);+}++staticvoidwait_for_signal(void)+{+pid_tpid=getpid();+key_tmy_ipc_key=ftok(MY_IPC_KEY_PATH,'s');+intshare_mem_id=shmget(my_ipc_key,sizeof(pid_t),IPC_CREAT|0666);+pid_t*share_mem=(pid_t*)shmat(share_mem_id,NULL,0);+*share_mem=pid;++printf("%s,%d, set my pid %d\n",__func__,__LINE__,pid);++structsigactionusr_action;+sigset_tblock_mask;+sigfillset(&block_mask);+usr_action.sa_handler=sig_handler_int;+usr_action.sa_mask=block_mask;//block all signal inside signal handler.+usr_action.sa_flags=SA_NODEFER;//do not block SIGUSR1 within sig_handler_int.+sigaction(SIGINT,&usr_action,NULL);++sem_init(&event_sem,0,0);+while(intrested_event==0){+sem_wait(&event_sem);+if(intrested_event){+printf("%s,%d, received intrested_event signal.\n",__func__,__LINE__);+}+}+intrested_event=0;+shmdt(share_mem);+shmctl(share_mem_id,IPC_RMID,NULL);+}intreceiver_process(intargc,char*argv[]){+wait_for_signal();
In sender process, acquire the receiver process’spid through the share memory object, then send the signal.
+#include<signal.h>+#include<sys/ipc.h>+#include<sys/shm.h>+#defineMY_IPC_KEY_PATH"/data/misc/bluedroid"+staticvoidwakeup_receiver(void)+{+key_tmy_ipc_key;+intshare_mem_id;+pid_t*share_mem;+pid_tpid;++my_ipc_key=ftok(MY_IPC_KEY_PATH,'s');+share_mem_id=shmget(my_ipc_key,sizeof(pid_t),0666);+share_mem=(pid_t*)shmat(share_mem_id,NULL,0);+pid=*share_mem;+shmdt(share_mem);++if(pid>0){+intret=sigquue(pid,SIGINT,0);if(ret!=0){printf("send signal failed %s.\n",strerror(errno));return;}+printf("%s,%d, send signale to pid %d\n",__func__,__LINE__,pid);+}+else{+printf("%s,%d, pid %d not ready\n",__func__,__LINE__,pid);+}++}voidsender(void){+wakeup_receiver();}
A note: shmget() is not available in Android, so this is not a valid IPC for Android.
Reference:
Footnote: signal lost
1 unreliable signal:
earlier version of UNIX signal is unreliable
2 real-time signal:
POSIX added signals, and
3 signal lost:
. when signal handler is running, blocked signals is “lost” (?!)
. when signal handler is running, the same signal is blocked by default, add SA_NODEFER in sigaction.sa_flags unblock it.
Bug: Keep on resetting BT adapter cause Android crash
Bug description: In GTV/Honeycomb’s System Setting UI, keep on turning on/off Bluetooth for about 270 times, Android will crash, and UI restart from blackscreen.
My analyze:
First thing is to located where Android system crashed from logcat, there must be something related to zygote, then I found this:
1
03-08 20:15:00.026 I/Zygote ( 3749): Exit zygote because system server (3780) has terminated
Above that I found a fatal error message:
1
03-08 20:14:59.226 F/Looper ( 3780): Could not create wake pipe. errno=24
then all system service died like below:
1
03-08 20:14:59.316 I/ServiceManager( 690): service 'usagestats' died
Tracing down the fatal error message in Android XRef, the error occurs when creating a new Looper instance, in Honeycomb/frameworks/base/libs/utils/Looper.cpp, where 3 new pips need to be created for one new Looper object.
So I guess the isssue is kind of file/socket/pipe handle leakage issue, to verify this, compare “lsof” result after turn on/off Bluetooth once, I can see there’s 3 newly opened pipes in system_service everytime I performed a turn on/off Bluetooth. Then I found from website that someone reported similar issue on Android Bug List
The symptom is quite clear now, but how to locate leakage position in Android?
The first clue is that Looper object is used for Android Handler object, it’s hard to dump call stack in Android native C++ service, but it’s easy to do that in Java.
So I added a dump stack in Handler’s construction method, caught below message when I turn on Bluetooth:
After doing some source code search, I reached the place in BluetoothService.java when problem occurs:
When turn off BT, BluetoothService will do:
123
2146voidremoveProfileState(Stringaddress){2147mDeviceProfileState.remove(address);//Comment out this line so BluetoothService will not create a new BluetoothDeviceProfileState every time BT reset.2148}
Problem here is, we only need do a reset when turn off BT, then we can reuse the BluetoothDeviceProfileState object after we turn it on again, but Android removed it instead, then need to create a new BluetoothDeviceProfileState object next time. If we did not remove the object every time we turn off BT, then the issue disappeared.
My comments:
For Java class who claims system resource in its construction method, we must declare corresponding interface to release them, GC can not do this job for us.