• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.connectivity.tethering;
18 
19 import static com.android.internal.util.BitUtils.uint16;
20 
21 import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
22 import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
23 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
24 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
25 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
26 import android.os.Handler;
27 import android.os.RemoteException;
28 import android.net.util.SharedLog;
29 import android.system.OsConstants;
30 
31 import java.util.ArrayList;
32 
33 
34 /**
35  * Capture tethering dependencies, for injection.
36  *
37  * @hide
38  */
39 public class OffloadHardwareInterface {
40     private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
41     private static final String YIELDS = " -> ";
42     // Change this value to control whether tether offload is enabled or
43     // disabled by default in the absence of an explicit Settings value.
44     // See accompanying unittest to distinguish 0 from non-0 values.
45     private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0;
46     private static final String NO_INTERFACE_NAME = "";
47     private static final String NO_IPV4_ADDRESS = "";
48     private static final String NO_IPV4_GATEWAY = "";
49 
configOffload()50     private static native boolean configOffload();
51 
52     private final Handler mHandler;
53     private final SharedLog mLog;
54     private IOffloadControl mOffloadControl;
55     private TetheringOffloadCallback mTetheringOffloadCallback;
56     private ControlCallback mControlCallback;
57 
58     public static class ControlCallback {
onStarted()59         public void onStarted() {}
onStoppedError()60         public void onStoppedError() {}
onStoppedUnsupported()61         public void onStoppedUnsupported() {}
onSupportAvailable()62         public void onSupportAvailable() {}
onStoppedLimitReached()63         public void onStoppedLimitReached() {}
64 
onNatTimeoutUpdate(int proto, String srcAddr, int srcPort, String dstAddr, int dstPort)65         public void onNatTimeoutUpdate(int proto,
66                                        String srcAddr, int srcPort,
67                                        String dstAddr, int dstPort) {}
68     }
69 
70     public static class ForwardedStats {
71         public long rxBytes;
72         public long txBytes;
73 
ForwardedStats()74         public ForwardedStats() {
75             rxBytes = 0;
76             txBytes = 0;
77         }
78 
add(ForwardedStats other)79         public void add(ForwardedStats other) {
80             rxBytes += other.rxBytes;
81             txBytes += other.txBytes;
82         }
83 
toString()84         public String toString() {
85             return String.format("rx:%s tx:%s", rxBytes, txBytes);
86         }
87     }
88 
OffloadHardwareInterface(Handler h, SharedLog log)89     public OffloadHardwareInterface(Handler h, SharedLog log) {
90         mHandler = h;
91         mLog = log.forSubComponent(TAG);
92     }
93 
getDefaultTetherOffloadDisabled()94     public int getDefaultTetherOffloadDisabled() {
95         return DEFAULT_TETHER_OFFLOAD_DISABLED;
96     }
97 
initOffloadConfig()98     public boolean initOffloadConfig() {
99         return configOffload();
100     }
101 
initOffloadControl(ControlCallback controlCb)102     public boolean initOffloadControl(ControlCallback controlCb) {
103         mControlCallback = controlCb;
104 
105         if (mOffloadControl == null) {
106             try {
107                 mOffloadControl = IOffloadControl.getService();
108             } catch (RemoteException e) {
109                 mLog.e("tethering offload control not supported: " + e);
110                 return false;
111             }
112             if (mOffloadControl == null) {
113                 mLog.e("tethering IOffloadControl.getService() returned null");
114                 return false;
115             }
116         }
117 
118         final String logmsg = String.format("initOffloadControl(%s)",
119                 (controlCb == null) ? "null"
120                         : "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
121 
122         mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog);
123         final CbResults results = new CbResults();
124         try {
125             mOffloadControl.initOffload(
126                     mTetheringOffloadCallback,
127                     (boolean success, String errMsg) -> {
128                         results.success = success;
129                         results.errMsg = errMsg;
130                     });
131         } catch (RemoteException e) {
132             record(logmsg, e);
133             return false;
134         }
135 
136         record(logmsg, results);
137         return results.success;
138     }
139 
stopOffloadControl()140     public void stopOffloadControl() {
141         if (mOffloadControl != null) {
142             try {
143                 mOffloadControl.stopOffload(
144                         (boolean success, String errMsg) -> {
145                             if (!success) mLog.e("stopOffload failed: " + errMsg);
146                         });
147             } catch (RemoteException e) {
148                 mLog.e("failed to stopOffload: " + e);
149             }
150         }
151         mOffloadControl = null;
152         mTetheringOffloadCallback = null;
153         mControlCallback = null;
154         mLog.log("stopOffloadControl()");
155     }
156 
getForwardedStats(String upstream)157     public ForwardedStats getForwardedStats(String upstream) {
158         final String logmsg = String.format("getForwardedStats(%s)",  upstream);
159 
160         final ForwardedStats stats = new ForwardedStats();
161         try {
162             mOffloadControl.getForwardedStats(
163                     upstream,
164                     (long rxBytes, long txBytes) -> {
165                         stats.rxBytes = (rxBytes > 0) ? rxBytes : 0;
166                         stats.txBytes = (txBytes > 0) ? txBytes : 0;
167                     });
168         } catch (RemoteException e) {
169             record(logmsg, e);
170             return stats;
171         }
172 
173         mLog.log(logmsg + YIELDS + stats);
174         return stats;
175     }
176 
setLocalPrefixes(ArrayList<String> localPrefixes)177     public boolean setLocalPrefixes(ArrayList<String> localPrefixes) {
178         final String logmsg = String.format("setLocalPrefixes([%s])",
179                 String.join(",", localPrefixes));
180 
181         final CbResults results = new CbResults();
182         try {
183             mOffloadControl.setLocalPrefixes(localPrefixes,
184                     (boolean success, String errMsg) -> {
185                         results.success = success;
186                         results.errMsg = errMsg;
187                     });
188         } catch (RemoteException e) {
189             record(logmsg, e);
190             return false;
191         }
192 
193         record(logmsg, results);
194         return results.success;
195     }
196 
setDataLimit(String iface, long limit)197     public boolean setDataLimit(String iface, long limit) {
198 
199         final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
200 
201         final CbResults results = new CbResults();
202         try {
203             mOffloadControl.setDataLimit(
204                     iface, limit,
205                     (boolean success, String errMsg) -> {
206                         results.success = success;
207                         results.errMsg = errMsg;
208                     });
209         } catch (RemoteException e) {
210             record(logmsg, e);
211             return false;
212         }
213 
214         record(logmsg, results);
215         return results.success;
216     }
217 
setUpstreamParameters( String iface, String v4addr, String v4gateway, ArrayList<String> v6gws)218     public boolean setUpstreamParameters(
219             String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
220         iface = (iface != null) ? iface : NO_INTERFACE_NAME;
221         v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS;
222         v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY;
223         v6gws = (v6gws != null) ? v6gws : new ArrayList<>();
224 
225         final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
226                 iface, v4addr, v4gateway, String.join(",", v6gws));
227 
228         final CbResults results = new CbResults();
229         try {
230             mOffloadControl.setUpstreamParameters(
231                     iface, v4addr, v4gateway, v6gws,
232                     (boolean success, String errMsg) -> {
233                         results.success = success;
234                         results.errMsg = errMsg;
235                     });
236         } catch (RemoteException e) {
237             record(logmsg, e);
238             return false;
239         }
240 
241         record(logmsg, results);
242         return results.success;
243     }
244 
addDownstreamPrefix(String ifname, String prefix)245     public boolean addDownstreamPrefix(String ifname, String prefix) {
246         final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix);
247 
248         final CbResults results = new CbResults();
249         try {
250             mOffloadControl.addDownstream(ifname, prefix,
251                     (boolean success, String errMsg) -> {
252                         results.success = success;
253                         results.errMsg = errMsg;
254                     });
255         } catch (RemoteException e) {
256             record(logmsg, e);
257             return false;
258         }
259 
260         record(logmsg, results);
261         return results.success;
262     }
263 
removeDownstreamPrefix(String ifname, String prefix)264     public boolean removeDownstreamPrefix(String ifname, String prefix) {
265         final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix);
266 
267         final CbResults results = new CbResults();
268         try {
269             mOffloadControl.removeDownstream(ifname, prefix,
270                     (boolean success, String errMsg) -> {
271                         results.success = success;
272                         results.errMsg = errMsg;
273                     });
274         } catch (RemoteException e) {
275             record(logmsg, e);
276             return false;
277         }
278 
279         record(logmsg, results);
280         return results.success;
281     }
282 
record(String msg, Throwable t)283     private void record(String msg, Throwable t) {
284         mLog.e(msg + YIELDS + "exception: " + t);
285     }
286 
record(String msg, CbResults results)287     private void record(String msg, CbResults results) {
288         final String logmsg = msg + YIELDS + results;
289         if (!results.success) {
290             mLog.e(logmsg);
291         } else {
292             mLog.log(logmsg);
293         }
294     }
295 
296     private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
297         public final Handler handler;
298         public final ControlCallback controlCb;
299         public final SharedLog log;
300 
TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog)301         public TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) {
302             handler = h;
303             controlCb = cb;
304             log = sharedLog;
305         }
306 
307         @Override
onEvent(int event)308         public void onEvent(int event) {
309             handler.post(() -> {
310                 switch (event) {
311                     case OffloadCallbackEvent.OFFLOAD_STARTED:
312                         controlCb.onStarted();
313                         break;
314                     case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
315                         controlCb.onStoppedError();
316                         break;
317                     case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
318                         controlCb.onStoppedUnsupported();
319                         break;
320                     case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
321                         controlCb.onSupportAvailable();
322                         break;
323                     case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
324                         controlCb.onStoppedLimitReached();
325                         break;
326                     default:
327                         log.e("Unsupported OffloadCallbackEvent: " + event);
328                 }
329             });
330         }
331 
332         @Override
updateTimeout(NatTimeoutUpdate params)333         public void updateTimeout(NatTimeoutUpdate params) {
334             handler.post(() -> {
335                     controlCb.onNatTimeoutUpdate(
336                         networkProtocolToOsConstant(params.proto),
337                         params.src.addr, uint16(params.src.port),
338                         params.dst.addr, uint16(params.dst.port));
339             });
340         }
341     }
342 
networkProtocolToOsConstant(int proto)343     private static int networkProtocolToOsConstant(int proto) {
344         switch (proto) {
345             case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
346             case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
347             default:
348                 // The caller checks this value and will log an error. Just make
349                 // sure it won't collide with valid OsContants.IPPROTO_* values.
350                 return -Math.abs(proto);
351         }
352     }
353 
354     private static class CbResults {
355         boolean success;
356         String errMsg;
357 
358         @Override
toString()359         public String toString() {
360             if (success) {
361                 return "ok";
362             } else {
363                 return "fail: " + errMsg;
364             }
365         }
366     }
367 }
368