1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "sta_saved_device_appraisal.h"
16 #include "wifi_logger.h"
17 #include "wifi_config_center.h"
18 #include "wifi_common_util.h"
19 
20 DEFINE_WIFILOG_LABEL("StaSavedDeviceAppraisal");
21 
22 #define BINARY_CODE 0001
23 #define LAST_SELECTION_SCORE_DECAY_S 300
24 #define MAX(A, B) (((A) >= (B)) ? (A) : (B))
25 namespace OHOS {
26 namespace Wifi {
StaSavedDeviceAppraisal(bool supportFmRoamingFlag,int instId)27 StaSavedDeviceAppraisal::StaSavedDeviceAppraisal(bool supportFmRoamingFlag, int instId)
28     : signalScorePerLevel(WifiSettings::GetInstance().GetScoretacticsScoreSlope(instId)),
29       signalBaseScore(WifiSettings::GetInstance().GetScoretacticsInitScore(instId)),
30       sameBssidScore(WifiSettings::GetInstance().GetScoretacticsSameBssidScore(instId)),
31       sameDeviceScore(WifiSettings::GetInstance().GetScoretacticsSameNetworkScore(instId)),
32       frequency5GHzScore(WifiSettings::GetInstance().GetScoretacticsFrequency5GHzScore(instId)),
33       userSelectedDeviceScore(WifiSettings::GetInstance().GetScoretacticsLastSelectionScore(instId)),
34       safetyDeviceScore(WifiSettings::GetInstance().GetScoretacticsSecurityScore(instId)),
35       normalDeviceScore(WifiSettings::GetInstance().GetScoretacticsNormalScore(instId)),
36       firmwareRoamFlag(supportFmRoamingFlag),
37       m_instId(instId)
38 {}
~StaSavedDeviceAppraisal()39 StaSavedDeviceAppraisal::~StaSavedDeviceAppraisal()
40 {
41     WIFI_LOGI("Enter ~StaSavedDeviceAppraisal.\n");
42 }
43 
DeviceAppraisals(WifiDeviceConfig & electedDevice,std::vector<InterScanInfo> & scanInfos,WifiLinkedInfo & info)44 ErrCode StaSavedDeviceAppraisal::DeviceAppraisals(
45     WifiDeviceConfig &electedDevice, std::vector<InterScanInfo> &scanInfos, WifiLinkedInfo &info)
46 {
47     WIFI_LOGD("Enter DeviceAppraisals.\n");
48     int highestScore = 0;
49     int sign = 0;
50     InterScanInfo scanInfoElected;
51     scanInfoElected.rssi = VALUE_LIMIT_MIN_RSSI;
52 
53     for (auto scanInfo : scanInfos) {
54         WifiDeviceConfig device;
55         std::string deviceKeyMgmt;
56         scanInfo.GetDeviceMgmt(deviceKeyMgmt);
57         if (WifiSettings::GetInstance().GetDeviceConfig(scanInfo.ssid, deviceKeyMgmt, device) != 0) {
58             WIFI_LOGD("Skip unsaved ssid Network %{public}s.", SsidAnonymize(scanInfo.ssid).c_str());
59             continue;
60         }
61 
62         if (WhetherSkipDevice(device)) {
63             continue;
64         }
65 
66         int score = 0;
67         AppraiseDeviceQuality(score, scanInfo, device, info, device.connFailedCount >= MAX_RETRY_COUNT);
68         WIFI_LOGD("The device networkId:%{public}d ssid:%{public}s score:%{public}d rssi:%{public}d.",
69             device.networkId, SsidAnonymize(scanInfo.ssid).c_str(), score, scanInfo.rssi);
70         if (CheckHigherPriority(score, highestScore, scanInfo.rssi, scanInfoElected.rssi)) {
71             highestScore = score;
72             scanInfoElected.rssi = scanInfo.rssi;
73             electedDevice = device;
74             electedDevice.bssid = scanInfo.bssid;
75             sign = 1;
76             WIFI_LOGD("set highestScore: %{public}d, ssid: %{public}s", highestScore, SsidAnonymize(device.ssid).c_str());
77         } else {
78             WIFI_LOGD("The config %{public}s is ignored!\n", MacAnonymize(scanInfo.ssid).c_str());
79         }
80     }
81     if (sign == 1) {
82         WIFI_LOGI("DeviceAppraisals, networkId:%{public}d, ssid:%{public}s, bssid:%{public}s.",
83             electedDevice.networkId, SsidAnonymize(electedDevice.ssid).c_str(),
84             MacAnonymize(electedDevice.bssid).c_str());
85         if (info.connState == ConnState::CONNECTED && electedDevice.networkId == info.networkId) {
86             WifiDeviceConfig networkInfo;
87             electedDevice = networkInfo;
88             WIFI_LOGI("The electedDevice is the current connected device. Skip the device selection.");
89             return WIFI_OPT_FAILED;
90         } else {
91             WIFI_LOGI("The device is selected successfully.\n");
92             return WIFI_OPT_SUCCESS;
93         }
94     } else {
95         WIFI_LOGI("Skip all scan results.\n");
96         return WIFI_OPT_FAILED;
97     }
98 }
99 
WhetherSkipDevice(WifiDeviceConfig & device)100 bool StaSavedDeviceAppraisal::WhetherSkipDevice(WifiDeviceConfig &device)
101 {
102     /* Skip this type of device and evaluate it by other appraisals */
103     if (device.isPasspoint || device.isEphemeral) {
104         WIFI_LOGI("Skip isPasspoint or isEphemeral Network %{public}s.", SsidAnonymize(device.ssid).c_str());
105         return true;
106     }
107 
108     if (device.status == static_cast<int>(WifiDeviceConfigStatus::DISABLED)) {
109         WIFI_LOGI("Skip disabled Network %{public}s.", SsidAnonymize(device.ssid).c_str());
110         return true;
111     }
112     std::string bssid = WifiConfigCenter::GetInstance().GetConnectTimeoutBssid(m_instId);
113     if (!bssid.empty() && bssid == device.bssid) {
114         WIFI_LOGI("Skip the connect timeout Network %{public}s.", SsidAnonymize(device.ssid).c_str());
115         return true;
116     }
117     return false;
118 }
119 
AppraiseDeviceQuality(int & score,InterScanInfo & scanInfo,WifiDeviceConfig & device,WifiLinkedInfo & info,bool flip)120 void StaSavedDeviceAppraisal::AppraiseDeviceQuality(int &score, InterScanInfo &scanInfo,
121     WifiDeviceConfig &device, WifiLinkedInfo &info, bool flip)
122 {
123     WIFI_LOGD("Enter AppraiseDeviceQuality.\n");
124     int rssi = scanInfo.rssi;
125     /* Converts a signal to a grid number */
126     int signalStrength = CalculateSignalBars(rssi, MAX_SIGNAL_BAR_NUM);
127     /* Signal strength score */
128     score += signalBaseScore + signalStrength * signalScorePerLevel;
129     WIFI_LOGD("signalstrength score is %{public}d.\n", score);
130 
131     /* 5 GHz frequency band: bonus point */
132     if (Whether5GDevice(scanInfo.frequency)) {
133         score += frequency5GHzScore;
134         WIFI_LOGD("5G score is %{public}d.\n", frequency5GHzScore);
135     }
136 
137     /* normal device config: bonus point */
138     if (device.uid == WIFI_INVALID_UID) {
139         score += normalDeviceScore;
140         WIFI_LOGD("normal score is %{public}d.\n", normalDeviceScore);
141     }
142 
143     /* Bonus points for last user selection */
144     int userLastSelectedNetworkId = WifiConfigCenter::GetInstance().GetUserLastSelectedNetworkId(m_instId);
145     if (userLastSelectedNetworkId != INVALID_NETWORK_ID && userLastSelectedNetworkId == device.networkId) {
146         time_t userLastSelectedNetworkTimeVal = WifiConfigCenter::GetInstance().GetUserLastSelectedNetworkTimeVal(
147             m_instId);
148         time_t now = time(nullptr);
149         time_t timeDifference = now - userLastSelectedNetworkTimeVal;
150         /*
151          * Basic score of the device selected by the user: 120.
152          * One point is deducted from every 5 points since the time when the user
153          * selects the device.
154          */
155         if (timeDifference > 0) {
156             int decay = static_cast<int>(timeDifference / LAST_SELECTION_SCORE_DECAY_S);
157             int bonus = MAX((userSelectedDeviceScore - decay), (0));
158             score += bonus;
159             WIFI_LOGI("lastselected score is %{public}d.\n", bonus);
160         }
161     }
162     /*
163      * If the current device is the same as the elected device, bonus points
164      * are added.
165      */
166     if (info.detailedState == DetailedState::WORKING && scanInfo.ssid == info.ssid) {
167         score += sameDeviceScore;
168         WIFI_LOGI("samenetwork score is %{public}d.\n", sameDeviceScore);
169         /*
170          * When firmware roaming is supported, the same BSSID is added to different
171          * BSSIDs.
172          */
173         if (firmwareRoamFlag && scanInfo.bssid != info.bssid) {
174             score += sameBssidScore;
175             WIFI_LOGI("roamingsupport score is %{public}d.\n", sameBssidScore);
176         }
177     }
178 
179     if (info.detailedState == DetailedState::WORKING && info.bssid == scanInfo.bssid) {
180         score += sameBssidScore;
181         WIFI_LOGI("SameBssid score is %{public}d.\n", sameBssidScore);
182     }
183 
184     if (device.keyMgmt != "NONE" && device.keyMgmt.size() != 0) {
185         score += safetyDeviceScore;
186         WIFI_LOGI("security score is %{public}d.\n", safetyDeviceScore);
187     }
188 
189     if (flip) { // lowest priority ssid, filp the score
190         score = 0 - score;
191     }
192 }
193 
Whether5GDevice(int frequency)194 bool StaSavedDeviceAppraisal::Whether5GDevice(int frequency)
195 {
196     if (frequency > MIN_5_FREQUENCY && frequency < MAX_5_FREQUENCY) {
197         return true;
198     } else {
199         return false;
200     }
201 }
202 
CalculateSignalBars(int rssi,int signalBars)203 int StaSavedDeviceAppraisal::CalculateSignalBars(int rssi, int signalBars)
204 {
205     WIFI_LOGD("Enter CalculateSignalBars");
206     if (rssi <= VALUE_LIMIT_MIN_RSSI) {
207         return 0;
208     } else if (rssi >= VALUE_LIMIT_MAX_RSSI) {
209         return signalBars - 1;
210     } else {
211         float inputRange = (VALUE_LIMIT_MAX_RSSI - VALUE_LIMIT_MIN_RSSI);
212         float outputRange = (signalBars - 1);
213         return static_cast<int>(static_cast<float>(rssi - VALUE_LIMIT_MIN_RSSI) * outputRange / inputRange);
214     }
215 }
216 
CheckHigherPriority(int score,int lastScore,int rssi,int selectedRssi)217 bool StaSavedDeviceAppraisal::CheckHigherPriority(int score, int lastScore, int rssi, int selectedRssi)
218 {
219     bool higerPriority = false;
220     if (lastScore == 0) {
221         higerPriority = true; // first higerPriority
222     } else if (lastScore > 0) {
223         higerPriority = score > lastScore || // compare score, if equal then compare rssi
224             (score == lastScore && rssi > selectedRssi);
225     } else {
226         if (score >= 0) {
227             higerPriority = true; // > 0 higher priority
228         } else {
229             higerPriority = score < lastScore || // both low priority then compare score
230                 (score == lastScore && rssi > selectedRssi);
231         }
232     }
233     return higerPriority;
234 }
235 }  // namespace Wifi
236 }  // namespace OHOS