1 /* 2 * Copyright (C) 2018 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.ons; 18 19 import android.content.Context; 20 import android.os.Handler; 21 import android.os.HandlerThread; 22 import android.os.Message; 23 import android.os.PersistableBundle; 24 import android.telephony.AccessNetworkConstants; 25 import android.telephony.AvailableNetworkInfo; 26 import android.telephony.CarrierConfigManager; 27 import android.telephony.CellInfo; 28 import android.telephony.CellInfoLte; 29 import android.telephony.CellInfoNr; 30 import android.telephony.CellSignalStrengthNr; 31 import android.telephony.NetworkScan; 32 import android.telephony.NetworkScanRequest; 33 import android.telephony.RadioAccessSpecifier; 34 import android.telephony.TelephonyManager; 35 import android.telephony.TelephonyScanManager; 36 import android.util.ArraySet; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.telephony.Rlog; 40 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.List; 44 import java.util.Set; 45 import java.util.concurrent.TimeUnit; 46 import java.util.stream.Collectors; 47 48 /** 49 * Network Scan controller class which will scan for the specific bands as requested and 50 * provide results to caller when ready. 51 */ 52 public class ONSNetworkScanCtlr { 53 private static final String LOG_TAG = "ONSNetworkScanCtlr"; 54 private static final boolean DBG = true; 55 private static final int SEARCH_PERIODICITY_SLOW = (int) TimeUnit.MINUTES.toSeconds(5); 56 private static final int SEARCH_PERIODICITY_FAST = (int) TimeUnit.MINUTES.toSeconds(1); 57 private static final int MAX_SEARCH_TIME = (int) TimeUnit.MINUTES.toSeconds(1); 58 private static final int SCAN_RESTART_TIME = (int) TimeUnit.MINUTES.toMillis(1); 59 private final Object mLock = new Object(); 60 61 /* message to handle scan responses from modem */ 62 private static final int MSG_SCAN_RESULTS_AVAILABLE = 1; 63 private static final int MSG_SCAN_COMPLETE = 2; 64 private static final int MSG_SCAN_ERROR = 3; 65 66 private Boolean mIs4gScanEnabled = null; 67 68 @VisibleForTesting 69 static final RadioAccessSpecifier DEFAULT_5G_RAS = new RadioAccessSpecifier( 70 AccessNetworkConstants.AccessNetworkType.NGRAN, 71 new int[] { 72 AccessNetworkConstants.NgranBands.BAND_48, 73 AccessNetworkConstants.NgranBands.BAND_71}, 74 null); 75 @VisibleForTesting 76 static final RadioAccessSpecifier DEFAULT_4G_RAS = new RadioAccessSpecifier( 77 AccessNetworkConstants.AccessNetworkType.NGRAN, 78 new int[] { 79 AccessNetworkConstants.EutranBand.BAND_48, 80 AccessNetworkConstants.EutranBand.BAND_71}, 81 null); 82 83 /* scan object to keep track of current scan request */ 84 private NetworkScan mCurrentScan; 85 private boolean mIsScanActive; 86 private NetworkScanRequest mCurrentScanRequest; 87 private List<String> mMccMncs; 88 private TelephonyManager mTelephonyManager; 89 private CarrierConfigManager configManager; 90 private int mRsrpEntryThreshold; 91 private int mSsRsrpEntryThreshold; 92 @VisibleForTesting 93 protected NetworkAvailableCallBack mNetworkAvailableCallBack; 94 HandlerThread mThread; 95 private Handler mHandler; 96 97 @VisibleForTesting 98 public TelephonyScanManager.NetworkScanCallback mNetworkScanCallback = 99 new TelephonyScanManager.NetworkScanCallback() { 100 101 @Override 102 public void onResults(List<CellInfo> results) { 103 logDebug("Total results :" + results.size()); 104 for (CellInfo cellInfo : results) { 105 logDebug("cell info: " + cellInfo); 106 } 107 108 Message message = Message.obtain(mHandler, MSG_SCAN_RESULTS_AVAILABLE, results); 109 message.sendToTarget(); 110 } 111 112 @Override 113 public void onComplete() { 114 logDebug("Scan completed!"); 115 Message message = Message.obtain(mHandler, MSG_SCAN_COMPLETE, NetworkScan.SUCCESS); 116 mHandler.sendMessageDelayed(message, SCAN_RESTART_TIME); 117 } 118 119 @Override 120 public void onError(@NetworkScan.ScanErrorCode int error) { 121 logDebug("Scan error " + error); 122 Message message = Message.obtain(mHandler, MSG_SCAN_ERROR, error); 123 message.sendToTarget(); 124 } 125 }; 126 127 /** 128 * call back for network availability 129 */ 130 public interface NetworkAvailableCallBack { 131 132 /** 133 * Returns the scan results to the user, this callback will be called multiple times. 134 */ onNetworkAvailability(List<CellInfo> results)135 void onNetworkAvailability(List<CellInfo> results); 136 137 /** 138 * on error 139 * @param error 140 */ onError(int error)141 void onError(int error); 142 } 143 getConfigBundle()144 private PersistableBundle getConfigBundle() { 145 if (configManager != null) { 146 // If an invalid subId is used, this bundle will contain default values. 147 return configManager.getConfig(); 148 } 149 return null; 150 } 151 getIntCarrierConfig(String key)152 private int getIntCarrierConfig(String key) { 153 PersistableBundle b = getConfigBundle(); 154 if (b != null) { 155 return b.getInt(key); 156 } else { 157 // Return static default defined in CarrierConfigManager. 158 return CarrierConfigManager.getDefaultConfig().getInt(key); 159 } 160 } 161 getBooleanCarrierConfig(String key)162 private boolean getBooleanCarrierConfig(String key) { 163 PersistableBundle b = getConfigBundle(); 164 if (b != null) { 165 return b.getBoolean(key); 166 } else { 167 // Return static default defined in CarrierConfigManager. 168 return CarrierConfigManager.getDefaultConfig().getBoolean(key); 169 } 170 } 171 172 /** 173 * analyze scan results 174 * @param results contains all available cells matching the scan request at current location. 175 */ analyzeScanResults(List<CellInfo> results)176 public void analyzeScanResults(List<CellInfo> results) { 177 /* Inform registrants about availability of network */ 178 if (!mIsScanActive || results == null) { 179 return; 180 } 181 List<CellInfo> filteredResults = new ArrayList<CellInfo>(); 182 mIs4gScanEnabled = getIs4gScanEnabled(); 183 synchronized (mLock) { 184 for (CellInfo cellInfo : results) { 185 if (mMccMncs.contains(getMccMnc(cellInfo))) { 186 if (cellInfo instanceof CellInfoNr) { 187 CellInfoNr nrCellInfo = (CellInfoNr) cellInfo; 188 int ssRsrp = ((CellSignalStrengthNr) nrCellInfo.getCellSignalStrength()) 189 .getSsRsrp(); 190 logDebug("cell info ssRsrp: " + ssRsrp); 191 if (ssRsrp >= mSsRsrpEntryThreshold) { 192 filteredResults.add(cellInfo); 193 } 194 } 195 if (mIs4gScanEnabled && cellInfo instanceof CellInfoLte) { 196 int rsrp = ((CellInfoLte) cellInfo).getCellSignalStrength().getRsrp(); 197 logDebug("cell info rsrp: " + rsrp); 198 if (rsrp >= mRsrpEntryThreshold) { 199 filteredResults.add(cellInfo); 200 } 201 } 202 } 203 } 204 } 205 if ((filteredResults.size() >= 1) && (mNetworkAvailableCallBack != null)) { 206 /* Todo: change to aggregate results on success. */ 207 mNetworkAvailableCallBack.onNetworkAvailability(filteredResults); 208 } 209 } 210 invalidateScanOnError(int error)211 private void invalidateScanOnError(int error) { 212 logDebug("scan invalidated on error"); 213 if (mNetworkAvailableCallBack != null) { 214 mNetworkAvailableCallBack.onError(error); 215 } 216 217 synchronized (mLock) { 218 mIsScanActive = false; 219 mCurrentScan = null; 220 } 221 } 222 ONSNetworkScanCtlr(Context c, TelephonyManager telephonyManager, NetworkAvailableCallBack networkAvailableCallBack)223 public ONSNetworkScanCtlr(Context c, TelephonyManager telephonyManager, 224 NetworkAvailableCallBack networkAvailableCallBack) { 225 init(c, telephonyManager, networkAvailableCallBack); 226 } 227 228 /** 229 * initialize Network Scan controller 230 * @param c context 231 * @param telephonyManager Telephony manager instance 232 * @param networkAvailableCallBack callback to be called when network selection is done 233 */ init(Context context, TelephonyManager telephonyManager, NetworkAvailableCallBack networkAvailableCallBack)234 public void init(Context context, TelephonyManager telephonyManager, 235 NetworkAvailableCallBack networkAvailableCallBack) { 236 log("init called"); 237 mThread = new HandlerThread(LOG_TAG); 238 mThread.start(); 239 mHandler = new Handler(mThread.getLooper()) { 240 @Override 241 public void handleMessage(Message msg) { 242 switch (msg.what) { 243 case MSG_SCAN_RESULTS_AVAILABLE: 244 logDebug("Msg received for scan results"); 245 /* Todo: need to aggregate the results */ 246 analyzeScanResults((List<CellInfo>) msg.obj); 247 break; 248 case MSG_SCAN_COMPLETE: 249 logDebug("Msg received for scan complete"); 250 restartScan(); 251 break; 252 case MSG_SCAN_ERROR: 253 logDebug("Msg received for scan error"); 254 invalidateScanOnError((int) msg.obj); 255 break; 256 default: 257 log("invalid message"); 258 break; 259 } 260 } 261 }; 262 mTelephonyManager = telephonyManager; 263 mNetworkAvailableCallBack = networkAvailableCallBack; 264 configManager = (CarrierConfigManager) context.getSystemService( 265 Context.CARRIER_CONFIG_SERVICE); 266 } 267 268 /* get mcc mnc from cell info if the cell is for LTE */ 269 @VisibleForTesting getMccMnc(CellInfo cellInfo)270 protected String getMccMnc(CellInfo cellInfo) { 271 if (cellInfo instanceof CellInfoLte) { 272 return ((CellInfoLte) cellInfo).getCellIdentity().getMccString() 273 + ((CellInfoLte) cellInfo).getCellIdentity().getMncString(); 274 } 275 else if (cellInfo instanceof CellInfoNr) { 276 return ((CellInfoNr) cellInfo).getCellIdentity().getMccString() 277 + ((CellInfoNr) cellInfo).getCellIdentity().getMncString(); 278 } 279 280 return null; 281 } 282 getIs4gScanEnabled()283 private boolean getIs4gScanEnabled() { 284 // TODO: make this a null check 285 if (mIs4gScanEnabled != null) { 286 return mIs4gScanEnabled; 287 } 288 return getBooleanCarrierConfig( 289 CarrierConfigManager.KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL); 290 } 291 292 @VisibleForTesting setIs4gScanEnabled(boolean enabled)293 void setIs4gScanEnabled(boolean enabled) { 294 mIs4gScanEnabled = enabled; 295 } 296 297 @VisibleForTesting createNetworkScanRequest(ArrayList<AvailableNetworkInfo> availableNetworks, int periodicity)298 NetworkScanRequest createNetworkScanRequest(ArrayList<AvailableNetworkInfo> availableNetworks, 299 int periodicity) { 300 RadioAccessSpecifier[] ras; 301 ArrayList<String> mccMncs = new ArrayList<String>(); 302 Set<Integer> bandSet5G = new ArraySet<>(); 303 Set<Integer> bandSet4G = new ArraySet<>(); 304 305 mIs4gScanEnabled = getIs4gScanEnabled(); 306 307 /* retrieve mcc mncs and bands for available networks */ 308 for (AvailableNetworkInfo availableNetwork : availableNetworks) { 309 mccMncs.addAll(availableNetwork.getMccMncs()); 310 List<RadioAccessSpecifier> radioAccessSpecifiers = 311 availableNetwork.getRadioAccessSpecifiers(); 312 if (radioAccessSpecifiers.isEmpty()) { 313 if (mIs4gScanEnabled) { 314 bandSet4G.addAll(availableNetwork.getBands()); 315 } 316 bandSet5G.addAll(availableNetwork.getBands()); 317 } else { 318 for (RadioAccessSpecifier radioAccessSpecifier : radioAccessSpecifiers) { 319 int radioAccessNetworkType = radioAccessSpecifier.getRadioAccessNetwork(); 320 if (mIs4gScanEnabled && 321 radioAccessNetworkType == 322 AccessNetworkConstants.AccessNetworkType.EUTRAN) { 323 bandSet4G.addAll(Arrays.stream(radioAccessSpecifier.getBands()) 324 .boxed().collect(Collectors.toList())); 325 } else if (radioAccessNetworkType == 326 AccessNetworkConstants.AccessNetworkType.NGRAN) { 327 bandSet5G.addAll(Arrays.stream(radioAccessSpecifier.getBands()) 328 .boxed().collect(Collectors.toList())); 329 } 330 } 331 } 332 } 333 334 int rasSize = 1; 335 if (mIs4gScanEnabled && bandSet4G.isEmpty() == bandSet5G.isEmpty()) { 336 rasSize = 2; 337 } 338 ras = new RadioAccessSpecifier[rasSize]; 339 340 if (bandSet4G.isEmpty() && bandSet5G.isEmpty()) { 341 // Set the default RadioAccessSpecifiers if none were set and no bands were set. 342 ras[0] = DEFAULT_5G_RAS; 343 if (mIs4gScanEnabled) { 344 ras[1] = DEFAULT_4G_RAS; 345 } 346 } else { 347 if (mIs4gScanEnabled && !bandSet4G.isEmpty()) { 348 ras[0] = new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, 349 bandSet4G.stream().mapToInt(band->band).toArray(), null); 350 } 351 if (!bandSet5G.isEmpty()) { 352 ras[rasSize - 1] = new RadioAccessSpecifier( 353 AccessNetworkConstants.AccessNetworkType.NGRAN, 354 bandSet5G.stream().mapToInt(band->band).toArray(), null); 355 } else if (!mIs4gScanEnabled) { 356 // Reached if only 4G was specified but 4G scan is disabled. 357 ras[0] = DEFAULT_5G_RAS; 358 } 359 } 360 361 NetworkScanRequest networkScanRequest = new NetworkScanRequest( 362 NetworkScanRequest.SCAN_TYPE_PERIODIC, ras, periodicity, MAX_SEARCH_TIME, false, 363 NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC, mccMncs); 364 synchronized (mLock) { 365 mMccMncs = mccMncs; 366 } 367 return networkScanRequest; 368 } 369 370 /** 371 * start less interval network scan 372 * @param availableNetworks list of subscriptions for which the scanning needs to be started. 373 * @return true if successfully accepted request. 374 */ startFastNetworkScan(ArrayList<AvailableNetworkInfo> availableNetworks)375 public boolean startFastNetworkScan(ArrayList<AvailableNetworkInfo> availableNetworks) { 376 NetworkScanRequest networkScanRequest = createNetworkScanRequest(availableNetworks, 377 SEARCH_PERIODICITY_FAST); 378 return startNetworkScan(networkScanRequest); 379 } 380 381 startNetworkScan(NetworkScanRequest networkScanRequest)382 private boolean startNetworkScan(NetworkScanRequest networkScanRequest) { 383 NetworkScan networkScan; 384 synchronized (mLock) { 385 /* if the request is same as existing one, then make sure to not proceed */ 386 if (mIsScanActive && mCurrentScanRequest.equals(networkScanRequest)) { 387 return true; 388 } 389 390 /* Need to stop current scan if we already have one */ 391 stopNetworkScan(); 392 393 /* user lower threshold to enable modem stack */ 394 mRsrpEntryThreshold = 395 getIntCarrierConfig( 396 CarrierConfigManager.KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT); 397 398 mSsRsrpEntryThreshold = getIntCarrierConfig( 399 CarrierConfigManager.OpportunisticNetwork.KEY_ENTRY_THRESHOLD_SS_RSRP_INT); 400 401 /* start new scan */ 402 networkScan = mTelephonyManager.requestNetworkScan(networkScanRequest, 403 mNetworkScanCallback); 404 405 mCurrentScan = networkScan; 406 mIsScanActive = true; 407 mCurrentScanRequest = networkScanRequest; 408 } 409 410 logDebug("startNetworkScan " + networkScanRequest); 411 return true; 412 } 413 restartScan()414 private void restartScan() { 415 NetworkScan networkScan; 416 logDebug("restartScan"); 417 synchronized (mLock) { 418 if (mCurrentScanRequest != null) { 419 networkScan = mTelephonyManager.requestNetworkScan(mCurrentScanRequest, 420 mNetworkScanCallback); 421 mIsScanActive = true; 422 } 423 } 424 } 425 426 /** 427 * stop network scan 428 */ stopNetworkScan()429 public void stopNetworkScan() { 430 logDebug("stopNetworkScan"); 431 synchronized (mLock) { 432 if (mIsScanActive && mCurrentScan != null) { 433 try { 434 mCurrentScan.stopScan(); 435 } catch (IllegalArgumentException iae) { 436 logDebug("Scan failed with exception " + iae); 437 } 438 mIsScanActive = false; 439 mCurrentScan = null; 440 mCurrentScanRequest = null; 441 } 442 } 443 } 444 log(String msg)445 private static void log(String msg) { 446 Rlog.d(LOG_TAG, msg); 447 } 448 logDebug(String msg)449 private static void logDebug(String msg) { 450 if (DBG) { 451 Rlog.d(LOG_TAG, msg); 452 } 453 } 454 } 455