adrift in the sea of experience

Friday, January 25, 2008

Recording phone calls on a S60 phone. Part 2: working prototype

<voice type="farnsworth">Good news everyone!</voice>

I messed around with the PyS60 python interpreter for my S60 phone, and managed to record phone calls! The first step is to install PyS60 as described on the PyS60 wiki. No further tools are needed, the python runtime and shell on your phone are sufficient. Next, we need a script which listens for phone calls being connected, either incoming or outgoing, and which starts and stops recording those calls:

import appuifw
import e32
import telephone
import audio
import time

# Must be either None, or a sound file which is recording.
sound_file = None

# Directory used to save call recordings. Must exist.
# Filename will be appended to this path.
recordings_folder = "e:\\CallRecordings\\"

def cb_calling(args):

global sound_file
state = args[0]

# if transition to connected state and not recording, start recording
if (state == telephone.EStatusConnected) and (sound_file == None):
number = args[1]
if number == None:
number = "None"
filename = recordings_folder + time.strftime("%Y%m%d_%H%M_%S_") + number + '.wav'
#filename = recordings_folder + 'test.wav'
print filename
sound_file = audio.Sound.open(filename)
sound_file.record()

# if transition to non-connected state and recording, stop recording
elif (state != telephone.EStatusConnected) and (sound_file != None):
sound_file.stop()
sound_file.close()
sound_file = None


def quit():
app_lock.signal()

appuifw.app.exit_key_handler= quit
app_lock=e32.Ao_lock()

telephone.call_state(cb_calling)
telephone.incoming_call()

app_lock.wait()

The body of the script only sets the exit key handler, registers a callback function for detecting telephone call state changes, and starts waiting for a signal on a lock object. After that, the main thread will not do any more work. It was surprising to discover that real multi-threading is central to developing in Pys60. I was expecting to see a "message queue with single threaded message pump" kind of model, like most PC application frameworks.

Copying this python script to a \Python directory enables you to run it on the phone with the python shell application. Once running, it will listen for phone calls and record them to the recordings_folder.

Unfortunately, there is an annoyance: apparently nokia feels obligated to insert a beeping sound into the call every 5 seconds if it is being recorded. The beeping is not present in the recording though. I did not find any more information on this "feature", other than that it appears to be built into the phone. I guess nokia is trying to legally cover their ass here. I researched Belgian law regulating phone call recording, but did not find any obligation to inform participants that you are recording them. If you are not recording with intent to deceive or harm and are participating in the call yourself, then it's OK to record without warning. (Disclaimer: I am not a lawyer)

Can we get rid of these annoying beeps? Is there a way to automatically start the call recorder code on phone boot? Questions, questions...

To be continued!

2 comments:

sobers said...

Your work has been extremely helpful. I do appreciate it greatly.

Moreover, I am keen to hear your further thoughts on your following comment:

"It was surprising to discover that real multi-threading is central to developing in Pys60. I was expecting to see a "message queue with single threaded message pump" kind of model, like most PC application frameworks."

Could you explain exactly what you were expecting and what was indeed surprising with an example?

Look forward.

Best,
wirefree

Wim Coenen said...

@wirefree:

A message queue model is one where events are queued and handled in sequence. Systems which use this can be recognized by looking at the initialization code, it will typically end with a call to something like Application.Start() which does not return until the application is exited. The main thread does all work inside this call.

In multi-threaded systems on the other hand, all events may be handled on their own thread. This is more dangerous because data needs to be protected from concurrent access by multiple threads of execution. Systems which use this model can also be recognized by looking at the initialization code, it will typically end with a wait() call which only waits until some signal is received. The main thread is not involved in handling events.