1 /*
2  * Copyright (C) 2019 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 android.net.shared;
18 
19 import static android.net.RouteInfo.RTN_UNICAST;
20 import static android.system.OsConstants.EBUSY;
21 
22 import android.net.INetd;
23 import android.net.IpPrefix;
24 import android.net.RouteInfo;
25 import android.net.TetherConfigParcel;
26 import android.os.RemoteException;
27 import android.os.ServiceSpecificException;
28 import android.os.SystemClock;
29 import android.util.Log;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /**
35  * Implements common operations on INetd
36  * @hide
37  */
38 public class NetdUtils {
39     private static final String TAG = NetdUtils.class.getSimpleName();
40 
41     /** Start tethering. */
tetherStart(final INetd netd, final boolean usingLegacyDnsProxy, final String[] dhcpRange)42     public static void tetherStart(final INetd netd, final boolean usingLegacyDnsProxy,
43             final String[] dhcpRange) throws RemoteException, ServiceSpecificException {
44         final TetherConfigParcel config = new TetherConfigParcel();
45         config.usingLegacyDnsProxy = usingLegacyDnsProxy;
46         config.dhcpRanges = dhcpRange;
47         netd.tetherStartWithConfiguration(config);
48     }
49 
50     /** Setup interface for tethering. */
tetherInterface(final INetd netd, final String iface, final IpPrefix dest)51     public static void tetherInterface(final INetd netd, final String iface, final IpPrefix dest)
52             throws RemoteException, ServiceSpecificException {
53         tetherInterface(netd, iface, dest, 20 /* maxAttempts */, 50 /* pollingIntervalMs */);
54     }
55 
56     /** Setup interface with configurable retries for tethering. */
tetherInterface(final INetd netd, final String iface, final IpPrefix dest, int maxAttempts, int pollingIntervalMs)57     public static void tetherInterface(final INetd netd, final String iface, final IpPrefix dest,
58             int maxAttempts, int pollingIntervalMs)
59             throws RemoteException, ServiceSpecificException {
60         netd.tetherInterfaceAdd(iface);
61         networkAddInterface(netd, iface, maxAttempts, pollingIntervalMs);
62         List<RouteInfo> routes = new ArrayList<>();
63         routes.add(new RouteInfo(dest, null, iface, RTN_UNICAST));
64         RouteUtils.addRoutesToLocalNetwork(netd, iface, routes);
65     }
66 
67     /**
68      * Retry Netd#networkAddInterface for EBUSY error code.
69      * If the same interface (e.g., wlan0) is in client mode and then switches to tethered mode.
70      * There can be a race where puts the interface into the local network but interface is still
71      * in use in netd because the ConnectivityService thread hasn't processed the disconnect yet.
72      * See b/158269544 for detail.
73      */
networkAddInterface(final INetd netd, final String iface, int maxAttempts, int pollingIntervalMs)74     private static void networkAddInterface(final INetd netd, final String iface,
75             int maxAttempts, int pollingIntervalMs)
76             throws ServiceSpecificException, RemoteException {
77         for (int i = 1; i <= maxAttempts; i++) {
78             try {
79                 netd.networkAddInterface(INetd.LOCAL_NET_ID, iface);
80                 return;
81             } catch (ServiceSpecificException e) {
82                 if (e.errorCode == EBUSY && i < maxAttempts) {
83                     SystemClock.sleep(pollingIntervalMs);
84                     continue;
85                 }
86 
87                 Log.e(TAG, "Retry Netd#networkAddInterface failure: " + e);
88                 throw e;
89             }
90         }
91     }
92 
93     /** Reset interface for tethering. */
untetherInterface(final INetd netd, String iface)94     public static void untetherInterface(final INetd netd, String iface)
95             throws RemoteException, ServiceSpecificException {
96         try {
97             netd.tetherInterfaceRemove(iface);
98         } finally {
99             netd.networkRemoveInterface(INetd.LOCAL_NET_ID, iface);
100         }
101     }
102 }
103