1 /* 2 * Copyright (C) 2022 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.telephony.qns; 18 19 import static com.android.telephony.qns.QnsConstants.INVALID_ID; 20 21 import android.content.Context; 22 import android.net.ConnectivityManager; 23 import android.net.LinkProperties; 24 import android.net.Network; 25 import android.net.NetworkCapabilities; 26 import android.net.NetworkRequest; 27 import android.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.Message; 30 import android.telephony.AccessNetworkConstants; 31 import android.util.Log; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.io.BufferedReader; 36 import java.io.IOException; 37 import java.io.InputStreamReader; 38 import java.net.Inet4Address; 39 import java.net.InetAddress; 40 import java.net.UnknownHostException; 41 import java.util.ArrayList; 42 import java.util.List; 43 44 /** 45 * This class provides support for the RTT verification for Wifi. It schedules the RTT verification 46 * based on the UE state(Wifi connected, Cellular available, IMS registered on WLAN, operator 47 * support for RTT, etc). 48 */ 49 class WifiBackhaulMonitor { 50 private static final int EVENT_START_RTT_CHECK = 1; 51 private static final int EVENT_IMS_REGISTRATION_STATE_CHANGED = 2; 52 private final String mTag; 53 private final ConnectivityManager mConnectivityManager; 54 private final QnsImsManager mQnsImsManager; 55 private final ConnectivityManager.NetworkCallback mNetworkCallback; 56 private final Context mContext; 57 private final int mSlotIndex; 58 59 private final QnsRegistrantList mRegistrantList; 60 private final HandlerThread mHandlerThread; 61 private final Handler mHandler; 62 private final QnsCarrierConfigManager mConfigManager; 63 private final QnsTimer mQnsTimer; 64 private boolean mRttResult = false; 65 66 ArrayList<InetAddress> mValidIpList = new ArrayList<>(); 67 private boolean mIsCallbackRegistered = false; 68 private boolean mIsRttScheduled = false; 69 private boolean mIsCellularAvailable = false; 70 private boolean mIsIwlanConnected = false; 71 private boolean mIsRttRunning = false; 72 private String mInterfaceName = null; 73 private int mRttTimerId = INVALID_ID; 74 75 private class BackhaulHandler extends Handler { BackhaulHandler()76 BackhaulHandler() { 77 super(mHandlerThread.getLooper()); 78 } 79 80 @Override handleMessage(Message msg)81 public void handleMessage(Message msg) { 82 super.handleMessage(msg); 83 log("handleMessage what = " + msg.what); 84 QnsAsyncResult ar; 85 switch (msg.what) { 86 case EVENT_START_RTT_CHECK: 87 onRttCheckStarted(); 88 break; 89 case EVENT_IMS_REGISTRATION_STATE_CHANGED: 90 ar = (QnsAsyncResult) msg.obj; 91 onImsRegistrationStateChanged((QnsImsManager.ImsRegistrationState) ar.mResult); 92 break; 93 default: 94 log("Invalid event = " + msg.what); 95 } 96 } 97 } 98 99 private class WiFiStatusCallback extends ConnectivityManager.NetworkCallback { 100 101 @Override onAvailable(Network network)102 public void onAvailable(Network network) { 103 super.onAvailable(network); 104 if (network != null) { 105 LinkProperties lp = mConnectivityManager.getLinkProperties(network); 106 if (lp != null && lp.getInterfaceName().contains("wlan")) { 107 mInterfaceName = lp.getInterfaceName(); 108 } 109 } 110 } 111 112 @Override onLost(Network network)113 public void onLost(Network network) { 114 super.onLost(network); 115 stopRttSchedule(); 116 mInterfaceName = null; 117 mRttResult = false; 118 } 119 } 120 /** 121 * Constructor to create WifiBackhaulMonitor instance. 122 */ WifiBackhaulMonitor( Context context, QnsCarrierConfigManager configManager, QnsImsManager imsManager, QnsTimer qnstimer, int slotIndex)123 WifiBackhaulMonitor( 124 Context context, 125 QnsCarrierConfigManager configManager, 126 QnsImsManager imsManager, 127 QnsTimer qnstimer, 128 int slotIndex) { 129 mSlotIndex = slotIndex; 130 mTag = WifiBackhaulMonitor.class.getSimpleName() + "[" + mSlotIndex + "]"; 131 mContext = context; 132 mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); 133 mConfigManager = configManager; 134 mQnsImsManager = imsManager; 135 mQnsTimer = qnstimer; 136 mNetworkCallback = new WiFiStatusCallback(); 137 mRegistrantList = new QnsRegistrantList(); 138 mHandlerThread = new HandlerThread(mTag); 139 mHandlerThread.start(); 140 mHandler = new BackhaulHandler(); 141 } 142 143 /** This method returns true if operator supports RTT feature. */ isRttCheckEnabled()144 boolean isRttCheckEnabled() { 145 return mConfigManager.getWlanRttServerAddressConfig() != null; 146 } 147 148 /** 149 * Registers to receive the change in Round-trip-time(RTT) ICMP pings for Wifi. 150 * 151 * @param h {@link Handler} to handle the result of the RTT pings. 152 * @param what event which will be notified in handler. 153 */ registerForRttStatusChange(Handler h, int what)154 void registerForRttStatusChange(Handler h, int what) { 155 mRegistrantList.addUnique(h, what, null); 156 if (!mIsCallbackRegistered) { 157 mQnsImsManager.registerImsRegistrationStatusChanged( 158 mHandler, EVENT_IMS_REGISTRATION_STATE_CHANGED); 159 mConnectivityManager.registerNetworkCallback( 160 new NetworkRequest.Builder() 161 .clearCapabilities() 162 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 163 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 164 .build(), 165 mNetworkCallback); 166 mIsCallbackRegistered = true; 167 } 168 } 169 170 /** 171 * Unregisters the handler for RTT ICMP pings. 172 * 173 * @param h {@link Handler} to unregister the event 174 */ unRegisterForRttStatusChange(Handler h)175 void unRegisterForRttStatusChange(Handler h) { 176 mRegistrantList.remove(h); 177 if (mRegistrantList.size() == 0) { 178 clearAll(); 179 } 180 } 181 182 /** Triggers the request to check RTT. */ requestRttCheck()183 void requestRttCheck() { 184 if (!mIsRttRunning) { 185 if (mRttTimerId != INVALID_ID) { 186 mQnsTimer.unregisterTimer(mRttTimerId); 187 mRttTimerId = INVALID_ID; 188 } 189 mHandler.sendEmptyMessage(EVENT_START_RTT_CHECK); 190 } else { 191 log("RTT check is already running"); 192 } 193 } 194 195 /** Updates cellular availability in WifiBackhaulMonitor. */ setCellularAvailable(boolean cellularAvailable)196 void setCellularAvailable(boolean cellularAvailable) { 197 if (mIsCellularAvailable != cellularAvailable) { 198 mIsCellularAvailable = cellularAvailable; 199 if (mIsCellularAvailable) { 200 startRttSchedule(); 201 } else { 202 stopRttSchedule(); 203 } 204 } 205 } 206 onRttCheckStarted()207 private void onRttCheckStarted() { 208 mIsRttRunning = true; 209 mRttResult = startRttCheck(); 210 if (mIsRttScheduled && mRttResult) { 211 mIsRttScheduled = false; 212 startRttSchedule(); 213 } 214 mIsRttRunning = false; 215 notifyRttResult(); 216 } 217 onImsRegistrationStateChanged(QnsImsManager.ImsRegistrationState info)218 private void onImsRegistrationStateChanged(QnsImsManager.ImsRegistrationState info) { 219 if (info.getTransportType() == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { 220 if (info.getEvent() == QnsConstants.IMS_REGISTRATION_CHANGED_REGISTERED) { 221 mIsIwlanConnected = true; 222 startRttSchedule(); 223 } else if (info.getEvent() == QnsConstants.IMS_REGISTRATION_CHANGED_UNREGISTERED) { 224 mIsIwlanConnected = false; 225 stopRttSchedule(); 226 } 227 } else if (info.getTransportType() == AccessNetworkConstants.TRANSPORT_TYPE_WWAN 228 && info.getEvent() == QnsConstants.IMS_REGISTRATION_CHANGED_REGISTERED) { 229 mIsIwlanConnected = false; 230 stopRttSchedule(); 231 } 232 } 233 startRttSchedule()234 private void startRttSchedule() { 235 if (!mIsRttScheduled && mIsCellularAvailable && mIsIwlanConnected) { 236 int delay = mConfigManager.getWlanRttOtherConfigs()[4]; 237 if (delay > 0) { 238 startRttSchedule(delay); 239 } 240 } 241 } 242 startRttSchedule(int delay)243 private void startRttSchedule(int delay) { 244 log("start RTT schedule for " + delay); 245 mRttTimerId = mQnsTimer.registerTimer(Message.obtain(mHandler, EVENT_START_RTT_CHECK), 246 delay); 247 mIsRttScheduled = true; 248 } 249 stopRttSchedule()250 private void stopRttSchedule() { 251 if (mIsRttScheduled) { 252 log("stop RTT schedule"); 253 mQnsTimer.unregisterTimer(mRttTimerId); 254 mRttTimerId = INVALID_ID; 255 mIsRttScheduled = false; 256 } 257 } 258 notifyRttResult()259 private void notifyRttResult() { 260 mRegistrantList.notifyResult(mRttResult); 261 mValidIpList.clear(); 262 } 263 startRttCheck()264 private boolean startRttCheck() { 265 if (mInterfaceName == null) { 266 log("Wifi interface is not set for RTT check"); 267 return false; 268 } 269 int[] config = mConfigManager.getWlanRttOtherConfigs(); 270 if (config == null || config.length == 0) { 271 log("No configurations are set for RTT check"); 272 return true; 273 } 274 275 int pingCount = config[0]; 276 int intervalTime = Math.max(config[1], 200); 277 int pingSize = config[2]; 278 int requiredRttAverage = config[3]; 279 String rttPingServer = mConfigManager.getWlanRttServerAddressConfig(); 280 281 List<String>[] hostAddresses; 282 try { 283 hostAddresses = getHostAddresses(rttPingServer); 284 } catch (UnknownHostException e) { 285 log("Host not found for " + rttPingServer); 286 return true; 287 } 288 289 boolean rttResult = true; 290 Runtime runtime = Runtime.getRuntime(); 291 int ver = 0; 292 String[] pings = new String[] {"ping", "ping6"}; // ping for IPv4 and IPv6 293 for (String ping : pings) { 294 List<String> addresses = hostAddresses[ver]; 295 for (String address : addresses) { 296 StringBuilder command = new StringBuilder(ping); 297 command.append(" -I ").append(mInterfaceName); 298 command.append(" -i ").append((float) intervalTime / 1000); 299 command.append(" -s ").append(pingSize); 300 command.append(" -c ").append(pingCount); 301 command.append(" ").append(address); 302 try { 303 Process p = runtime.exec(command.toString()); 304 BufferedReader br = 305 new BufferedReader(new InputStreamReader(p.getInputStream())); 306 String s; 307 while ((s = br.readLine()) != null) { 308 if (s.contains("/avg/")) { 309 int i = s.indexOf("/", s.indexOf("=")); 310 String time = s.substring(i + 1, s.indexOf("/", i + 2)); 311 float avgRtt = Float.parseFloat(time); 312 rttResult = avgRtt <= requiredRttAverage; 313 if (rttResult) { 314 log("RTT check is success."); 315 return true; 316 } 317 } 318 } 319 } catch (IOException | NumberFormatException e) { 320 e.printStackTrace(); 321 } 322 } 323 ver++; 324 } 325 326 log("RTT Result: " + rttResult); 327 return rttResult; 328 } 329 getHostAddresses(String rttPingServer)330 private List<String>[] getHostAddresses(String rttPingServer) throws UnknownHostException { 331 List<String>[] lists = new List[2]; 332 lists[0] = new ArrayList<>(); // for IPv4 333 lists[1] = new ArrayList<>(); // for IPv6 334 InetAddress[] inetAddress = InetAddress.getAllByName(rttPingServer); 335 for (InetAddress addr : inetAddress) { 336 if (addr instanceof Inet4Address) { 337 lists[0].add(addr.getHostAddress()); 338 } else { 339 lists[1].add(addr.getHostAddress()); 340 } 341 } 342 return lists; 343 } 344 345 /** Closes the current instance. */ close()346 void close() { 347 mHandlerThread.quit(); 348 clearAll(); 349 } 350 351 /** Method to clear all settings in WifiBackhaulMonitor */ clearAll()352 void clearAll() { 353 stopRttSchedule(); 354 mRegistrantList.removeAll(); 355 if (mIsCallbackRegistered) { 356 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 357 mQnsImsManager.unregisterImsRegistrationStatusChanged(mHandler); 358 mIsCallbackRegistered = false; 359 } 360 mIsRttRunning = false; 361 mIsCellularAvailable = false; 362 mIsIwlanConnected = false; 363 mIsRttScheduled = false; 364 } 365 366 @VisibleForTesting getRttTimerId()367 int getRttTimerId() { 368 return mRttTimerId; 369 } 370 log(String s)371 private void log(String s) { 372 Log.d(mTag, s); 373 } 374 } 375