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