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 com.android.server.wifi;
18 
19 import static android.net.wifi.WifiManager.WIFI_FEATURE_MBO;
20 import static android.net.wifi.WifiManager.WIFI_FEATURE_OCE;
21 
22 import android.telephony.PhoneStateListener;
23 import android.telephony.TelephonyManager;
24 import android.util.Log;
25 
26 import com.android.server.wifi.SupplicantStaIfaceHal.MboAssocDisallowedReasonCode;
27 
28 /**
29  * MboOceController is responsible for controlling MBO and OCE operations.
30  */
31 public class MboOceController {
32     private static final String TAG = "MboOceController";
33 
34     /** State of MBO/OCE module. */
35     private boolean mEnabled = false;
36     private boolean mIsMboSupported = false;
37     private boolean mIsOceSupported = false;
38     private boolean mVerboseLoggingEnabled = false;
39 
40     private final TelephonyManager mTelephonyManager;
41     private final ActiveModeWarden mActiveModeWarden;
42     private final WifiThreadRunner mWifiThreadRunner;
43 
44     /**
45      * Create new instance of MboOceController.
46      */
MboOceController(TelephonyManager telephonyManager, ActiveModeWarden activeModeWarden, WifiThreadRunner wifiThreadRunner)47     public MboOceController(TelephonyManager telephonyManager, ActiveModeWarden activeModeWarden,
48             WifiThreadRunner wifiThreadRunner) {
49         mTelephonyManager = telephonyManager;
50         mActiveModeWarden = activeModeWarden;
51         mWifiThreadRunner = wifiThreadRunner;
52     }
53 
54     /**
55      * Enable MBO and OCE functionality.
56      */
enable()57     public void enable() {
58         ClientModeManager clientModeManager =
59                 mActiveModeWarden.getPrimaryClientModeManagerNullable();
60         if (clientModeManager == null) {
61             return;
62         }
63         long supportedFeatures = clientModeManager.getSupportedFeatures();
64         mIsMboSupported = (supportedFeatures & WIFI_FEATURE_MBO) != 0;
65         mIsOceSupported = (supportedFeatures & WIFI_FEATURE_OCE) != 0;
66         mEnabled = true;
67         if (mVerboseLoggingEnabled) {
68             Log.d(TAG, "Enable MBO-OCE MBO support: " + mIsMboSupported
69                     + " OCE support: " + mIsOceSupported);
70         }
71         if (mIsMboSupported) {
72             // Register for data connection state change events (Cellular).
73             mTelephonyManager.listen(mDataConnectionStateListener,
74                     PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
75         }
76     }
77 
78     /**
79      * Disable MBO and OCE functionality.
80      */
disable()81     public void disable() {
82         if (mVerboseLoggingEnabled) {
83             Log.d(TAG, "Disable MBO-OCE");
84         }
85         if (mIsMboSupported) {
86             // Un-register for data connection state change events (Cellular).
87             mTelephonyManager.listen(mDataConnectionStateListener, PhoneStateListener.LISTEN_NONE);
88         }
89         mEnabled = false;
90     }
91 
92     /**
93      * Enable/Disable verbose logging.
94      *
95      * @param verbose true to enable and false to disable.
96      */
enableVerboseLogging(boolean verbose)97     public void enableVerboseLogging(boolean verbose) {
98         mVerboseLoggingEnabled = verbose;
99     }
100 
101     /**
102      * Listen for changes to the data connection state (Cellular).
103      */
104     private PhoneStateListener mDataConnectionStateListener = new PhoneStateListener(){
105         public void onDataConnectionStateChanged(int state, int networkType) {
106             mWifiThreadRunner.post(
107                     () -> {
108                         boolean dataAvailable;
109                         ClientModeManager clientModeManager =
110                                 mActiveModeWarden.getPrimaryClientModeManagerNullable();
111                         if (clientModeManager == null) {
112                             return;
113                         }
114                         if (!mEnabled) {
115                             Log.e(TAG, "onDataConnectionStateChanged called when MBO is "
116                                     + "disabled!!");
117                             return;
118                         }
119                         if (state == TelephonyManager.DATA_CONNECTED) {
120                             dataAvailable = true;
121                         } else if (state == TelephonyManager.DATA_DISCONNECTED) {
122                             dataAvailable = false;
123                         } else {
124                             Log.e(TAG, "onDataConnectionStateChanged unexpected State: " + state);
125                             return;
126                         }
127                         if (mVerboseLoggingEnabled) {
128                             Log.d(TAG, "Cell Data: " + dataAvailable);
129                         }
130                         clientModeManager.setMboCellularDataStatus(dataAvailable);
131                     }, TAG + "#onDataConnectionStateChanged"
132             );
133         }
134     };
135 
136     /**
137      * BtmFrameData carries the data retried from received BTM
138      * request frame handled in supplicant.
139      */
140     public static class BtmFrameData {
141         public @MboOceConstants.BtmResponseStatus int mStatus =
142                 MboOceConstants.BTM_RESPONSE_STATUS_INVALID;
143         public int mBssTmDataFlagsMask = 0;
144         public long mBlockListDurationMs = 0;
145         public @MboOceConstants.MboTransitionReason int mTransitionReason =
146                 MboOceConstants.MBO_TRANSITION_REASON_INVALID;
147         public @MboOceConstants.MboCellularDataConnectionPreference int mCellPreference =
148                 MboOceConstants.MBO_CELLULAR_DATA_CONNECTION_INVALID;
149 
150         @Override
toString()151         public String toString() {
152             return new StringBuilder("BtmFrameData status=").append(mStatus).append(
153                     ", flags=").append(mBssTmDataFlagsMask).append(
154                     ", assocRetryDelay=").append(mBlockListDurationMs).append(
155                     ", transitionReason=").append(mTransitionReason).append(
156                     ", cellPref=").append(mCellPreference).toString();
157         }
158     }
159 
160     /**
161      * OceRssiBasedAssocRejectAttr is extracted from (Re-)Association response frame from an OCE AP
162      * to indicate that the AP has rejected the (Re-)Association request on the basis of
163      * insufficient RSSI.
164      * Refer OCE spec v1.0 section 4.2.2 Table 7.
165      */
166     public static class OceRssiBasedAssocRejectAttr {
167         /*
168          * Delta RSSI - The difference in dB between the minimum RSSI at which
169          * the AP would accept a (Re-)Association request from the STA before
170          * Retry Delay expires and the AP's measurement of the RSSI at which the
171          * (Re-)Association request was received.
172          */
173         public int mDeltaRssi;
174         /*
175          * Retry Delay - The time period in seconds for which the AP will not
176          * accept any subsequent (Re-)Association requests from the STA, unless
177          * the received RSSI has improved by Delta RSSI.
178          */
179         public int mRetryDelayS;
180 
OceRssiBasedAssocRejectAttr(int deltaRssi, int retryDelayS)181         public OceRssiBasedAssocRejectAttr(int deltaRssi, int retryDelayS) {
182             this.mDeltaRssi = deltaRssi;
183             this.mRetryDelayS = retryDelayS;
184         }
185 
186         @Override
toString()187         public String toString() {
188             return new StringBuilder("OceRssiBasedAssocRejectAttr Delta Rssi=")
189                     .append(mDeltaRssi).append(
190                     ", Retry Delay=").append(mRetryDelayS).toString();
191         }
192     }
193 
194     /**
195      * MboAssocDisallowedAttr is extracted from (Re-)Association response frame from the MBO AP
196      * to indicate that the AP is not accepting new associations.
197      * Refer MBO spec v1.2 section 4.2.4 Table 13 for the details of reason code.
198      */
199     public static class MboAssocDisallowedAttr {
200         /*
201          * Reason Code - The reason why the AP is not accepting new
202          * associations.
203          */
204         public @MboOceConstants.MboAssocDisallowedReasonCode int mReasonCode;
205 
MboAssocDisallowedAttr(int reasonCode)206         public MboAssocDisallowedAttr(int reasonCode) {
207             mReasonCode = halToFrameworkMboAssocRDisallowedReasonCode(reasonCode);
208         }
209 
210         @Override
toString()211         public String toString() {
212             return new StringBuilder("MboAssocDisallowedAttr Reason code=")
213                     .append(mReasonCode).toString();
214         }
215 
216         private @MboOceConstants.MboAssocDisallowedReasonCode int
halToFrameworkMboAssocRDisallowedReasonCode(int reasonCode)217                 halToFrameworkMboAssocRDisallowedReasonCode(int reasonCode) {
218             switch (reasonCode) {
219                 case MboAssocDisallowedReasonCode.RESERVED:
220                     return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_RESERVED_0;
221                 case MboAssocDisallowedReasonCode.UNSPECIFIED:
222                     return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_UNSPECIFIED;
223                 case MboAssocDisallowedReasonCode.MAX_NUM_STA_ASSOCIATED:
224                     return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_MAX_NUM_STA_ASSOCIATED;
225                 case MboAssocDisallowedReasonCode.AIR_INTERFACE_OVERLOADED:
226                     return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_AIR_INTERFACE_OVERLOADED;
227                 case MboAssocDisallowedReasonCode.AUTH_SERVER_OVERLOADED:
228                     return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_AUTH_SERVER_OVERLOADED;
229                 case MboAssocDisallowedReasonCode.INSUFFICIENT_RSSI:
230                     return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_INSUFFICIENT_RSSI;
231                 default:
232                     return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_RESERVED;
233             }
234         }
235     }
236 }
237