• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.wifi;
18 
19 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY;
20 
21 import android.annotation.NonNull;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.net.wifi.SupplicantState;
25 import android.net.wifi.WifiConfiguration;
26 import android.net.wifi.WifiManager;
27 import android.os.BatteryStatsManager;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.Parcelable;
31 import android.os.UserHandle;
32 import android.util.Log;
33 
34 import com.android.internal.util.State;
35 import com.android.internal.util.StateMachine;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 
40 /**
41  * Tracks the state changes in supplicant and provides functionality
42  * that is based on these state changes:
43  * - detect a failed WPA handshake that loops indefinitely
44  * - authentication failure handling
45  * TODO(b/159944009): Need to rework this class to handle make before break transition on STA +
46  * STA devices (Apps will not see SUPPLICANT_STATE_CHANGED_ACTION when switching wifi networks)
47  */
48 public class SupplicantStateTracker extends StateMachine {
49 
50     private static final String TAG = "SupplicantStateTracker";
51 
52     private final Context mContext;
53     private final WifiConfigManager mWifiConfigManager;
54     private final WifiMonitor mWifiMonitor;
55     private final BatteryStatsManager mBatteryStatsManager;
56     private final String mInterfaceName;
57     private final ClientModeManager mClientModeManager;
58     private final ClientModeManagerBroadcastQueue mBroadcastQueue;
59 
60     private boolean mVerboseLoggingEnabled = false;
61     /* Indicates authentication failure in supplicant broadcast.
62      * TODO: enhance auth failure reporting to include notification
63      * for all type of failures: EAP, WPS & WPA networks */
64     private boolean mAuthFailureInSupplicantBroadcast = false;
65 
66     /* Authentication failure reason
67      * see {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_NONE},
68      *     {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_TIMEOUT},
69      *     {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_WRONG_PSWD},
70      *     {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_EAP_FAILURE}
71      */
72     private int mAuthFailureReason;
73 
74     private final State mUninitializedState = new UninitializedState();
75     private final State mDefaultState = new DefaultState();
76     private final State mInactiveState = new InactiveState();
77     private final State mDisconnectState = new DisconnectedState();
78     private final State mScanState = new ScanState();
79     private final State mHandshakeState = new HandshakeState();
80     private final State mCompletedState = new CompletedState();
81     private final State mDormantState = new DormantState();
82 
83     /** enable/disable verbose logging. */
enableVerboseLogging(boolean verbose)84     public void enableVerboseLogging(boolean verbose) {
85         mVerboseLoggingEnabled = verbose;
86     }
87 
getTag()88     private String getTag() {
89         return TAG + "[" + (mInterfaceName == null ? "unknown" : mInterfaceName) + "]";
90     }
91 
SupplicantStateTracker( @onNull Context context, @NonNull WifiConfigManager wifiConfigManager, @NonNull BatteryStatsManager batteryStatsManager, @NonNull Looper looper, @NonNull WifiMonitor wifiMonitor, @NonNull String interfaceName, @NonNull ClientModeManager clientModeManager, @NonNull ClientModeManagerBroadcastQueue broadcastQueue)92     public SupplicantStateTracker(
93             @NonNull Context context,
94             @NonNull WifiConfigManager wifiConfigManager,
95             @NonNull BatteryStatsManager batteryStatsManager,
96             @NonNull Looper looper,
97             @NonNull WifiMonitor wifiMonitor,
98             @NonNull String interfaceName,
99             @NonNull ClientModeManager clientModeManager,
100             @NonNull ClientModeManagerBroadcastQueue broadcastQueue) {
101         super(TAG, looper);
102         mContext = context;
103         mWifiConfigManager = wifiConfigManager;
104         mBatteryStatsManager = batteryStatsManager;
105         mWifiMonitor = wifiMonitor;
106         mInterfaceName = interfaceName;
107         mClientModeManager = clientModeManager;
108         mBroadcastQueue = broadcastQueue;
109 
110         registerForWifiMonitorEvents();
111 
112         addState(mDefaultState); {
113             // disconnected states
114             addState(mUninitializedState, mDefaultState);
115             addState(mInactiveState, mDefaultState);
116             addState(mDisconnectState, mDefaultState);
117             // connected states
118             addState(mScanState, mDefaultState);
119             addState(mHandshakeState, mDefaultState);
120             addState(mCompletedState, mDefaultState);
121             addState(mDormantState, mDefaultState);
122         }
123 
124         setInitialState(mUninitializedState);
125         setLogRecSize(50);
126         setLogOnlyTransitions(true);
127         // start the state machine
128         start();
129     }
130 
131     private static final int[] WIFI_MONITOR_EVENTS = {
132             WifiMonitor.ASSOCIATION_REJECTION_EVENT,
133             WifiMonitor.AUTHENTICATION_FAILURE_EVENT,
134             WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,
135     };
136 
registerForWifiMonitorEvents()137     private void registerForWifiMonitorEvents() {
138         for (int event : WIFI_MONITOR_EVENTS) {
139             mWifiMonitor.registerHandler(mInterfaceName, event, getHandler());
140         }
141     }
142 
deregisterForWifiMonitorEvents()143     private void deregisterForWifiMonitorEvents() {
144         for (int event : WIFI_MONITOR_EVENTS) {
145             mWifiMonitor.deregisterHandler(mInterfaceName, event, getHandler());
146         }
147     }
148 
149     /**
150      * Called when the owner ClientModeImpl is stopped. No more actions shall be performed on this
151      * instance after it is stopped.
152      */
stop()153     public void stop() {
154         deregisterForWifiMonitorEvents();
155 
156         quitNow();
157     }
158 
handleNetworkConnectionFailure(int netId, int disableReason)159     private void handleNetworkConnectionFailure(int netId, int disableReason) {
160         if (mVerboseLoggingEnabled) {
161             Log.d(getTag(), "handleNetworkConnectionFailure netId=" + netId
162                     + " reason " + disableReason);
163         }
164 
165         /* update network status */
166         mWifiConfigManager.updateNetworkSelectionStatus(netId, disableReason);
167     }
168 
transitionOnSupplicantStateChange(StateChangeResult stateChangeResult)169     private void transitionOnSupplicantStateChange(StateChangeResult stateChangeResult) {
170         SupplicantState supState = stateChangeResult.state;
171 
172         if (mVerboseLoggingEnabled) {
173             Log.d(getTag(), "Supplicant state: " + supState.toString() + "\n");
174         }
175 
176         switch (supState) {
177             case DISCONNECTED:
178                 transitionTo(mDisconnectState);
179                 break;
180             case INTERFACE_DISABLED:
181                 //we should have received a disconnection already, do nothing
182                 break;
183             case SCANNING:
184                 transitionTo(mScanState);
185                 break;
186             case AUTHENTICATING:
187             case ASSOCIATING:
188             case ASSOCIATED:
189             case FOUR_WAY_HANDSHAKE:
190             case GROUP_HANDSHAKE:
191                 transitionTo(mHandshakeState);
192                 break;
193             case COMPLETED:
194                 transitionTo(mCompletedState);
195                 break;
196             case DORMANT:
197                 transitionTo(mDormantState);
198                 break;
199             case INACTIVE:
200                 transitionTo(mInactiveState);
201                 break;
202             case UNINITIALIZED:
203             case INVALID:
204                 transitionTo(mUninitializedState);
205                 break;
206             default:
207                 Log.e(getTag(), "Unknown supplicant state " + supState);
208                 break;
209         }
210     }
211 
supplicantStateToBatteryStatsSupplicantState(SupplicantState state)212     private static int supplicantStateToBatteryStatsSupplicantState(SupplicantState state) {
213         switch (state) {
214             case DISCONNECTED:
215                 return BatteryStatsManager.WIFI_SUPPL_STATE_DISCONNECTED;
216             case INTERFACE_DISABLED:
217                 return BatteryStatsManager.WIFI_SUPPL_STATE_INTERFACE_DISABLED;
218             case INACTIVE:
219                 return BatteryStatsManager.WIFI_SUPPL_STATE_INACTIVE;
220             case SCANNING:
221                 return BatteryStatsManager.WIFI_SUPPL_STATE_SCANNING;
222             case AUTHENTICATING:
223                 return BatteryStatsManager.WIFI_SUPPL_STATE_AUTHENTICATING;
224             case ASSOCIATING:
225                 return BatteryStatsManager.WIFI_SUPPL_STATE_ASSOCIATING;
226             case ASSOCIATED:
227                 return BatteryStatsManager.WIFI_SUPPL_STATE_ASSOCIATED;
228             case FOUR_WAY_HANDSHAKE:
229                 return BatteryStatsManager.WIFI_SUPPL_STATE_FOUR_WAY_HANDSHAKE;
230             case GROUP_HANDSHAKE:
231                 return BatteryStatsManager.WIFI_SUPPL_STATE_GROUP_HANDSHAKE;
232             case COMPLETED:
233                 return BatteryStatsManager.WIFI_SUPPL_STATE_COMPLETED;
234             case DORMANT:
235                 return BatteryStatsManager.WIFI_SUPPL_STATE_DORMANT;
236             case UNINITIALIZED:
237                 return BatteryStatsManager.WIFI_SUPPL_STATE_UNINITIALIZED;
238             case INVALID:
239                 return BatteryStatsManager.WIFI_SUPPL_STATE_INVALID;
240             default:
241                 Log.w(TAG, "Unknown supplicant state " + state);
242                 return BatteryStatsManager.WIFI_SUPPL_STATE_INVALID;
243         }
244     }
245 
sendSupplicantStateChangedBroadcast( SupplicantState state, boolean failedAuth, int reasonCode)246     private void sendSupplicantStateChangedBroadcast(
247             SupplicantState state, boolean failedAuth, int reasonCode) {
248         int supplState = supplicantStateToBatteryStatsSupplicantState(state);
249         if (mClientModeManager.getRole() == ROLE_CLIENT_PRIMARY) {
250             mBatteryStatsManager.reportWifiSupplicantStateChanged(supplState, failedAuth);
251         }
252         String summary = "broadcast=SUPPLICANT_STATE_CHANGED_ACTION"
253                 + " state=" + state
254                 + " failedAuth=" + failedAuth
255                 + " reasonCode=" + reasonCode;
256         if (mVerboseLoggingEnabled) Log.d(TAG, "Queuing " + summary);
257         mBroadcastQueue.queueOrSendBroadcast(
258                 mClientModeManager,
259                 () -> {
260                     if (mVerboseLoggingEnabled) Log.d(TAG, "Sending " + summary);
261                     sendSupplicantStateChangedBroadcast(mContext, state, failedAuth, reasonCode);
262                 });
263     }
264 
sendSupplicantStateChangedBroadcast( Context context, SupplicantState state, boolean failedAuth, int reasonCode)265     private static void sendSupplicantStateChangedBroadcast(
266             Context context, SupplicantState state, boolean failedAuth, int reasonCode) {
267         Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
268         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
269                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
270         intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable) state);
271         if (failedAuth) {
272             intent.putExtra(
273                     WifiManager.EXTRA_SUPPLICANT_ERROR,
274                     WifiManager.ERROR_AUTHENTICATING);
275             intent.putExtra(
276                     WifiManager.EXTRA_SUPPLICANT_ERROR_REASON,
277                     reasonCode);
278         }
279         context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
280     }
281 
282     /********************************************************
283      * HSM states
284      *******************************************************/
285 
286     class DefaultState extends State {
287         @Override
enter()288         public void enter() {
289             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + "\n");
290         }
291         @Override
processMessage(Message message)292         public boolean processMessage(Message message) {
293             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + message.toString() + "\n");
294             switch (message.what) {
295                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
296                     mAuthFailureInSupplicantBroadcast = true;
297                     mAuthFailureReason = message.arg1;
298                     break;
299                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
300                     StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
301                     SupplicantState state = stateChangeResult.state;
302                     sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast,
303                             mAuthFailureReason);
304                     mAuthFailureInSupplicantBroadcast = false;
305                     mAuthFailureReason = WifiManager.ERROR_AUTH_FAILURE_NONE;
306                     transitionOnSupplicantStateChange(stateChangeResult);
307                     break;
308                 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
309                 default:
310                     Log.e(getTag(), "Ignoring " + message);
311                     break;
312             }
313             return HANDLED;
314         }
315     }
316 
317     /*
318      * This indicates that the supplicant state as seen
319      * by the framework is not initialized yet. We are
320      * in this state right after establishing a control
321      * channel connection before any supplicant events
322      * or after we have lost the control channel
323      * connection to the supplicant
324      */
325     class UninitializedState extends State {
326         @Override
enter()327         public void enter() {
328             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + "\n");
329         }
330     }
331 
332     class InactiveState extends State {
333         @Override
enter()334         public void enter() {
335             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + "\n");
336         }
337     }
338 
339     class DisconnectedState extends State {
340         @Override
enter()341         public void enter() {
342             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + "\n");
343         }
344     }
345 
346     class ScanState extends State {
347         @Override
enter()348         public void enter() {
349             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + "\n");
350         }
351     }
352 
353     class HandshakeState extends State {
354         /**
355          * The max number of the WPA supplicant loop iterations before we
356          * decide that the loop should be terminated:
357          */
358         private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
359         private int mLoopDetectIndex;
360         private int mLoopDetectCount;
361 
362         @Override
enter()363         public void enter() {
364             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + "\n");
365             mLoopDetectIndex = 0;
366             mLoopDetectCount = 0;
367         }
368         @Override
processMessage(Message message)369         public boolean processMessage(Message message) {
370             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + message.toString() + "\n");
371             switch (message.what) {
372                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
373                     StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
374                     SupplicantState state = stateChangeResult.state;
375                     if (SupplicantState.isHandshakeState(state)) {
376                         if (mLoopDetectIndex > state.ordinal()) {
377                             mLoopDetectCount++;
378                         }
379                         if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
380                             Log.d(getTag(), "Supplicant loop detected, disabling network "
381                                     + stateChangeResult.networkId);
382                             handleNetworkConnectionFailure(stateChangeResult.networkId,
383                                     WifiConfiguration.NetworkSelectionStatus
384                                             .DISABLED_AUTHENTICATION_FAILURE);
385                         }
386                         mLoopDetectIndex = state.ordinal();
387                         sendSupplicantStateChangedBroadcast(state,
388                                 mAuthFailureInSupplicantBroadcast, mAuthFailureReason);
389                     } else {
390                         //Have the DefaultState handle the transition
391                         return NOT_HANDLED;
392                     }
393                     break;
394                 default:
395                     return NOT_HANDLED;
396             }
397             return HANDLED;
398         }
399     }
400 
401     class CompletedState extends State {
402         @Override
enter()403         public void enter() {
404             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + "\n");
405         }
406         @Override
processMessage(Message message)407         public boolean processMessage(Message message) {
408             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + message.toString() + "\n");
409             switch (message.what) {
410                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
411                     StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
412                     SupplicantState state = stateChangeResult.state;
413                     sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast,
414                             mAuthFailureReason);
415                     /* Ignore any connecting state in completed state. Group re-keying
416                      * events and other auth events that do not affect connectivity are
417                      * ignored
418                      */
419                     if (SupplicantState.isConnecting(state)) {
420                         break;
421                     }
422                     transitionOnSupplicantStateChange(stateChangeResult);
423                     break;
424                 default:
425                     return NOT_HANDLED;
426             }
427             return HANDLED;
428         }
429     }
430 
431     //TODO: remove after getting rid of the state in supplicant
432     class DormantState extends State {
433         @Override
enter()434         public void enter() {
435             if (mVerboseLoggingEnabled) Log.d(getTag(), getName() + "\n");
436         }
437     }
438 
439     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)440     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
441         super.dump(fd, pw, args);
442         pw.println("mAuthFailureInSupplicantBroadcast " + mAuthFailureInSupplicantBroadcast);
443         pw.println("mAuthFailureReason " + mAuthFailureReason);
444         pw.println();
445     }
446 }
447