@@ -100,10 +100,13 @@ export class AndroidAdbInterceptor implements Interceptor {
100
100
)
101
101
) ,
102
102
port : proxyPort ,
103
+ localTunnelPort : proxyPort ,
103
104
certFingerprint : generateSPKIFingerprint ( this . config . https . certContent )
104
105
} ;
105
106
const intentData = urlSafeBase64 ( JSON . stringify ( setupParams ) ) ;
106
107
108
+ await this . adbClient . reverse ( options . deviceId , 'tcp:' + proxyPort , 'tcp:' + proxyPort ) . catch ( ( ) => { } ) ;
109
+
107
110
// Use ADB to launch the app with the proxy details
108
111
await this . adbClient . startActivity ( options . deviceId , {
109
112
wait : true ,
@@ -112,7 +115,40 @@ export class AndroidAdbInterceptor implements Interceptor {
112
115
} ) ;
113
116
114
117
this . deviceProxyMapping [ proxyPort ] = this . deviceProxyMapping [ proxyPort ] || [ ] ;
115
- this . deviceProxyMapping [ proxyPort ] . push ( options . deviceId ) ;
118
+
119
+ if ( ! this . deviceProxyMapping [ proxyPort ] . includes ( options . deviceId ) ) {
120
+ this . deviceProxyMapping [ proxyPort ] . push ( options . deviceId ) ;
121
+
122
+ let tunnelConnectFailures = 0 ;
123
+
124
+ // The reverse tunnel can break when connecting/disconnecting from the VPN. This is a problem! It can
125
+ // also break in other cases, e.g. when ADB is restarted for some reason. To handle this, we constantly
126
+ // reinforce the tunnel while HTTP Toolkit is running & the device is connected.
127
+ const tunnelCheckInterval = setInterval ( async ( ) => {
128
+ if ( this . deviceProxyMapping [ proxyPort ] . includes ( options . deviceId ) ) {
129
+ try {
130
+ await this . adbClient . reverse ( options . deviceId , 'tcp:' + proxyPort , 'tcp:' + proxyPort )
131
+ tunnelConnectFailures = 0 ;
132
+ } catch ( e ) {
133
+ tunnelConnectFailures += 1 ;
134
+ console . log ( `${ options . deviceId } ADB tunnel failed` , e ) ;
135
+
136
+ if ( tunnelConnectFailures >= 5 ) {
137
+ // After 10 seconds disconnected, give up
138
+ console . log ( `${ options . deviceId } disconnected, dropping the ADB tunnel` ) ;
139
+ this . deviceProxyMapping [ proxyPort ] = this . deviceProxyMapping [ proxyPort ]
140
+ . filter ( id => id !== options . deviceId ) ;
141
+ clearInterval ( tunnelCheckInterval ) ;
142
+ }
143
+ }
144
+ } else {
145
+ // Deactivation at shutdown will clear the proxy data, and so clear this interval
146
+ // will automatically shut down.
147
+ clearInterval ( tunnelCheckInterval ) ;
148
+ }
149
+ } , 2000 ) ;
150
+ tunnelCheckInterval . unref ( ) ; // Don't let this block shutdown
151
+ }
116
152
}
117
153
118
154
async deactivate ( port : number | string ) : Promise < void | { } > {
0 commit comments