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

时间:2021-09-26 13:28:13
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

  1. #import <UIKit/UIKit.h>
  2. #include <notify.h>
  3. #include <stdio.h>
  4. #include <stdarg.h>
  5. #include <string.h>
  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. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  27. // printf("NOTIFICATION: %s\n", [name UTF8String]);
  28. if (!userInfo) return;
  29. NSDictionary *info = (NSDictionary*)userInfo;
  30. int dcount = CFDictionaryGetCount(userInfo);
  31. id keys = [(NSDictionary*)userInfo allKeys];
  32. int i;
  33. for (i = 0; i < dcount; i++)
  34. {
  35. id key = [keys objectAtIndex:i];
  36. dolog(@"  %@: %@", key, [info objectForKey:key]);
  37. }
  38. if ([[(NSDictionary *)userInfo allKeys]
  39. containsObject:@"kCTSMSMessage"]) // SMS Message
  40. {
  41. CTSMSMessage *message = (CTSMSMessage *)
  42. [(NSDictionary *)userInfo objectForKey:@"kCTSMSMessage"];
  43. NSString *address = CTSMSMessageCopyAddress(NULL, message);
  44. NSString *text = CTSMSMessageCopyText(NULL, message);
  45. NSArray *lines = [text componentsSeparatedByString:@"\n"];
  46. printf("  %s %d\n", [address cString], [lines count]);
  47. printf("  %s\n", [text cString]);
  48. fflush(stdout);
  49. }
  50. [pool release];
  51. return ;
  52. }
  53. static void signalHandler(int sigraised)
  54. {
  55. printf("\nInterrupted.\n");
  56. exit(0);
  57. }
  58. int main(int argc, char **argv)
  59. {
  60. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  61. // Initialize listener by adding CT Center observer implicit
  62. id ct = CTTelephonyCenterGetDefault();
  63. CTTelephonyCenterAddObserver( ct, NULL, callback,NULL,NULL,
  64. CFNotificationSuspensionBehaviorHold);
  65. // Handle Interrupts
  66. sig_t oldHandler = signal(SIGINT, signalHandler);
  67. if (oldHandler == SIG_ERR)
  68. {
  69. printf("Could not establish new signal handler");
  70. exit(1);
  71. }
  72. // Run loop lets me catch notifications
  73. printf("Starting run loop and watching for notification.\n");
  74. CFRunLoopRun();
  75. // Shouldn't ever get here. Bzzzt
  76. printf("Unexpectedly back from CFRunLoopRun()!\n");
  77. [pool release];
  78. }

2.

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

执行

  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. +(NSString *) phoneNumber {
  3. NSString *phone = CTSettingCopyMyPhoneNumber();
  4. return phone;
  5. }