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。
1.
首先添加coreTelephony.framework
- #import
- #include
- #include
- #include
- #include
- typedef struct __CTSMSMessage CTSMSMessage;
- NSString *CTSMSMessageCopyAddress(void *, CTSMSMessage *);
- NSString *CTSMSMessageCopyText(void *, CTSMSMessage *);
- id CTTelephonyCenterGetDefault(void);
- void CTTelephonyCenterAddObserver(id,id,CFNotificationCallback,NSString*,void*,int);
- void dolog(id formatstring,...)
- {
- va_list arglist;
- if (formatstring)
- {
- va_start(arglist, formatstring);
- id outstring = [[NSString alloc] initWithFormat:formatstring arguments:arglist];
- printf("%s\n", [outstring UTF8String]);
- va_end(arglist);
- }
- }
- static void callback(CFNotificationCenterRef center,
- void *observer, CFStringRef name,
- const void *object, CFDictionaryRef userInfo)
- {
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- if (!userInfo) return;
-
- NSDictionary *info = (NSDictionary*)userInfo;
- int dcount = CFDictionaryGetCount(userInfo);
- id keys = [(NSDictionary*)userInfo allKeys];
- int i;
- for (i = 0; i < dcount; i++)
- {
- id key = [keys objectAtIndex:i];
- dolog(@" %@: %@", key, [info objectForKey:key]);
- }
-
-
- if ([[(NSDictionary *)userInfo allKeys]
- containsObject:@"kCTSMSMessage"])
- {
- CTSMSMessage *message = (CTSMSMessage *)
- [(NSDictionary *)userInfo objectForKey:@"kCTSMSMessage"];
- NSString *address = CTSMSMessageCopyAddress(NULL, message);
- NSString *text = CTSMSMessageCopyText(NULL, message);
- NSArray *lines = [text componentsSeparatedByString:@"\n"];
-
- printf(" %s %d\n", [address cString], [lines count]);
- printf(" %s\n", [text cString]);
- fflush(stdout);
- }
-
- [pool release];
-
- return ;
- }
- static void signalHandler(int sigraised)
- {
- printf("\nInterrupted.\n");
- exit(0);
- }
- int main(int argc, char **argv)
- {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-
- id ct = CTTelephonyCenterGetDefault();
- CTTelephonyCenterAddObserver( ct, NULL, callback,NULL,NULL,
- CFNotificationSuspensionBehaviorHold);
-
-
- sig_t oldHandler = signal(SIGINT, signalHandler);
- if (oldHandler == SIG_ERR)
- {
- printf("Could not establish new signal handler");
- exit(1);
- }
-
-
- printf("Starting run loop and watching for notification.\n");
- CFRunLoopRun();
-
-
- printf("Unexpectedly back from CFRunLoopRun()!\n");
- [pool release];
- }
2.
- extern NSString* const kCTSMSMessageReceivedNotification;
- extern NSString* const kCTSMSMessageReplaceReceivedNotification;
- extern NSString* const kCTSIMSupportSIMStatusNotInserted;
- extern NSString* const kCTSIMSupportSIMStatusReady;
-
-
-
- typedef struct __CTCall CTCall;
- extern NSString *CTCallCopyAddress(void*, CTCall *);
-
- void* CTSMSMessageSend(id server,id msg);
- typedef struct __CTSMSMessage CTSMSMessage;
- NSString *CTSMSMessageCopyAddress(void *, CTSMSMessage *);
- NSString *CTSMSMessageCopyText(void *, CTSMSMessage *);
-
-
- int CTSMSMessageGetRecordIdentifier(void * msg);
- NSString * CTSIMSupportGetSIMStatus();
- NSString * CTSIMSupportCopyMobileSubscriberIdentity();
-
- id CTSMSMessageCreate(void* unknow,NSString* number,NSString* text);
- void * CTSMSMessageCreateReply(void* unknow,void * forwardTo,NSString* text);
-
-
- id CTTelephonyCenterGetDefault(void);
- void CTTelephonyCenterAddObserver(id,id,CFNotificationCallback,NSString*,void*,int);
- void CTTelephonyCenterRemoveObserver(id,id,NSString*,void*);
- int CTSMSMessageGetUnreadCount(void);
-
- static void callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
- {
- NSString *notifyname=(NSString *)name;
- if ([notifyname isEqualToString:@"kCTCallStatusChangeNotification"])
- {
- NSDictionary *info = (NSDictionary*)userInfo;
-
- NSString *state=[[info objectForKey:@"kCTCallStatus"] stringValue];
- if ([state isEqualToString:@"5"])
- NSLog(@"未接:%@",state);
-
- }
- else if ([notifyname isEqualToString:@"kCTCallIdentificationChangeNotification"])
- {
-
-
-
-
-
- NSDictionary *info = (NSDictionary *)userInfo;
- CTCall *call = (CTCall *)[info objectForKey:@"kCTCall"];
- NSString *caller = CTCallCopyAddress(NULL, call);
- NSLog(@"电话号码:%@",caller);
-
-
-
-
-
-
-
- }
- else if ([notifyname isEqualToString:@"kCTRegistrationDataStatusChangedNotification"])
- {
-
- }
- else if ([notifyname isEqualToString:@"kCTSMSMessageReceivedNotification"])
- {
- if ([[(NSDictionary *)userInfo allKeys]
- containsObject:@"kCTSMSMessage"])
- {
- CTSMSMessage *message = (CTSMSMessage *)
- [(NSDictionary *)userInfo objectForKey:@"kCTSMSMessage"];
- NSString *address = CTSMSMessageCopyAddress(NULL, message);
- NSString *text = CTSMSMessageCopyText(NULL, message);
-
-
-
-
- fflush(stdout);
-
- }
- }
- else if ([notifyname isEqualToString:@"kCTMessageReceivedNotification"])
- {
-
-
-
-
-
- NSDictionary *info = (NSDictionary *)userInfo;
- CFNumberRef msgID = (CFNumberRef)[info objectForKey:@"kCTMessageIdKey"];
- int result;
- CFNumberGetValue((CFNumberRef)msgID, kCFNumberSInt32Type, &result);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- else if ([notifyname isEqualToString:@"kCTIndicatorsSignalStrengthNotification"])
- {
-
-
-
-
-
-
- }
- else if ([notifyname isEqualToString:@"kCTRegistrationStatusChangedNotification"])
- {
-
-
-
-
-
- }
- else if ([notifyname isEqualToString:@"kCTRegistrationDataStatusChangedNotification"])
- {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- else if ([notifyname isEqualToString:@"kCTRegistrationCellChangedNotification"])
- {
-
-
-
-
-
-
- }
- else if ([notifyname isEqualToString:@"kCTIndicatorRadioTransmitNotification"])
- {
-
-
-
- }
-
-
-
-
- NSLog(@"名字:%@-详细:%@",notifyname,userInfo);
-
-
- }
-
- static void signalHandler(int sigraised)
- {
- printf("\nInterrupted.\n");
- exit(0);
- }
- "font-family:Arial, Verdana, sans-serif;">"white-space: normal;">
-
执行
- id ct = CTTelephonyCenterGetDefault();
- CTTelephonyCenterAddObserver(ct, NULL, callback, NULL, NULL, CFNotificationSuspensionBehaviorHold);
-
- sig_t oldHandler = signal(SIGINT, signalHandler);
- if (oldHandler == SIG_ERR)
- {
- printf("Could not establish new signal handler");
- exit(1);
- }
-
- printf("Starting run loop and watching for notification.\n");
-
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
- extern NSString* CTSettingCopyMyPhoneNumber();
-
-
- +(NSString *) phoneNumber {
- NSString *phone = CTSettingCopyMyPhoneNumber();
-
- return phone;
- }