• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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