summaryrefslogtreecommitdiff
path: root/common/moduleutils/src/android/net/shared/NetdUtils.java
blob: 5fa29c9b18166c0109204cccb1da7ab79cc8a7a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.shared;

import static android.net.RouteInfo.RTN_UNICAST;
import static android.system.OsConstants.EBUSY;

import android.net.INetd;
import android.net.IpPrefix;
import android.net.RouteInfo;
import android.net.TetherConfigParcel;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * Implements common operations on INetd
 * @hide
 */
public class NetdUtils {
    private static final String TAG = NetdUtils.class.getSimpleName();

    /** Start tethering. */
    public static void tetherStart(final INetd netd, final boolean usingLegacyDnsProxy,
            final String[] dhcpRange) throws RemoteException, ServiceSpecificException {
        final TetherConfigParcel config = new TetherConfigParcel();
        config.usingLegacyDnsProxy = usingLegacyDnsProxy;
        config.dhcpRanges = dhcpRange;
        netd.tetherStartWithConfiguration(config);
    }

    /** Setup interface for tethering. */
    public static void tetherInterface(final INetd netd, final String iface, final IpPrefix dest)
            throws RemoteException, ServiceSpecificException {
        tetherInterface(netd, iface, dest, 20 /* maxAttempts */, 50 /* pollingIntervalMs */);
    }

    /** Setup interface with configurable retries for tethering. */
    public static void tetherInterface(final INetd netd, final String iface, final IpPrefix dest,
            int maxAttempts, int pollingIntervalMs)
            throws RemoteException, ServiceSpecificException {
        netd.tetherInterfaceAdd(iface);
        networkAddInterface(netd, iface, maxAttempts, pollingIntervalMs);
        List<RouteInfo> routes = new ArrayList<>();
        routes.add(new RouteInfo(dest, null, iface, RTN_UNICAST));
        RouteUtils.addRoutesToLocalNetwork(netd, iface, routes);
    }

    /**
     * Retry Netd#networkAddInterface for EBUSY error code.
     * If the same interface (e.g., wlan0) is in client mode and then switches to tethered mode.
     * There can be a race where puts the interface into the local network but interface is still
     * in use in netd because the ConnectivityService thread hasn't processed the disconnect yet.
     * See b/158269544 for detail.
     */
    private static void networkAddInterface(final INetd netd, final String iface,
            int maxAttempts, int pollingIntervalMs)
            throws ServiceSpecificException, RemoteException {
        for (int i = 1; i <= maxAttempts; i++) {
            try {
                netd.networkAddInterface(INetd.LOCAL_NET_ID, iface);
                return;
            } catch (ServiceSpecificException e) {
                if (e.errorCode == EBUSY && i < maxAttempts) {
                    SystemClock.sleep(pollingIntervalMs);
                    continue;
                }

                Log.e(TAG, "Retry Netd#networkAddInterface failure: " + e);
                throw e;
            }
        }
    }

    /** Reset interface for tethering. */
    public static void untetherInterface(final INetd netd, String iface)
            throws RemoteException, ServiceSpecificException {
        try {
            netd.tetherInterfaceRemove(iface);
        } finally {
            netd.networkRemoveInterface(INetd.LOCAL_NET_ID, iface);
        }
    }
}