RSS

Search Engine

Friday, September 17, 2010

Phonecalls

Again, there was a bit of a break in the blog. One reason is personal: I moved to London to take a new job. The other reason is that this time I went into the hairy issue which is call handling in Android.

I worked with the m3-rc37a version of the emulator and I can tell you that emulator crashes are really common if you play with the telephony stuff in this version. Don't be surprised if the example program distributed with this blog entry does not show the behaviour I promise but crashes instead. It just happens with this emulator version.



You can find the example program here.

The task I gave myself is catching incoming and outgoing calls which is easy. There needs to be just an IntentReceiver defined in the manifest and the onReceiveIntent method implemented in the intent receiver class. The manifest entry declares the filter for the IntentReceiver (again, the XML is mangled due to limitations of the blog engine).

[receiver class=".PhoneIntentReceiver"]
[intent-filter]
[action android:value="android.intent.action.PHONE_STATE" /]
[/intent-filter]
[/receiver]


PHONE_STATE is a general intent for signalling all sorts of phone events. The example program dumps the event bundle when the event comes in. In case of outgoing call, the event is delivered with state=OFFHOOK when the call starts then state=IDLE when the call finishes (state is a key in the intent bundle ("extras") and its string values are OFFHOOK and IDLE). In case of the incoming call (you can simulate it by telnet localhost 5554 then issuing the gsm call phone_number command on the console) the state transition is RINGING then IDLE again.

So far so good. Having a notification on incoming and outgoing calls is less useful, however, if the caller or the called number is not known. This turned out to be a really hairy issue for which I did not find the answer (just desperate help requests). I remembered the good old Series60 days and went for the call log.

The call log is surprisingly co-located in the database of the Contacts application. Try this:

adb shell
#cd /data/data/com.google.android.providers.contacts/databases

# sqlite3 contacts.db

SQLite version 3.5.0
Enter ".help" for instructions
sqlite> .dump

...

CREATE TABLE calls (_id INTEGER PRIMARY KEY,number TEXT,number_key TEXT,number_type TEXT,date INTEGER,duration INTEGER,type INTEGER,person INTEGER,new INTEGER);

INSERT INTO "calls" VALUES(1,'+44444','44444+','Home',1200263250247,0,3,NULL,0);
INSERT INTO "calls" VALUES(2,'+4444445','5444444+','Home',1200263859817,5,2,NULL,1);
INSERT INTO "calls" VALUES(3,'+1234','4321+','Home',1200263990161,2,2,NULL,1);


There is the log. Unfortunately (or fortunately? :-)) Android enforces strict database separation for applications so the Contacts database cannot be accessed from within another Android application. Luckily, the Contacts application decided to share the data as Content Provider. There is even a facilitator class: CallLog.Calls. Dumping the call log is relatively easy after that (look at the PhoneIntent class which is an Activity that makes this dump on pressing a button).

Now the only thing remained to see how the database is updated with regards to phone calls. It turned out that the number is not visible when the call goes out or comes in (RINGING or OFFHOOK state) but is accessible through the content provider when the IDLE transition event comes in. I was not able, therefore, to capture the number of the currently ongoing call but I was able to capture the number of the call that has just finished.

Testing the example application was a pain for me. When working with calls, the emulator tended to crash on my PC without installing any applications. In order to monitor the event transitions, you have to bring up the phone application because it does not come to the front by itself. You can do that by pressing some digits at the emulator's opening screen. If you don't bring the phone app to front manually, you won't be able to answer or reject the call.




I propose that you don't run adb logcat while the call goes on - it tends to increase the chance of emulator crash. You can examine whether the number was retrieved succesfully from the call log by running adb logcat after the call was finished.

0 comments:

Post a Comment