Every phone or data device that operates on the GSM (Global System for Mobile) network contains a small, postage stamp sized card that is known as the Subscriber Identity Module (SIM). The SIM is actually a smart card that contains both a processor and storage for contacts, messages, and data. Typical SIM cards have between 16KB and 64KB of storage, which provides you with plenty of room for hundreds of phone numbers, messages, or other information. You can usually remove the SIM card from the phone, which enables you to move your phone numbers, contacts, and other data between devices. For example, if you remove the SIM card from your GSM phone and place it into a Pocket PC Phone Edition device, the Pocket PC will have access to all of the dialing information, messages, and even phone numbers that the old device had. In essence, the SIM card is the storage medium for all of your personal and subscription data. Pocket PC Phone Edition provides a set of functions called the SIM Manager (see Figure 8.3), which enables you to interact with the SIM card currently in the device. These APIs enable you to change the status of the phone locked state, manipulate contact entries that are on the SIM card, and read messages currently stored on the card. Figure 8.3. Pocket PC Phone Edition SIM Manager
In order to use the SIM Manager APIs in your applications, you need to include the simmgr.h header file in your project, and link with the cellcore.lib library. Initialization and NotificationsBefore you can access any information on the SIM card, you first must call the SimInitialize() function. This function actually has two purposes: One, it provides a handle to the SIM, which you will use for additional calls to get information from the card. Two, SimInitialize() can be used to register a callback function that will send you notifications whenever changes occur to the SIM while you have the handle open: HRESULT SimInitialize(DWORD dwFlags, SIMCALLBACK lpfnCallBack, DWORD dwParam, LPHSIM lphSim); The first parameter, dwFlags, specifies whether or not you want to receive notification messages from the SIM. By setting the flag to SIM_INIT_SIMCARD_NOTIFICATIONS, notifications will be sent to the callback function that is pointed to by the lpfnCallback parameter. If you do not need to receive informational messages from the SIM, you can set dwFlags to 0, and lpfnCallBack to NULL. The dwParam parameter is a DWORD value that is sent to your callback function every time a notification is sent. The last parameter, lphSim, is a pointer to an HSIM handle that you will use for making additional calls to the SIM card. The following example initializes the SIM card for reading data without using a callback: HRESULT hr = S_OK; HSIM hSim = NULL; // Initialize and open the SIM w/o a callback hr = SimInitialize(0, NULL, 0, &hSim); if(FAILED(hr)) { MessageBox(NULL, TEXT("Could not open the SIM"), TEXT("Error"), MB_OK|MB_ICONERROR); return FALSE; } // Continue with app... If you decide to receive notifications from the SIM when changes occur, then you need to pass a pointer to a callback function (identified by the lpfnCallBack parameter). Whenever the device or another application attempts to modify the contents of the SIM card, your function will then be called. The callback function you create must have the following definition: void SIMCallbackFunction(DWORD dwNotifyCode, const void *lpData, DWORD dwDataSize, DWORD dwParam); The first parameter that will be sent to your function specifies the notification that was received from the SIM. Next, the contents of the lpData pointer will depend on which notification message you are receiving. For example, if you are sent a SIM_NOTIFY_CARD_REMOVED, then lpData will be NULL. However, if you received a SIM_NOTIFY_PBE_DELETED notification, lpData will point to a SIMPBECHANGE structure that provides additional information about the phonebook entry that has been modified. Table 8.1 lists both the notification messages and the structure information sent to lpData.
The dwDataSize parameter will specify, in bytes, the size of the structure returned in lpData. The final parameter, dwParam, is the DWORD value that was previously set in your original call to the SimInitialize() function. If any changes to the files on the SIM occur (SIM_NOTIFY_FILE_REFRESH), your callback function will be passed a SIMFILEREFRESH structure that looks like the following: typedef struct simfilerefresh_tag{ DWORD cbSize; DWORD dwParams; DWORD dwFlags; DWORD dwFileCount; DWORD rgdwAddress[MAX_FILES]; } SIMFILEREFRESH, FAR *LPSIMFILEREFRESH; The first field, cbSize, is the size, in bytes, of the SIMFILEREFRESH structure. Next, the dwParams field specifies which of the fields in the SIMFILEREFRESH structure are valid, and can be one or more of the following values:
The dwFlags field contains additional details about what has changed with regard to the SIM's file system, and can be one or more of the following:
The dwFileCount field, as the name implies, contains a DWORD value that indicates how many files have changed, and indicates the number of rgdwAddress structures in the array of files. For example, the following code would handle a notification that some files have changed on the SIM: // The SIM notification callback void SimCallbackProc(DWORD dwNotifyCode, const void *lpData, DWORD dwDataSize, DWORD dwParam) { // Handle a file change if(dwNotifyCode == SIM_NOTIFY_FILE_REFRESH) { SIMFILEREFRESH *pfileRefresh = (SIMFILEREFRESH *)lpData; TCHAR tchNotifyMessage[1024] = TEXT("\0"); if(!pfileRefresh) return; // Handle the file change notification if(pfileRefresh->dwParams & SIM_PARAM_FILEREFRESH_FILECOUNT) { wsprintf(tchNotifyMessage, TEXT("%d files have changed"), pfileRefresh->dwFileCount); MessageBox(NULL, tchNotifyMessage, TEXT("SIM File Change"), MB_OK); } // Get the DWORD address of the SIM files that have changed // if(pfileRefresh->dwParams & SIM_PARAM_FILEREFRESH_FILEARRAY) { DWORD dwFiles = 0; for(dwFiles; dwFiles < pfileRefresh->dwFileCount; dwFiles++) { wsprintf(tchNotifyMessage, TEXT("File change #%d. File address: %d"), dwFiles, pfileRefresh->rgdwAddress[dwFiles]); MessageBox(NULL, tchNotifyMessage, TEXT("SIM Files Change"), MB_OK); } } } return; } // Initialize the SIM BOOL InitializeSIM(HSIM *phSim) { HRESULT hr = S_OK; // Initialize and open the SIM w/ a callback hr = SimInitialize(SIM_INIT_SIMCARD_NOTIFICATIONS, (SIMCALLBACK)SimCallbackProc, 0, phSim); if(FAILED(hr)) { MessageBox(NULL, TEXT("Could not open the SIM"), TEXT("Error"), MB_OK|MB_ICONERROR); return FALSE; } return TRUE; } If the SIM detects any changes to a message located on the card (SIM_NOTIFY_MSG_STORED, SIM_NOTIFY_MSG_DELETED, or SIM_NOTIFY_MSG_RECIEVED), the callback function is passed a SIMMESSAGECHANGE structure, which is defined as follows: typedef struct simmessagechange_tag{ DWORD dwEntry; DWORD dwStorage; } SIMMESSAGECHANGE, FAR *LPSIMMESSAGECHANGE; This structure has only two fields: the index to the entry that has changed and its storage location. The dwStorage field will be set to the SIM_SMSSTORAGE_BROADCAST location if its storage location is with system broadcast messages; otherwise, it will be set to SIM_SMSSTORAGE_SIM. Finally, the last type of notification you can receive deals with entries in SIM's phonebook (SIM_NOTIFY_PBE_STORED, SIM_NOTIFY_PBE_DELETED), and uses the SIMPBECHANGE structure: typedef struct simpbechange_tag{ DWORD dwEntry; DWORD dwStorage; } SIMPBECHANGE, FAR *LPSIMPBECHANGE; The first field is the index to the phonebook entry that has changed. The dwStorage field specifies the phonebook in which the entry is located, and can be one of the following values:
The following code shows how to handle a notification message that indicates a change to one of the phonebook entries: // The SIM notification callback void SimCallbackProc(DWORD dwNotifyCode, const void *lpData, DWORD dwDataSize, DWORD dwParam) { // Handle a phonebook change if(dwNotifyCode == SIM_NOTIFY_PBE_STORED || dwNotifyCode == SIM_NOTIFY_PBE_DELETED) { SIMPBECHANGE *pPBNotify = (SIMPBECHANGE *)lpData; TCHAR tchNotifyMessage[1024] = TEXT("\0"); TCHAR tchPhonebook[256] = TEXT("\0"); if(!pPBNotify) return; switch(pPBNotify->dwStorage) { case SIM_PBSTORAGE_EMERGENCY: wsprintf(tchPhonebook, TEXT("Emergency Number List")); break; case SIM_PBSTORAGE_FIXEDDIALING: wsprintf(tchPhonebook, TEXT("Fixed Dialing List")); break; case SIM_PBSTORAGE_LASTDIALING: wsprintf(tchPhonebook, TEXT("Last Numbered Dialed List")); break; case SIM_PBSTORAGE_OWNNUMBERS: wsprintf(tchPhonebook, TEXT("Own Number List")); break; case SIM_PBSTORAGE_SIM: wsprintf(tchPhonebook, TEXT("General List")); break; default: wsprintf(tchPhonebook, TEXT("Unknown List")); break; } // Build a message wsprintf(tchNotifyMessage, TEXT("A phonebook entry in the %s at index %d has been "), tchPhonebook, pPBNotify->dwEntry); // Type of notification if(dwNotifyCode == SIM_NOTIFY_PBE_DELETED) _tcscat(tchNotifyMessage, TEXT("deleted.")); else if(dwNotifyCode == SIM_NOTIFY_PBE_STORED) _tcscat(tchNotifyMessage, TEXT("modified.")); } return; } When you are finished working with information on the SIM, you can simply call the SimDeinitialize() function, which is defined as follows: HRESULT SimDeinitialize(HSIM hSim); The only parameter that the function needs is a handle to the SIM module that was previously returned by the original call to SimInitialize(). The following code sample shows how to properly release the handle to the SIM module: // Close the SIM handle if(hSim) SimDeinitialize(hSim); SIM DetailsTo get more information about what functionality the SIM supports, you can use the following function: HRESULT SimGetDevCaps(HSIM hSim, DWORD dwCapsType, LPSIMCAPS lpSimCaps); This function can be used to query specific capabilities of the SIM by functional area, or to return information about the phonebook or locking passwords. To use it, you need to pass the handle to the SIM that was returned from a previous call to SimInitialize() in the hSim parameter. The dwCapsType parameter can be used to set which capabilities you are interested in, and can be one or more of the following values:
The last parameter, lpSimCaps, should point to a SIMCAPS structure that will receive the capabilities specified in the dwCapsType parameter. The structure is defined as follows: typedef struct simcaps_tag{ DWORD cbSize; DWORD dwParams; DWORD dwPBStorages; DWORD dwMinPBIndex; DWORD dwMaxPBIndex; DWORD dwMaxPBEAddressLength; DWORD dwMaxPBETextLength; DWORD dwLockFacilities; DWORD dwReadMsgStorages; DWORD dwWriteMsgStorages; DWORD dwNumLockingPwdLengths; SIMLOCKINGPWDLENGTH rgLockingPwdLengths[SIM_NUMLOCKFACILITIES]; } SIMCAPS, FAR *LPSIMCAPS; The cbSize field must be set to the size of the SIMCAPS structure before calling the SimGetDevCaps() function. The dwParams field indicates what other fields in the structure contain data, and can be set to one or more of the values described in Table 8.2.
The dwPBStorages, dwMinPBIndex, and dwMaxPBIndex fields contain the total number (as well as the minimum and maximum number) of entries that are available for the SIM card's phonebook. The maximum length of an address is stored in the dwMaxPBEAddressLength field, and dwMaxPBETextLength will contain the maximum length for any text string in an entry. The dwReadMsgStorages and dwWriteMsgStorages fields pertain to the total number of read and write storage areas that are available on the SIM, respectively. The last set of available fields deals with the locking capabilities of the SIM card. The dwLockFacilities field contains the total number of supported locking facilities that are available on the SIM. Each of these is described in the array of SIMLOCKINGPWDLENGTH structures specified by the rgLockingPwdLengths field. The SIMLOCKINGPWDLENGTH structure contains information about the locking facility index and its maximum password length, and has the following prototype: typedef struct simlockingpwdlength{ DWORD dwFacility; DWORD dwPasswordLength; } SIMLOCKINGPWDLENGTH, FAR *LPSIMLOCKINGPWDLENGTH; The following example queries the available capabilities of a SIM card: // Get the SIM capabilities SIMCAPS simInfo; memset(&simInfo, 0, sizeof(SIMCAPS)); simInfo.cbSize = sizeof(SIMCAPS); hr = SimGetDevCaps(hSim, SIM_CAPSTYPE_ALL, &simInfo); if(FAILED(hr)) { SimDeinitialize(hSim); MessageBox(NULL, TEXT("Could not query the SIM module"), TEXT("Error"), MB_OK|MB_ICONERROR); return FALSE; } To get the status of an SMS storage location, you can call the following function: HRESULT SimGetSmsStorageStatus(HSIM hSim, DWORD dwStorage, LPDWORD lpdwUsed, LPDWORD lpdwTotal); The first parameter is the previously opened handle to the SIM card, and is followed by dwStorage, which should indicate which storage location on the SIM to query. This can be set to either the SIM_SMSSTORAGE_BROADCAST location for the broadcast message location or SIM_SMSSTORAGE_SIM, for the general storage location. The lpdwUsed parameter should point to a DWORD value that will receive the number of locations that are used, and lpdwTotal is the total number of locations available. The following example shows how you can use SimGetSmsStorageStatus() to query the storage locations on the SIM: // Get the SIM general storage usage DWORD dwUsed = 0, dwTotal = 0; TCHAR tchStorage[1024] = TEXT("\0"); hr = SimGetSmsStorageStatus(hSim, SIM_SMSSTORAGE_SIM, &dwUsed, &dwTotal); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not get general storage information"), TEXT("Error"), MB_OK|MB_ICONERROR); else { wsprintf(tchStorage, TEXT("General SIM Storage:\r\n%d Used\r\n%d Free\r\n%d Total"), dwUsed, dwTotal-dwUsed, dwTotal); MessageBox(NULL, tchStorage, TEXT("SIM Usage"), MB_OK|MB_ICONINFORMATION); } // Get the SIM broadcast storage usage dwUsed = dwTotal = 0; hr = SimGetSmsStorageStatus(hSim, SIM_SMSSTORAGE_BROADCAST, &dwUsed, &dwTotal); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not get broadcast storage information"), TEXT("Error"), MB_OK|MB_ICONERROR); else { wsprintf(tchStorage, TEXT("General SIM Storage:\r\n%d Used\r\n%d Free\r\n%d Total"), dwUsed, dwTotal-dwUsed, dwTotal); MessageBox(NULL, tchStorage, TEXT("SIM Broadcast Usage"), MB_OK|MB_ICONINFORMATION); } The SIM PhonebookBefore you start reading and writing entries to the SIM phonebooks, let's first examine how to obtain the number of entries in a particular phonebook location (such as the Emergency dial list). To do so, you can call the following function: HRESULT SimGetPhonebookStatus(HSIM hSim, DWORD dwLocation, LPDWORD lpdwUsed, LPDWORD lpdwTotal); The first parameter is the previously opened handle to the SIM card, and is followed by dwLocation, which indicates what phonebook on the SIM to query. This should be set to one of the following: SIM_PBSTORAGE_EMERGENCY, SIM_PBSTORAGE_FIXEDDIALING, SIM_PBSTORAGE_LASTDIALING, SIM_PBSTORAGE_OWNNUMBERS, or SIM_PBSTORAGE_SIM. The lpdwUsed parameter should point to a DWORD value that will receive the number of entries used in the specified phonebook. The last parameter, lpdwTotal, is the total number of entry "slots" that are available for the phonebook. The following example shows how you can use SimGetPhonebookStatus() to retrieve the number of entries in the Emergency phonebook: // Get phonebook status DWORD dwUsed = 0, dwTotal = 0; TCHAR tchPhonebook[1024] = TEXT("\0"); hr = SimGetPhonebookStatus(hSim, SIM_PBSTORAGE_EMERGENCY, &dwUsed, &dwTotal); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not get Emergency Phonebook information"), TEXT("Error"), MB_OK|MB_ICONERROR); else { wsprintf(tchPhonebook, TEXT("Phonebook Usage:\r\n%d Used\r\n%d Free\r\n%d Total"), dwUsed, dwTotal-dwUsed, dwTotal); MessageBox(NULL, tchPhonebook, TEXT("SIM Emergency Phonebook"), MB_OK|MB_ICONINFORMATION); } To read and write entries from the SIM phonebook, you use the following two functions: HRESULT SimReadPhonebookEntry(HSIM hSim, DWORD dwLocation, DWORD dwIndex, LPSIMPHONEBOOKENTRY lpPhonebookEntry); and HRESULT SimWritePhonebookEntry(HSIM hSim, DWORD dwLocation, DWORD dwIndex, LPSIMPHONEBOOKENTRY lpPhonebookEntry); Both functions take the same four parameters: the hSim parameter should be the handle to the device's SIM card, dwLocation should be set to one of the phonebook locations (such as SIM_PBSTORAGE_EMERGENCY), and the dwIndex parameter should be the index of the entry to write or read. The lpPhonebookEntry parameter points to a SIMPHONEBOOKENTRY structure that contains information about the phonebook entry. If you are using the SimWritePhonebookEntry() function, the entry will be saved to the location specified by the dwLocation parameter. When reading an entry, the structure will be filled with the data from the SIM. The structure used for a phonebook entry is defined as follows: typedef struct simphonebookentry_tag{ DWORD cbSize; DWORD dwParams; TCHAR lpszAddress[MAX_LENGTH_ADDRESS]; DWORD dwAddressType; DWORD dwNumPlan; TCHAR lpszText[MAX_LENGTH_PHONEBOOKENTRYTEXT]; } SIMPHONEBOOKENTRY, *LPSIMPHONEBOOKENTRY; The first field, cbSize, should be set to the size of the SIMPHONEBOOKENTRY structure before calling either the SimReadPhonebookEntry() or SimWritePhonebookEntry() functions. The dwParams field will indicate which of the fields in the structure contain valid data, and can be one or more of the values in Table 8.3.
The entry's phone number is stored as a null-terminated string in the lpszAddress field. Details about what type of number the entry is will be indicated by using one of the flags in Table 8.4 in the dwAddressType field.
If the entry is a SIM_ADDRTYPE_UNKNOWN, SIM_ADDRTYPE_INTERNATIONAL, or SIM_ADDRTYPE_NATIONAL address type, the dwNumPlan field will provide additional information about the numbering plan (i.e., the format) used for the address. It can be one of the following:
Finally, the lpszText field contains a null-terminated string that contains any text associated with this entry. The following code reads the first phonebook entry from the general SIM phonebook: SIMPHONEBOOKENTRY simPhoneEntry; memset(&simPhoneEntry, 0, sizeof(SIMPHONEBOOKENTRY)); simPhoneEntry.cbSize = sizeof(SIMPHONEBOOKENTRY); hr = SimReadPhonebookEntry(hSim, SIM_PBSTORAGE_SIM, 1, &simPhoneEntry); if(FAILED(hr)) { MessageBox(NULL, TEXT("Could not read phonebook entry"), TEXT("Error"), MB_OK|MB_ICONERROR); return FALSE; } // Process entry here If you need to write an entry to the phonebook, you can use the following: // Write a phonebook entry to the SIM SIMPHONEBOOKENTRY simPhoneEntry; memset(&simPhoneEntry, 0, sizeof(SIMPHONEBOOKENTRY)); simPhoneEntry.cbSize = sizeof(SIMPHONEBOOKENTRY); simPhoneEntry.dwParams = SIM_PARAM_PBE_ALL; simPhoneEntry.dwAddressType = SIM_ADDRTYPE_NATIONAL; simPhoneEntry.dwNumPlan = SIM_NUMPLAN_TELEPHONE; wsprintf(simPhoneEntry.lpszAddress, TEXT("5555551212")); wsprintf(simPhoneEntry.lpszText, TEXT("Jeremy")); hr = SimWritePhonebookEntry(hSim, SIM_PBSTORAGE_SIM, 3, &simPhoneEntry); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not write new phonebook entry"), TEXT("Error"), MB_OK|MB_ICONERROR); else MessageBox(NULL, TEXT("New entry added successfully"), TEXT("SIM Write"), MB_OK); Finally, to delete a phonebook entry from the SIM card, you can use the SimDeletePhonebookEntry() function, which is defined as follows: HRESULT SimDeletePhonebookEntry(HSIM hSim, DWORD dwLocation, DWORD dwIndex); The only parameters that the function needs are the handle to the SIM card, the phonebook location, and the index to the entry to delete. The following short code sample shows how you can delete a SIM phonebook entry: hr = SimDeletePhonebookEntry(hSim, SIM_PBSTORAGE_SIM, 3); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not delete entry"), TEXT("Error"), MB_OK|MB_ICONERROR); else MessageBox(NULL, TEXT("Entry deleted."), TEXT("SIM Delete"), MB_OK); Working with SIM-Based MessagesTo read or write a Short Message Service text message in a specific storage location on the SIM, you can call the following two functions: HRESULT SimReadMessage(HSIM hSim, DWORD dwStorage, DWORD dwIndex, LPSIMMESSAGE lpSimMessage); and HRESULT SimWriteMessage(HSIM hSim, DWORD dwStorage, LPDWORD lpdwIndex, LPSIMMESSAGE lpSimMessage); Both of these functions need an open handle to the SIM card, as well as a storage location (either SIM_SMSSTORAGE_BROADCAST or SIM_SMSSTORAGE_SIM) as the first two parameters. When you are reading a message from the SIM, the third parameter, dwIndex, should be set to the index of the message you want to read. If you are writing a message, lpdwIndex will point to a DWORD value that receives the index of the message that was written when you called SimWriteMessage(). The last parameter is a pointer to a SIMMESSAGE structure. When reading a message, this structure's fields will be filled in with the contents of the message you specify with the dwIndex parameter. If you are writing a message, you should fill in the structure with the details of the message you want to save. The SIMMESSAGE structure is defined as follows: typedef struct simmessage_tag{ DWORD cbSize; DWORD dwParams; TCHAR lpszAddress[MAX_LENGTH_ADDRESS]; DWORD dwAddressType; DWORD dwNumPlan; SYSTEMTIME stReceiveTime; DWORD cbHdrLength; BYTE rgbHeader[MAX_LENGTH_HEADER]; TCHAR lpszMessage[MAX_LENGTH_MESSAGE]; } SIMMESSAGE, FAR *LPSIMMESSAGE; The first field, cbSize, should be set to the size of the SIMMESSAGE structure before calling either the SimReadMessage() or SimWriteMessage() functions. The dwParams field will indicate which of the fields in the structure contain valid data, and can be set to one or more of the values in Table 8.5.
The incoming message address and numbering plan information fields are similar to those described for SIM phonebook entries. The stReceiveTime field will contain a SYSTEMTIME structure that contains the timestamp for the incoming message. The message header is stored in rgbHeader, and its length is specified by the cbHdrLength field. The actual message body is stored in lpszMessage. To delete a message from the SIM, you can use the SimDeleteMessage() function. This function needs a handle to the open a SIM card, a storage location (either SIM_SMSSTORAGE_BROADCAST or SIM_SMSSTORAGE_SIM), and the index of the message to delete. The function is defined as follows: HRESULT SimDeleteMessage(HSIM hSim, DWORD dwStorage, DWORD dwIndex); The SIM File SystemRecall that the Subscriber Identity Module (SIM) card not only contains your phonebook and text messages, but is actually a smart card containing a processor and a file system. The file system that a SIM card uses is based on the ISO-7816 standard for smart card devices, and is fully specified by the GSM 11.11 standard (more information about both standards can be downloaded from http://www.etsi.org). Although the file system on the SIM card is analogous to that of a file system on the desktop or Pocket PC device (you can read, write, or delete files), there are a few subtle differences:
Figure 8.4 shows the basic tree structure of the SIM file system. Figure 8.4. SIM card file system structure
The most noticeable difference between a regular file system (typically found on a PC) and the SIM file system (besides basic terminology) is that each file in the latter has a four-byte file identifier that uniquely identifies it, rather than a filename. File identifiers are constructed in the following manner on a GSM SIM card. The first two bytes are used to identify the type of file:
The second two bytes are a unique identifier for the file, and are subject to the following rules:
Table 8.6 describes the different record types that the SIM card file system can store.
Every SIM card that is provisioned with a Pocket PC Phone Edition device already contains several required files that are needed in order to maintain compatibility with the GSM protocol. Although most of these records are marked as read-only by your wireless carrier, they contain a lot of useful information that can be used in your own applications. Table 8.7 describes the files located in the GSM SIM file system (more detailed information about each file can also be found in the GSM 11.11 specification).
Reading and Writing RecordsNow that you have a general idea about how the SIM file system is laid out, you can examine what is required to read and write records. Before you can read the contents of an actual record, you must first get information from the SIM about the record such as the type of file it is, the buffer length you need to allocate, and so on. To do so, you can use the SimGetRecordInfo() function, which is defined as follows: HRESULT SimGetRecordInfo(HSIM hSim, DWORD dwAddress, LPSIMRECORDINFO lpSimRecordInfo); The first parameter for this function is the handle to the open SIM card that you previously received from your call to SimInitialize(). The dwAddress parameter is the file identifier of the record you want to open (in Table 8.7, for example, the SIM card serial number would have the file address of 0x2FE2). The last parameter should point to a SIMRECORDINFO structure. After the function returns, the structure will contain information about the record that will enable you to read the record's contents. The SIMRECORDINFO structure looks like the following: typedef struct simrecordinfo_tag{ DWORD cbSize; DWORD dwParams; DWORD dwRecordType; DWORD dwItemCount; DWORD dwSize; } SIMRECORDINFO, FAR *LPSIMRECORDINFO; The first field, cbSize, should be set to the size of a SIMRECORDINFO structure before calling the SimGetRecordInfo() function. The dwParams field will indicate which of the remaining fields of the structure contain valid data, as described in Table 8.8.
The dwRecordType field indicates the type of record for which you are requesting information, and can be one of the following values:
The dwItemCount field specifies the number of items in the record, and is typically used with records that are either SIM_RECORDTYPE_CYCLIC or SIM_RECORDTYPE_LINEAR. The last field, dwSize, is the size, in bytes, of each item. The following example reads the record information for the IMSI (or subscriber identity 0x6F07) from the SIM: // Read from the SIM file system SIMRECORDINFO srecordInfo; DWORD dwIMSIAddress = 0x6f07; memset(&srecordInfo, 0, sizeof(SIMRECORDINFO)); srecordInfo.cbSize = sizeof(SIMRECORDINFO); hr = SimGetRecordInfo(hSim, dwIMSIAddress, &srecordInfo); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not read from SIM file system"), TEXT("Error"), MB_OK|MB_ICONERROR); // Continue to read record... Now that you have the record information for the file you are interested in, you can read the actual contents of the record. You can read the contents of a SIM file by calling the SimReadRecord() function, which is defined as follows: HRESULT SimReadRecord(HSIM hSim, DWORD dwAddress, DWORD dwRecordType, DWORD dwIndex, LPBYTE lpData, DWORD dwBufferSize, LPDWORD lpdwBytesRead); The first parameter is the handle to the open SIM, and is followed by dwAddress, which should specify the unique file identifier for the record you are interested in reading. The next parameter, dwRecordType, specifies the type of record that you are reading, which was returned from your previous call to SimGetRecordInfo(). The dwIndex parameter is used when you have either a SIM_RECORDTYPE_CYCLIC or SIM_RECORDTYPE_LINEAR type of record, and should specify which index within the record to read. The actual contents of the record are returned to the buffer specified by the lpData parameter. The size of the buffer should be set in the dwBufferSize parameter, and the actual number of bytes that are returned to you will be placed in the pointer specified by the lpdwBytesRead parameter. Reading a record is fairly straightforward, as shown by the following sample code that reads in the IMSI information from the SIM: // Continue to read record... LPBYTE lpIMSIBuffer = NULL; DWORD dwBytesRead = 0; // Allocate a buffer to read in the IMSI lpIMSIBuffer = (LPBYTE)LocalAlloc(LPTR, srecordInfo.dwSize); if(!lpIMSIBuffer) { OutputDebugString(TEXT("Could not allocate buffer for IMSI.")); return FALSE; } hr = SimReadRecord(hSim, dwIMSIAddress, srecordInfo.dwRecordType, 0, lpIMSIBuffer, srecordInfo.dwSize, &dwBytesRead); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not read IMSI number"), TEXT("Error"), MB_OK|MB_ICONERROR); // Do something here with the IMSI number... // Free up used buffer LocalFree(lpIMSIBuffer); You can store information on the SIM card by calling the following function: HRESULT SimWriteRecord(HSIM hSim, DWORD dwAddress, DWORD dwRecordType, DWORD dwIndex, LPBYTE lpData, DWORD dwByteCount); The parameters for writing a SIM file are similar to those for the SimReadRecord() function. The first parameter is the handle to the SIM card, dwAddress is the file identifier for the record you want to write, and dwRecordType is the type of SIM file record. The dwIndex parameter is used with records that are either SIM_RECORDTYPE_CYCLIC or SIM_RECORDTYPE_LINEAR and specifies which index within the record to write. The data to be written to the SIM card is specified by the lpData parameter, and the dwByteCount parameter should contain the number of bytes to write to the SIM. SIM LockingSIM cards have built-in security features that enable you to lock the card so that unauthorized calls cannot be made using your phone account. If the SIM itself is locked, the PIN number you specify will need to be entered before you can place a call, regardless of the device or phone in which the SIM is placed. WARNING: You should use the SIM locking facilities with caution. If you forget your password or PIN, the SIM card will be in a locked state and you will need to contact your carrier to unlock the SIM card. You can check the current status of the SIM by calling the following function: HRESULT SimGetLockingStatus(HSIM hSim, DWORD dwLockingFacility, LPTSTR lpszPassword, BOOL *pfEnabled); The first parameter is the open handle to the SIM card. The second parameter specifies a locking mechanism for the SIM, and can be one of the values described in Table 8.9.
The lpszPassword parameter should point to a null-terminated string that contains the facility's password. The last parameter, pfEnabled, should point to a Boolean value that will be filled in when the function returns with a TRUE value if the particular password facility is enabled. The following example determines whether the SIM card is locked: // Get the locking status... BOOL fLock = FALSE; hr = SimGetLockingStatus(hSim, SIM_LOCKFACILITY_SIM, TEXT(""), &fLock); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not get the Locking status from the SIM!"), TEXT("Error"), MB_OK|MB_ICONERROR); if(fLock) MessageBox(NULL, TEXT("SIM is currently locked"), TEXT("SIM Password"), MB_OK|MB_ICONINFORMATION); else MessageBox(NULL, TEXT("SIM is currently unlocked"), TEXT("SIM Password"), MB_OK|MB_ICONINFORMATION); To set the locking status (again, use with caution!), you can use the SimSetLockingStatus() function: HRESULT SimSetLockingStatus(HSIM hSim, DWORD dwLockingFacility, LPTSTR lpszPassword, BOOL fEnabled); The first parameter is the handle to the SIM card, and is followed by the locking facility you want to enable. The lpszPassword parameter is a null-terminated string containing the password you want to set. The last parameter, fEnabled, should be set to TRUE to enable locking. To unlock a particular facility, you would also call SimSetLockingStatus() with the fEnabled parameter set to FALSE. To change the password for a specific locking facility, you can call the following function: HRESULT SimChangeLockingPassword(HSIM hSim, DWORD dwLockingFacility, LPTSTR lpszOldPassword, LPTSTR lpszNewPassword); As with all of the other SIM functions, the first parameter is an open handle to the SIM card. The second parameter, dwLockingFacility, is the locking mechanism for which you want to change the password. The last two parameters are null-terminated strings that should specify both the old and new passwords, respectively. To see if the SIM is currently waiting for a password to be entered, you can simply call the SimGetPhoneLockedState() function. The function requires an open handle to the SIM card, and will return a SIM_LOCKEDSTATE value in the lpdwLockedState pointer when it returns. The function is defined as follows: HRESULT SimGetPhoneLockedState(HSIM hSim, LPDWORD lpdwLockedState); The possible locked states for a SIM card are listed in Table 8.10.
You can use the following code fragment to find out the current locked state for a SIM: // Get the current locked state DWORD dwLockedState = 0; TCHAR tchLocked[256] = TEXT("\0"); hr = SimGetPhoneLockedState(hSim, &dwLockedState); if(FAILED(hr)) MessageBox(NULL, TEXT("Could not get the current SIM locked state!"), TEXT("Error"), MB_OK|MB_ICONERROR); switch(dwLockedState) { case SIM_LOCKEDSTATE_READY: wsprintf(tchLocked, TEXT("SIM Ready (Unlocked)")); break; case SIM_LOCKEDSTATE_SIM_PIN: wsprintf(tchLocked, TEXT("Waiting for the SIM PIN")); break; case SIM_LOCKEDSTATE_SIM_PUK: wsprintf(tchLocked, TEXT("Waiting for the SIM Unblock key")); break; case SIM_LOCKEDSTATE_PH_SIM_PIN: wsprintf(tchLocked, TEXT("Waiting for the SIM Personalization PIN")); break; case SIM_LOCKEDSTATE_PH_FSIM_PIN: wsprintf(tchLocked, TEXT("Waiting for the first SIM Personalization PIN")); break; case SIM_LOCKEDSTATE_PH_FSIM_PUK: wsprintf(tchLocked, TEXT("Waiting for the first SIM Unblock Key")); break; case SIM_LOCKEDSTATE_SIM_PIN2: wsprintf(tchLocked, TEXT("Waiting for the SIM PIN2")); break; case SIM_LOCKEDSTATE_SIM_PUK2: wsprintf(tchLocked, TEXT("Waiting for the SIM Unblock Key 2")); break; case SIM_LOCKEDSTATE_PH_NET_PIN: wsprintf(tchLocked, TEXT("Waiting for Network Personalization Pin")); break; case SIM_LOCKEDSTATE_PH_NET_PUK: wsprintf(tchLocked, TEXT("Waiting for Network Personalization Unblock Key")); break; case SIM_LOCKEDSTATE_PH_NETSUB_PIN: wsprintf(tchLocked, TEXT("Waiting for Network Subset Personalization Pin")); break; case SIM_LOCKEDSTATE_PH_NETSUB_PUK: wsprintf(tchLocked, TEXT("Waiting for Network Subset Personalization Unblock Key")); break; case SIM_LOCKEDSTATE_PH_SP_PIN: wsprintf(tchLocked, TEXT("Waiting for Service Provider Personalization PIN")); break; case SIM_LOCKEDSTATE_PH_SP_PUK: wsprintf(tchLocked, TEXT("Waiting for Service Provider Personalization Unblock Key")); break; case SIM_LOCKEDSTATE_PH_CORP_PIN: wsprintf(tchLocked, TEXT("Waiting for Corporate Pin")); break; case SIM_LOCKEDSTATE_PH_CORP_PUK: wsprintf(tchLocked, TEXT("Waiting for Corporate Unblock Key")); break; default: wsprintf(tchLocked, TEXT("Unknown")); } MessageBox(NULL, tchLocked, TEXT("SIM Locked State"), MB_OK|MB_ICONINFORMATION); Finally, to have your application send a password to unlock a phone, you can simply use the following function: HRESULT SimUnlockPhone(HSIM hSim, LPTSTR lpszPassword, LPTSTR lpszNewPin); The first parameter should point to a valid handle for the SIM. The lpszPassword parameter should be a null-terminated string that specifies the password to unlock the device. The last parameter, lpszNewPin, is an optional second password string, and can be set to NULL if it is not required. |