blob: 4d9cc3ef1d03ba936ac2d1fc3210fe6970004bd1 [file] [log] [blame]
/*
* Copyright (C) 2016 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.internal.telephony;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.telephony.Rlog;
import java.util.Arrays;
/**
* This class loads configuration from CarrierConfig and uses it to determine
* what RATs are within a ratcheting family. For example all the HSPA/HSDPA/HSUPA RATs.
* Then, until reset the class will only ratchet upwards within the family (order
* determined by the CarrierConfig data). The ServiceStateTracker will reset this
* on cell-change.
*/
public class RatRatcheter {
private final static String LOG_TAG = "RilRatcheter";
/**
* This is a map of RAT types -> RAT families for rapid lookup.
* The RAT families are defined by RAT type -> RAT Rank SparseIntArrays, so
* we can compare the priorities of two RAT types by comparing the values
* stored in the SparseIntArrays, higher values are higher priority.
*/
private final SparseArray<SparseIntArray> mRatFamilyMap = new SparseArray<>();
private final Phone mPhone;
private boolean mVoiceRatchetEnabled = true;
private boolean mDataRatchetEnabled = true;
/**
* Updates the ServiceState with a new set of cell bandwidths IFF the new bandwidth list has a
* higher aggregate bandwidth.
*
* @return Whether the bandwidths were updated.
*/
public static boolean updateBandwidths(int[] bandwidths, ServiceState serviceState) {
if (bandwidths == null) {
return false;
}
int ssAggregateBandwidth = Arrays.stream(serviceState.getCellBandwidths()).sum();
int newAggregateBandwidth = Arrays.stream(bandwidths).sum();
if (newAggregateBandwidth > ssAggregateBandwidth) {
serviceState.setCellBandwidths(bandwidths);
return true;
}
return false;
}
/** Constructor */
public RatRatcheter(Phone phone) {
mPhone = phone;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
try {
Context contextAsUser = phone.getContext().createPackageContextAsUser(
phone.getContext().getPackageName(), 0, UserHandle.ALL);
contextAsUser.registerReceiver(mConfigChangedReceiver,
intentFilter, null /* broadcastPermission */, null);
} catch (PackageManager.NameNotFoundException e) {
Rlog.e(LOG_TAG, "Package name not found: " + e.getMessage());
}
resetRatFamilyMap();
}
private @NetworkType int ratchetRat(@NetworkType int oldNetworkType,
@NetworkType int newNetworkType) {
int oldRat = ServiceState.networkTypeToRilRadioTechnology(oldNetworkType);
int newRat = ServiceState.networkTypeToRilRadioTechnology(newNetworkType);
synchronized (mRatFamilyMap) {
final SparseIntArray oldFamily = mRatFamilyMap.get(oldRat);
if (oldFamily == null) {
return newNetworkType;
}
final SparseIntArray newFamily = mRatFamilyMap.get(newRat);
if (newFamily != oldFamily) {
return newNetworkType;
}
// now go with the higher of the two
final int oldRatRank = newFamily.get(oldRat, -1);
final int newRatRank = newFamily.get(newRat, -1);
return ServiceState.rilRadioTechnologyToNetworkType(
oldRatRank > newRatRank ? oldRat : newRat);
}
}
/** Ratchets RATs and cell bandwidths if oldSS and newSS have the same RAT family. */
public void ratchet(@NonNull ServiceState oldSS, @NonNull ServiceState newSS,
boolean locationChange) {
// temporarily disable rat ratchet on location change.
if (locationChange) {
mVoiceRatchetEnabled = false;
mDataRatchetEnabled = false;
return;
}
// Different rat family, don't need rat ratchet and update cell bandwidths.
if (!isSameRatFamily(oldSS, newSS)) {
return;
}
updateBandwidths(oldSS.getCellBandwidths(), newSS);
boolean newUsingCA = oldSS.isUsingCarrierAggregation()
|| newSS.isUsingCarrierAggregation()
|| newSS.getCellBandwidths().length > 1;
NetworkRegistrationInfo oldCsNri = oldSS.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
NetworkRegistrationInfo newCsNri = newSS.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
if (mVoiceRatchetEnabled) {
int newPsNetworkType = ratchetRat(oldCsNri.getAccessNetworkTechnology(),
newCsNri.getAccessNetworkTechnology());
newCsNri.setAccessNetworkTechnology(newPsNetworkType);
newSS.addNetworkRegistrationInfo(newCsNri);
} else if (oldCsNri.getAccessNetworkTechnology() != oldCsNri.getAccessNetworkTechnology()) {
// resume rat ratchet on following rat change within the same location
mVoiceRatchetEnabled = true;
}
NetworkRegistrationInfo oldPsNri = oldSS.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
NetworkRegistrationInfo newPsNri = newSS.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
if (mDataRatchetEnabled) {
int newPsNetworkType = ratchetRat(oldPsNri.getAccessNetworkTechnology(),
newPsNri.getAccessNetworkTechnology());
newPsNri.setAccessNetworkTechnology(newPsNetworkType);
newSS.addNetworkRegistrationInfo(newPsNri);
} else if (oldPsNri.getAccessNetworkTechnology() != newPsNri.getAccessNetworkTechnology()) {
// resume rat ratchet on following rat change within the same location
mDataRatchetEnabled = true;
}
newSS.setIsUsingCarrierAggregation(newUsingCA);
}
private boolean isSameRatFamily(ServiceState ss1, ServiceState ss2) {
synchronized (mRatFamilyMap) {
// Either the two technologies are the same or their families must be non-null
// and the same.
int dataRat1 = ServiceState.networkTypeToRilRadioTechnology(
ss1.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
.getAccessNetworkTechnology());
int dataRat2 = ServiceState.networkTypeToRilRadioTechnology(
ss2.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
.getAccessNetworkTechnology());
// The api getAccessNetworkTechnology@NetworkRegistrationInfo always returns LTE though
// data rat is LTE CA. Because it uses mIsUsingCarrierAggregation to indicate whether
// it is LTE CA or not. However, we need its actual data rat to check if they are the
// same family. So convert it to LTE CA.
if (dataRat1 == ServiceState.RIL_RADIO_TECHNOLOGY_LTE
&& ss1.isUsingCarrierAggregation()) {
dataRat1 = ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
}
if (dataRat2 == ServiceState.RIL_RADIO_TECHNOLOGY_LTE
&& ss2.isUsingCarrierAggregation()) {
dataRat2 = ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
}
if (dataRat1 == dataRat2) return true;
if (mRatFamilyMap.get(dataRat1) == null) {
return false;
}
return mRatFamilyMap.get(dataRat1) == mRatFamilyMap.get(dataRat2);
}
}
private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
resetRatFamilyMap();
}
}
};
private void resetRatFamilyMap() {
synchronized(mRatFamilyMap) {
mRatFamilyMap.clear();
final CarrierConfigManager configManager = (CarrierConfigManager)
mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager == null) return;
PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
if (b == null) return;
// Reads an array of strings, eg:
// ["GPRS, EDGE", "EVDO, EVDO_A, EVDO_B", "HSPA, HSDPA, HSUPA, HSPAP"]
// Each string defines a family and the order of rats within the string express
// the priority of the RAT within the family (ie, we'd move up to later-listed RATs, but
// not down).
String[] ratFamilies = b.getStringArray(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES);
if (ratFamilies == null) return;
for (String ratFamily : ratFamilies) {
String[] rats = ratFamily.split(",");
if (rats.length < 2) continue;
SparseIntArray currentFamily = new SparseIntArray(rats.length);
int pos = 0;
for (String ratString : rats) {
int ratInt;
try {
ratInt = Integer.parseInt(ratString.trim());
} catch (NumberFormatException e) {
Rlog.e(LOG_TAG, "NumberFormatException on " + ratString);
break;
}
if (mRatFamilyMap.get(ratInt) != null) {
Rlog.e(LOG_TAG, "RAT listed twice: " + ratString);
break;
}
currentFamily.put(ratInt, pos++);
mRatFamilyMap.put(ratInt, currentFamily);
}
}
}
}
}