| /* |
| * 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 com.android.server.wifi; |
| |
| import static android.net.wifi.WifiManager.WIFI_FEATURE_MBO; |
| import static android.net.wifi.WifiManager.WIFI_FEATURE_OCE; |
| |
| import android.hardware.wifi.supplicant.V1_4.ISupplicantStaIfaceCallback.MboAssocDisallowedReasonCode; |
| import android.telephony.PhoneStateListener; |
| import android.telephony.TelephonyManager; |
| import android.util.Log; |
| |
| /** |
| * MboOceController is responsible for controlling MBO and OCE operations. |
| */ |
| public class MboOceController { |
| private static final String TAG = "MboOceController"; |
| |
| /** State of MBO/OCE module. */ |
| private boolean mEnabled = false; |
| private boolean mIsMboSupported = false; |
| private boolean mIsOceSupported = false; |
| private boolean mVerboseLoggingEnabled = false; |
| |
| private final TelephonyManager mTelephonyManager; |
| private final ActiveModeWarden mActiveModeWarden; |
| |
| /** |
| * Create new instance of MboOceController. |
| */ |
| public MboOceController(TelephonyManager telephonyManager, ActiveModeWarden activeModeWarden) { |
| mTelephonyManager = telephonyManager; |
| mActiveModeWarden = activeModeWarden; |
| } |
| |
| /** |
| * Enable MBO and OCE functionality. |
| */ |
| public void enable() { |
| ClientModeManager clientModeManager = |
| mActiveModeWarden.getPrimaryClientModeManagerNullable(); |
| if (clientModeManager == null) { |
| return; |
| } |
| long supportedFeatures = clientModeManager.getSupportedFeatures(); |
| mIsMboSupported = (supportedFeatures & WIFI_FEATURE_MBO) != 0; |
| mIsOceSupported = (supportedFeatures & WIFI_FEATURE_OCE) != 0; |
| mEnabled = true; |
| if (mVerboseLoggingEnabled) { |
| Log.d(TAG, "Enable MBO-OCE MBO support: " + mIsMboSupported |
| + " OCE support: " + mIsOceSupported); |
| } |
| if (mIsMboSupported) { |
| // Register for data connection state change events (Cellular). |
| mTelephonyManager.listen(mDataConnectionStateListener, |
| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE); |
| } |
| } |
| |
| /** |
| * Disable MBO and OCE functionality. |
| */ |
| public void disable() { |
| if (mVerboseLoggingEnabled) { |
| Log.d(TAG, "Disable MBO-OCE"); |
| } |
| if (mIsMboSupported) { |
| // Un-register for data connection state change events (Cellular). |
| mTelephonyManager.listen(mDataConnectionStateListener, PhoneStateListener.LISTEN_NONE); |
| } |
| mEnabled = false; |
| } |
| |
| /** |
| * Enable/Disable verbose logging. |
| * |
| * @param verbose true to enable and false to disable. |
| */ |
| public void enableVerboseLogging(boolean verbose) { |
| mVerboseLoggingEnabled = verbose; |
| } |
| |
| /** |
| * Listen for changes to the data connection state (Cellular). |
| */ |
| private PhoneStateListener mDataConnectionStateListener = new PhoneStateListener(){ |
| public void onDataConnectionStateChanged(int state, int networkType) { |
| boolean dataAvailable; |
| |
| ClientModeManager clientModeManager = |
| mActiveModeWarden.getPrimaryClientModeManagerNullable(); |
| if (clientModeManager == null) { |
| return; |
| } |
| if (!mEnabled) { |
| Log.e(TAG, "onDataConnectionStateChanged called when MBO is disabled!!"); |
| return; |
| } |
| if (state == TelephonyManager.DATA_CONNECTED) { |
| dataAvailable = true; |
| } else if (state == TelephonyManager.DATA_DISCONNECTED) { |
| dataAvailable = false; |
| } else { |
| Log.e(TAG, "onDataConnectionStateChanged unexpected State: " + state); |
| return; |
| } |
| if (mVerboseLoggingEnabled) { |
| Log.d(TAG, "Cell Data: " + dataAvailable); |
| } |
| clientModeManager.setMboCellularDataStatus(dataAvailable); |
| } |
| }; |
| |
| /** |
| * BtmFrameData carries the data retried from received BTM |
| * request frame handled in supplicant. |
| */ |
| public static class BtmFrameData { |
| public @MboOceConstants.BtmResponseStatus int mStatus = |
| MboOceConstants.BTM_RESPONSE_STATUS_INVALID; |
| public int mBssTmDataFlagsMask = 0; |
| public long mBlockListDurationMs = 0; |
| public @MboOceConstants.MboTransitionReason int mTransitionReason = |
| MboOceConstants.MBO_TRANSITION_REASON_INVALID; |
| public @MboOceConstants.MboCellularDataConnectionPreference int mCellPreference = |
| MboOceConstants.MBO_CELLULAR_DATA_CONNECTION_INVALID; |
| |
| @Override |
| public String toString() { |
| return new StringBuilder("BtmFrameData status=").append(mStatus).append( |
| ", flags=").append(mBssTmDataFlagsMask).append( |
| ", assocRetryDelay=").append(mBlockListDurationMs).append( |
| ", transitionReason=").append(mTransitionReason).append( |
| ", cellPref=").append(mCellPreference).toString(); |
| } |
| } |
| |
| /** |
| * OceRssiBasedAssocRejectAttr is extracted from (Re-)Association response frame from an OCE AP |
| * to indicate that the AP has rejected the (Re-)Association request on the basis of |
| * insufficient RSSI. |
| * Refer OCE spec v1.0 section 4.2.2 Table 7. |
| */ |
| public static class OceRssiBasedAssocRejectAttr { |
| /* |
| * Delta RSSI - The difference in dB between the minimum RSSI at which |
| * the AP would accept a (Re-)Association request from the STA before |
| * Retry Delay expires and the AP's measurement of the RSSI at which the |
| * (Re-)Association request was received. |
| */ |
| public int mDeltaRssi; |
| /* |
| * Retry Delay - The time period in seconds for which the AP will not |
| * accept any subsequent (Re-)Association requests from the STA, unless |
| * the received RSSI has improved by Delta RSSI. |
| */ |
| public int mRetryDelayS; |
| |
| public OceRssiBasedAssocRejectAttr(int deltaRssi, int retryDelayS) { |
| this.mDeltaRssi = deltaRssi; |
| this.mRetryDelayS = retryDelayS; |
| } |
| |
| @Override |
| public String toString() { |
| return new StringBuilder("OceRssiBasedAssocRejectAttr Delta Rssi=") |
| .append(mDeltaRssi).append( |
| ", Retry Delay=").append(mRetryDelayS).toString(); |
| } |
| } |
| |
| /** |
| * MboAssocDisallowedAttr is extracted from (Re-)Association response frame from the MBO AP |
| * to indicate that the AP is not accepting new associations. |
| * Refer MBO spec v1.2 section 4.2.4 Table 13 for the details of reason code. |
| */ |
| public static class MboAssocDisallowedAttr { |
| /* |
| * Reason Code - The reason why the AP is not accepting new |
| * associations. |
| */ |
| public @MboOceConstants.MboAssocDisallowedReasonCode int mReasonCode; |
| |
| public MboAssocDisallowedAttr(int reasonCode) { |
| mReasonCode = halToFrameworkMboAssocRDisallowedReasonCode(reasonCode); |
| } |
| |
| @Override |
| public String toString() { |
| return new StringBuilder("MboAssocDisallowedAttr Reason code=") |
| .append(mReasonCode).toString(); |
| } |
| |
| private @MboOceConstants.MboAssocDisallowedReasonCode int |
| halToFrameworkMboAssocRDisallowedReasonCode(int reasonCode) { |
| switch (reasonCode) { |
| case MboAssocDisallowedReasonCode.RESERVED: |
| return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_RESERVED_0; |
| case MboAssocDisallowedReasonCode.UNSPECIFIED: |
| return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_UNSPECIFIED; |
| case MboAssocDisallowedReasonCode.MAX_NUM_STA_ASSOCIATED: |
| return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_MAX_NUM_STA_ASSOCIATED; |
| case MboAssocDisallowedReasonCode.AIR_INTERFACE_OVERLOADED: |
| return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_AIR_INTERFACE_OVERLOADED; |
| case MboAssocDisallowedReasonCode.AUTH_SERVER_OVERLOADED: |
| return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_AUTH_SERVER_OVERLOADED; |
| case MboAssocDisallowedReasonCode.INSUFFICIENT_RSSI: |
| return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_INSUFFICIENT_RSSI; |
| default: |
| return MboOceConstants.MBO_ASSOC_DISALLOWED_REASON_RESERVED; |
| } |
| } |
| } |
| } |