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 */
16package com.android.internal.telephony;
17
18import android.content.BroadcastReceiver;
19import android.content.Context;
20import android.content.Intent;
21import android.content.IntentFilter;
22import android.os.PersistableBundle;
23import android.os.UserHandle;
24import android.telephony.CarrierConfigManager;
25import android.telephony.Rlog;
26import android.telephony.ServiceState;
27import android.util.SparseArray;
28import android.util.SparseIntArray;
29
30import 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 */
39public 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     */
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 */
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
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. */
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
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
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