Add a Carrier-config specific ratcheter of RATs.
Only reports transitions to higher RATs within a given family.
Reset when we move to a new cell.
Avoids costly propogation of RAT flapping within the family,
for example every outgoing data pops us into a higher RAT and
idle time pops us back down.
bug:27360321
Change-Id: Ifd219eda701eb2c87a8b6798d919f71fab804f5d
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
new file mode 100644
index 0000000..b782bc8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -0,0 +1,127 @@
+/*
+ * 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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+
+/**
+ * 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;
+
+ /** Constructor */
+ public RatRatcheter(Phone phone) {
+ mPhone = phone;
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ phone.getContext().registerReceiverAsUser(mConfigChangedReceiver, UserHandle.ALL,
+ intentFilter, null, null);
+ resetRatFamilyMap();
+ }
+
+ public int ratchetRat(int oldRat, int newRat) {
+ synchronized (mRatFamilyMap) {
+ final SparseIntArray oldFamily = mRatFamilyMap.get(oldRat);
+ if (oldFamily == null) return newRat;
+
+ final SparseIntArray newFamily = mRatFamilyMap.get(newRat);
+ if (newFamily != oldFamily) return newRat;
+
+ // now go with the higher of the two
+ final int oldRatRank = newFamily.get(oldRat, -1);
+ final int newRatRank = newFamily.get(newRat, -1);
+ return (oldRatRank > newRatRank ? oldRat : newRat);
+ }
+ }
+
+ 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.getConfig();
+ 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 1470364..c5cf95c 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -274,6 +274,9 @@
private final SstSubscriptionsChangedListener mOnSubscriptionsChangedListener =
new SstSubscriptionsChangedListener();
+
+ private final RatRatcheter mRatRatcheter;
+
private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
public final AtomicInteger mPreviousSubId =
new AtomicInteger(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -484,13 +487,10 @@
private String mCurrentCarrier = null;
public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
- initOnce(phone, ci);
- updatePhoneType();
- }
-
- private void initOnce(GsmCdmaPhone phone, CommandsInterface ci) {
mPhone = phone;
mCi = ci;
+
+ mRatRatcheter = new RatRatcheter(mPhone);
mVoiceCapable = mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_voice_capable);
mUiccController = UiccController.getInstance();
@@ -540,6 +540,8 @@
mEventLog = new TelephonyEventLog(mPhone.getPhoneId());
mPhone.notifyOtaspChanged(OTASP_UNINITIALIZED);
+
+ updatePhoneType();
}
@VisibleForTesting
@@ -2541,6 +2543,17 @@
boolean hasVoiceRegStateChanged =
mSS.getVoiceRegState() != mNewSS.getVoiceRegState();
+ boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
+
+ // ratchet the new tech up through it's rat family but don't drop back down
+ // until cell change
+ if (hasLocationChanged == false) {
+ mNewSS.setRilVoiceRadioTechnology(mRatRatcheter.ratchetRat(
+ mSS.getRilVoiceRadioTechnology(), mNewSS.getRilVoiceRadioTechnology()));
+ mNewSS.setRilDataRadioTechnology(mRatRatcheter.ratchetRat(
+ mSS.getRilDataRadioTechnology(), mNewSS.getRilDataRadioTechnology()));
+ }
+
boolean hasRilVoiceRadioTechnologyChanged =
mSS.getRilVoiceRadioTechnology() != mNewSS.getRilVoiceRadioTechnology();
@@ -2557,7 +2570,6 @@
boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
- boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
TelephonyManager tm =
(TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);