[Software Integration] MS Visual C# + Nokia PC Connectivity API 3.2
I’m having curiosity about retrieving SMS from Phone to my PC and including sending SMS from PC via Mobile Phone as GSM modem. So I decide to research about it. And I found the Nokia PC Connectivity API. Somehow using the API needs an extensive knowledge of Programming in .Net studio. So if your beginner to Visual C#, try to study more before taking this step.
Download Nokia PC Connectivity API 3.2 (134)
What’s in the Nokia PC Connectivity API tool package?
The Nokia PC Connectivity API tool package contains the PC Connectivity API headers, API documentation, and sample applications. The latest version of the tool includes updates to the Device Management, File System, and Content Access APIs, as well as support for Microsoft Visual Studio 2008.
For more information, visit the Nokia PC Connectivity API features page »
All I can say about this new area since I’m a web developer, its like HELL! hahahahaa…
By the way, Nokia Connectivity API documentation is limited and no enough sample resources. There are very few samples or let me say no enough sample in the internet so far. That’s why its like hell.
Hi, i am studying about same API. i want send sms.
I have seen PIM navigator example, i can read messages but the button on SmsMessageDlg is not implemented.
have you tried it?
have you solved a probem?
q
Thanks
ah sorry i have tried on predefoutbox folder and it is being send
Hi there Giovani… when you visit at Nokia Standard Forum, there’s no existing code for sending SMS yet, but they said there’s a sample code in Nokia Launchpad and Nokia Premium which is exclusively to paid membership. So what I did, I combine the PIMNavigator and Ranjan.D concept about sending SMS, see http://www.codeproject.com/KB/cs/SMS.aspx?fid=457949 and from there, you can play with sample codes using their Nokia PC Connectivity API and Ranjan.D codes.
Ranjan.D used GSMComm library by Stefan Mayr, see http://www.scampers.org/steve/sms/samples.htm and you will finally solve your problems.
but http://www.codeproject.com/KB/cs/SMS.aspx?fid=457949 don’t use Nokia PC Connectivity API, it use only GSMComm library. it works to send message but don’t works to read all message.
have you got idea?
thank you very much
so what i did was to get only the sending SMS feature of Ranjan.D project using GSMComm library.
nice! i’m gonna make my own journal
hi can some one please help me on how to use the nokia connectivity api 3.2 to receive sms from nokia N70.
Please mail me at
cool_dd123@yahoo.com
Thanks
Spent a lot of effort developing a C# program based
on forum & sample PIMNavigator
Works fine for SEND
Works fine for FIRST RECEIVE Message
Subsequent INPUT Messages fail at
DAContentAccess.CAReadItem with code -2146435035
If I restart program works fine again for 1 RECEIVE Message.
A huge amount of effort spent but at end stuck i RECEIVE.
using System;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Data;
using Oracle.DataAccess.Client;
using CAContentAccess;
using Errors = PCCSErrors.PCCSErrors;
using TypeDefinitions = PCCSTypeDefinitions.PCCSTypeDefinitions;
namespace MyWindowsService{
public class SMS
{
private int intSMSHandle;
private int intStatusCode;
private OracleConnection con;
private CAContentAccess.DAContentAccessDefinitions.CANotifyCallbackDelegate pCANotifyCallBack;
// private CAContentAccess.DAContentAccessDefinitions.CAOperationCallbackDelegate pCAOperationCallback;
private long msgid;
private string mobileno;
private string sendtext;
private string recvtext;
private string strSerialNumber; // 353080029098956
private int hOperHandle;
static void Main(string[] args)
{
SMS mySMS=new SMS();
mySMS.LoopForEver();
}
private void LoopForEver()
{
intSMSHandle= 0;
//Initializing API
intStatusCode = DAContentAccess.CAAPI_Initialize(30, 0);
strSerialNumber= “353080029098956″;
try{
//Opening connection to send messages
intSMSHandle = OpenSMSConnection(strSerialNumber);
}catch(Exception ex){
// throw ex;
Console.WriteLine(“Opening SMS connection Exception ex = ” + ex.Message); // jnc
Console.WriteLine(ex.StackTrace);
Environment.Exit(-1);
}
pCANotifyCallBack = CANotifyCallBack;
// pCAOperationCallback = CAOperationCallback;
int iResult = DAContentAccess.CARegisterNotifyCallback(
intSMSHandle,
TypeDefinitions.API_REGISTER,
pCANotifyCallBack);
if (iResult != Errors.CONA_OK) {
Console.WriteLine(“DAContentAccess.CARegisterNotifyCallback Failed = ” + iResult); // jnc
Environment.Exit(-1);
}
// int iResult = DAContentAccess.CARegisterOperationCallBack(
// intSMSHandle,
// TypeDefinitions.API_REGISTER,
// pCAOperationCallback);
//
// if (iResult != Errors.CONA_OK)
// Environment.Exit(-1);
// create connection string using EZCONNECT format
// this format specifies the server and the Oracle
// service name as the datasource
// using the format: server/oracle service name
// no tnsnames.ora or sqlnet.ora file is needed
string constr = “User Id=hr; ” +
“Password=hr; ” +
“Data Source=localhost/xe”;
// create connection object
con = new OracleConnection(constr);
// use “try” block to open connection
// if an error occurs, simply display the message
// since there is a “catch” block a “using” statement is not used
try {
// attempt to open the connection
con.Open();
}
catch (OracleException ex) {
// an OracleException was thrown
Console.WriteLine(“OracleException ex = ” + ex.Message); // jnc
Console.WriteLine(ex.StackTrace);
Environment.Exit(-1);
}
string strQuery = @”SELECT msgid, mobileno, smstext
FROM zsmstraffic
WHERE status = ‘I’
AND direction = ‘S’”;
// Create the OracleCommand object
OracleCommand cmdQuery = new OracleCommand(strQuery);
cmdQuery.Connection = con;
cmdQuery.CommandType = CommandType.Text;
OracleDataReader reader;
for(;;) {
try
{
// Execute command, create OracleDataReader object
reader = cmdQuery.ExecuteReader();
if(reader.HasRows)
{
while (reader.Read())
{
msgid=reader.GetInt64(0);
mobileno=reader.GetString(1);
sendtext=reader.GetString(2);
sendtext=sendtext.Replace(“\\n”, “\n”);
break;
}
SendSMS(“353080029098956″, mobileno, sendtext, true);
OracleCommand cmdUpdate = con.CreateCommand();
cmdUpdate.CommandText = @”UPDATE zsmstraffic
SET status = ‘X’
WHERE msgid = ” + msgid;
cmdUpdate.ExecuteNonQuery();
cmdUpdate.Dispose();
}
else
{
System.Threading.Thread.Sleep(60000); // 1 minute
Console.WriteLine(“Sleeping for 1 minute”); // jnc
}
}
catch (Exception ex)
{
Console.WriteLine(“FOREVER LOOP Exception ex = ” + ex.Message); // jnc
Console.WriteLine(ex.StackTrace);
Environment.Exit(-1);
}
} // for(;;)
// Closing the SMS connection
CloseSMSConnection(intSMSHandle);
// Terminating api
DAContentAccess.CAAPI_Terminate(0);
// clean up the connection object
con.Dispose();
}
public void SendSMS(string strSerialNumber, string strPhoneNo, string strMessage, bool DontDelete){
try{
// Not sure why this is needed, but didnt work without it
DAContentAccessDefinitions.CA_FOLDER_INFO folderInfo=
new DAContentAccessDefinitions.CA_FOLDER_INFO();
folderInfo.iSize = Marshal.SizeOf(folderInfo);
IntPtr bufItem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DAContentAccessDefinitions.CA_FOLDER_INFO)));
Marshal.StructureToPtr(folderInfo, bufItem, true);
intStatusCode = DAContentAccess.CAGetFolderInfo(intSMSHandle, bufItem);
int intOperationHandle = 0;
// Starting SMS-sending operation
intStatusCode = DAContentAccess.CABeginOperation(intSMSHandle, 0, ref intOperationHandle);
if(intStatusCode != Errors.CONA_OK){
Console.WriteLine(“DAContentAccess.CABeginOperation Failed = ” + intStatusCode); // jnc
Environment.Exit(-1);
}
// Putting the message straight to SENT-folder so it will be sent.
// You could also put it to outbox, but then the code below that
// deletes the message after sending doesnt work like this.
DAContentAccessDefinitions.CA_ITEM_ID itemUid = new DAContentAccessDefinitions.CA_ITEM_ID();
itemUid.iSize = Marshal.SizeOf(itemUid);
itemUid.iFolderId = CADataDefinitions.CA_MESSAGE_FOLDER_OUTBOX;
itemUid.iStatus = 0;
itemUid.iTemporaryID = 0;
itemUid.iUidLen = 0;
itemUid.pbUid = IntPtr.Zero;
IntPtr buf = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DAContentAccessDefinitions.CA_ITEM_ID)));
Marshal.StructureToPtr(itemUid, buf, true);
// Creating the SMS-message object
CADataDefinitions.CA_DATA_MSG objSMSMessage = new CADataDefinitions.CA_DATA_MSG();
objSMSMessage.iSize = Marshal.SizeOf(objSMSMessage);
CADataDefinitions.CA_SET_DATA_FORMAT(ref objSMSMessage.iInfoField, CADataDefinitions.CA_DATA_FORMAT_UNICODE);
CADataDefinitions.CA_SET_DATA_CODING(ref objSMSMessage.iInfoField, CADataDefinitions.CA_DATA_CODING_UNICODE);
CADataDefinitions.CA_SET_MESSAGE_STATUS(ref objSMSMessage.iInfoField, CADataDefinitions.CA_MESSAGE_STATUS_DRAFT);
CADataDefinitions.CA_SET_MESSAGE_TYPE(ref objSMSMessage.iInfoField, CADataDefinitions.CA_SMS_SUBMIT);
objSMSMessage.bAddressCount = 1;
// Setting the phone number
objSMSMessage.pAddress = CreateSMSPhoneNoReference(strPhoneNo);
objSMSMessage.iDataLength = strMessage.Length * 2;
// Setting the message
objSMSMessage.pbData = Marshal.StringToCoTaskMemUni(strMessage);
// Setting the timestamp
CreateTimestamp(ref objSMSMessage.messageDate);
IntPtr buf2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CADataDefinitions.CA_DATA_MSG)));
Marshal.StructureToPtr(objSMSMessage, buf2, true);
// creating the SMS to the phone
intStatusCode =DAContentAccess.CAWriteItem(intOperationHandle, buf, 0,
CADataDefinitions.CA_DATA_FORMAT_STRUCT, buf2);
if(intStatusCode != Errors.CONA_OK){
Console.WriteLine(“DAContentAccess.CAWriteItem Failed = ” + intStatusCode); // jnc
Environment.Exit(-1);
}
Marshal.FreeHGlobal(buf2);
Marshal.FreeHGlobal(buf);
Marshal.FreeCoTaskMem(objSMSMessage.pbData);
Marshal.FreeCoTaskMem(objSMSMessage.pAddress);
// Creating a list object so that we can get info of the SMS that was created to the phone (for deleting purposes)
DAContentAccessDefinitions.CA_ID_LIST lstIDs = new DAContentAccessDefinitions.CA_ID_LIST();
lstIDs.iSize = Marshal.SizeOf(typeof(DAContentAccessDefinitions.CA_ID_LIST));
IntPtr lstbuf = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DAContentAccessDefinitions.CA_ID_LIST)));
Marshal.StructureToPtr(lstIDs, lstbuf, true);
// Committing creation of the SMS, at this time the SMS is created to the phone
// Returns the ID of the SMS to the list
intStatusCode = DAContentAccess.CACommitOperations(intOperationHandle, lstbuf);
if(intStatusCode != Errors.CONA_OK){
Console.WriteLine(“DAContentAccess.CACommitOperations Failed = ” + intStatusCode); // jnc
Environment.Exit(-1);
}
// Deleting the message after send if that was wanted
if(!DontDelete){
// Picking the first (and only) message from list and deleting it
lstIDs = (DAContentAccessDefinitions.CA_ID_LIST)
Marshal.PtrToStructure(lstbuf, typeof(DAContentAccessDefinitions.CA_ID_LIST));
DAContentAccessDefinitions.CA_ITEM_ID objViestiUID=
GetUidFromBuffer(0, lstIDs.pUIDs);
DeleteMessage(intOperationHandle, objViestiUID);
intStatusCode = DAContentAccess.CACommitOperations(intOperationHandle, IntPtr.Zero);
if(intStatusCode != Errors.CONA_OK){
Console.WriteLine(“DAContentAccess.CACommitOperations Failed = ” + intStatusCode); // jnc
Environment.Exit(-1);
}
}
// Ending the transaction
intStatusCode = DAContentAccess.CAEndOperation(intOperationHandle);
if(intStatusCode != Errors.CONA_OK){
Console.WriteLine(“DAContentAccess.CAEndOperation Failed = ” + intStatusCode); // jnc
Environment.Exit(-1);
}
}catch(Exception ex2){
Console.WriteLine(“TRY SendSMS Exception ex2 = ” + ex2.Message); // jnc
Console.WriteLine(ex2.StackTrace);
Environment.Exit(-1);
}
}
private void DeleteMessage(int intOperationHandle, DAContentAccessDefinitions.CA_ITEM_ID objItem){
IntPtr bufViesti = Marshal.AllocHGlobal(Marshal.SizeOf(objItem));
Marshal.StructureToPtr(objItem, bufViesti, true);
int intStatusCode = DAContentAccess.CADeleteItem(intOperationHandle, bufViesti, 0);
if (intStatusCode != Errors.CONA_OK){
Console.WriteLine(“DAContentAccess.CADeleteItem Failed = ” + intStatusCode); // jnc
Environment.Exit(-1);
}
}
private DAContentAccessDefinitions.CA_ITEM_ID GetUidFromBuffer(int iIndex, IntPtr pUIds){
// Calculate beginning of item ‘iIndex’
Int64 iPtr = pUIds.ToInt64() + (iIndex * Marshal.SizeOf(typeof(DAContentAccessDefinitions.CA_ITEM_ID)));
// Convert integer to pointer
IntPtr ptr = new IntPtr(iPtr);
// Copy data from buffer
return (DAContentAccessDefinitions.CA_ITEM_ID)
Marshal.PtrToStructure(ptr, typeof(DAContentAccessDefinitions.CA_ITEM_ID));
}
private int OpenSMSConnection(string strSerialNumber){
IntPtr pstrSerialNumber = Marshal.StringToCoTaskMemUni(strSerialNumber);
int iMedia = TypeDefinitions.API_MEDIA_ALL;
int iTarget = CADataDefinitions.CA_TARGET_SMS_MESSAGES;
int intSMSHandle = 0;
int intStatusCode = DAContentAccess.DAOpenCA(pstrSerialNumber, ref iMedia, iTarget, ref intSMSHandle);
if (intStatusCode != Errors.CONA_OK) {
Console.WriteLine(“DAContentAccess.DAOpenCA Failed = ” + intStatusCode); // jnc
Environment.Exit(-1);
}
Marshal.FreeCoTaskMem(pstrSerialNumber);
return intSMSHandle;
}
private void CloseSMSConnection(int intSMSHandle){
// Close PIM connection
int intStatusCode = DAContentAccess.DACloseCA(intSMSHandle);
if (intStatusCode != Errors.CONA_OK) {
Console.WriteLine(“DAContentAccess.DACloseCA Failed = ” + intStatusCode); // jnc
Environment.Exit(-1);
}
}
private IntPtr CreateSMSPhoneNoReference(string strNumber){
CADataDefinitions.CA_DATA_ADDRESS dataAddress = new CADataDefinitions.CA_DATA_ADDRESS();
dataAddress.iSize = Marshal.SizeOf(dataAddress);
dataAddress.iAddressInfo = CADataDefinitions.CA_MSG_ADDRESS_TYPE_NUMBER;
dataAddress.pstrAddress = strNumber;
// Allocate memory for buffer
IntPtr bufDataAddress = Marshal.AllocHGlobal(Marshal.SizeOf(dataAddress));
Marshal.StructureToPtr(dataAddress, bufDataAddress, true);
return bufDataAddress;
}
private void CreateTimestamp(ref CADataDefinitions.CA_DATA_DATE pimDate){
DateTime dateCurrent = DateTime.Now;
pimDate.iSize = Marshal.SizeOf(pimDate);
pimDate.wYear = System.Convert.ToUInt16(dateCurrent.Year);
pimDate.bMonth = (byte)dateCurrent.Month;
pimDate.bDay = (byte)dateCurrent.Day;
pimDate.bHour = (byte)dateCurrent.Hour;
pimDate.bMinute = (byte)dateCurrent.Minute;
pimDate.bSecond = (byte)dateCurrent.Second;
}
public int CANotifyCallBack(int hCAHandle, int iReason, int iParam, IntPtr pItemID)
{
Console.WriteLine(“Start of CANotifyCallBack”); // jnc
Console.WriteLine(“iReason = ” + iReason); // jnc
if (iReason == CAContentAccess.DAContentAccessDefinitions.CA_REASON_ITEM_ADDED)
{
Console.WriteLine(“SMS Detected CA_REASON_ITEM_ADDED”); // jnc
intStatusCode = GetSMSDetails(pItemID);
if(intStatusCode==0) {
mobileno = “+91″ + mobileno.Substring(mobileno.Length – 10, 10);
Console.WriteLine(“India MobileNo = ” + mobileno); // jnc
recvtext = recvtext.Replace(“\n”, “\\n”);
recvtext = Regex.Replace(recvtext, @”[^\u0020-\u007F]“, ” “);
Console.WriteLine(“CLEAN Text = ” + recvtext); // jnc
OracleCommand cmdInsert = con.CreateCommand();
cmdInsert.CommandText = ” INSERT INTO zsmstraffic ” +
” (msgid,status,direction,msgdate,mobileno,smstext) ” +
” VALUES (msgid_seq.nextval,’I',’R',sysdate,’” + mobileno + “‘, ‘” + recvtext + “‘) “;
Console.WriteLine(“SQL INSERT = ” + cmdInsert.CommandText); // jnc
cmdInsert.ExecuteNonQuery();
cmdInsert.Dispose();
}
}
return 0;
}
//===================================================================
// GetSMSDetails
//
// Read selected SMS from phone and show details in list view.
//
//===================================================================
private int GetSMSDetails(IntPtr pItemID)
{
// Read SMS item data from device
Console.WriteLine(“Start of GetSMSDetails”); // jnc
hOperHandle=0;
int iRet = DAContentAccess.CABeginOperation(intSMSHandle, 0,ref hOperHandle);
if (iRet != Errors.CONA_OK) {
Console.WriteLine(“DAContentAccess.CABeginOperation Failed = ” + iRet); // jnc
return 1;
}
// Creating the SMS-message object
CADataDefinitions.CA_DATA_MSG dataSMS = new CADataDefinitions.CA_DATA_MSG();
dataSMS.iSize = Marshal.SizeOf(dataSMS);
dataSMS.bAddressCount = 0;
dataSMS.iInfoField = 0;
dataSMS.iDataLength = 0;
dataSMS.pAddress = IntPtr.Zero;
dataSMS.pbData = IntPtr.Zero;
GetEmptyPIMDate(ref dataSMS.messageDate);
IntPtr pDataSMS = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CADataDefinitions.CA_DATA_MSG)));
Marshal.StructureToPtr(dataSMS, pDataSMS, true);
iRet = DAContentAccess.CAReadItem(hOperHandle,
pItemID,
0,
CADataDefinitions.CA_DATA_FORMAT_STRUCT,
pDataSMS);
if (iRet == Errors.CONA_OK)
{
// Copy data from buffer
dataSMS = (CADataDefinitions.CA_DATA_MSG)
Marshal.PtrToStructure(pDataSMS,
typeof(CADataDefinitions.CA_DATA_MSG));
}
else
{
Console.WriteLine(“pItemID = ” + pItemID); // jnc
Console.WriteLine(“pDataSMS = ” + pDataSMS); // jnc
Console.WriteLine(“DAContentAccess.CAReadItem Failed = ” + iRet); // jnc
int intStatusCode = DAContentAccess.CAEndOperation(hOperHandle);
return 1;
}
// Marshal.FreeHGlobal(buffer);
int iResult = DAContentAccess.CAEndOperation(hOperHandle);
if (iResult != Errors.CONA_OK) {
Console.WriteLine(“DAContentAccess.CAEndOperation Failed = ” + iResult); // jnc
return 1;
}
int i;
// Addresses
for (i = 0; i 0)
{
if ((CADataDefinitions.CA_GET_DATA_FORMAT(dataSMS.iInfoField)) == CADataDefinitions.CA_DATA_FORMAT_UNICODE)
{
if (!IntPtr.Zero.Equals(dataSMS.pbData))
{
recvtext = Marshal.PtrToStringUni(dataSMS.pbData, dataSMS.iDataLength / 2);
Console.WriteLine(“Received SMS Text = ” + recvtext); // jnc
}
}
}
// // Free memory allocated by DA API
// iRet = DAContentAccess.CAFreeItemData(intSMSHandle, CADataDefinitions.CA_DATA_FORMAT_STRUCT, pDataSMS);
// if (iRet != Errors.CONA_OK) {
// Console.WriteLine(“DAContentAccess.CAFreeItemData Failed = ” + iRet); // jnc
// return 1;
// }
// Marshal.FreeHGlobal(pDataSMS);
// pDataSMS = IntPtr.Zero;
return 0;
}
//===================================================================
//
// Structure to map CA_ITEM_ID “permanently” into managed memory
//
//===================================================================
private struct CAItemID
{
public int iFolderId;
public int iTemporaryID;
public byte[] abUID;
public int iStatus;
}
//===================================================================
// MapUIDToCAItemID
//
// Maps CA_ITEM_ID structure to CaItemID structure
//
//===================================================================
private CAItemID MapUIDToCAItemID(CAContentAccess.DAContentAccessDefinitions.CA_ITEM_ID UID)
{
CAItemID functionReturnValue = new CAItemID();
functionReturnValue.iFolderId = UID.iFolderId;
functionReturnValue.iTemporaryID = UID.iTemporaryID;
functionReturnValue.iStatus = UID.iStatus;
if (UID.iUidLen > 0)
{
functionReturnValue.abUID = new byte[UID.iUidLen];
Marshal.Copy(UID.pbUid, functionReturnValue.abUID, 0, UID.iUidLen);
}
else
{
functionReturnValue.abUID = null;
}
return functionReturnValue;
}
//===================================================================
// MapCAItemIDToUID
//
// Maps CaItemID structure to CA_ITEM_ID structure
// Remember to free allocated memory after use (FreeUIDMappingMemory).
//
//===================================================================
private CAContentAccess.DAContentAccessDefinitions.CA_ITEM_ID MapCAItemIDToUID(CAItemID caID)
{
CAContentAccess.DAContentAccessDefinitions.CA_ITEM_ID functionReturnValue = new CAContentAccess.DAContentAccessDefinitions.CA_ITEM_ID();
functionReturnValue.iSize = Marshal.SizeOf(typeof(CAContentAccess.DAContentAccessDefinitions.CA_ITEM_ID));
functionReturnValue.iFolderId = caID.iFolderId;
functionReturnValue.iTemporaryID = caID.iTemporaryID;
functionReturnValue.iStatus = caID.iStatus;
if (caID.abUID == null)
{
functionReturnValue.iUidLen = 0;
functionReturnValue.pbUid = IntPtr.Zero;
}
else
{
int iSize = caID.abUID.GetUpperBound(0) – caID.abUID.GetLowerBound(0)+1;
functionReturnValue.iUidLen = iSize;
functionReturnValue.pbUid = Marshal.AllocHGlobal(iSize);
Marshal.Copy(caID.abUID, 0, functionReturnValue.pbUid, iSize);
}
return functionReturnValue;
}
//===================================================================
// GetEmptyPIMDate
//
// Gets empty CA_DATA_DATE
//
//===================================================================
private void GetEmptyPIMDate(ref CAContentAccess.CADataDefinitions.CA_DATA_DATE pimDate)
{
pimDate.iSize = Marshal.SizeOf(pimDate);
pimDate.bDay = 0;
pimDate.lBias = 0;
pimDate.bHour = 0;
pimDate.bMinute = 0;
pimDate.bMonth = 0;
pimDate.bSecond = 0;
pimDate.lTimeZoneBias = 0;
pimDate.wYear = 0;
}
}
}
greetings Jayanta..
try to delay the retrieving of data from you phone and from the network…
try using System.Threading …
System.Threading.Sleep(3);
so that your phone can buffer the last retrieve…