1 /* 2 * Copyright (C) 2016 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 package com.android.internal.telephony; 17 18 import android.content.BroadcastReceiver; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.os.PersistableBundle; 23 import android.os.UserHandle; 24 import android.telephony.CarrierConfigManager; 25 import android.telephony.Rlog; 26 import android.telephony.ServiceState; 27 import android.util.SparseArray; 28 import android.util.SparseIntArray; 29 30 import java.util.Arrays; 31 32 /** 33 * This class loads configuration from CarrierConfig and uses it to determine 34 * what RATs are within a ratcheting family. For example all the HSPA/HSDPA/HSUPA RATs. 35 * Then, until reset the class will only ratchet upwards within the family (order 36 * determined by the CarrierConfig data). The ServiceStateTracker will reset this 37 * on cell-change. 38 */ 39 public class RatRatcheter { 40 private final static String LOG_TAG = "RilRatcheter"; 41 42 /** 43 * This is a map of RAT types -> RAT families for rapid lookup. 44 * The RAT families are defined by RAT type -> RAT Rank SparseIntArrays, so 45 * we can compare the priorities of two RAT types by comparing the values 46 * stored in the SparseIntArrays, higher values are higher priority. 47 */ 48 private final SparseArray<SparseIntArray> mRatFamilyMap = new SparseArray<>(); 49 50 private final Phone mPhone; 51 private boolean mVoiceRatchetEnabled = true; 52 private boolean mDataRatchetEnabled = true; 53 54 /** 55 * Updates the ServiceState with a new set of cell bandwidths IFF the new bandwidth list has a 56 * higher aggregate bandwidth. 57 * 58 * @return Whether the bandwidths were updated. 59 */ updateBandwidths(int[] bandwidths, ServiceState serviceState)60 public static boolean updateBandwidths(int[] bandwidths, ServiceState serviceState) { 61 if (bandwidths == null) { 62 return false; 63 } 64 65 int ssAggregateBandwidth = Arrays.stream(serviceState.getCellBandwidths()).sum(); 66 int newAggregateBandwidth = Arrays.stream(bandwidths).sum(); 67 68 if (newAggregateBandwidth > ssAggregateBandwidth) { 69 serviceState.setCellBandwidths(bandwidths); 70 return true; 71 } 72 73 return false; 74 } 75 76 /** Constructor */ RatRatcheter(Phone phone)77 public RatRatcheter(Phone phone) { 78 mPhone = phone; 79 80 IntentFilter intentFilter = new IntentFilter(); 81 intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 82 phone.getContext().registerReceiverAsUser(mConfigChangedReceiver, UserHandle.ALL, 83 intentFilter, null, null); 84 resetRatFamilyMap(); 85 } 86 ratchetRat(int oldRat, int newRat)87 private int ratchetRat(int oldRat, int newRat) { 88 synchronized (mRatFamilyMap) { 89 final SparseIntArray oldFamily = mRatFamilyMap.get(oldRat); 90 if (oldFamily == null) return newRat; 91 92 final SparseIntArray newFamily = mRatFamilyMap.get(newRat); 93 if (newFamily != oldFamily) return newRat; 94 95 // now go with the higher of the two 96 final int oldRatRank = newFamily.get(oldRat, -1); 97 final int newRatRank = newFamily.get(newRat, -1); 98 return (oldRatRank > newRatRank ? oldRat : newRat); 99 } 100 } 101 102 /** Ratchets RATs and cell bandwidths if oldSS and newSS have the same RAT family. */ ratchet(ServiceState oldSS, ServiceState newSS, boolean locationChange)103 public void ratchet(ServiceState oldSS, ServiceState newSS, boolean locationChange) { 104 if (!locationChange && isSameRatFamily(oldSS, newSS)) { 105 updateBandwidths(oldSS.getCellBandwidths(), newSS); 106 } 107 // temporarily disable rat ratchet on location change. 108 if (locationChange) { 109 mVoiceRatchetEnabled = false; 110 mDataRatchetEnabled = false; 111 return; 112 } 113 if (mVoiceRatchetEnabled) { 114 int newVoiceRat = ratchetRat(oldSS.getRilVoiceRadioTechnology(), 115 newSS.getRilVoiceRadioTechnology()); 116 newSS.setRilVoiceRadioTechnology(newVoiceRat); 117 } else if (oldSS.getRilVoiceRadioTechnology() != newSS.getRilVoiceRadioTechnology()) { 118 // resume rat ratchet on following rat change within the same location 119 mVoiceRatchetEnabled = true; 120 } 121 122 if (mDataRatchetEnabled) { 123 int newDataRat = ratchetRat(oldSS.getRilDataRadioTechnology(), 124 newSS.getRilDataRadioTechnology()); 125 newSS.setRilDataRadioTechnology(newDataRat); 126 } else if (oldSS.getRilDataRadioTechnology() != newSS.getRilDataRadioTechnology()) { 127 // resume rat ratchet on following rat change within the same location 128 mDataRatchetEnabled = true; 129 } 130 131 boolean newUsingCA = oldSS.isUsingCarrierAggregation() 132 || newSS.isUsingCarrierAggregation() 133 || newSS.getCellBandwidths().length > 1; 134 newSS.setIsUsingCarrierAggregation(newUsingCA); 135 } 136 isSameRatFamily(ServiceState ss1, ServiceState ss2)137 private boolean isSameRatFamily(ServiceState ss1, ServiceState ss2) { 138 synchronized (mRatFamilyMap) { 139 // Either the two technologies are the same or their families must be non-null 140 // and the same. 141 if (ss1.getRilDataRadioTechnology() == ss2.getRilDataRadioTechnology()) return true; 142 if (mRatFamilyMap.get(ss1.getRilDataRadioTechnology()) == null) return false; 143 return mRatFamilyMap.get(ss1.getRilDataRadioTechnology()) 144 == mRatFamilyMap.get(ss2.getRilDataRadioTechnology()); 145 } 146 } 147 148 private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() { 149 @Override 150 public void onReceive(Context context, Intent intent) { 151 final String action = intent.getAction(); 152 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { 153 resetRatFamilyMap(); 154 } 155 } 156 }; 157 resetRatFamilyMap()158 private void resetRatFamilyMap() { 159 synchronized(mRatFamilyMap) { 160 mRatFamilyMap.clear(); 161 162 final CarrierConfigManager configManager = (CarrierConfigManager) 163 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 164 if (configManager == null) return; 165 PersistableBundle b = configManager.getConfig(); 166 if (b == null) return; 167 168 // Reads an array of strings, eg: 169 // ["GPRS, EDGE", "EVDO, EVDO_A, EVDO_B", "HSPA, HSDPA, HSUPA, HSPAP"] 170 // Each string defines a family and the order of rats within the string express 171 // the priority of the RAT within the family (ie, we'd move up to later-listed RATs, but 172 // not down). 173 String[] ratFamilies = b.getStringArray(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES); 174 if (ratFamilies == null) return; 175 for (String ratFamily : ratFamilies) { 176 String[] rats = ratFamily.split(","); 177 if (rats.length < 2) continue; 178 SparseIntArray currentFamily = new SparseIntArray(rats.length); 179 int pos = 0; 180 for (String ratString : rats) { 181 int ratInt; 182 try { 183 ratInt = Integer.parseInt(ratString.trim()); 184 } catch (NumberFormatException e) { 185 Rlog.e(LOG_TAG, "NumberFormatException on " + ratString); 186 break; 187 } 188 if (mRatFamilyMap.get(ratInt) != null) { 189 Rlog.e(LOG_TAG, "RAT listed twice: " + ratString); 190 break; 191 } 192 currentFamily.put(ratInt, pos++); 193 mRatFamilyMap.put(ratInt, currentFamily); 194 } 195 } 196 } 197 } 198 } 199