Wednesday, January 16, 2008

Phonecalls

Update: example program and further information for Android 2.2. 

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.

37 comments:

Anonymous said...

Hello,Gabor Paller,I find your blog very useful.Can you send me a copy of the Phonecalls' source code? My Email is : lichengliang82421@hotmail.com,Thank you very much!

Unknown said...

Hai ,

Your code helped me a lot in simulating the Phone calls,but iam getting only as call log empty.
Can you please tell me how to simulate the calls.
Can you please tell me the steps.
My gmail id is ssasikr@gmail.com


Thanks in advance


Regards,
Harshadha.

Naveen Garg said...

Hello Gabor Paller,
This code really helped me a lot. Now i am trying to make the same thing for SMS. Please tell me any good source of info or provide any sample code.
I also figured out that there is a little bit of prob in ur logic as it prints on one entry. U should modify it and should return array of strings or vector in place of just one string. Then before printing convert it to a single string with '\n' inserted for each entry.

Naveen Garg said...

U have mentioned about IDLE and OFFHOOK, i would like to know what are such states for SMS and how to use them.

Anonymous said...

Why Google is not saying anything about this?

Issac Trotts said...

When I try to make an Eclipse project for it, it tells me "No activity name defined in /Users/me/downloads/phoneintent/AndroidManifest.xml."

I have much to learn...

Geesun said...

I'm use the keyword incoming_number get the income tel num, but I can not get the outgoing number when I call somebody. anyone has suggestion?
Here is the code for get the incoming tel num:

public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(intent.getAction().equals(ACTION)){
Bundle bundle = intent.getExtras();
String phoneName = bundle.getString("incoming_number");

String phoneState = bundle.getString("state");
}

Anonymous said...

You can get the incoming call in the emulator (code looks bad but it works):
It will receive notification of both SMS and Incoming calls.

Tried to past the XML but your blog wouldn't allow the tags.

?xml version="1.0" encoding="utf-8"?
manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.prankapp"
android:versionCode="1"
android:versionName="1.0.0"
uses-permission android:name="android.permission.RECEIVE_SMS" /
uses-permission android:name="android.permission.READ_PHONE_STATE" /
application android:icon="@drawable/icon" android:label="@string/app_name"
!-- The name of the reciver class --
receiver android:name="PrankAApp"
android:enabled="true"
android:exported="true"
android:permission=""
intent-filter
action android:name="android.intent.action.MAIN" /
category android:name="android.intent.category.LAUNCHER" /
action android:name="android.provider.Telephony.SMS_RECEIVED" /
action android:name="android.intent.action.PHONE_STATE" /
/intent-filter

/receiver
!-- ACTIVITY!
activity android:name=".PrankAApp" android:label="@string/app_name"

/activity
--
/application
/manifest




Java
package com.android.prankapp;

//import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.content.BroadcastReceiver;

public class PrankAApp extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

Log.d("PrankAApp","BEGIN1");
if(intent.getData()!=null)
{
Log.d("PrankAApp",intent.getDataString() );
}
else
{
Log.d("PrankAApp","DataString == NULL" );
}
Log.d("PrankAApp",intent.toString());
TelephonyManager myTelManager = (TelephonyManager)context.getSystemService(context.TELEPHONY_SERVICE);
//To unregister a listener, pass the listener object and set the events argument to LISTEN_NONE (0).
MyPhoneStateListener myListner = new MyPhoneStateListener();
myTelManager.listen(myListner, PhoneStateListener.LISTEN_CALL_STATE);
if(myTelManager.getCallState() == myTelManager.CALL_STATE_RINGING)
{
//incoming phonecall, get nr
String phoneNr = myTelManager.getLine1Number();
Log.d("PrankAApp",phoneNr);
//This will get the internal nr for Androide emulator 15555218135
//Will it get the REAL clients nr?
}
else
{
Integer myInt = myTelManager.getCallState();
String error = "State NR:"+myInt.toString();
Log.d("PrankAApp",error);
}

}
private class MyPhoneStateListener extends PhoneStateListener
{
public void onCallStateChanged(int state, String incomingNumber)
{
Log.d("PrankAApp","incoming nr:"+incomingNumber);
}
}



}

Anonymous said...

Hello, can you try to change screen when call is coming? If yes, please share this with me. My email address is npak243@gmail.com.

Thanks,
NPAK

Anonymous said...

hello, your blog is useful very much, could you help me on this,i could not find the action to reject a call,would you please tell me something about that?thanks a lot!

Todd Halfpenny said...

Hey there... I think you;re logging the terminating number.
For incoming calls you want the follwowing

Bundle bundle = intent.getExtras();
String phoneNr= bundle.getString("incoming_number");

Unknown said...

Dear


I tried to configure you code with android SDK 1.1 and android 1.5 but it is giving errors at some points. What could be wrong in it ? It seems it does not have android.net.ContentURI; so it is giving error there.

Gabor Paller said...

This is an old code, it has to be adapted to new SDKs. It is earlier than 0.9 (let alone, 1.1). I guess, it needs quite an adaptation.

Unknown said...

Hi is there any way our application answers the incoming call in andriod sdk1.5, if so how can any one please provide some insite in to it.

Lucas said...

Although it's old... it works perfectly with 1.5-cupcake ( the getExtras method :) )

Sabarish V said...

Hai,


Can any one post the Full code for the same application which works in Android 1.1

Thanks & Regards,
Sabarish V

Unknown said...

Hi, Gabor Paller
Very nice blog, you code helped me allot.

Regards!
Imtiaz Ali Shah

Anonymous said...

The blog is very much usefull..
Thanks..
How to get outgoing number?
Also, I want to save conversation as audio file. How can i do that?

Any code snippet will be helpfull.

Thanks

Rashmi said...
This comment has been removed by the author.
Nextcome said...

please send me code of call handling...how did you make such nice caller id..

Nextcome said...

please send me the code for call handling... how do u do that ?

nice caller id..

plz send on this id : nextcome@gmail.com

omasampath said...
This comment has been removed by the author.
omasampath said...

Hi,
This is good hint for me. Please send me your code.

omasampath@gmail.com

Thank You.
sampath

kryll said...

Hello,
Could you send me your perfect code?

krylloff@gmail.com

Thanks
Eugene

Garima said...

Hi,

is there any way by which you can even get access to audio streams during call. I mean the uplink audio stream, something that the user is saying through his mic?

Garima said...

Hi,

is there any way by which you can even get access to audio streams during call. I mean the uplink audio stream, something that the user is saying through his mic?

Gabor Paller said...

Garima, I don't think that is possible.

Anonymous said...

Hello,
Very usefull blog :)

I have a question I cannot find answer by now in google:

How can I check if the incomming call is forwarded (redirected) from another number.
I guess it's only some flag not the full info about the number.

Best Regards
Ivan

Unknown said...

hello all,

can any one suggest how to intercept an incoming and outgoing call in android?

Unknown said...

Has anyone found and an answer to:
How can I check if the incomming call is forwarded (redirected) from another number?

markonato said...

Excellent job buddy...
How you done the part shown on the picture about answer and reject on incoming call? Pls send me on god1is1a1dj@yahoo.com

Anonymous said...

So now 4 years have passed yet I still see no apps out there that do anything with this info. There are 1,000 area code lookup apps -- that's a no-brainer -- but I want to see where the call is coming from BEFORE I answer it! And I know others have had the same thought.

But that would require two things, that ought to be fairly simple in an open source o/s: notice of inbound call (with number) and a way to write to the display while the phone is ringing. Yet with a reportedly unprecedented number of people developing for this platform, so far as I've seen this has yet to be accomplished. WTF?

So this is supposed to be the future of mobile phones? Screw that, I say bring back HDML!

Gabor Paller said...

Anonymous, there are two separate issues here.
1. Whether the functionality your are looking for is possible through some hack.
2. Whether it SHOULD be possible.

Regarding question #1, it is definitely possible. For example these guys have done it (disclaimer: my ex-employers). They implemented the feature in a variety of devices, usually exploiting some bug in the platform. Then we arrive to question #2: the implementors are normally silent about the method used because intercepting calls before the platform dialler does is considered a serious security flaw and they don't want the platform manufacturer to figure it out and patch the hole.

VISHAL said...

i want to use button for picking up my phone when it is riinging ......
help me please by sending code to my email. my email id is vishaljha.amc@gmail.com

VISHAL said...

i want to make application which will be able to pick the phone can anyone send me the code for answering phone call with a button. my email id is vishaljha.amc@gmail.com.please if anyone know please help me

Unknown said...

can u please send me the copy of source code.. it will be very useful for me . my email id is jayaramaiyer@gmail.com...

thank you in advance..

Shinu said...

Hi
Do you have any idea about how to modify incoming and outgoing call screen view in android. I want a custom incoming and outgoing call screen If yes, please share this with me. My email address is shinupeter@tataelxsi.co.in