• 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.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.NetworkScan;
30 import android.telephony.NetworkScanRequest;
31 import android.telephony.RadioAccessSpecifier;
32 import android.telephony.Rlog;
33 import android.telephony.SubscriptionInfo;
34 import android.telephony.TelephonyManager;
35 import android.telephony.TelephonyScanManager;
36 import android.util.ArraySet;
37 
38 import com.android.internal.annotations.VisibleForTesting;
39 
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.Set;
43 import java.util.concurrent.TimeUnit;
44 
45 /**
46  * Network Scan controller class which will scan for the specific bands as requested and
47  * provide results to caller when ready.
48  */
49 public class ONSNetworkScanCtlr {
50     private static final String LOG_TAG = "ONSNetworkScanCtlr";
51     private static final boolean DBG = true;
52     private static final int SEARCH_PERIODICITY_SLOW = (int) TimeUnit.MINUTES.toSeconds(5);
53     private static final int SEARCH_PERIODICITY_FAST = (int) TimeUnit.MINUTES.toSeconds(1);
54     private static final int MAX_SEARCH_TIME = (int) TimeUnit.MINUTES.toSeconds(1);
55     private static final int SCAN_RESTART_TIME = (int) TimeUnit.MINUTES.toMillis(1);
56     private final Object mLock = new Object();
57 
58     /* message  to handle scan responses from modem */
59     private static final int MSG_SCAN_RESULTS_AVAILABLE = 1;
60     private static final int MSG_SCAN_COMPLETE = 2;
61     private static final int MSG_SCAN_ERROR = 3;
62 
63     /* scan object to keep track of current scan request */
64     private NetworkScan mCurrentScan;
65     private boolean mIsScanActive;
66     private NetworkScanRequest mCurrentScanRequest;
67     private List<String> mMccMncs;
68     private TelephonyManager mTelephonyManager;
69     private CarrierConfigManager configManager;
70     private int mRsrpEntryThreshold;
71     @VisibleForTesting
72     protected NetworkAvailableCallBack mNetworkAvailableCallBack;
73     HandlerThread mThread;
74     private Handler mHandler;
75 
76     @VisibleForTesting
77     public TelephonyScanManager.NetworkScanCallback mNetworkScanCallback =
78             new TelephonyScanManager.NetworkScanCallback() {
79 
80         @Override
81         public void onResults(List<CellInfo> results) {
82             logDebug("Total results :" + results.size());
83             for (CellInfo cellInfo : results) {
84                 logDebug("cell info: " + cellInfo);
85             }
86 
87             Message message = Message.obtain(mHandler, MSG_SCAN_RESULTS_AVAILABLE, results);
88             message.sendToTarget();
89         }
90 
91         @Override
92         public void onComplete() {
93             logDebug("Scan completed!");
94             Message message = Message.obtain(mHandler, MSG_SCAN_COMPLETE, NetworkScan.SUCCESS);
95             mHandler.sendMessageDelayed(message, SCAN_RESTART_TIME);
96         }
97 
98         @Override
99         public void onError(@NetworkScan.ScanErrorCode int error) {
100             logDebug("Scan error " + error);
101             Message message = Message.obtain(mHandler, MSG_SCAN_ERROR, error);
102             message.sendToTarget();
103         }
104     };
105 
106     /**
107      * call back for network availability
108      */
109     public interface NetworkAvailableCallBack {
110 
111         /**
112          * Returns the scan results to the user, this callback will be called multiple times.
113          */
onNetworkAvailability(List<CellInfo> results)114         void onNetworkAvailability(List<CellInfo> results);
115 
116         /**
117          * on error
118          * @param error
119          */
onError(int error)120         void onError(int error);
121     }
122 
getIntCarrierConfig(String key)123     private int getIntCarrierConfig(String key) {
124         PersistableBundle b = null;
125         if (configManager != null) {
126             // If an invalid subId is used, this bundle will contain default values.
127             b = configManager.getConfig();
128         }
129         if (b != null) {
130             return b.getInt(key);
131         } else {
132             // Return static default defined in CarrierConfigManager.
133             return CarrierConfigManager.getDefaultConfig().getInt(key);
134         }
135     }
136 
137     /**
138      * analyze scan results
139      * @param results contains all available cells matching the scan request at current location.
140      */
analyzeScanResults(List<CellInfo> results)141     public void analyzeScanResults(List<CellInfo> results) {
142         /* Inform registrants about availability of network */
143         if (!mIsScanActive || results == null) {
144           return;
145         }
146         List<CellInfo> filteredResults = new ArrayList<CellInfo>();
147         synchronized (mLock) {
148             for (CellInfo cellInfo : results) {
149                 if (mMccMncs.contains(getMccMnc(cellInfo))) {
150                     if (cellInfo instanceof CellInfoLte) {
151                         int rsrp = ((CellInfoLte) cellInfo).getCellSignalStrength().getRsrp();
152                         logDebug("cell info rsrp: " + rsrp);
153                         if (rsrp >= mRsrpEntryThreshold) {
154                             filteredResults.add(cellInfo);
155                         }
156                     }
157                 }
158             }
159         }
160         if ((filteredResults.size() >= 1) && (mNetworkAvailableCallBack != null)) {
161             /* Todo: change to aggregate results on success. */
162             mNetworkAvailableCallBack.onNetworkAvailability(filteredResults);
163         }
164     }
165 
invalidateScanOnError(int error)166     private void invalidateScanOnError(int error) {
167         logDebug("scan invalidated on error");
168         if (mNetworkAvailableCallBack != null) {
169             mNetworkAvailableCallBack.onError(error);
170         }
171 
172         synchronized (mLock) {
173             mIsScanActive = false;
174             mCurrentScan = null;
175         }
176     }
177 
ONSNetworkScanCtlr(Context c, TelephonyManager telephonyManager, NetworkAvailableCallBack networkAvailableCallBack)178     public ONSNetworkScanCtlr(Context c, TelephonyManager telephonyManager,
179             NetworkAvailableCallBack networkAvailableCallBack) {
180         init(c, telephonyManager, networkAvailableCallBack);
181     }
182 
183     /**
184      * initialize Network Scan controller
185      * @param c context
186      * @param telephonyManager Telephony manager instance
187      * @param networkAvailableCallBack callback to be called when network selection is done
188      */
init(Context context, TelephonyManager telephonyManager, NetworkAvailableCallBack networkAvailableCallBack)189     public void init(Context context, TelephonyManager telephonyManager,
190             NetworkAvailableCallBack networkAvailableCallBack) {
191         log("init called");
192         mThread = new HandlerThread(LOG_TAG);
193         mThread.start();
194         mHandler =  new Handler(mThread.getLooper()) {
195             @Override
196             public void handleMessage(Message msg) {
197                 switch (msg.what) {
198                     case MSG_SCAN_RESULTS_AVAILABLE:
199                         logDebug("Msg received for scan results");
200                         /* Todo: need to aggregate the results */
201                         analyzeScanResults((List<CellInfo>) msg.obj);
202                         break;
203                     case MSG_SCAN_COMPLETE:
204                         logDebug("Msg received for scan complete");
205                         restartScan();
206                         break;
207                     case MSG_SCAN_ERROR:
208                         logDebug("Msg received for scan error");
209                         invalidateScanOnError((int) msg.obj);
210                         break;
211                     default:
212                         log("invalid message");
213                         break;
214                 }
215             }
216         };
217         mTelephonyManager = telephonyManager;
218         mNetworkAvailableCallBack = networkAvailableCallBack;
219         configManager = (CarrierConfigManager) context.getSystemService(
220                 Context.CARRIER_CONFIG_SERVICE);
221     }
222 
223     /* get mcc mnc from cell info if the cell is for LTE */
getMccMnc(CellInfo cellInfo)224     private String getMccMnc(CellInfo cellInfo) {
225         if (cellInfo instanceof CellInfoLte) {
226             return ((CellInfoLte) cellInfo).getCellIdentity().getMccString()
227                     + ((CellInfoLte) cellInfo).getCellIdentity().getMncString();
228         }
229 
230         return null;
231     }
232 
createNetworkScanRequest(ArrayList<AvailableNetworkInfo> availableNetworks, int periodicity)233     private NetworkScanRequest createNetworkScanRequest(ArrayList<AvailableNetworkInfo> availableNetworks,
234             int periodicity) {
235         RadioAccessSpecifier[] ras = new RadioAccessSpecifier[1];
236         ArrayList<String> mccMncs = new ArrayList<String>();
237         Set<Integer> bandSet = new ArraySet<>();
238 
239         /* by default add band 48 */
240         bandSet.add(AccessNetworkConstants.EutranBand.BAND_48);
241         /* retrieve mcc mncs and bands for available networks */
242         for (AvailableNetworkInfo availableNetwork : availableNetworks) {
243             mccMncs.addAll(availableNetwork.getMccMncs());
244             bandSet.addAll(availableNetwork.getBands());
245         }
246 
247         int[] bands = bandSet.stream().mapToInt(band->band).toArray();
248         /* create network scan request */
249         ras[0] = new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, bands,
250                 null);
251         NetworkScanRequest networkScanRequest = new NetworkScanRequest(
252                 NetworkScanRequest.SCAN_TYPE_PERIODIC, ras, periodicity, MAX_SEARCH_TIME, false,
253                 NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC, mccMncs);
254         synchronized (mLock) {
255             mMccMncs = mccMncs;
256         }
257         return networkScanRequest;
258     }
259 
260     /**
261      * start less interval network scan
262      * @param availableNetworks list of subscriptions for which the scanning needs to be started.
263      * @return true if successfully accepted request.
264      */
startFastNetworkScan(ArrayList<AvailableNetworkInfo> availableNetworks)265     public boolean startFastNetworkScan(ArrayList<AvailableNetworkInfo> availableNetworks) {
266         NetworkScanRequest networkScanRequest = createNetworkScanRequest(availableNetworks,
267                 SEARCH_PERIODICITY_FAST);
268         return startNetworkScan(networkScanRequest);
269     }
270 
271 
startNetworkScan(NetworkScanRequest networkScanRequest)272     private boolean startNetworkScan(NetworkScanRequest networkScanRequest) {
273         NetworkScan networkScan;
274         synchronized (mLock) {
275             /* if the request is same as existing one, then make sure to not proceed */
276             if (mIsScanActive && mCurrentScanRequest.equals(networkScanRequest)) {
277                 return true;
278             }
279 
280             /* Need to stop current scan if we already have one */
281             stopNetworkScan();
282 
283             /* user lower threshold to enable modem stack */
284             mRsrpEntryThreshold =
285                 getIntCarrierConfig(
286                     CarrierConfigManager.KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT);
287 
288             /* start new scan */
289             networkScan = mTelephonyManager.requestNetworkScan(networkScanRequest,
290                     mNetworkScanCallback);
291 
292             mCurrentScan = networkScan;
293             mIsScanActive = true;
294             mCurrentScanRequest = networkScanRequest;
295         }
296 
297         logDebug("startNetworkScan " + networkScanRequest);
298         return true;
299     }
300 
restartScan()301     private void restartScan() {
302         NetworkScan networkScan;
303         logDebug("restartScan");
304         synchronized (mLock) {
305             if (mCurrentScanRequest != null) {
306                 networkScan = mTelephonyManager.requestNetworkScan(mCurrentScanRequest,
307                         mNetworkScanCallback);
308                 mIsScanActive = true;
309             }
310         }
311     }
312 
313     /**
314      * stop network scan
315      */
stopNetworkScan()316     public void stopNetworkScan() {
317         logDebug("stopNetworkScan");
318         synchronized (mLock) {
319             if (mIsScanActive && mCurrentScan != null) {
320                 try {
321                     mCurrentScan.stopScan();
322                 } catch (IllegalArgumentException iae) {
323                     logDebug("Scan failed with exception " + iae);
324                 }
325                 mIsScanActive = false;
326                 mCurrentScan = null;
327                 mCurrentScanRequest = null;
328             }
329         }
330     }
331 
log(String msg)332     private static void log(String msg) {
333         Rlog.d(LOG_TAG, msg);
334     }
335 
logDebug(String msg)336     private static void logDebug(String msg) {
337         if (DBG) {
338             Rlog.d(LOG_TAG, msg);
339         }
340     }
341 }
342