blob: 88f9f6c482e4818fd976552bd66ea9e5ae5ec4c2 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ans;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.telephony.AccessNetworkConstants;
import android.telephony.CellInfo;
import android.telephony.CellInfoLte;
import android.telephony.NetworkScan;
import android.telephony.NetworkScanRequest;
import android.telephony.RadioAccessSpecifier;
import android.telephony.Rlog;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyScanManager;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Network Scan controller class which will scan for the specific bands as requested and
* provide results to caller when ready.
*/
public class ANSNetworkScanCtlr {
private static final String LOG_TAG = "ANSNetworkScanCtlr";
private static final boolean DBG = true;
private static final int SEARCH_PERIODICITY_SLOW = (int) TimeUnit.MINUTES.toSeconds(5);
private static final int SEARCH_PERIODICITY_FAST = (int) TimeUnit.MINUTES.toSeconds(1);
private static final int MAX_SEARCH_TIME = (int) TimeUnit.MINUTES.toSeconds(1);
private final Object mLock = new Object();
/* message to handle scan responses from modem */
private static final int MSG_SCAN_RESULTS_AVAILABLE = 1;
private static final int MSG_SCAN_COMPLETE = 2;
private static final int MSG_SCAN_ERROR = 3;
/* scan object to keep track of current scan request */
private NetworkScan mCurrentScan;
private boolean mIsScanActive;
private NetworkScanRequest mCurrentScanRequest;
private List<String> mMccMncs;
private TelephonyManager mTelephonyManager;
@VisibleForTesting
protected NetworkAvailableCallBack mNetworkAvailableCallBack;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SCAN_RESULTS_AVAILABLE:
logDebug("Msg received for scan results");
/* Todo: need to aggregate the results */
analyzeScanResults((List<CellInfo>) msg.obj);
break;
case MSG_SCAN_COMPLETE:
logDebug("Msg received for scan complete");
restartScan();
break;
case MSG_SCAN_ERROR:
logDebug("Msg received for scan error");
invalidateScanOnError((int) msg.obj);
break;
default:
log("invalid message");
break;
}
}
};
@VisibleForTesting
public TelephonyScanManager.NetworkScanCallback mNetworkScanCallback =
new TelephonyScanManager.NetworkScanCallback() {
@Override
public void onResults(List<CellInfo> results) {
logDebug("Total results :" + results.size());
for (CellInfo cellInfo : results) {
logDebug("cell info: " + cellInfo);
}
Message message = Message.obtain(mHandler, MSG_SCAN_RESULTS_AVAILABLE, results);
message.sendToTarget();
}
@Override
public void onComplete() {
logDebug("Scan completed!");
Message message = Message.obtain(mHandler, MSG_SCAN_COMPLETE, NetworkScan.SUCCESS);
message.sendToTarget();
}
@Override
public void onError(@NetworkScan.ScanErrorCode int error) {
logDebug("Scan error " + error);
Message message = Message.obtain(mHandler, MSG_SCAN_ERROR, error);
message.sendToTarget();
}
};
/**
* call back for network availability
*/
public interface NetworkAvailableCallBack {
/**
* Returns the scan results to the user, this callback will be called multiple times.
*/
void onNetworkAvailability(List<CellInfo> results);
/**
* on error
* @param error
*/
void onError(int error);
}
/**
* analyze scan results
* @param results contains all available cells matching the scan request at current location.
*/
public void analyzeScanResults(List<CellInfo> results) {
/* Inform registrants about availability of network */
if (mIsScanActive && results != null) {
List<CellInfo> filteredResults = new ArrayList<CellInfo>();
synchronized (mLock) {
for (CellInfo cellInfo : results) {
if (mMccMncs.contains(getMccMnc(cellInfo))) {
filteredResults.add(cellInfo);
}
}
}
if ((filteredResults.size() >= 1) && (mNetworkAvailableCallBack != null)) {
/* Todo: change to aggregate results on success. */
mNetworkAvailableCallBack.onNetworkAvailability(filteredResults);
}
}
}
private void invalidateScanOnError(int error) {
logDebug("scan invalidated on error");
if (mNetworkAvailableCallBack != null) {
mNetworkAvailableCallBack.onError(error);
}
synchronized (mLock) {
mIsScanActive = false;
mCurrentScan = null;
}
}
public ANSNetworkScanCtlr(Context c, TelephonyManager telephonyManager,
NetworkAvailableCallBack networkAvailableCallBack) {
init(c, telephonyManager, networkAvailableCallBack);
}
/**
* initialize Network Scan controller
* @param c context
* @param telephonyManager Telephony manager instance
* @param networkAvailableCallBack callback to be called when network selection is done
*/
public void init(Context c, TelephonyManager telephonyManager,
NetworkAvailableCallBack networkAvailableCallBack) {
log("init called");
mTelephonyManager = telephonyManager;
mNetworkAvailableCallBack = networkAvailableCallBack;
}
/* get mcc mnc from cell info if the cell is for LTE */
private String getMccMnc(CellInfo cellInfo) {
if (cellInfo instanceof CellInfoLte) {
return ((CellInfoLte) cellInfo).getCellIdentity().getMccString()
+ ((CellInfoLte) cellInfo).getCellIdentity().getMncString();
}
return null;
}
private NetworkScanRequest createNetworkScanRequest(List<SubscriptionInfo> subscriptionInfos,
int periodicity) {
RadioAccessSpecifier[] ras = new RadioAccessSpecifier[1];
int[] bands = new int[1];
/* hardcoding band for now, Todo b/113753823 */
bands[0] = AccessNetworkConstants.EutranBand.BAND_48;
ArrayList<String> mccMncs = new ArrayList<String>();
/* retrieve mcc mncs for a subscription id */
for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
mccMncs.add(subscriptionInfo.getMccString() + subscriptionInfo.getMncString());
}
/* create network scan request */
ras[0] = new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, bands,
null);
NetworkScanRequest networkScanRequest = new NetworkScanRequest(
NetworkScanRequest.SCAN_TYPE_PERIODIC, ras, periodicity, MAX_SEARCH_TIME, false,
NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC, mccMncs);
synchronized (mLock) {
mMccMncs = mccMncs;
}
return networkScanRequest;
}
/**
* start high interval network scan
* @param subscriptionInfos list of subscriptions for which the scanning needs to be started.
* @return true if successfully accepted request.
*/
public boolean startSlowNetworkScan(List<SubscriptionInfo> subscriptionInfos) {
NetworkScanRequest networkScanRequest = createNetworkScanRequest(subscriptionInfos,
SEARCH_PERIODICITY_SLOW);
return startNetworkScan(networkScanRequest);
}
/**
* start less interval network scan
* @param subscriptionInfos list of subscriptions for which the scanning needs to be started.
* @return true if successfully accepted request.
*/
public boolean startFastNetworkScan(List<SubscriptionInfo> subscriptionInfos) {
NetworkScanRequest networkScanRequest = createNetworkScanRequest(subscriptionInfos,
SEARCH_PERIODICITY_FAST);
return startNetworkScan(networkScanRequest);
}
private boolean startNetworkScan(NetworkScanRequest networkScanRequest) {
NetworkScan networkScan;
synchronized (mLock) {
/* if the request is same as existing one, then make sure to not proceed */
if (mIsScanActive && mCurrentScanRequest.equals(networkScanRequest)) {
return true;
}
/* Need to stop current scan if we already have one */
stopNetworkScan();
/* start new scan */
networkScan = mTelephonyManager.requestNetworkScan(networkScanRequest,
mNetworkScanCallback);
mCurrentScan = networkScan;
mIsScanActive = true;
mCurrentScanRequest = networkScanRequest;
}
logDebug("startNetworkScan " + networkScanRequest);
return true;
}
private void restartScan() {
NetworkScan networkScan;
synchronized (mLock) {
if (mCurrentScanRequest != null) {
networkScan = mTelephonyManager.requestNetworkScan(mCurrentScanRequest,
mNetworkScanCallback);
mIsScanActive = true;
}
}
}
/**
* stop network scan
*/
public void stopNetworkScan() {
logDebug("stopNetworkScan");
synchronized (mLock) {
if (mIsScanActive && mCurrentScan != null) {
try {
mCurrentScan.stopScan();
} catch (IllegalArgumentException iae) {
logDebug("Scan failed with exception " + iae);
}
mIsScanActive = false;
mCurrentScan = null;
mCurrentScanRequest = null;
}
}
}
private static void log(String msg) {
Rlog.d(LOG_TAG, msg);
}
private static void logDebug(String msg) {
if (DBG) {
Rlog.d(LOG_TAG, msg);
}
}
}