Stark Wong 的個人開發網站
 


 此頁面:更新於 2019 年 5 月 2 日 12 時 04 分 09 秒,頁面處理需時 0.0008 秒
 網站內容版權所有(C)Stark Wong。頁面(不包括檔案)可自由連結。網站系統版本 1.90-AngularJSBase (2015/9/27)
 
網站地圖

Notice: Undefined variable: row in /var/www/html/blog.inc.php on line 22
Android 程式斷網化 - 從第三方程式庫着手

要將一個本身可離線使用的程式徹底斷網,其實並不需要修改每一個發出連線要求的位置。其實無論是原生程式,還是 React Native 程式,絕大多數不是使用第三方程式庫就是有一個中央的地方處理網路要求。所以只需要修改這個中央的地方就可以將所有網路要求都處理掉。

當去除 INTERNET 權限後,React Native 程式在執行時出現這樣的致命錯誤:

--------- beginning of crash
FATAL EXCEPTION: OkHttp Dispatcher
Process:                           , PID: 2938
java.lang.SecurityException: Permission denied (missing INTERNET permission?)
        at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:151)
        at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:105)
        at java.net.InetAddress.getAllByName(InetAddress.java:1154)
        at okhttp3.Dns$1.lookup(Unknown Source:2)
        at okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(Unknown Source:129)
        at okhttp3.internal.connection.RouteSelector.nextProxy(Unknown Source:20)
        at okhttp3.internal.connection.RouteSelector.next(Unknown Source:17)
        at okhttp3.internal.connection.StreamAllocation.findConnection(Unknown Source:109)
        at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(Unknown Source:0)
        at okhttp3.internal.connection.StreamAllocation.newStream(Unknown Source:22)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(Unknown Source:25)
        at okhttp3.internal.http.RealInterceptorChain.proceed(Unknown Source:158)
        at okhttp3.internal.http.RealInterceptorChain.proceed(Unknown Source:6)
        at okhttp3.internal.cache.CacheInterceptor.intercept(Unknown Source:132)
        at okhttp3.internal.http.RealInterceptorChain.proceed(Unknown Source:158)
        at okhttp3.internal.http.RealInterceptorChain.proceed(Unknown Source:6)
        at okhttp3.internal.http.BridgeInterceptor.intercept(Unknown Source:161)
        at okhttp3.internal.http.RealInterceptorChain.proceed(Unknown Source:158)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(Unknown Source:48)
        at okhttp3.internal.http.RealInterceptorChain.proceed(Unknown Source:158)
        at okhttp3.internal.http.RealInterceptorChain.proceed(Unknown Source:6)
        at okhttp3.RealCall.getResponseWithInterceptorChain(Unknown Source:115)
        at okhttp3.RealCall$AsyncCall.execute(Unknown Source:11)
        at okhttp3.internal.NamedRunnable.run(Unknown Source:17)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)

由於 React Native 的網路部份是使用 OkHttp3,所以當進行網路要求的時候自然也是由 OkHttp3 拋出錯誤。直接拋出 SecurityException 的位置 java.net.Inet6AddressImpl 由於是系統套件,我們無法修改。但我們可以從最接近系統呼叫的地方 okhttp3.Dns$1 找尋修改的機會。

okhttp_dns_1

上圖標示的位置就是拋出 SecurityException 的呼叫。要注意的是 Stack Dump 中所標示的第二行,由於 smali 已被移除行號,所以無法直接對應正確的位置,這時候只能透過方法名稱 (即 lookup) 來尋找正確的方法。在這裡有好幾個解決方法,例如將該句 invoke-static 替換成拋出 UnknownHostException,或將參數 p1 替換成不會構成 SecurityException 的地址 (例如 127.0.0.1)。不過這裡可以用另外一個更簡單的方法。從 invoke-static 該句向上看,有一句 if-eqz p1, :cond_0,這個用 Pseudo code 的意思就是 if (p1 == null) goto cond_0。而 cond_0 又是什麼呢?

okhttp_dns_2

Bingo! 也就是說如果將 null 傳入 p1 的時候會拋出 UnknownHostException,這部份正是我們想要的結果,那麼我們只需要修改跳到 cond_0 的條件即可。if-eqz p1, :cond_0 這句我們只需要由 if-eqz (如果等於 0) 改成 if-nez (如果不等於 0) 就可以將所有有效參數都全部導向 cond_0 而拋出 UnknownHostException。

這樣就已經處理大部份的網路要求。然後還剩下什麼呢?當程式使用 Firebase 的時候,Firebase 初始化時也會拋出 SecurityException,這個會在下一篇再討論。


撰寫於:2021/8/21 23:35:19 / 回應已關閉
正在讀取回響內容...
其他內容請回到主頁