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