blob: a72f691d9396b99251a0f347254cc41aa408c4db [file] [log] [blame]
/**
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package com.android.settings.network.telephony;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.NetworkScan;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyScanManager;
import android.util.Log;
import com.android.internal.telephony.OperatorInfo;
import com.android.settings.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* This class receives the incremental scan results intent from QCRIL Message Tunnel, processes it
* and sends them to the network search results activity {@link NetworkSelectSettings.java}.
*/
public class LegacyIncrementalScanBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "LegacyIncrementalScanBroadcastReceiver";
private static final String ACTION_INCREMENTAL_NW_SCAN_IND
= "qualcomm.intent.action.ACTION_INCREMENTAL_NW_SCAN_IND";
private static final String EXTRA_SCAN_RESULT = "scan_result";
private static final String EXTRA_INCREMENTAL_SCAN_DATA = "incr_nw_scan_data";
private static final String EXTRA_INSTANCE_ID = "sub_id";
private static final int QUERY_EXCEPTION = -1;
// Network scan was successful and complete
private static final int NAS_QUERY_COMPLETE = 0;
// Network scan was partial
private static final int NAS_QUERY_PARTIAL = 1;
// Network scan was aborted
private static final int NAS_QUERY_ABORT = 2;
// Network scan did not complete due to a radio link failure recovery in progress
private static final int NAS_QUERY_REJ_IN_RLF = 3;
// Sending incremental network scan errors
private static final int NAS_QUERY_INCREMENT_ERROR = 4;
// Periodic network scan gave partial results
private static final int NAS_QUERY_PARTIAL_PERIODIC = 5;
private static int sPhoneCount;
private Context mContext;
// QueryDetails for each phoneId
private QueryDetails[] mQueryDetails;
private final TelephonyScanManager.NetworkScanCallback mNetworkScanCallback;
// TODO: This class may not be required since handling of incremental results is already
// being taken care of in {@link NetworkSelectSettings} class.
class QueryDetails {
String[] storedScanInfo;
QueryDetails() {
storedScanInfo = null;
}
void concatScanInfo(String[] scanInfo) {
String[] concatScanInfo = new String[storedScanInfo.length + scanInfo.length];
System.arraycopy(storedScanInfo, 0, concatScanInfo, 0, storedScanInfo.length);
System.arraycopy(scanInfo, 0, concatScanInfo, storedScanInfo.length,
scanInfo.length);
storedScanInfo = concatScanInfo;
}
void reset() {
storedScanInfo = null;
}
}
public LegacyIncrementalScanBroadcastReceiver(Context context,
TelephonyScanManager.NetworkScanCallback mInternalNetworkScanCallback) {
mContext = context;
TelephonyManager tm =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
sPhoneCount = tm.getActiveModemCount();
mQueryDetails = new QueryDetails[sPhoneCount];
for (int i = 0; i < sPhoneCount; i++) {
mQueryDetails[i] = new QueryDetails();
}
mNetworkScanCallback = mInternalNetworkScanCallback;
}
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive " + intent);
if (ACTION_INCREMENTAL_NW_SCAN_IND.equals(intent.getAction())) {
broadcastIncrementalQueryResults(intent);
}
}
private void onResults(List<CellInfo> results) {
mNetworkScanCallback.onResults(results);
}
private void onComplete() {
mNetworkScanCallback.onComplete();
}
private void onError(int error) {
mNetworkScanCallback.onError(error);
}
private void broadcastIncrementalQueryResults(Intent intent) {
int result = intent.getIntExtra(EXTRA_SCAN_RESULT, QUERY_EXCEPTION);
int phoneId = intent.getIntExtra(EXTRA_INSTANCE_ID,
SubscriptionManager.INVALID_SIM_SLOT_INDEX);
Log.d(TAG, "broadcastIncrementalQueryResults: phoneid: " + phoneId + ", result: " + result);
if (phoneId < 0 || phoneId >= sPhoneCount) {
// Invalid phoneId
onError(NetworkScan.ERROR_INVALID_SCAN);
return;
}
if (result == NAS_QUERY_REJ_IN_RLF) {
onError(NetworkScan.ERROR_RADIO_INTERFACE_ERROR);
return;
}
if (result == NAS_QUERY_ABORT) {
onError(NetworkScan.ERROR_MODEM_ERROR);
return;
}
if (result == NAS_QUERY_COMPLETE || result == NAS_QUERY_PARTIAL) {
String[] scanInfo = intent.getStringArrayExtra(EXTRA_INCREMENTAL_SCAN_DATA);
QueryDetails queryDetails = mQueryDetails[phoneId];
Log.d(TAG, "broadcastIncrementalQueryResults"
+ ", scanInfo.length: " + (scanInfo == null ? 0 : scanInfo.length));
if (queryDetails.storedScanInfo != null && scanInfo != null) {
queryDetails.concatScanInfo(scanInfo);
} else {
queryDetails.storedScanInfo = scanInfo;
}
if (queryDetails.storedScanInfo != null) {
List<CellInfo> cellInfos = getCellInfosFromScanResult(queryDetails.storedScanInfo);
onResults(cellInfos);
}
if (result == NAS_QUERY_COMPLETE) {
// Clear the cache, otherwise the results for the next scan will be combined with
// the current one.
queryDetails.reset();
onComplete();
}
}
}
private List<CellInfo> getCellInfosFromScanResult(String[] scanInfos) {
Log.d(TAG, "Number of operators: " + (scanInfos.length)/4);
List<CellInfo> cellInfoList = new ArrayList<CellInfo>();
if (scanInfos.length >= 4 && (scanInfos.length % 4 == 0)) {
// The scan results are grouped into four elements per operator.
for (int i = 0; i < scanInfos.length / 4; i++) {
int j = 4 * i;
String operatorAlphaLong = scanInfos[0 + j];
String operatorAlphaShort = scanInfos[1 + j];
String operatorNumeric = scanInfos[2 + j];
String operatorStateString = scanInfos[3 + j];
OperatorInfo operatorInfo = new OperatorInfo(operatorAlphaLong,
operatorAlphaShort,
operatorNumeric,
operatorStateString);
CellInfo cellinfo =
CellInfoUtil.convertLegacyIncrScanOperatorInfoToCellInfo(operatorInfo);
Log.d(TAG, "OperatorInfo: " + operatorInfo.toString()
+ " CellInfo: " + CellInfoUtil.cellInfoToString(cellinfo));
cellInfoList.add(cellinfo);
}
}
return cellInfoList;
}
}