私有API-修復(fù)iOS 10不彈出"是否允許xxx訪問數(shù)據(jù)"導(dǎo)致app無法聯(lián)網(wǎng)的bug

目錄

  • 問題描述
  • 修復(fù)方法
    • 彈出授權(quán)框
      • 調(diào)用方式
    • 讓系統(tǒng)更新蜂窩網(wǎng)絡(luò)權(quán)限數(shù)據(jù)
      • 調(diào)用方式
      • 出現(xiàn)了玄學(xué)
    • 用控制臺跟蹤進程間通信
  • 檢查網(wǎng)絡(luò)權(quán)限情況
  • 檢測國行機型和是否有蜂窩功能
  • 測試修復(fù)是否成功的方法
  • 工具代碼和Demo
  • 參考

問題描述

iOS 10有一個系統(tǒng)bug:app在第一次安裝時英妓,第一次聯(lián)網(wǎng)操作會彈出一個授權(quán)框贿衍,提示"是否允許xxx訪問數(shù)據(jù)干旁?"唠梨。而有時候系統(tǒng)并不會彈出授權(quán)框涧郊,導(dǎo)致app無法聯(lián)網(wǎng)。

詳細(xì)情況見:

iOS 10 的坑:新機首次安裝 app缩多,請求網(wǎng)絡(luò)權(quán)限“是否允許使用數(shù)據(jù)”

iOS 10 不提示「是否允許應(yīng)用訪問數(shù)據(jù)」拆又,導(dǎo)致應(yīng)用無法使用的解決方案

關(guān)鍵點總結(jié):

  • 只有iOS 10以上、國行機型温兼、有蜂窩網(wǎng)絡(luò)功能的設(shè)備存在這個授權(quán)問題庆揪,WiFi版的iPad沒有這個問題;
  • 由于授權(quán)框是在有網(wǎng)絡(luò)操作時才彈出的妨托,這就導(dǎo)致app第一次網(wǎng)絡(luò)訪問必定失敻组弧吝羞;
  • 當(dāng)出現(xiàn)不彈出授權(quán)框的bug時,去設(shè)置里更改任意app的蜂窩網(wǎng)絡(luò)權(quán)限内颗,或者打開無線局域網(wǎng)助理钧排,讓系統(tǒng)更新一下蜂窩網(wǎng)絡(luò)相關(guān)的數(shù)據(jù),可以解決這個bug均澳。

這個系統(tǒng)bug出現(xiàn)時恨溜,對用戶來說是很麻煩的,app也需要提供詳細(xì)的提示語來應(yīng)對這種情況找前,十分不優(yōu)雅糟袁。

修復(fù)方法

春節(jié)有點空,找到了幾個相關(guān)的私有API來修復(fù)這個bug躺盛。

彈出授權(quán)框

首先找到的是一個能直接彈出授權(quán)框的API项戴。

//Image: /System/Library/PrivateFrameworks/FTServices.framework/FTServices

@interface FTNetworkSupport : NSObject
+ (id)sharedInstance;
- (bool)dataActiveAndReachable;
@end

頭文件參考:FTNetworkSupport.h

當(dāng)app之前沒有請求過網(wǎng)絡(luò)權(quán)限時,調(diào)用dataActiveAndReachable會彈出"是否允許xxx訪問數(shù)據(jù)槽惫?"的授權(quán)框周叮,如果網(wǎng)絡(luò)權(quán)限已經(jīng)確定,則不會彈出界斜。

調(diào)用方式

由于FTNetworkSupport是在PrivateFrameworks目錄下仿耽,app并沒有加載這個庫,所以要使用里面的類前各薇,需要用dlopen加載FTServices.framework,簡單示意如下:

#import <dlfcn.h>

//加載FTServices.framework
void * FTServicesHandle = dlopen("/System/Library/PrivateFrameworks/FTServices.framework/FTServices", RTLD_LAZY);
Class NetworkSupport = NSClassFromString(@"FTNetworkSupport");
id networkSupport = [NetworkSupport performSelector:NSSelectorFromString(@"sharedInstance")];
[networkSupport performSelector:NSSelectorFromString(@"dataActiveAndReachable")];
//卸載FTServices.framework
dlclose(FTServicesHandle);

這個API能解決網(wǎng)絡(luò)權(quán)限導(dǎo)致第一個聯(lián)網(wǎng)操作失敗的問題项贺,但是它還是存在有時候不會彈出授權(quán)框的bug。

讓系統(tǒng)更新蜂窩網(wǎng)絡(luò)權(quán)限數(shù)據(jù)

既然更改任意app的蜂窩網(wǎng)絡(luò)權(quán)限后峭判,能讓app彈出授權(quán)框敬扛,那么只要找到一個方法,能讓系統(tǒng)更新一下網(wǎng)絡(luò)權(quán)限相關(guān)的數(shù)據(jù)就可以了朝抖。

hopper反編譯一下系統(tǒng)的設(shè)置app用到的庫PreferencesUI.framework,找到了里面修改app網(wǎng)絡(luò)權(quán)限的API谍珊。用到的是CoreTelephony.framework里的兩個私有C函數(shù):

CTServerConnection* _CTServerConnectionCreateOnTargetQueue(CFAllocatorRef, NSString *, dispatch_queue_t, void*/*一個block類型的參數(shù)*/)

void _CTServerConnectionSetCellularUsagePolicy(CTServerConnection *, NSString *, NSDictionary *)

大部分時間都花在測試這兩個函數(shù)上了治宣。幾個月前我也研究過這兩個函數(shù)嘗試修復(fù)這個bug,但是那時候發(fā)現(xiàn)沒什么作用砌滞,就不了了之了侮邀。

調(diào)用方式

要調(diào)用私有C函數(shù),需要用dlsym贝润,簡單示意如下:

void *CoreTelephonyHandle = dlopen("/System/Library/Frameworks/CoreTelephony.framework/CoreTelephony", RTLD_LAZY);

//用函數(shù)指針來調(diào)用私有C函數(shù)绊茧,用符號名從庫里尋找函數(shù)地址
CFTypeRef (*connectionCreateOnTargetQueue)(CFAllocatorRef, NSString *, dispatch_queue_t, void*) = dlsym(CoreTelephonyHandle, "_CTServerConnectionCreateOnTargetQueue");
int (*changeCellularPolicy)(CFTypeRef, NSString *, NSDictionary *) = dlsym(CoreTelephonyHandle, "_CTServerConnectionSetCellularUsagePolicy");

//使用設(shè)置app的bundle id進行偽裝
CFTypeRef connection = connectionCreateOnTargetQueue(kCFAllocatorDefault,@"com.apple.Preferences",dispatch_get_main_queue(),NULL);
//請求修改本app的網(wǎng)絡(luò)權(quán)限為allowed,不會真的修改打掘,只能觸發(fā)系統(tǒng)更新一下相關(guān)的數(shù)據(jù)
changeCellularPolicy(connection, @"需要授權(quán)的app的bundle id", @{@"kCTCellularUsagePolicyDataAllowed":@YES});

dlclose(CoreTelephonyHandle);

注意华畏,在聲明connectionCreateOnTargetQueue和changeCellularPolicy函數(shù)指針時鹏秋,參數(shù)類型要嚴(yán)格對應(yīng),如果類型錯誤亡笑,可能會導(dǎo)致系統(tǒng)對參數(shù)執(zhí)行錯誤的內(nèi)存管理侣夷,出現(xiàn)crash。CTServerConnection是私有的仑乌,是CFTypeRef的子類百拓,所以這里可以用CFTypeRef來代替。

出現(xiàn)了玄學(xué)

_CTServerConnectionSetCellularUsagePolicy函數(shù)的第二個參數(shù)是需要修改的app的bundle id晰甚。在測試時衙传,發(fā)現(xiàn)傳入這個參數(shù)時,對象必須是用字面量語法創(chuàng)建的NSString厕九,例如@"com.who.testDemo"蓖捶,當(dāng)傳入[NSBundle mainBundle].bundleIdentifier這種動態(tài)生成的NSString時,仍然會出現(xiàn)不彈出授權(quán)框的bug止剖,也就是并沒有修復(fù)成功腺阳。連續(xù)測試5-10次就能重現(xiàn)。

不過穿香,用

NSMutableString *bundleIdentifier = [NSMutableString stringWithString:@"com.who"];
[bundleIdentifier appendString:@".testDemo"];

這樣的字符串也沒問題亭引。相同點是最終都是來自字面量語法創(chuàng)建的NSString

這個玄學(xué)問題目前還沒有找到原因皮获。

研究了一下字面量創(chuàng)建出的NSString焙蚓,的確是有些特殊的。參考:Constant Strings in Objective-C洒宝。它是一個__NSCFConstantString類型的字符串购公,在app的整個生命周期內(nèi),這個對象的內(nèi)存都不會被釋放雁歌。難道iOS的XPC對使用到的字符串還有要求宏浩?

時間有限,這個問題以后再研究吧靠瞎。

<a name="debug-trace"></a>用控制臺跟蹤進程間通信

這幾個私有API都用了進程間通信比庄,要進行調(diào)試跟蹤有點麻煩。

可以使用Mac上的控制臺查看設(shè)備的實時log乏盐,尋找通信行為佳窑。打開控制臺app,在左側(cè)選擇連接到Mac的iOS設(shè)備父能,就可以看到設(shè)備log了神凑。

下面是調(diào)用了_CTServerConnectionSetCellularUsagePolicy之后的log,傳入bundle id時用的是字面量創(chuàng)建的字符串:

使用字面量字符串傳入bundle id

高亮的那行是測試demo打的log何吝,可以認(rèn)為就是在這里調(diào)用了_CTServerConnectionSetCellularUsagePolicy溉委,
可以看到鹃唯,調(diào)用之后系統(tǒng)更新了本app的權(quán)限狀態(tài)。CommCenter就是這幾個私有API通信的對應(yīng)進程薛躬,用于管理設(shè)備的網(wǎng)絡(luò)俯渤。參考CommCenter - The iPhone Wiki

下面是用[NSBundle mainBundle].bundleIdentifier傳入_CTServerConnectionSetCellularUsagePolicy的第二個參數(shù)時的log:

使用動態(tài)創(chuàng)建的字符串傳入bundle id

沒有看到系統(tǒng)更新app權(quán)限的相關(guān)log型宝,進程間通信可能失敗了八匠。因此可以確定,使用_CTServerConnectionSetCellularUsagePolicy時必須傳入字面量語法創(chuàng)建的字符串趴酣。

檢查網(wǎng)絡(luò)權(quán)限情況

由于dataActiveAndReachable里面有異步操作梨树,所以不能立即用dlclose卸載FTServices.framework。解決方法是監(jiān)聽到蜂窩權(quán)限開啟時再卸載岖寞。

CoreTelephony里的CTCellularData可以用來監(jiān)測app的蜂窩網(wǎng)絡(luò)權(quán)限抡四,并且這不是個私有API。你也可以用它來幫助用戶檢測蜂窩權(quán)限是否被關(guān)閉仗谆,并給出提示指巡,防止出現(xiàn)用戶關(guān)了網(wǎng)絡(luò)權(quán)限導(dǎo)致app無法聯(lián)網(wǎng)的情況。

CTCellularData的頭文件如下:

typedef NS_ENUM(NSUInteger, CTCellularDataRestrictedState) {
    kCTCellularDataRestrictedStateUnknown,//權(quán)限未知
    kCTCellularDataRestricted,//蜂窩權(quán)限被關(guān)閉隶垮,有 網(wǎng)絡(luò)權(quán)限完全關(guān)閉 or 只有WiFi權(quán)限 兩種情況
    kCTCellularDataNotRestricted//蜂窩權(quán)限開啟
};

@interface CTCellularData : NSObject
///權(quán)限更改時的回調(diào)
@property (copy, nullable) CellularDataRestrictionDidUpdateNotifier cellularDataRestrictionDidUpdateNotifier;
///當(dāng)前的蜂窩權(quán)限
@property (nonatomic, readonly) CTCellularDataRestrictedState restrictedState;
@end

使用方法:

#import <CoreTelephony/CTCellularData.h>

CTCellularData *cellularDataHandle = [[CTCellularData alloc] init];
cellularDataHandle.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState state) {
        //蜂窩權(quán)限更改時的回調(diào)
    };

使用時需要注意的關(guān)鍵點:

  • CTCellularData只能檢測蜂窩權(quán)限藻雪,不能檢測WiFi權(quán)限。
  • 一個CTCellularData實例新建時狸吞,restrictedStatekCTCellularDataRestrictedStateUnknown勉耀,之后在cellularDataRestrictionDidUpdateNotifier里會有一次回調(diào),此時才能獲取到正確的權(quán)限狀態(tài)蹋偏。
  • 當(dāng)用戶在設(shè)置里更改了app的權(quán)限時便斥,cellularDataRestrictionDidUpdateNotifier會收到回調(diào),如果要停止監(jiān)聽威始,必須將cellularDataRestrictionDidUpdateNotifier設(shè)置為nil枢纠。
  • 賦值給cellularDataRestrictionDidUpdateNotifier的block并不會自動釋放,即便你給一個局部變量的CTCellularData實例設(shè)置監(jiān)聽黎棠,當(dāng)權(quán)限更改時晋渺,還是會收到回調(diào),所以記得將block置nil葫掉。

檢測國行機型和是否有蜂窩功能

非國行機型,以及沒有蜂窩功能的設(shè)備是不需要進行修復(fù)的跟狱。因此也要尋找相關(guān)的私有API進行檢測俭厚。

用到的私有API如下:

//Image: /System/Library/PrivateFrameworks/AppleAccount.framework/AppleAccount

@interface AADeviceInfo : NSObject
///是否有蜂窩功能
- (bool)hasCellularCapability;
///設(shè)備的區(qū)域代碼,例如國行機就是CH
- (id)regionCode;
@end

頭文件參考:AADeviceInfo.h

使用方式和FTServices.framework類似驶臊,不再重復(fù)挪挤。

測試修復(fù)是否成功的方法

我的測試方式是每次運行都修改項目的bundle identifierdisplay name叼丑,讓系統(tǒng)每次都把它當(dāng)做一個新app,使用Release模式扛门,測試是否每次都能夠彈出授權(quán)框鸠信。由于需要不斷修改bundle identifier,寫了個腳本在每次build時自動運行论寨,會自動累加幾個地方的bundle identifier后面的數(shù)字星立。demo里已經(jīng)附帶了這個腳本。

你也可以測試一下不執(zhí)行修復(fù)時葬凳,進行聯(lián)網(wǎng)操作是否會彈出授權(quán)框绰垂。我的測試結(jié)果是大約運行5-10次時,就會出現(xiàn)不彈出授權(quán)框的bug火焰。需要把項目改為Release模式才能出現(xiàn)劲装,Debug模式下不會出bug。

注意昌简,由于build后自動累加的關(guān)系占业,ZIKCellularAuthorization.h里的AppBundleIdentifier是下一次app運行時的值。如果你覺得這個腳本把你搞暈了纯赎,可以在Build Phases/Run Script里關(guān)掉谦疾,在sh ${PROJECT_DIR}/IncreaseBundleId.sh前面加個#注釋掉就行了。

沒有測試覆蓋安裝同一個bundle identifier的app址否,或者更新了版本號的app是否也會出現(xiàn)這個bug餐蔬,現(xiàn)在是認(rèn)為只有第一次安裝時才會出現(xiàn)bug。

工具代碼和Demo

地址在ZIKCellularAuthorization佑附,用到的私有API已經(jīng)經(jīng)過混淆樊诺。測試前記得先把Build Configuration改為Release模式。有幫助請點個Star~

參考

iOS 10 的坑:新機首次安裝 app音同,請求網(wǎng)絡(luò)權(quán)限“是否允許使用數(shù)據(jù)”

iOS 10 不提示「是否允許應(yīng)用訪問數(shù)據(jù)」词爬,導(dǎo)致應(yīng)用無法使用的解決方案

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市权均,隨后出現(xiàn)的幾起案子顿膨,更是在濱河造成了極大的恐慌,老刑警劉巖叽赊,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恋沃,死亡現(xiàn)場離奇詭異,居然都是意外死亡必指,警方通過查閱死者的電腦和手機囊咏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人梅割,你說我怎么就攤上這事霜第。” “怎么了户辞?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵泌类,是天一觀的道長。 經(jīng)常有香客問我底燎,道長刃榨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任书蚪,我火速辦了婚禮喇澡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘殊校。我一直安慰自己晴玖,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布为流。 她就那樣靜靜地躺著呕屎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敬察。 梳的紋絲不亂的頭發(fā)上秀睛,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音莲祸,去河邊找鬼蹂安。 笑死,一個胖子當(dāng)著我的面吹牛锐帜,可吹牛的內(nèi)容都是我干的田盈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼缴阎,長吁一口氣:“原來是場噩夢啊……” “哼允瞧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蛮拔,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤述暂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后建炫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畦韭,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年肛跌,在試婚紗的時候發(fā)現(xiàn)自己被綠了艺配。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片据过。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖妒挎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情西饵,我是刑警寧澤酝掩,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站眷柔,受9級特大地震影響期虾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驯嘱,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一镶苞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鞠评,春花似錦茂蚓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至负乡,卻和暖如春牍白,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抖棘。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工茂腥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人切省。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓最岗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親数尿。 傳聞我的和親對象是個殘疾皇子仑性,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容