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 /**
27  * MboOceController is responsible for controlling MBO and OCE operations.
28  */
29 public class MboOceController {
30     private static final String TAG = "MboOceController";
31 
32     /** State of MBO/OCE module. */
33     private boolean mEnabled = false;
34     private boolean mIsMboSupported = false;
35     private boolean mIsOceSupported = false;
36     private boolean mVerboseLoggingEnabled = false;
37 
38     private final WifiNative mWifiNative;
39     private final TelephonyManager mTelephonyManager;
40 
41     /**
42      * Create new instance of MboOceController.
43      */
MboOceController(TelephonyManager telephonyManager, WifiNative wifiNative)44     public MboOceController(TelephonyManager telephonyManager,
45                             WifiNative wifiNative) {
46         mTelephonyManager = telephonyManager;
47         mWifiNative = wifiNative;
48     }
49 
50     /**
51      * Enable MBO and OCE functionality.
52      */
enable()53     public void enable() {
54         String iface = mWifiNative.getClientInterfaceName();
55         if (iface == null) {
56             return;
57         }
58         mIsMboSupported = (mWifiNative.getSupportedFeatureSet(iface) & WIFI_FEATURE_MBO) != 0;
59         mIsOceSupported = (mWifiNative.getSupportedFeatureSet(iface) & WIFI_FEATURE_OCE) != 0;
60         mEnabled = true;
61         if (mVerboseLoggingEnabled) {
62             Log.d(TAG, "Enable MBO-OCE MBO support: " + mIsMboSupported
63                     + " OCE support: " + mIsOceSupported);
64         }
65         if (mIsMboSupported) {
66             // Register for data connection state change events (Cellular).
67             mTelephonyManager.listen(mDataConnectionStateListener,
68                     PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
69         }
70     }
71 
72     /**
73      * Disable MBO and OCE functionality.
74      */
disable()75     public void disable() {
76         if (mVerboseLoggingEnabled) {
77             Log.d(TAG, "Disable MBO-OCE");
78         }
79         if (mIsMboSupported) {
80             // Un-register for data connection state change events (Cellular).
81             mTelephonyManager.listen(mDataConnectionStateListener, PhoneStateListener.LISTEN_NONE);
82         }
83         mEnabled = false;
84     }
85 
86     /**
87      * Enable/Disable verbose logging.
88      *
89      * @param verbose true to enable and false to disable.
90      */
enableVerboseLogging(boolean verbose)91     public void enableVerboseLogging(boolean verbose) {
92         mVerboseLoggingEnabled = verbose;
93     }
94 
95     /**
96      * Listen for changes to the data connection state (Cellular).
97      */
98     private PhoneStateListener mDataConnectionStateListener = new PhoneStateListener(){
99         public void onDataConnectionStateChanged(int state, int networkType) {
100             boolean dataAvailable;
101 
102             String iface = mWifiNative.getClientInterfaceName();
103             if (iface == null) {
104                 return;
105             }
106             if (!mEnabled) {
107                 Log.e(TAG, "onDataConnectionStateChanged called when MBO is disabled!!");
108                 return;
109             }
110             if (state == TelephonyManager.DATA_CONNECTED) {
111                 dataAvailable = true;
112             } else if (state == TelephonyManager.DATA_DISCONNECTED) {
113                 dataAvailable = false;
114             } else {
115                 Log.e(TAG, "onDataConnectionStateChanged unexpected State: " + state);
116                 return;
117             }
118             if (mVerboseLoggingEnabled) {
119                 Log.d(TAG, "Cell Data: " + dataAvailable);
120             }
121             mWifiNative.setMboCellularDataStatus(iface, dataAvailable);
122         }
123     };
124 
125     /**
126      * BtmFrameData carries the data retried from received BTM
127      * request frame handled in supplicant.
128      */
129     public static class BtmFrameData {
130         public @MboOceConstants.BtmResponseStatus int mStatus =
131                 MboOceConstants.BTM_RESPONSE_STATUS_INVALID;
132         public int mBssTmDataFlagsMask = 0;
133         public long mBlackListDurationMs = 0;
134         public @MboOceConstants.MboTransitionReason int mTransitionReason =
135                 MboOceConstants.MBO_TRANSITION_REASON_INVALID;
136         public @MboOceConstants.MboCellularDataConnectionPreference int mCellPreference =
137                 MboOceConstants.MBO_CELLULAR_DATA_CONNECTION_INVALID;
138 
139         @Override
toString()140         public String toString() {
141             return new StringBuilder("BtmFrameData status=").append(mStatus).append(
142                     ", flags=").append(mBssTmDataFlagsMask).append(
143                     ", assocRetryDelay=").append(mBlackListDurationMs).append(
144                     ", transitionReason=").append(mTransitionReason).append(
145                     ", cellPref=").append(mCellPreference).toString();
146         }
147     }
148 }
149