整理:iOS 短信与电话事件的获取

background information:

Core Telephony

iOS 4.0 的官方 API 裡頭,多了一個叫做 Core Telephony 的 framework;一直以來 Core Telephony 都是 private API,現在開放出來,但是從文件來看,裡頭根本沒有幾行,既沒有告訴你應該怎麼用,也沒有範例,你從 framework 裡頭寥寥四個 class 的 header 中,也搞不清楚,究竟可以把這個東西用在什麼用途上。

目前只知道可以拿來做兩件事情:1. 知道目前你這隻 iPhone 用的是哪個電信商的服務;2. 知道現在 iPhone 是不是在打電話。

※ 電信商資訊

用 CTTelephonyNetworkInfo 與 CTCarrier 這兩個 class,就可以取得電信商資訊,例如:

CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = info.subscriberCellularProvider;
NSLog(@"carrier:%@", [carrier description]);

倒出來的結果像是:

CTCarrier (0x140dc0) {
	Carrier name: [中華電信]
	Mobile Country Code: [466]
	Mobile Network Code:[92]
	ISO Country Code:[tw]
	Allows VOIP? [YES]
}

然後,如果你對 CTTelephonyNetworkInfo 餵一個 block 進去,像是:

info.subscriberCellularProviderDidUpdateNotifier = ^(CTCarrier *carrier) {NSLog(@"carrier:%@", [carrier description]);};

如此一來,當你的 iPhone 漫遊到了其他網路的時候,就會執行你這段 block,但光是知道手機現在漫遊在哪個電信商的網路裡頭,大概能做的,就是一些跟電信商關係密切的服務之類,你或許可以決定軟體裡頭有哪些功能,一定要在某個電信商的網路才能用;電信商自己做 iPhone 軟體的時候大概會想做這種事情。

※ 通話資料

用 CTCallCenter 與 CTCall 這兩個 class,便可以知道目前 iPhone 是否在通話中。CTCallCenter 的用途是用來監控是不是有電話打進來、正在接聽、或是已經掛斷,而 CTCall 則是將每一則通話事件包裝成一個物件。我們先寫一小段程式-

CTCallCenter *center = [[CTCallCenter alloc] init];
center.callEventHandler = ^(CTCall *call) {
	NSLog(@"call:%@", [call description]);
};

然後,在實機上執行,接著打通電話到這支 iPhone 上,打通電話進去,然後馬上掛斷(人好端端的,幹嘛為了測試程式跟自己的電話費帳單過不去呢?)就可以看到 iPhone 執行了我們的 block,把 CTCall 物件倒出來:

CTCall (0x143400) {
	callState: [CTCallStateIncoming]
	Call ID: [CE5F9337-1990-4254-8797-1CCEA85B061B]
}
CTCall (0x10bac0) {
	callState: [CTCallStateDisconnected]
	Call ID: [CE5F9337-1990-4254-8797-1CCEA85B061B]
}

CTCall 物件只有兩個屬性,一是通話狀態(來電中、通話中…),二是這則通話的 unique id,除此之外沒有其他資訊,你沒辦法知道這通電話是從哪裡打來的,只能知道有電話進來而已,也沒辦法透過這個 API 打電話出去。

大抵上可以想到的用途,就是當你的程式執行到一半的時候,程式流程被電話打斷,這時候就可能要中斷原本正在做的事情,在通話結束之後恢復。

最後,CTCallCenter 與 CTTelephonyNetworkInfo,在模擬器上是沒有辦法用的,呼叫 alloc、init 之後回傳的結果只會是 nil。

Be Sociable, Share!

1.

首先添加coreTelephony.framework

[cpp]  view plain copy
  1. #import   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. typedef struct __CTSMSMessage CTSMSMessage;  
  7. NSString *CTSMSMessageCopyAddress(void *, CTSMSMessage *);  
  8. NSString *CTSMSMessageCopyText(void *, CTSMSMessage *);  
  9. id CTTelephonyCenterGetDefault(void);  
  10. void CTTelephonyCenterAddObserver(id,id,CFNotificationCallback,NSString*,void*,int);  
  11. void dolog(id formatstring,...)  
  12. {  
  13.     va_list arglist;  
  14.     if (formatstring)  
  15.     {  
  16.         va_start(arglist, formatstring);  
  17.         id outstring = [[NSString alloc] initWithFormat:formatstring arguments:arglist];  
  18.         printf("%s\n", [outstring UTF8String]);  
  19.         va_end(arglist);  
  20.     }  
  21. }  
  22. static void callback(CFNotificationCenterRef center,  
  23.                      void *observer, CFStringRef name,  
  24.                      const void *object, CFDictionaryRef userInfo)  
  25. {  
  26.       
  27.     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  28.     // printf("NOTIFICATION: %s\n", [name UTF8String]);  
  29.     if (!userInfo) return;  
  30.       
  31.     NSDictionary *info = (NSDictionary*)userInfo;  
  32.     int dcount = CFDictionaryGetCount(userInfo);  
  33.     id keys = [(NSDictionary*)userInfo allKeys];  
  34.     int i;  
  35.     for (i = 0; i < dcount; i++)  
  36.     {  
  37.         id key = [keys objectAtIndex:i];  
  38.         dolog(@"  %@: %@", key, [info objectForKey:key]);  
  39.     }      
  40.       
  41.       
  42.     if ([[(NSDictionary *)userInfo allKeys]  
  43.          containsObject:@"kCTSMSMessage"]) // SMS Message  
  44.     {  
  45.         CTSMSMessage *message = (CTSMSMessage *)  
  46.         [(NSDictionary *)userInfo objectForKey:@"kCTSMSMessage"];  
  47.         NSString *address = CTSMSMessageCopyAddress(NULL, message);  
  48.         NSString *text = CTSMSMessageCopyText(NULL, message);  
  49.         NSArray *lines = [text componentsSeparatedByString:@"\n"];  
  50.           
  51.         printf("  %s %d\n", [address cString], [lines count]);  
  52.         printf("  %s\n", [text cString]);  
  53.         fflush(stdout);  
  54.     }  
  55.       
  56.     [pool release];  
  57.       
  58.     return ;  
  59. }  
  60. static void signalHandler(int sigraised)  
  61. {  
  62.     printf("\nInterrupted.\n");  
  63.     exit(0);  
  64. }  
  65. int main(int argc, char **argv)  
  66. {  
  67.     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  68.       
  69.     // Initialize listener by adding CT Center observer implicit  
  70.     id ct = CTTelephonyCenterGetDefault();  
  71.     CTTelephonyCenterAddObserver( ct, NULL, callback,NULL,NULL,  
  72.                                  CFNotificationSuspensionBehaviorHold);  
  73.       
  74.     // Handle Interrupts  
  75.     sig_t oldHandler = signal(SIGINT, signalHandler);  
  76.     if (oldHandler == SIG_ERR)  
  77.     {  
  78.         printf("Could not establish new signal handler");  
  79.         exit(1);  
  80.     }  
  81.       
  82.     // Run loop lets me catch notifications  
  83.     printf("Starting run loop and watching for notification.\n");  
  84.     CFRunLoopRun();  
  85.       
  86.     // Shouldn't ever get here. Bzzzt  
  87.     printf("Unexpectedly back from CFRunLoopRun()!\n");  
  88.     [pool release];  
  89. }  
2.

[cpp]  view plain copy
  1. extern NSString* const kCTSMSMessageReceivedNotification;  
  2. extern NSString* const kCTSMSMessageReplaceReceivedNotification;  
  3. extern NSString* const kCTSIMSupportSIMStatusNotInserted;  
  4. extern NSString* const kCTSIMSupportSIMStatusReady;   
  5.   
  6.   
  7.   
  8. typedef struct __CTCall CTCall;  
  9. extern NSString *CTCallCopyAddress(void*, CTCall *);  
  10.   
  11. void* CTSMSMessageSend(id server,id msg);  
  12. typedef struct __CTSMSMessage CTSMSMessage;    
  13. NSString *CTSMSMessageCopyAddress(void *, CTSMSMessage *);    
  14. NSString *CTSMSMessageCopyText(void *, CTSMSMessage *);  
  15.   
  16.   
  17. int CTSMSMessageGetRecordIdentifier(void * msg);  
  18. NSString * CTSIMSupportGetSIMStatus();    
  19. NSString * CTSIMSupportCopyMobileSubscriberIdentity();   
  20.   
  21. id  CTSMSMessageCreate(void* unknow/*always 0*/,NSString* number,NSString* text);  
  22. void * CTSMSMessageCreateReply(void* unknow/*always 0*/,void * forwardTo,NSString* text);  
  23.   
  24.   
  25. id CTTelephonyCenterGetDefault(void);  
  26. void CTTelephonyCenterAddObserver(id,id,CFNotificationCallback,NSString*,void*,int);  
  27. void CTTelephonyCenterRemoveObserver(id,id,NSString*,void*);  
  28. int CTSMSMessageGetUnreadCount(void);   
  29.   
  30. static void callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)   
  31. {  
  32.     NSString *notifyname=(NSString *)name;  
  33.     if ([notifyname isEqualToString:@"kCTCallStatusChangeNotification"])//电话  
  34.     {  
  35.         NSDictionary *info = (NSDictionary*)userInfo;  
  36.           
  37.         NSString *state=[[info objectForKey:@"kCTCallStatus"] stringValue];  
  38.         if ([state isEqualToString:@"5"])//disconnect  
  39.             NSLog(@"未接:%@",state);  
  40.           
  41.     }  
  42.     else if ([notifyname isEqualToString:@"kCTCallIdentificationChangeNotification"])  
  43.     {  
  44. //        CTCallCenter *center = [[CTCallCenter alloc] init];  
  45. //        center.callEventHandler = ^(CTCall *call) {  
  46. //            NSLog(@"call:%@", [call description]);  
  47. //        };  
  48.           
  49.         NSDictionary *info = (NSDictionary *)userInfo;  
  50.         CTCall *call = (CTCall *)[info objectForKey:@"kCTCall"];  
  51.         NSString *caller = CTCallCopyAddress(NULL, call);  
  52.         NSLog(@"电话号码:%@",caller);  
  53.         //CTCallDisconnect(call);  
  54.         /* or one of the following functions: CTCallAnswer 
  55.          CTCallAnswerEndingActive 
  56.          CTCallAnswerEndingAllOthers 
  57.          CTCallAnswerEndingHeld 
  58.         */  
  59.           
  60.     }  
  61.     else if ([notifyname isEqualToString:@"kCTRegistrationDataStatusChangedNotification"])  
  62.     {  
  63.           
  64.     }  
  65.     else if ([notifyname isEqualToString:@"kCTSMSMessageReceivedNotification"])  
  66.     {//api 已过期  
  67.         if ([[(NSDictionary *)userInfo allKeys]    
  68.              containsObject:@"kCTSMSMessage"]) // SMS Message    
  69.         {    
  70.             CTSMSMessage *message = (CTSMSMessage *)    
  71.             [(NSDictionary *)userInfo objectForKey:@"kCTSMSMessage"];    
  72.             NSString *address = CTSMSMessageCopyAddress(NULL, message);    
  73.             NSString *text = CTSMSMessageCopyText(NULL, message);    
  74.             //NSArray *lines = [text componentsSeparatedByString:@"\n"];    
  75.               
  76.             //printf("  %s %d\n", [address cString], [lines count]);    
  77.             //printf("  %s\n", [text cString]);    
  78.             fflush(stdout);    
  79.           
  80.         }  
  81.     }  
  82.     else if ([notifyname isEqualToString:@"kCTMessageReceivedNotification"])//收到短信  
  83.     {  
  84.         /* 
  85.          kCTMessageIdKey = "-2147483636"; 
  86.          kCTMessageTypeKey = 1;  
  87.         */  
  88.           
  89.         NSDictionary *info = (NSDictionary *)userInfo;  
  90.         CFNumberRef msgID = (CFNumberRef)[info objectForKey:@"kCTMessageIdKey"];  
  91.         int result;  
  92.         CFNumberGetValue((CFNumberRef)msgID, kCFNumberSInt32Type, &result);  
  93.           
  94.         /* 
  95.         Class CTMessageCenter = NSClassFromString(@"CTMessageCenter"); 
  96.         id mc = [CTMessageCenter sharedMessageCenter]; 
  97.         id incMsg = [mc incomingMessageWithId: result]; 
  98.          
  99.         int msgType = (int)[incMsg messageType]; 
  100.          
  101.         if (msgType == 1) //experimentally detected number 
  102.         { 
  103.             id phonenumber = [incMsg sender]; 
  104.              
  105.             NSString *senderNumber = (NSString *)[phonenumber canonicalFormat]; 
  106.             id incMsgPart = [[incMsg items] objectAtIndex:0]; 
  107.             NSData *smsData = [incMsgPart data]; 
  108.             NSString *smsText = [[NSString alloc] initWithData:smsData encoding:NSUTF8StringEncoding]; 
  109.         } 
  110.         */  
  111.     }  
  112.     else if ([notifyname isEqualToString:@"kCTIndicatorsSignalStrengthNotification"])//信号  
  113.     {  
  114.         /* 
  115.         kCTIndicatorsGradedSignalStrength = 2; 
  116.         kCTIndicatorsRawSignalStrength = "-101"; 
  117.         kCTIndicatorsSignalStrength = 19; 
  118.         */  
  119.           
  120.     }  
  121.     else if ([notifyname isEqualToString:@"kCTRegistrationStatusChangedNotification"])//网络注册状态  
  122.     {  
  123.         /* 
  124.          kCTRegistrationInHomeCountry = 1; 
  125.          kCTRegistrationStatus = kCTRegistrationStatusRegisteredHome; 
  126.         */  
  127.           
  128.     }  
  129.     else if ([notifyname isEqualToString:@"kCTRegistrationDataStatusChangedNotification"])  
  130.     {  
  131.         /* 
  132.          kCTRegistrationDataActive = 1; 
  133.          kCTRegistrationDataAttached = 1; 
  134.          kCTRegistrationDataConnectionServices =     ( 
  135.          kCTDataConnectionServiceTypeInternet, 
  136.          kCTDataConnectionServiceTypeWirelessModemTraffic, 
  137.          kCTDataConnectionServiceTypeWirelessModemAuthentication 
  138.          ); 
  139.          kCTRegistrationDataContextID = 0; 
  140.          kCTRegistrationDataIndicator = kCTRegistrationDataIndicator3G; 
  141.          kCTRegistrationDataStatus = kCTRegistrationDataStatusAttachedAndActive; 
  142.          kCTRegistrationDataStatusInternationalRoaming = 1; 
  143.          kCTRegistrationRadioAccessTechnology = kCTRegistrationRadioAccessTechnologyUTRAN; 
  144.         */   
  145.     }  
  146.     else if ([notifyname isEqualToString:@"kCTRegistrationCellChangedNotification"])  
  147.     {  
  148.         /* 
  149.          kCTRegistrationGsmCellId = 93204174; 
  150.          kCTRegistrationGsmLac = 55583; 
  151.          kCTRegistrationInHomeCountry = 1; 
  152.          kCTRegistrationRadioAccessTechnology = kCTRegistrationRadioAccessTechnologyUTRAN;  
  153.         */  
  154.     }  
  155.     else if ([notifyname isEqualToString:@"kCTIndicatorRadioTransmitNotification"])  
  156.     {  
  157.         /* 
  158.         kCTRadioTransmitDCHStatus = 1; 
  159.         */   
  160.     }  
  161.     //int unread=CTSMSMessageGetUnreadCount();  
  162.     //if (unread>0)  
  163.         //NSLog(@"未读短信:%d",unread);  
  164.           
  165.     NSLog(@"名字:%@-详细:%@",notifyname,userInfo);  
  166.       
  167.        
  168. }  
  169.   
  170. static void signalHandler(int sigraised)    
  171. {    
  172.     printf("\nInterrupted.\n");    
  173.     exit(0);    
  174. }   
  175. "font-family:Arial, Verdana, sans-serif;">"white-space: normal;">  
  176.   

执行

[cpp]  view plain copy
  1. id ct = CTTelephonyCenterGetDefault();  
  2.     CTTelephonyCenterAddObserver(ct, NULL, callback, NULL, NULL, CFNotificationSuspensionBehaviorHold);  
  3.     // Handle Interrupts    
  4.     sig_t oldHandler = signal(SIGINT, signalHandler);    
  5.     if (oldHandler == SIG_ERR)    
  6.     {    
  7.         printf("Could not establish new signal handler");    
  8.         exit(1);    
  9.     }   
  10.     // Run loop lets me catch notifications    
  11.     printf("Starting run loop and watching for notification.\n");    
  12.     //CFRunLoopRun();   


3.

/********private api********/

无庸置疑,以下的所有操作必须建立在已越狱的iPhone手机上。

首先,在建立工程之后我们要引入一个名为CoreTelephony.framework的框架,他是一个是一个有关电话、短信和邮件通讯的框架。

注:使用4.x的SDK的开发者可以清楚的看到这个框架中有关电话和运营商的各种类(/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.x.sdk/System/Library/Frameworks/CoreTelephony.framework),而其在3.x的SDK中是一个私有框架(/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.x.sdk/System/Library/PrivateFrameworks/CoreTelephony.framework),我们可以使用class-dump(目前最新版本为3.3.4)去dump去得到那些有关的类。

接着,我们要引入一些没有在文档中出现,但又确实存在的函数,由于我们的实现文件为.mm文件所以引入时最好使用extern "C"方式:

extern "C" CFNotificationCenterRef CTTelephonyCenterGetDefault(void); // 获得 TelephonyCenter (电话消息中心) 的引用
extern "C" void CTTelephonyCenterAddObserver(CFNotificationCenterRef center, const void *observer, CFNotificationCallback callBack, CFStringRef name, const void *object, CFNotificationSuspensionBehavior suspensionBehavior);
extern "C" void CTTelephonyCenterRemoveObserver(CFNotificationCenterRef center, const void *observer, CFStringRef name, const void *object);
extern "C" NSString *CTCallCopyAddress(void *, CTCall *call); //获得来电号码
extern "C" void CTCallDisconnect(CTCall *call); // 挂断电话
extern "C" void CTCallAnswer(CTCall *call); // 接电话
extern "C" void CTCallAddressBlocked(CTCall *call);
extern "C" int CTCallGetStatus(CTCall *call); // 获得电话状态 拨出电话时为3,有呼入电话时为4,挂断电话时为5
extern "C" int CTCallGetGetRowIDOfLastInsert(void); // 获得最近一条电话记录在电话记录数据库中的位置

以上这些方法都在CoreTelephony这个库中实现,这也是为什么我们要一开始就引入这个框架了。

第三步:我们就要是要在SpringBaord启动之后就将我们的回调函数注册到CTTelephonyCenter这个消息中心,注册我们就放在Hook到SpringBoard的applicationDidFinishLaunching:的那个函数中去:

CTTelephonyCenterAddObserver(CTTelephonyCenterGetDefault(), NULL, &callBack, CFSTR("kCTCallStatusChangeNotification"), NULL, CFNotificationSuspensionBehaviorHold);
  
这里用到的CTTelephonyCenterAddObsever这个函数基本上与文档中给出的CFNotificationCenterAddObserver这个函数一样,所以我们可以参照后者对前者进行使用(经我测试用法基本相同)。

最后,就是对回调函数的实现:

static void callBack(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {

  if ([(NSString *)name isEqualToString:@"kCTCallStatusChangeNotification"]) {

    CTCall *call = (CTCall *)[(NSDictionary *)userInfo objectForKey:@"kCTCall"];

    NSString *caller = CTCallCopyAddress(NULL, call); // caller 便是来电号码

    CTCallDisconnect(call); // 挂断电话

    CTCallAnswer(call); // 接电话

    CTCallGetStatus(CTCall *call); // 获得电话状态 拨出电话时为3,有呼入电话时为4,挂断电话时为5

    CTCallGetGetRowIDOfLastInsert(void); // 获得最近一条电话记录在电话记录数据库(call_history.db)中的位置(ROWID)

  }

}

以上代码经测试可以使用:

测试机型:iPhone 3GS

系统版本:iOS 4.3.3

手机状态:已越狱

测试结果:Perfect!

http://www.cnblogs.com/OtionSky/archive/2011/11/10/iPhone_TelephoneCenter.html



1.私有api获取电话号码

需要CoreTelephony.framework

  1. extern NSString* CTSettingCopyMyPhoneNumber();  
  2.   
  3.   
  4. +(NSString *) phoneNumber {  
  5.     NSString *phone = CTSettingCopyMyPhoneNumber();  
  6.   
  7.     return phone;  

你可能感兴趣的:(IOS)