You can use the Palm OS® Telephony Manager to communicate between a handheld and a phone or to access telephony services in an application intended for a smartphone. This chapter contains the following sections that describe how to use the Palm OS Telephony API:
- Telephony Service Types describes the component parts of the telephony API.
- Using the Telephony API describes how to use the telephony API in your applications.
For detailed information about the Telephony Manager data types, constants, and functions, see the following chapters in the Palm OS Programmer's API Reference:
- Chapter 73, "Telephony Basic Services."
- Chapter 74, "Telephony Security and Configuration."
- Chapter 75, "Telephony Network."
- Chapter 76, "Telephony Calls."
- Chapter 77, "Telephony SMS."
- Chapter 78, "Telephony Phone Book."
The "Telephony Basic Services" chapter describes the basic services and provides a map to the other functions.
Telephony Service Types
The telephony API organizes functions within sets called service sets. Each service set contains a related set of functions that may or may not be available on a particular mobile device or network. You can use the TelIs<ServiceSet>Available
macro to determine if a service set is supported in the current environment, and you can use the TelIs<FunctionName>Supported
macro to determine if a specific function is supported in the current environment.
NOTE: Sometimes a service set is supported, but not all of the functions in that service set are supported. See Testing the Telephony Environment for more information.
Each function in the telephony API is prefixed with Tel
; each telephony service set adds an addition 3 characters to the prefix. Table 10.1 describes the telephony service sets.
Using the Telephony API
This section provides examples excerpted from the Phone Book Application (PhBkApp
) sample program, which provides the following capabilities:
- creates, modifies, and deletes entries on a phone, using the SIM and built-in storage on the phone device
- imports entries from the Address Book application
- exports entries to the Address Book application
The PhBkApp
program opens and accesses the Telephony Manager library and makes a number of calls into the library. It provides an excellent example of using telephony services in your applications.
Accessing the Telephony Manager Library
Before you can use the Telephony Manager library, you must load the library and obtain a reference number for it. Each of the functions in the library requires a reference number argument, which is used with the system code to access a shared library.
Each of the functions in the library also requires an application attachment identifier, which you can obtain by calling the TelOpen
function.
The example function LoadTelMgrLibrary
, which is shown in Listing 10.1, makes sure that the Telephony Manager library is loaded, obtains an application attachment identifier, and returns a reference number for it.
Listing 10.1 Loading the Telephony Manager library
Err LoadTelMgrLibrary(UInt16 *telRefNumP, UInt16 *telAppIdP) { Err err; err = SysLibFind(kTelMgrLibName, telRefNumP); if (err != errNone) { err = SysLibLoad(kTelMgrDatabaseType, kTelMgrDatabaseCreator, telRefNumP); if (err) return err; } err = TelOpen(*telRefNumP, kTelMgrVersion, telAppIdP); return err; }
The LoadTelMgrLibrary
function first calls the SysLibFind
function to determine if the library has already been loaded, which might be the case if your code has been called by another application that has already loaded the library.
If the library has not already been loaded, LoadTelMgrLibrary
calls the SysLibLoad
function to load the library and obtain a reference number for it.
After obtaining a reference number for the library, LoadTelMgrLibrary
calls the TelOpen
function to open the loaded library. TelOpen
opens the Telephony library using the currently selected Connection Manager profile. If you need to use a specific profile, call TelOpenProfile
instead.
Note that if you are writing an application for a handheld to communicate with a phone, you do not need to establish a network connection between the two. After the Telephony library is successfully opened, each call to the Telephony Manager opens a connection to the phone, performs the necessary operation, and then closes the connection. If you are going to make several calls in succession, use TelOpenPhoneConnection
to open the connection to the phone and leave it open. Then use TelClosePhoneConnection
when you are done.
Closing the Telephony Manager Library
When you are done with the library, you should close it by calling the TelClose
function, which releases any resources associated with your use of the Telephony Manager.
As shown in Listing 10.2, you must test the return value of the TelClose
function; if the result is not telErrLibStillInUse
, you must unload the shared library by calling the SysLibRemove
function.
Listing 10.2 Closing the Telephony Manager library
Err UnloadTelMgrLibrary(UInt16 telRefNum, UInt16 telAppId) { if ((TelClose(telRefNum, telAppId)!= telErrLibStillInUse)) SysLibRemove(telRefNum); return errNone; }
Testing the Telephony Environment
Before running your application, you need to verify that the environment in which it is running (the Palm Powered™ handheld and the telephone device) supports the facilities that your application needs. The Telephony Manager allows you to determine if a specific service set is available, and also allows you to determine if a specific function call is supported.
The code excerpt in Listing 10.3 shows how the PhBkApp
program verifies that the environment supports the capabilities that it needs, which include all of the phone book-related features of the Telephony Manager. The PhBkApp
program first tests for the availability of the phone book services, and then determines if several specific functions are supported. Note that the PhBkApp
refuses to run if any of the capabilities it is using are not available.
Listing 10.3 Testing for the presence of specific capabilities
err = TelIsPhbServiceAvailable(gDataP->refNum, gDataP->appId, NULL); // Test if phone book capabilities are present if (err != errNone) return err; // Check that this phone supports adding entry services err = TelIsPhbAddEntrySupported(gDataP->refNum, gDataP->appId, NULL); if (err != errNone) return err; // Check that this phone supports selecting a phone book err = TelIsPhbSelectPhonebookSupported(gDataP->refNum, gDataP->appId, NULL); if (err != errNone) return err; // Check that this phone supports getting entries err = TelIsPhbGetEntriesSupported(gDataP->refNum, gDataP->appId, NULL); if (err != errNone) return err; // Check that this phone supports getting entry count err = TelIsPhbGetEntryCountSupported(gDataP->refNum, gDataP->appId, NULL); if (err != errNone) return err; // Check that this phone supports deleting an entry err = TelIsPhbDeleteEntrySupported(gDataP->refNum, gDataP->appId, NULL); return err;
For a complete list of the service availability macros, see TelIs<ServiceSet>Available
in Chapter 73, "Telephony Basic Services," in Palm OS Programmer's API Reference.
For more information about determining if a specific function is supported, see TelIs<FunctionName>Supported
in Chapter 73, "Telephony Basic Services," in Palm OS Programmer's API Reference.
Using Synchronous and Asynchronous Calls
Almost all of the telephony API functions can be called either synchronously or asynchronously. If you call a function asynchronously, your application receives an event to notify it that the function has completed; the event that you receive contains status and other information returned by the function.
This section provides a simple example of calling the TelPhbAddEntry
function both synchronously and asynchronously to illustrate the difference.
When you call a function synchronously, you need to test the result value returned by the function to determine if the call was successful. For example, the code in Listing 10.4 calls the TelPhAddEntry
function synchronously.
Listing 10.4 Calling a function synchronously
err = TelPhbAddEntry(gTelRefNum, gTelAppID, &gEntry, NULL); printf("Result of adding entry is %d", err);
To call the same function asynchronously, you must do the following (see Listing 10.5):
- Pass a pointer to an unsigned integer as the last argument to the call instead of passing
NULL
.An asynchronous function call returns immediately. Upon return, the last argument contains an ID to identify this particular operation (the transaction ID).
- In your application's main event loop, use
TelGetEvent
instead ofEvtGetEvent
to get the next event.TelGetEvent
checks both the Telephony Manager's asynchronous reply queue and the system event queue. If a telephony event is available, it returns that. If not, it returns the first event in the system event queue.If you're operating outside of the event loop and are only interested in telephony events, you can call
TelGetTelephonyEvent
instead.TelGetTelephonyEvent
returns only telephony events. - Check for the
kTelephonyEvent
event type.When the function call completes,
TelGetEvent
returns aTelEventType
structure with the event type set tokTelephonyEvent
. If the event type is not equal to this event, the normal event loop should process it. - Check the
functionId
and optionally thetransId
field of theTelEventType
structure.The
functionId
field is a constant that tells you which Telephony Manager call has completed. ThetransId
field tells you which instance of the call completed in case you have made two or more asynchronous calls to the same function.
Listing 10.5 Calling a function asynchronously
err = TelPhbAddEntry(gTelRefNum, gTelAppID, &gEntry, &transId); ... } static void ProcessTelephonyEvent(TelEventType *eventP) { switch(eventP->functionId) { ... case kTelPhbAddEntryMessage: printf("Result of adding entry is %d", eventP->returnCode); break; ... } static void AppEventLoop(void) { UInt16 error; EventType event; do { TelGetEvent(gTelRefNum, gTelAppId, (TelEventType *)&event, evtWaitForever); if (event->eType == kTelephonyEvent) { ProcessTelephonyEvent((TelEventType *)&event); } else { if (! SysHandleEvent(&event)) if (! MenuHandleEvent(0, &event, &error)) if (! AppHandleEvent(&event)) FrmDispatchEvent(&event); } while (event.eType != appStopEvent); }
Registering for Notifications
If you need to receive events from the Telephony Manager when your application is not the current application, you should register for the kTelTelephonyNotification
. For example, if you are writing an application that should handle incoming phone calls or receive SMS messages, it is likely that your application will not be the active application when the call or SMS is received. For these types of events, the Telephony Manager posts a kTelTelephonyNotification
.
If you've registered for the notification, your application receives a sysAppLaunchCmdNotify
launch code for every telephony event. The notifyDetailsP
field of this notification's parameter block points to a TelNotificationType
structure. The notificationId
field of this structure identifies which specific telephony event has occurred.
Listing 10.6 shows how to register for and receive incoming SMS message notifications.
Listing 10.6 Receiving an SMS message
UInt32 PilotMain(UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) { Err error; switch (cmd) { //Register for the notification when we are first //installed and upon each system reset. case sysAppLaunchCmdSyncNotify: case sysAppLaunchCmdReset: error = SysCurrentAppDatabase(&gCardNo, &gAppID); if (error) return error; SysNotifyRegister(gCarNo, gAppID, kTelephonyNotification, NULL, sysNotifyNormalPriority, NULL); break; //Once registered, we receive incoming SMS message //notifications as a sysAppLaunchCmdNotify launch code case sysAppLaunchCmdNotify: if((SysNotifyParamType *)cmdPBP->notifyType == kTelephonyNotification) && ((SysNotifyParamType*) cmdPBP->notifyDetailsP->notificationId == kTelSmsLaunchCmdIncomingMessage)) ProcessIncomingSms(cmdPBP->notifyDetailsP); } }
Using Data Structures With Variably-sized Fields
Many of the telephony functions use data structures that have variably-sized buffer fields. For example, the TelPhbGetEntry()
function uses the TelPhbEntryType
structure, which contains two such fields.
typedef struct _TelPhbEntryType { UInt16 phoneIndex; Char* fullName; UInt8 fullNameSize; Char* dialNumber; UInt8 dialNumberSize; } TelPhbEntryType;
The fullName
and dialNumber
buffers are variable-sized strings that you allocate in the heap. When you initialize one of these structures to pass to the TelPhbGetEntry()
function, you must preallocate the buffers and store the allocated size in the corresponding size fields.
Listing 10.7 initializes a TelPhbEntryType
data structure and passes it to the TelPhbGetEntry
function to retrieve an entry from the phone book.
Listing 10.7 Initializing a TelPhbEntrypType structure
#define maxNameSize 45 #define maxNumSize 20 TelPhbEntryType myEntry; UInt16 theIndex = 1; myEntry.phoneIndex = theIndex; myEntry.fullName = MemPtrNew(maxNameSize); myEntry.fullNameSize = maxNameSize; myEntry.dialNumber = MemPtrNew(maxNumSize); myEntry.dialNumberSize = maxNumSize; err = TelPhbGetEntry(gPrefs->telRefNum, gPrefsP->telAppId, &myEntry, NULL);
Note that you can call the TelPhbGetEntryMaxSizes()
function to retrieve the maximum name size (in addition to other information) instead of hardcoding it, as done in the above example.
Upon return from the function, the buffer fields are filled in, and the size fields contain the actual number of bytes that were stored into the buffer fields.
If the allocated size of a buffer is not large enough to contain the entire value, the command function does the following:
- Returns the
telErrBufferSize
error. - Fills the buffer with as much data as it can, and truncates the data that does not fit. If the data ends with a null terminator and is truncated, the null terminator is retained.
- Sets the value of the size field to the actual size required to contain all of the data.
Note that for string buffers, the size includes the byte required for the null terminator character.
NOTE: When you call a function asynchronously, the
telErrBufferSize
error is returned in the returnCode
field of the event you receive upon completion of the function's execution.Also, when you call a function asynchronously, it is your responsibility to ensure that any data structure used by the function remains in memory until you receive the completion event.