1. 程式人生 > >基於Android5.1雙APN的實現---接上篇

基於Android5.1雙APN的實現---接上篇

雙APN問題解決,兩個核心呼叫:

mCm.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
                            Phone.FEATURE_ENABLE_MMS);

—建立mms型別的dcac(dc)。
此命令對應的command如下:

08-29 17:25:27.336 D/CommandListener(  230): [run Route CMD]: route add ccmni2 secondary 10.130.128.1 32 0.0.0.0
08-29 17:25:27.336 D/CommandListener(  230
): [run Route CMD]: route add ccmni2 secondary 10.130.128.1 32 0.0.0.0 08-29 17:25:27.366 D/CommandListener( 230): [run Route CMD]: route add ccmni2 secondary 0.0.0.0 0 10.130.128.1 08-29 17:25:27.366 D/CommandListener( 230): [run Route CMD]: route add ccmni2 secondary 0.0.0.0 0 10.130.128.1 08-29 17:25:27.395 D/CommandListener( 230
): [run Route CMD]: route remove ccmni2 default 0.0.0.0 0 10.130.128.1 08-29 17:25:27.395 D/CommandListener( 230): [run Route CMD]: route remove ccmni2 default 0.0.0.0 0 10.130.128.1

secondary為另一張路由表。若沒有配合其他操作,此處不會對路由有實際的影響。

mCm.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_MMS, netAddr);

—使用mms型別的dcac去訪問指定的netAddr地址(雙通路,未指定的依然走預設。如果MMS通路不存在,則走預設通路)

requestRouteToHostAddress()函式,通過socket與Netd通訊,將command傳送:

08-29 12:00:06.742 D/CommandListener(  230): [run Route CMD]: route add ccmni2 default 10.130.0.1 32 0.0 .0.0
08-29 12:00:06.747 D/CommandListener(  230): [run Route CMD]: route add ccmni2 default 61.135.169.125 32 10.130.0.1

其中倒數第二個引數分別為32和0:

    if (prefix_length == 32) {
        rt.rt_flags |= RTF_HOST;
    }

倒數第三個引數為destIP。
倒數第一個引數來源: //route來源於bestRoute

        if (route.getGateway() == null) {
            if (la.getAddress() instanceof Inet4Address) {
                cmd.appendArg("0.0.0.0");
            } else {
                cmd.appendArg("::0");
            }
        } else {
            cmd.appendArg(route.getGateway().getHostAddress());
        }

即gateway。

以上最終實現在libnetutils/ifc_utils.c中,通過
ifc_init()—建立與net驅動的socket連線
result = ioctl(ifc_ctl_sock, action, &rt)—通過ioctl將操作傳送到net驅動,最終建立一個新的route規則:

root@m4g009:/ # ip route show
ip route show
default via 10.18.163.254 dev ccmni0  scope link
10.18.163.254 dev ccmni0  scope link
10.130.0.1 dev ccmni2  scope link
61.135.169.125 via 10.130.0.1 dev ccmni2  scope link

default via 10.18.163.254 dev ccmni0 scope link—預設通路。
61.135.169.125 via 10.130.0.1 dev ccmni2 scope link從函式傳遞下來的IP地址。設定default對應的是0.0.0.0/0這個目的地址。—從何而來?如何設定/切換?

接下來,確認多路由下的route設定/framework bestroute。

核心log:

08-31 14:44:10.425 D/ConnectivityService(  732): handleConnectivityChange: changed linkProperty[0]: doReset=false resetMask=0
08-31 14:44:10.425 D/ConnectivityService(  732):    curLp=null
08-31 14:44:10.425 D/ConnectivityService(  732):    newLp={InterfaceName: ccmni0 LinkAddresses: [10.130.0.1/32,]  Routes: [0.0.0.0/0 -> 10.130.0.1,] DnsAddresses: [58.240.57.33,221.6.4.66,] Domains: n
08-31 14:44:10.426 D/ConnectivityService(  732): Adding 10.130.0.1/32 -> 0.0.0.0 for interface ccmni0
08-31 14:44:10.431 D/ConnectivityService(  732): Adding 0.0.0.0/0 -> 10.130.0.1 for interface ccmni0

Routes: [0.0.0.0/0 -> 10.130.0.1,]是最後設定到main table中。其來源DataCallResponse::setLinkProperties()。

ConnectivityService中
private NetworkStateTracker mNetTrackers[]
用於跟蹤所有網路的狀態。其中MobileDataStateTracker對應手機網路,WifiStateTracker對應wifi網路。如下描述,此兩種網路有各自獨立的default route(基於config.xml)。
同時存在優先順序,wifi > mobile.兩者預設無法共存,當wifi開啟時,mobile網路會被關閉,最終對應改變main table。

private void handleConnect(NetworkInfo info){
        // if this is a default net and other default is running
        // kill the one not preferred
        if (mNetConfigs[newNetType].isDefault()) {
          }
}

檢視開啟wifi時,handleConnectivityChange()的呼叫處理流程

    /**
     * The Mobile data connection.  When active, all data traffic
     * will use this network type's interface by default
     * (it has a default route)
     */
    public static final int TYPE_MOBILE      = 0;
    /**
     * The WIFI data connection.  When active, all data traffic
     * will use this network type's interface by default
     * (it has a default route).
     */
    public static final int TYPE_WIFI        = 1;

VPN建立時log,此時操作是非main table,但添加了 route rule

D/ConnectivityService(  732): getActiveNetworkInfo:NetworkInfo: type: mobile[LTE], state: CONNECTED/CONNECTED, reason: connected, extra: UNIM2M.NJM2MAPN, roaming: false, failover: true, isAvailable: true, isConnectedToProvisioningNetwork: false, simId: 0/10068
D/ConnectivityService(  732): getActiveNetworkInfo:NetworkInfo: type: mobile[LTE], state: CONNECTED/CONNECTED, reason: connected, extra: UNIM2M.NJM2MAPN, roaming: false, failover: true, isAvailable: true, isConnectedToProvisioningNetwork: false, simId: 0/10068
D/ConnectivityService(  732): getActiveNetworkInfo:NetworkInfo: type: mobile[LTE], state: CONNECTED/CONNECTED, reason: connected, extra: UNIM2M.NJM2MAPN, roaming: false, failover: true, isAvailable: true, isConnectedToProvisioningNetwork: false, simId: 0/10068
D/ConnectivityService(  732): getActiveNetworkInfo:NetworkInfo: type: mobile[LTE], state: CONNECTED/CONNECTED, reason: connected, extra: UNIM2M.NJM2MAPN, roaming: false, failover: true, isAvailable: true, isConnectedToProvisioningNetwork: false, simId: 0/10068
D/NetworkManagementService(  732): onEvent:600 Iface added tun0:4
D/NetworkManagementService(  732): onEvent:600 Iface added tun0:4
D/NetworkManagementService(  732): onEvent:600 Iface linkstate tun0 down:5
D/NetworkManagementService(  732): onEvent:600 Iface linkstate tun0 up:5
D/NetworkManagementService(  732): onEvent:600 Iface linkstate tun0 down:5
D/NetworkManagementService(  732): onEvent:600 Iface linkstate tun0 up:5
D/NetworkManagementService(  732): onEvent:614 Address updated 10.20.30.1/32 tun0 128 0:7
D/NetworkManagementService(  732): onEvent:614 Address updated 10.20.30.1/32 tun0 128 0:7
D/NetworkManagementService(  732): onEvent:614 Address removed 10.20.30.1/32 tun0 128 0:7
D/NetworkManagementService(  732): onEvent:614 Address removed 10.20.30.1/32 tun0 128 0:7
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip route add default dev tun0 table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip route add default dev tun0 table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip route add default dev tun0 table 60
D/NetworkManagementService(  732): onEvent:614 Address updated 10.20.30.1/24 tun0 128 0:7
D/NetworkManagementService(  732): onEvent:614 Address updated 10.20.30.1/24 tun0 128 0:7
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -6 route add default dev tun0 table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -6 route add default dev tun0 table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -6 route add default dev tun0 table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -6 route replace unreachable default table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -6 route replace unreachable default table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -6 route replace unreachable default table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -4 rule add prio 100 to 1.0.0.0/8 fwmark 60 table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -4 rule add prio 100 to 1.0.0.0/8 fwmark 60 table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -4 rule add prio 100 to 1.0.0.0/8 fwmark 60 table 60
E/SecondaryTablController(  231): runCmd : cmd : /system/bin/ip -4 rule add prio 100 to 2.0.0.0/8 fwmark 60 table 60
# ip rule add fwmark 3  table 3 

fwmark 3是標記,table 3 是路由表3 上邊。 意思為凡是標記了 3 的資料使用table3 路由表

之後使用iptables給相應的資料打上標記:

# iptables -A PREROUTING -t mangle -i eth0 -s 192.168.0.1 -192.168.0.100 -j MARK --set-mark 3

因為mangle的處理是優先於 nat 和fiter表的,所以相應資料包到達之後先打上標記,之後再通過ip rule規則。對應的資料包使用相應的路由表進行路由,最後讀取路由表資訊,將資料包送出閘道器。

startUsingNetworkFeature呼叫建立mms的dcac,對應的狀態:

E/SecondaryTablController(  232): runCmd : /system/bin/ip route add 10.130.0.2/32 via 0.0.0.0 dev ccmni2 table 60
E/SecondaryTablController(  232): runCmd : /system/bin/ip route add 0.0.0.0/0 via 10.130.0.2 dev ccmni2 table 60

開啟wifi熱點對應的log,其操作的是main table

D/NetworkManagementService(  732): onEvent:600 Iface linkstate wlan%d down:5
D/NetworkManagementService(  732): onEvent:600 Iface added wlan0:4
D/NetworkManagementService(  732): onEvent:600 Iface linkstate wlan0 down:5
D/NetworkManagementService(  732): onEvent:600 Iface added ap0:4
D/NetworkManagementService(  732): onEvent:600 Iface linkstate ap0 down:5
D/ConnectivityService(  732): received intent ==> android.net.conn.TETHER_STATE_CHANGED
D/ConnectivityService(  732): getMobileDataEnabled returning true
D/NetworkManagementService(  732): onEvent:600 Iface linkstate ap0 up:5
D/NetworkManagementService(  732): onEvent:600 Iface linkstate ap0 up:5
D/ConnectivityService(  732): received intent ==> android.net.conn.TETHER_STATE_CHANGED
D/ConnectivityService(  732): getMobileDataEnabled returning true
D/NetworkManagementService(  732): onEvent:600 Iface linkstate ap0 up:5
D/NetworkManagementService(  732): Enter setInterfaceConfig, iface=ap0
D/CommandListener(  231): Setting iface cfg
D/CommandListener(  231): Trying to bring up ap0
D/NetworkManagementService(  732): onEvent:614 Address updated 192.168.43.1/24 ap0 128 0:7
I/CommandListener_(  231): DumpRoute ++
I/CommandListener_(  231): Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
I/CommandListener_(  231): 0.0.0.0         10.13.28.50     0.0.0.0         UG    0      0        0 ccmni0
I/CommandListener_(  231): 10.13.28.50     0.0.0.0         255.255.255.255 UH    0      0        0 ccmni0
I/CommandListener_(  231): 192.168.43.0    0.0.0.0         255.255.255.0   U     0      0        0 ap0
I/CommandListener_(  231): DumpRoute --
D/ConnectivityService(  732): received intent ==> android.net.conn.TETHER_STATE_CHANGED
D/ConnectivityService(  732): received intent ==> android.net.conn.TETHER_STATE_CHANGED
D/ConnectivityService(  732): getMobileDataEnabled returning true
D/ConnectivityService(  732): getMobileDataEnabled returning true
D/NetworkManagementService(  732): onEvent:614 Address updated fe80::8:22ff:fe6c:e008/64 ap0 128 253:7

目前問題的疑問點:熱點和VPN,都會在main table更新規則,並且VPN有通過命令建立新的指向性rule。

100:    from all to 219.0.0.0/8 fwmark 0x3c lookup 60
100:    from all to 220.0.0.0/8 fwmark 0x3c lookup 60
100:    from all to 221.0.0.0/8 fwmark 0x3c lookup 60
100:    from all to 222.0.0.0/8 fwmark 0x3c lookup 60
100:    from all to 223.0.0.0/8 fwmark 0x3c lookup 60
100:    from all to 8.8.0.0/16 fwmark 0x3c lookup 60
32766:  from all lookup main
32767:  from all lookup default

10.20.30.0/24 dev tun0 proto kernel scope link src 10.20.30.1
如何進行的跳轉???
—Vpn實則代理機制,本機去代理IP通訊,代理再進行了一次route(VPN時,並沒有新建dcac,同時貌似VPN和HostAP無法同時開啟)
—Wifi熱點,本機是一個閘道器,如何讓STA機器通過閘道器時,由閘道器指定的通路出去???—NAT!!!Chain natctrl_tether_counters

Vpn開啟時log:

D/ConnectivityService(  782): Setting TCP values: [4094,87380,524288,4096,16384,524288] which comes from [net.tcp.buffersize.hspa]
D/ConnectivityService(  782): getMobileDataEnabled returning true
D/ConnectivityService(  782): getMobileDataEnabled returning true
D/ConnectivityService(  782): getMobileDataEnabled returning true
D/ConnectivityService(  782): getActiveNetworkInfo:NetworkInfo: type: mobile[HSPA], state: CONNECTED/CONNECTED, reason: nwTypeChanged, extra: UNIM2M.NJM2MAPN, roaming: false, failover: false, isAvailable: true, isConnectedToProvisioningNetwork: false, simId: 0/10068
D/ConnectivityService(  782): Setting TCP values: [4094,87380,1220608,4096,16384,1220608] which comes from [net.tcp.buffersize.hspap]
D/ConnectivityService(  782): getMobileDataEnabled returning true
D/ConnectivityService(  782): getMobileDataEnabled returning true
D/ConnectivityService(  782): getMobileDataEnabled returning true
D/ConnectivityService(  782): getActiveNetworkInfo:NetworkInfo: type: mobile[HSPA+], state: CONNECTED/CONNECTED, reason: nwTypeChanged, extra: UNIM2M.NJM2MAPN, roaming: false, failover: false, isAvailable: true, isConnectedToProvisioningNetwork: false, simId: 0/10068
D/NetworkManagementService(  782): onEvent:600 Iface added tun0:4
D/NetworkManagementService(  782): onEvent:600 Iface linkstate tun0 down:5
D/NetworkManagementService(  782): onEvent:600 Iface linkstate tun0 up:5
D/NetworkManagementService(  782): onEvent:614 Address updated 10.20.30.1/32 tun0 128 0:7
D/NetworkManagementService(  782): onEvent:614 Address removed 10.20.30.1/32 tun0 128 0:7
D/NetworkManagementService(  782): onEvent:614 Address updated 10.20.30.1/24 tun0 128 0:7

本機使用雙APN,同時還要控制當本機為AP模式時,STA的資料通路是怎樣的狀態?進行實驗:
1.del route default ccmni0—STA無法訪問網路
2.add route default ccmni0—-STA可以訪問網路
3.add route default ccmni2(mms)—-STA無法訪問網路

看上去ccmni0與Hostap之間存在一種繫結關係(iptables -v -L,Chain natctrl_tether_counters。iptables -t mangle -L -n檢視fwmark),或者是framework的狀態位的問題。
—可在framework中新增判斷呼叫,在ccmni2有效的情況下,從framework的呼叫開始直接改變,排除狀態位的影響
—Chain natctrl_tether_counters 這個規則導致的,所以從framework下手需要確認此規則從何時設定。
—上面的規則通過NatController::enableNat()函式來設定,其確實是framework而來。

呼叫類似route,其由Tethering.java共享網路發起呼叫。
Tethering.java:

                        for (Integer netType : mUpstreamIfaceTypes) {
                            NetworkInfo info = null;
                            try {
                                info = mConnService.getNetworkInfo(netType.intValue());
                            } catch (RemoteException e) { }
                            if ((info != null) && info.isConnected()) {
                                upType = netType.intValue();
                                break;
                            }
                        }

mUpstreamIfaceTypes陣列
通過讀取config.xml的配置config_tether_upstream_types陣列而來:

<!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
    <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
    <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->

指定了哪些網路型別可以支援AP模式,即可開啟NAT。

另,通過上面的程式碼發現,當多網口同時工作時,以陣列排列順序為優先順序。log如下:

7-06 18:00:12.586 D/Tethering(  696): startListeningForSimChanges
7-06 18:00:12.586 D/Tethering(  696): isGeminiSupport: false
7-06 18:00:12.586 I/Tethering(  696): ifaceTypes = null, use default
7-06 18:00:12.586 I/Tethering(  696): upstreamIfaceTypes.add:0
7-06 18:00:12.586 I/Tethering(  696): upstreamIfaceTypes.add:1
7-06 18:00:12.586 I/Tethering(  696): upstreamIfaceTypes.add:2
7-06 18:00:12.586 I/Tethering(  696): upstreamIfaceTypes.add:5
7-06 18:00:12.586 I/Tethering(  696): upstreamIfaceTypes.add:7
7-06 18:00:12.586 I/Tethering(  696): upstreamIfaceTypes.add:9
7-06 18:00:12.586 I/Tethering(  696): upstreamIfaceTypes.add:49
7-06 18:00:12.586 D/Tethering(  696): simchange mGenerationNumber=5, current generationNumber=5
7-06 18:00:12.586 D/Tethering(  696): got Sim changed to state LOADED, mSimAbsentSeen=false
7-06 18:00:12.589 I/Tethering(  696): checkDunRequired:2
7-06 18:00:12.589 D/Tethering(  696): mPreferredUpstreamMobileApn = 5
7-06 18:00:12.589 D/Tethering(  696): isTetheringIpv6Support: true
7-06 18:00:12.589 D/Tethering(  696): getIpv6FeatureEnable:0
7-06 18:00:12.589 D/Tethering(  696): [TetherMaster]chooseUpstreamType has upstream iface types:
7-06 18:00:12.589 D/Tethering(  696):  0
7-06 18:00:12.589 D/Tethering(  696):  1
7-06 18:00:12.589 D/Tethering(  696):  2
7-06 18:00:12.589 D/Tethering(  696):  5
7-06 18:00:12.589 D/Tethering(  696):  7
7-06 18:00:12.589 D/Tethering(  696):  9
7-06 18:00:12.589 D/Tethering(  696):  49
18:00:12.589 D/Tethering(  696): [TetherMaster]chooseUpstreamType(true), preferredApn =5, got type=0
18:00:12.592 I/Tethering(  696): checkDataEnabled:true
18:00:12.592 D/Tethering(  696): pre-checkDataEnabled + true
18:00:12.592 I/Tethering(  696): [MSM_TetherModeAlive][TetherMaster] mMobileApnReserved:-1
18:00:12.592 D/Tethering(  696): isTetheringIpv6Support: true
18:00:12.592 D/Tethering(  696): isTetheringIpv6Support: true
18:00:12.592 I/Tethering(  696): Finding IPv4 upstream interface on: {InterfaceName: ccmni0 LinkAddresses: [10.20.108.135/32,]  Routes: [0.0.0.
18:00:12.592 I/Tethering(  696): Found interface ccmni0
18:00:12.592 E/Tethering(  696): v4 and v6 Dnses: [/221.6.4.66, /58.240.57.33]
18:00:12.592 D/Tethering(  696): Setting DNS forwarders: Network=102, dnsServers=[221.6.4.66, 58.240.57.33]
18:00:12.596 I/Tethering(  696): [MSM_TetherModeAlive][TetherMaster] notifying tethered with iface =ccmni0
18:00:12.596 D/Tethering(  696): isTetheringIpv6Support: true
18:00:12.596 I/Tethering(  696): [ISM_Tethered] ap0 processMessage what=12
18:00:12.596 D/Tethering(  696): isTetheringIpv6Support: true
18:00:12.596 I/Tethering(  696): [ISM_Tethered:null] CMD_TETHER_CONNECTION_CHANGED mMyUpstreamIfaceName: null, mMyUpstreamIfaceNameIpv6:null, n
18:00:12.596 D/Tethering(  696): isTetheringIpv6Support: true
18:00:12.596 D/Tethering(  696): isTetheringIpv6Support: true
18:00:12.645 I/Tethering(  696): [ISM_Tethered] CMD_TETHER_CONNECTION_CHANGED enableNat for:null(ap0, ccmni0)
18:00:12.645 D/Tethering(  696): isTetheringIpv6Support: true
18:00:12.645 I/Tethering(  696): [ISM_Tethered] CMD_TETHER_CONNECTION_CHANGED finished!null
18:00:12.645 D/Tethering(  696): isTetheringIpv6Support: true
18:00:12.708 I/Tethering(  696): addressUpdated ap0, fe80::8:22ff:feba:5720/64

另,多次對映後,下面的Chain會一直保留,是否會引起問題???

Chain natctrl_tether_counters (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     all  --  ap0    ccmni2  anywhere             anywhere
    0     0 RETURN     all  --  ccmni2 ap0     anywhere             anywhere
    0     0 RETURN     all  --  ap0    ccmni0  anywhere             anywhere
    0     0 RETURN     all  --  ccmni0 ap0     anywhere             anywhere

Android使用了多個Linux核心的高階包過濾和路由特性來實現per-user VPN。
這些特性(通過netfilter核心框架實現)包含了Linux的iptables工具的owner模組,能夠使用資料包生成程序的UID、GID或者PID對資料包進行匹配。

此外,netfilter的另外一個重要特性是能夠為特定資料包打上特定的數字標籤(mark)。例如,的規則將所有目標埠為80(通常是網站伺服器)的資料包標記上0x1。之後這個標記可以被用來進行過濾和路由。
比如,通過新增將標記的包傳送給預定義的路由表的路由規則,將標記的包通過特定的介面進行傳送。最後新增一條路由,將匹配web路由表的資料包傳送到em3介面。

呼叫流程:

DcTracker::onDataSetupComplete()---A SETUP (aka bringUp) has completed,建立一個dcac後會回撥此方法
DcTracker::notifyDefaultData()
PhoneBase::notifyDataConnection()
DefaultPhoneNotifier::doNotifyDataConnection()---其中linkProperties = sender.getLinkProperties(apnType)>>>DcAsyncChannel::getLinkPropertiesSync()>>>DataConnection::getCopyLinkProperties()>>>DataCallResponse.SetupResult setLinkProperties()>>>DataCallResponse linkProperties.addRoute(new RouteInfo(ia))最終的route只存在ia閘道器,其他都為0

TelephonyRegistry::notifyDataConnection()
MobileDataStateTracker::onDataStateChanged()
MobileDataStateTracker::updateLinkProperitesAndCapatilities()---從intent中獲取mLinkProperties
MobileDataStateTracker::setDetailedState()---傳送EVENT_STATE_CHANGED到CS服務中的訊息佇列中
ConnectivityService::handleConnect()---        // if this is a default net and other default is running  kill the one not preferred
ConnectivityService::handleConnectivityChange()
ConnectivityService::updateRoutes()
ConnectivityService::addRoute()
ConnectivityService::modifyRoute()---mNetd.addRoute(ifaceName, r)
NetworkManagementService::modifyRoute()---mConnector.execute(cmd); final Command cmd = new Command("interface", "route", action, interfaceName, type);
CommandListener::InterfaceCmd::runCommand()---default型別ioctl(ifc_ctl_sock, action, &rt)通過socket直接與驅動通訊更新route規則;secondary型別sSecondaryTableCtrl->addRoute(),直接用runCmd呼叫/system/bin/ip route更新規則

總結:
需求:雙APN通路,其中APN1是普通apn,可訪問所有網路。APN2是特殊apn,只能訪問指定網路。預設雙通路同時工作,APN1為主通路(AP模式),APN2只提供給特殊app使用。當APN1的流量使用達到閥值,將主通路設定為APN2。
思路:遵循上面的兩句呼叫。

方案一:
“當APN1的流量使用達到閥值,將主通路設定為APN2”
—此處可使用類似VPN的處理,達到閥值時候,修改ip ru和對應table規則,使所有連線訪問走APN2通路對應的網口。需要注意Chain natctrl_tether_counters,其預設只支援部分網路型別(APN2基於Mms型別的話,需要修改framework配合)。

方案二:
當以Mms網路型別建立dcac後,其對應操作的是非main table。修改framework,使其直接修改main table,然後上層應用判定閥值,進行切換。同樣要注意Chain natctrl_tether_counters。
framework簡單修改:

diff --git a/base/services/java/com/android/server/ConnectivityService.java b/base/services/java/com/android/server/ConnectivityService.java
old mode 100644
new mode 100755
index be19c1a..b40e410
--- a/base/services/java/com/android/server/ConnectivityService.java
+++ b/base/services/java/com/android/server/ConnectivityService.java
@@ -3091,7 +3091,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             }
         }
         mCurrentLinkProperties[netType] = newLp;
-        boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault(), exempt);
+        boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault(), exempt, netType==2);

         if (resetMask != 0 || resetDns) {
             if (VDBG) log("handleConnectivityChange: resetting");
@@ -3173,7 +3173,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
      * returns a boolean indicating the routes changed
      */
     private boolean updateRoutes(LinkProperties newLp, LinkProperties curLp,
-            boolean isLinkDefault, boolean exempt) {
+            boolean isLinkDefault, boolean exempt, boolean isSpecial) {
         Collection<RouteInfo> routesToAdd = null;
         CompareResult<InetAddress> dnsDiff = new CompareResult<InetAddress>();
         CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
@@ -3189,7 +3189,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         boolean routesChanged = (routeDiff.removed.size() != 0 || routeDiff.added.size() != 0);

         for (RouteInfo r : routeDiff.removed) {
-            if (isLinkDefault || ! r.isDefaultRoute()) {
+            if (isLinkDefault || ! r.isDefaultRoute() || isSpecial) {
                 if (VDBG) log("updateRoutes: default remove route r=" + r);
                 removeRoute(curLp, r, TO_DEFAULT_TABLE);
             }
@@ -3245,7 +3245,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         }

         for (RouteInfo r :  routeDiff.added) {
-            if (isLinkDefault || ! r.isDefaultRoute()) {
+            if (isLinkDefault || ! r.isDefaultRoute() || isSpecial) {
                 addRoute(newLp, r, TO_DEFAULT_TABLE, exempt);
             } else {
                 // add to a secondary route table

此處還需要注