TetherInterfaceStateMachine.java revision 1eb8c69bed1615e9502e94b1a676773ed28abfd9
1/*
2 * Copyright (C) 2016 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
17package com.android.server.connectivity.tethering;
18
19import android.net.ConnectivityManager;
20import android.net.INetworkStatsService;
21import android.net.InterfaceConfiguration;
22import android.net.LinkAddress;
23import android.net.LinkProperties;
24import android.net.NetworkUtils;
25import android.os.INetworkManagementService;
26import android.os.Looper;
27import android.os.Message;
28import android.util.Log;
29import android.util.SparseArray;
30
31import com.android.internal.util.MessageUtils;
32import com.android.internal.util.Protocol;
33import com.android.internal.util.State;
34import com.android.internal.util.StateMachine;
35
36import java.net.InetAddress;
37
38/**
39 * @hide
40 *
41 * Tracks the eligibility of a given network interface for tethering.
42 */
43public class TetherInterfaceStateMachine extends StateMachine {
44    private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
45    private static final int USB_PREFIX_LENGTH = 24;
46    private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
47    private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
48
49    private final static String TAG = "TetherInterfaceSM";
50    private final static boolean DBG = false;
51    private final static boolean VDBG = false;
52    private static final Class[] messageClasses = {
53            TetherInterfaceStateMachine.class
54    };
55    private static final SparseArray<String> sMagicDecoderRing =
56            MessageUtils.findMessageNames(messageClasses);
57
58    private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
59    // request from the user that it wants to tether
60    public static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
61    // request from the user that it wants to untether
62    public static final int CMD_TETHER_UNREQUESTED          = BASE_IFACE + 3;
63    // notification that this interface is down
64    public static final int CMD_INTERFACE_DOWN              = BASE_IFACE + 4;
65    // notification from the master SM that it had trouble enabling IP Forwarding
66    public static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IFACE + 7;
67    // notification from the master SM that it had trouble disabling IP Forwarding
68    public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
69    // notification from the master SM that it had trouble starting tethering
70    public static final int CMD_START_TETHERING_ERROR       = BASE_IFACE + 9;
71    // notification from the master SM that it had trouble stopping tethering
72    public static final int CMD_STOP_TETHERING_ERROR        = BASE_IFACE + 10;
73    // notification from the master SM that it had trouble setting the DNS forwarders
74    public static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
75    // the upstream connection has changed
76    public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;
77    // new IPv6 tethering parameters need to be processed
78    public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IFACE + 13;
79
80    private final State mInitialState;
81    private final State mTetheredState;
82    private final State mUnavailableState;
83
84    private final INetworkManagementService mNMService;
85    private final INetworkStatsService mStatsService;
86    private final IControlsTethering mTetherController;
87
88    private final String mIfaceName;
89    private final int mInterfaceType;
90    private final IPv6TetheringInterfaceServices mIPv6TetherSvc;
91
92    private int mLastError;
93    private String mMyUpstreamIfaceName;  // may change over time
94
95    public TetherInterfaceStateMachine(String ifaceName, Looper looper, int interfaceType,
96                    INetworkManagementService nMService, INetworkStatsService statsService,
97                    IControlsTethering tetherController) {
98        super(ifaceName, looper);
99        mNMService = nMService;
100        mStatsService = statsService;
101        mTetherController = tetherController;
102        mIfaceName = ifaceName;
103        mInterfaceType = interfaceType;
104        mIPv6TetherSvc = new IPv6TetheringInterfaceServices(mIfaceName, mNMService);
105        mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
106
107        mInitialState = new InitialState();
108        addState(mInitialState);
109        mTetheredState = new TetheredState();
110        addState(mTetheredState);
111        mUnavailableState = new UnavailableState();
112        addState(mUnavailableState);
113
114        setInitialState(mInitialState);
115    }
116
117    public int interfaceType() {
118        return mInterfaceType;
119    }
120
121    // configured when we start tethering and unconfig'd on error or conclusion
122    private boolean configureIfaceIp(boolean enabled) {
123        if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
124
125        String ipAsString = null;
126        int prefixLen = 0;
127        if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
128            ipAsString = USB_NEAR_IFACE_ADDR;
129            prefixLen = USB_PREFIX_LENGTH;
130        } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
131            ipAsString = WIFI_HOST_IFACE_ADDR;
132            prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
133        } else {
134            // Nothing to do, BT does this elsewhere.
135            return true;
136        }
137
138        InterfaceConfiguration ifcg = null;
139        try {
140            ifcg = mNMService.getInterfaceConfig(mIfaceName);
141            if (ifcg != null) {
142                InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
143                ifcg.setLinkAddress(new LinkAddress(addr, prefixLen));
144                if (enabled) {
145                    ifcg.setInterfaceUp();
146                } else {
147                    ifcg.setInterfaceDown();
148                }
149                ifcg.clearFlag("running");
150                mNMService.setInterfaceConfig(mIfaceName, ifcg);
151            }
152        } catch (Exception e) {
153            Log.e(TAG, "Error configuring interface " + mIfaceName, e);
154            return false;
155        }
156
157        return true;
158    }
159
160    private void maybeLogMessage(State state, int what) {
161        if (DBG) {
162            Log.d(TAG, state.getName() + " got " +
163                    sMagicDecoderRing.get(what, Integer.toString(what)));
164        }
165    }
166
167    class InitialState extends State {
168        @Override
169        public void enter() {
170            mTetherController.notifyInterfaceStateChange(
171                    mIfaceName, TetherInterfaceStateMachine.this,
172                    IControlsTethering.STATE_AVAILABLE, mLastError);
173        }
174
175        @Override
176        public boolean processMessage(Message message) {
177            maybeLogMessage(this, message.what);
178            boolean retValue = true;
179            switch (message.what) {
180                case CMD_TETHER_REQUESTED:
181                    mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
182                    transitionTo(mTetheredState);
183                    break;
184                case CMD_INTERFACE_DOWN:
185                    transitionTo(mUnavailableState);
186                    break;
187                case CMD_IPV6_TETHER_UPDATE:
188                    mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
189                            (LinkProperties) message.obj);
190                    break;
191                default:
192                    retValue = false;
193                    break;
194            }
195            return retValue;
196        }
197    }
198
199    class TetheredState extends State {
200        @Override
201        public void enter() {
202            if (!configureIfaceIp(true)) {
203                mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
204                transitionTo(mInitialState);
205                return;
206            }
207
208            try {
209                mNMService.tetherInterface(mIfaceName);
210            } catch (Exception e) {
211                Log.e(TAG, "Error Tethering: " + e.toString());
212                mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
213                transitionTo(mInitialState);
214                return;
215            }
216
217            if (!mIPv6TetherSvc.start()) {
218                Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices");
219            }
220
221            if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
222            mTetherController.notifyInterfaceStateChange(
223                    mIfaceName, TetherInterfaceStateMachine.this,
224                    IControlsTethering.STATE_TETHERED, mLastError);
225        }
226
227        @Override
228        public void exit() {
229            // Note that at this point, we're leaving the tethered state.  We can fail any
230            // of these operations, but it doesn't really change that we have to try them
231            // all in sequence.
232            mIPv6TetherSvc.stop();
233            cleanupUpstream();
234
235            try {
236                mNMService.untetherInterface(mIfaceName);
237            } catch (Exception ee) {
238                mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
239                Log.e(TAG, "Failed to untether interface: " + ee.toString());
240            }
241
242            configureIfaceIp(false);
243        }
244
245        private void cleanupUpstream() {
246            if (mMyUpstreamIfaceName != null) {
247                // note that we don't care about errors here.
248                // sometimes interfaces are gone before we get
249                // to remove their rules, which generates errors.
250                // just do the best we can.
251                try {
252                    // about to tear down NAT; gather remaining statistics
253                    mStatsService.forceUpdate();
254                } catch (Exception e) {
255                    if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
256                }
257                try {
258                    mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
259                } catch (Exception e) {
260                    if (VDBG) Log.e(
261                            TAG, "Exception in removeInterfaceForward: " + e.toString());
262                }
263                try {
264                    mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
265                } catch (Exception e) {
266                    if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
267                }
268                mMyUpstreamIfaceName = null;
269            }
270            return;
271        }
272
273        @Override
274        public boolean processMessage(Message message) {
275            maybeLogMessage(this, message.what);
276            boolean retValue = true;
277            switch (message.what) {
278                case CMD_TETHER_UNREQUESTED:
279                    transitionTo(mInitialState);
280                    if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName);
281                    break;
282                case CMD_INTERFACE_DOWN:
283                    transitionTo(mUnavailableState);
284                    if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
285                    break;
286                case CMD_TETHER_CONNECTION_CHANGED:
287                    String newUpstreamIfaceName = (String)(message.obj);
288                    if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
289                            (mMyUpstreamIfaceName != null &&
290                            mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
291                        if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
292                        break;
293                    }
294                    cleanupUpstream();
295                    if (newUpstreamIfaceName != null) {
296                        try {
297                            mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
298                            mNMService.startInterfaceForwarding(mIfaceName,
299                                    newUpstreamIfaceName);
300                        } catch (Exception e) {
301                            Log.e(TAG, "Exception enabling Nat: " + e.toString());
302                            mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
303                            transitionTo(mInitialState);
304                            return true;
305                        }
306                    }
307                    mMyUpstreamIfaceName = newUpstreamIfaceName;
308                    break;
309                case CMD_IPV6_TETHER_UPDATE:
310                    mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
311                            (LinkProperties) message.obj);
312                    break;
313                case CMD_IP_FORWARDING_ENABLE_ERROR:
314                case CMD_IP_FORWARDING_DISABLE_ERROR:
315                case CMD_START_TETHERING_ERROR:
316                case CMD_STOP_TETHERING_ERROR:
317                case CMD_SET_DNS_FORWARDERS_ERROR:
318                    mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
319                    transitionTo(mInitialState);
320                    break;
321                default:
322                    retValue = false;
323                    break;
324            }
325            return retValue;
326        }
327    }
328
329    /**
330     * This state is terminal for the per interface state machine.  At this
331     * point, the master state machine should have removed this interface
332     * specific state machine from its list of possible recipients of
333     * tethering requests.  The state machine itself will hang around until
334     * the garbage collector finds it.
335     */
336    class UnavailableState extends State {
337        @Override
338        public void enter() {
339            mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
340            mTetherController.notifyInterfaceStateChange(
341                    mIfaceName, TetherInterfaceStateMachine.this,
342                    IControlsTethering.STATE_UNAVAILABLE, mLastError);
343        }
344    }
345}
346