Discussing the nuts and bolts of software development

Wednesday, July 02, 2008

 

Getting RIL!!

Have you ever wanted to mess around with calling functionality of a mobile phone running Windows Mobile?

I have this nifty phone but how do I control it programmatically?

Well, first of all, the Windows Mobile Radio Interface Layer works by having specific modules control the cellular component. Since we are in North America there are two different cellular protocol implementations: GSM and CDMA. Above this layer is an abstraction layer that hides the implementation of the underlying protocol so that applications/OEM phone developers can use the functionality of call handling (and other useful information) without getting down to how things work under the hood. So how do we, as an application, talk to this abstraction layer, which we'll just clump together and call it the RIL( Radio Interface Layer )? Good Question!

Since cellular devices are really just modems that use AT commands, the RIL operates mainly from an asynchronous callback system. In fact most of the RIL functions really just map to AT commands. The application accessing the RIL makes a request to the RIL, and the RIL takes some time to process this request and sends back a result code indicating the success or failure of the request. In addition the RIL spits out notifications which our application can hook into and be informed of status changes related to the cellular network. So let's dig a little with some RIL examples.

There are a few pitfalls by going down this path. Firstly you might run into situations where you're fighting for control with the resident dialer (the default phone application supplied to the mobile device; developed by Microsoft). There may also be some undocumented internal states that need to be obeyed or else it can lead to unintended behavior (i.e. device lock up).

There are also some differences to note between CDMA and GSM RIL implementations. With GSM devices there can be multiple lines, and as such the RIL can fetch call information for each line. RIL can also manage the GSM lines to perform actions such as putting the call on hold or switching lines. In the CDMA environment, there is only one "real" line and one "virtual" line. That is, one line is used to represent two lines. Access between the two lines is achieved by sending a "flash" command. Call management is also not possible with CDMA. So when you call "RIL_GetCallList" for GSM, for example, you may be returned information for one or more lines depending on the call state. For CDMA, you will only see at most one line.

You will have to get your hands on the ril.h and ril.lib files. Both of these files are floating around somewhere on the internet (try here). If you can get both of these files, link your application with ril.lib and away you go. If you can’t find ril.lib but have ril.h, you can dynamically link with the ril.dll file (contained in the “windows” folder on the device) since you know what some of the prototypes are.

First we need to initialize our application with the RIL so that we can receive both results and notifications callbacks. Here’s some code to do this:


...

// keep this handle handy as we need it for further RIL commands
HRIL rilHandle;

...

HRESULT hr = RIL_Initialize(
1, // index of the RIL port to use (e.g., 1 for RIL1:)
&resultCallBack, // this is a pointer to your result call back method
&notifyCallback, // this is a pointer to your notify call back method
RIL_NCLASS_ALL, // all notification (except device specific)
(DWORD) this, // custom param (could be a pointer to an instance of a class)
&rilHandle); // returned handle to RIL instance


...

void CALLBACK resultCallback(
DWORD dwCode,
HRESULT hrCmdID,
const void *lpData,
DWORD cbData,
DWORD dwParam)

{
// handle the results

...

void CALLBACK notifyCallback(
DWORD dwCode,
const void *lpData,
DWORD cbData,
DWORD dwParam)
{
// handle the notification

...


Note that we’ve initialize the RIL with a reference to our class as the user parameter which is passed to the result and notification callbacks. The parameter is optional; however having the class reference would be especially useful if you would like to perform further actions after receiving a result or notification. More on this later.

OK so what - I've just initialized myself with RIL - what can I do now?
To answer that question, anything under the Sun related to call handling, and then some!!

For example if you wish to check the call status of the phone you can do the following. First send a request for call list:


HRESULT cmdID;

...

void getCallList()
{
// call RIL_GetCallList with RIL handle from the initialization
cmdID = RIL_GetCallList(rilHandle);
}


Next we have to wait for the results:


...

void CALLBACK resultCallback(
DWORD dwCode,
HRESULT hrCmdID,
const void *lpData,
DWORD cbData,
DWORD dwParam)
{
If (cmdID == hrCmdID && RIL_RESULT_OK == (dwCode & 0xff))
{
// lpData points to an array of structures.
RILCALLINFO *lpCallInfo = (RILCALLINFO *)lpData;
// extract the call info from the structure and do something...
...


Notice how the cmdID return value of RIL_GetCallList matches hrCmdID in the callback. In a realistic scenario, multiple commands are sent and the results do not necessary return in the same order the commands were sent. As a result we would need to queue up the command IDs in order to properly identify the command represented by each callback

Now what is notifiyCallback useful for? This function is called basically whenever an event occurs that is of a notification class that was registered during initialization (we registered with RIL_NCLASS_ALL in this case). For example, for an incoming call you would receive and handle the “ring” notification as follows:


void CALLBACK notifyCallback(
DWORD dwCode,
const void *lpData,
DWORD cbData,
DWORD dwParam)
{
if(dwCode & RIL_NCLASS_CALLCTRL)
{
switch(dwCode & (0xff | RIL_NCLASS_CALLCTRL))
{
case RIL_NOTIFY_RING:
// do something here
...


Since the phone is ringing, it is probably best to get more information about the incoming call. If you remember from initialization we use the class reference as the custom parameter. This gives us the flexibility to send further RIL commands by either passing in the RIL handle (HRIL) or point to a function in the class. In this case we can point back to the getCallList function created earlier.


MyClass* myClass = (MyClass*)dwParam;
myClass->getCallList();


If we don’t have a function to point to, we can always send an RIL command on the fly by passing in the RIL handle from our class. For example to answer the call right away:


MyClass* myClass = (MyClass*)dwParam;
HRESULT hr = RIL_Answer(myClass->rilHandle);


As a note, if we're not using the interface anymore we'll need to de-register ourselves:


RIL_Deinitialize(rilHandle);


To learn more about RIL and all its wonders, you should closely examine the ril.h header. There is some information on the msdn site as well. The best way to learn about it, of course, is to just try it out yourself.

(Originally written and concept by Quan Nguyen; modified and edited by Henry Yi)

Labels: ,


Tuesday, June 17, 2008

 

Obtaining the voicemail number on Windows Mobile phones

The voicemail number, if available, may be stored in various locations on the Windows Mobile phone device depending on the type and carrier. In most cases, the number will show up somewhere in the registry. From experience I have found them in one of several locations under HKEY_CURRENT_USER:

\Software\Microsoft\Vmail\PhoneNumber1
\Software\Microsoft\Vmail\PhoneNumber2
\Software\Microsoft\Vmail\UserProvidedNumber1
\Software\Microsoft\Vmail\CarrierProviderNumber1
\System\State\Messages\vmail\VMailNumber
\Palm\State\Messages\vmail\VMailNumber

On some GSM devices however the number isn’t always updated properly in the registry. In that case you would have to fetch it direct from the SIM card. In fact it is probably safest to fetch it from the SIM card first since this ensures a more accurate number.

If you do some internet searches and dig through some of the 3GPP specifications for (U)SIM (i.e. 3GPP TS 31.102, 51.011) you will find that the voicemail number is stored in the EFMBDN (Mailbox Dialing Numbers) file located at either 6FC7 for 3G devices or 6F17 for 2G.

We can get the voicemail file from the SIM using the SIM Manager API. Note that some of these are privileged functions. You also have to make sure to add “cellcore.lib” as a dependency to your project. The code would look as follows:


// include the SIM Manager API
#include

// set the voicemail addresses
#define EF_MBDN_2G 0x6F17
#define EF_MBDN_3G 0x6FC7



std::string getVoicemailNumber()
{

std::string number;
HSIM hSim = 0;
HRESULT hr = S_OK;

// initialize
hr = SimInitialize (0, 0, 0, (LPHSIM)&hSim);

if (S_OK == hr)
{
DWORD address = EF_MBDN_2G;
SIMRECORDINFO simRecordInfo = {0};
simRecordInfo.cbSize = sizeof(SIMRECORDINFO);

// check the 2G address for the file
hr = SimGetRecordInfo(hSim, address, &simRecordInfo);

// The file may hold more than one number for devices with
// multiple lines; however we will assume only one line.
// A valid file needs to be "linear" type and at least 14 bytes.
if ( S_OK != hr ||
SIM_RECORDTYPE_LINEAR != simRecordInfo.dwRecordType ||
simRecordInfo.dwItemCount == 0 ||
simRecordInfo.dwSize < 14 )
{
// no valid 2G file, check the 3G address
address = EF_MBDN_3G;
memset(&simRecordInfo, 0, sizeof(simRecordInfo));
simRecordInfo.cbSize = sizeof(SIMRECORDINFO);
hr = SimGetRecordInfo(hSim, address, &simRecordInfo);
}

if ( S_OK == hr &&
SIM_RECORDTYPE_LINEAR == simRecordInfo.dwRecordType &&
simRecordInfo.dwItemCount > 0 &&
simRecordInfo.dwSize >= 14)
{
// allocate the specified size
LPBYTE buf = (LPBYTE)LocalAlloc(LPTR, simRecordInfo.dwSize);
DWORD bytesRead = 0;

// read the SIM file
hr = SimReadRecord(hSim, address, SIM_RECORDTYPE_LINEAR, 1,
buf, simRecordInfo.dwSize, &bytesRead);

if (S_OK == hr && bytesRead >= 14)
{
// handle the bytes received
...


Now that we have the file, we need to interpret the bytes into a useful number. The 3GPP specifications show the EFMBDN structure as follows:


Bytes Description M/O Length
----------------------------------------------------------------------
1 to X Alpha Identifier O X bytes
X+1 Length of BCD number/SSC contents M 1 byte
X+2 TON and NPI M 1 byte
X+3 to X+12 Dialling Number/SSC contents M 10 bytes
X+13 Capability/Configuration2 Record Identifier M 1 byte
X+14 Extension 6 Record Identifier M 1 byte


The first few bytes (1 to X) are the optional identifier. In most cases it will just show the words “voicemail”. The last 14 bytes describe the voicemail number with up to 10 bytes representing the number as binary coded decimals. For our purpose we can ignore Capability/Configuration2 Record Identifier and Extension 6 Record Identifier. The way to interpret the bytes is described in the 3GPP specifications 31.102 clause 4.4.2.3 for EFADN (Abbreviated dialing numbers). We can proceed to get the number from the bytes as follows:


...

// use the last 14 bytes
char data[14];
memcpy(&data, &buf[bytesRead-14], 14);

// byte 0 (X+1) is the length of dialing number
int length = data[0];

std::stringstream ss;

// TON and NPI determines the type of number
// see 3GPP specifications 31.102 clause 4.4.2.3
if (0x90 == (0xF0 & data[1]))
{
// add the + for international numbers
ss << '+';
}

// convert the BCD bytes to Ascii chars
for (int idx=2; idx < length+1; idx++)
{
// break the byte into 2 nibbles
BYTE nibble[2];
nibble[0] = data[idx] & 0xF;
nibble[1] = (data[idx] >> 4) & 0xF;

for (int i=0; i<2; i++)
{
if (nibble[i] == 0xA)
{
// 0xA represents a *
ss << '*';
}
else if (nibble[i] == 0xB)
{
// 0xB represents a #
ss << '#';
}
else if (nibble[i] < 0xA)
{
// add the number
ss << (char) ('0' + nibble[i]);
}
}
}

// set the voicemail number
ss >> number;


Now that we have the voicemail number we should free the buffer and sim handle and return the number:


// free the buffer
LocalFree( buf );
}

// deinitialize
SimDeinitialize( hSim );
}
return number;

}


There are a couple of things to note. Here I’ve handled both the 2G and 3G cases, however it is probably better to detect which type is being used beforehand. Also note that I have assumed the case of only one voicemail number. For a multi-line scenario you would have to check if there are more than one number stored in the SIM file. For a more detailed look on how the numbers are stored please refer to the 3GPP specifications.

Labels:


Monday, June 09, 2008

 

Detecting the phone buttons on WM5/6

When working on a project that involved creating a custom dialer for Windows Mobile devices, one of the more important tasks is enabling the detection of the Send (green, off-hook) and Hang-up (red, on-hook) button presses at all times. The difficulty with this feat is that the Windows Mobile OS has specific needs for these buttons so you have to be careful with how you go about using them.

For example the Send button is used for launching the phone application (cprog.exe by default), and also perform phone related tasks like making and answering calls or calling a contact. The Hang-up button is used to minimize dialogs and hang up active calls.

On some devices the Hang-up button can have other behaviors such as acting as a power button or enabling keyboard lock. It’s important to know these facts, because depending on what you intend to do with the Send or Hang-up buttons, you may cause existing features of the OS to not function.

There are several methods to detect the Send and Hang-up buttons, but only a few methods will let you safely co-exist with the existing features of the Windows Mobile OS. I’ll first talk about obtaining the key presses through use of keyboard hook (SetWindowsHook) and why you shouldn’t use it. This is the first method I used for the project but quickly learned about the various limitations. If you do an Internet search for “Windows Mobile SetWindowsHook” you will obtain several pages explaining how to use SetWindowsHook on the device to detect each key press. Using this method to detect the Send or Hang-up keys you may discover several set backs.
  1. You are not just intercepting the Send or Hang-up keys but every key press so you’ll have to be extra careful about handling them.
  2. If an existing application already has the keyboard hook, you may not be able to use it. On a device like the Palm Treo 700/750 you may find that the Key-guard feature no longer works. This is because the Palm Key-guard also relies on the keyboard hook and can no longer use it if another application has control of it.
  3. You may find that certain features that rely on Send/Hang-up no long work. For example you can no longer dial a number from Contacts with the Send button.

There are ways around these limitations of course, but it would require a lot of manual handling from your application.

Another method of key detection is the use of hotkey registration. Microsoft provides us with the “RegisterHotKey” function which essentially allows you to register any button with your application window as a hotkey using the virtual key codes (VK_TTALK for Send, VK_TEND for Hang-up). What this does is, when the registered key is pressed; it will send you a WM_HOTKEY message, even if your window is not in the foreground, allowing you to handle it.

However there are a couple limitations when dealing with the Send and Hang-up keys. You may find that attempting to register either buttons as a hot key will fail. The reason is that the TaskBar (PPC) or Tray (Smartphone) already has Send button registered as a hotkey and the phone application has the Hang-up button.

You can forcibly “steal” the hot key using either the “UnregisterHotKey” function or the hidden “UnregisterFunc1” function; however taking the hot key away from their respective owners would render some features useless as with the cases when using “SetWindowsHook”.

After some experimentation I have found that there is a unique way in which you may register the Send and Hang-up buttons (and possibly other buttons) as hot keys. I’ve discovered that it is possible to register a button using 0x2001 as a modifier (some others will work as well such 0x2002, etc). What this does is it will generate a WM_HOTKEY message for you when pressing the Send button without interfering with the existing hot key registration held by the TaskBar or Tray or phone application. The limitation here, however, is that you will only get “one shot”; that is, only one WM_HOTKEY is generated and successive key presses will have no notifications. The solution is simply to register the key again. The code would look as follows (note that error and results checking are left out for simplicity)


// setup the window to receive the hot key message
ON_MESSAGE(WM_HOTKEY, &MyappDlg::OnHotkey)

...

// define some of the values to use for registering hot keys
#define ONE_SHOT_MOD 0x2001
#define VK_TTALK_ID 0x07
#define VK_TEND_ID 0x08

...

// register the Send and Hang-up buttons with the unique modifier
::RegisterHotKey(m_hWnd, VK_TTALK_ID, ONE_SHOT_MOD, VK_TTALK);
::RegisterHotKey(m_hWnd, VK_TEND_ID, ONE_SHOT_MOD, VK_TEND);

...

// wait for and handle the hot key messages
afx_msg LRESULT MyappDlg::OnHotkey( WPARAM wParam, LPARAM lParam )
{
switch (lParam >> 16)
{
case VK_TTALK:
// reregister VK_TTALK to get the next key press
::RegisterHotKey(m_hWnd, VK_TTALK_ID, ONE_SHOT_MOD, VK_TTALK);
// handle the keypress...
break;
case VK_TEND:
// reregister VK_TEND to get the next key press
::RegisterHotKey(m_hWnd, VK_TEND_ID, ONE_SHOT_MOD, VK_TEND);
// handle the keypress...
break;

...

So there you have it. Each time the Send and Hang-up buttons are pressed you get a notification and you don’t have to worry about interfering with the normal operations of each button.

Labels:


This page is powered by Blogger. Isn't yours?