1 /* 2 * Copyright (C) 2020 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 17 package com.android.internal.telephony.dataconnection; 18 19 import android.annotation.ElapsedRealtimeLong; 20 import android.annotation.NonNull; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.os.AsyncResult; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.os.PersistableBundle; 29 import android.telephony.AccessNetworkConstants; 30 import android.telephony.Annotation; 31 import android.telephony.Annotation.ApnType; 32 import android.telephony.CarrierConfigManager; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.data.ApnSetting; 35 import android.telephony.data.ThrottleStatus; 36 37 import com.android.internal.telephony.Phone; 38 import com.android.internal.telephony.RetryManager; 39 import com.android.telephony.Rlog; 40 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.concurrent.ConcurrentHashMap; 45 46 /** 47 * Data throttler tracks the throttling status of the data network and notifies registrants when 48 * there are changes. The throttler is per phone and per transport type. 49 */ 50 public class DataThrottler extends Handler { 51 private static final String TAG = DataThrottler.class.getSimpleName(); 52 53 private static final int EVENT_SET_RETRY_TIME = 1; 54 private static final int EVENT_CARRIER_CONFIG_CHANGED = 2; 55 private static final int EVENT_RESET = 3; 56 private static final int EVENT_AIRPLANE_MODE_CHANGED = 4; 57 private static final int EVENT_TRACING_AREA_CODE_CHANGED = 5; 58 59 private final Phone mPhone; 60 private final int mSlotIndex; 61 private final @AccessNetworkConstants.TransportType int mTransportType; 62 private boolean mResetWhenAreaCodeChanged = false; 63 64 /** 65 * Callbacks that report the apn throttle status. 66 */ 67 private final List<DataThrottler.Callback> mCallbacks = new ArrayList<>(); 68 69 /** 70 * Keeps track of detailed information of the throttle status that is meant to be 71 * reported to other components. 72 */ 73 private final Map<Integer, ThrottleStatus> mThrottleStatus = new ConcurrentHashMap<>(); 74 75 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 76 @Override 77 public void onReceive(Context context, Intent intent) { 78 if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { 79 if (mPhone.getPhoneId() == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 80 SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { 81 if (intent.getBooleanExtra( 82 CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) { 83 // Ignore the rebroadcast one to prevent multiple carrier config changed 84 // event during boot up. 85 return; 86 } 87 int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 88 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 89 if (SubscriptionManager.isValidSubscriptionId(subId)) { 90 sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED); 91 } 92 } 93 } 94 } 95 }; 96 DataThrottler(Phone phone, int transportType)97 public DataThrottler(Phone phone, int transportType) { 98 super(null, false); 99 mPhone = phone; 100 mSlotIndex = phone.getPhoneId(); 101 mTransportType = transportType; 102 103 IntentFilter filter = new IntentFilter(); 104 filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 105 mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone); 106 107 mPhone.getServiceStateTracker().registerForAirplaneModeChanged(this, 108 EVENT_AIRPLANE_MODE_CHANGED, null); 109 mPhone.getServiceStateTracker().registerForAreaCodeChanged(this, 110 EVENT_TRACING_AREA_CODE_CHANGED, null); 111 } 112 113 @Override handleMessage(Message msg)114 public void handleMessage(Message msg) { 115 AsyncResult ar; 116 switch (msg.what) { 117 case EVENT_SET_RETRY_TIME: 118 int apnTypes = msg.arg1; 119 int newRequestType = msg.arg2; 120 long retryElapsedTime = (long) msg.obj; 121 setRetryTimeInternal(apnTypes, retryElapsedTime, newRequestType); 122 break; 123 case EVENT_CARRIER_CONFIG_CHANGED: 124 onCarrierConfigChanged(); 125 break; 126 case EVENT_RESET: 127 resetInternal(); 128 break; 129 case EVENT_AIRPLANE_MODE_CHANGED: 130 ar = (AsyncResult) msg.obj; 131 if (!(Boolean) ar.result) { 132 resetInternal(); 133 } 134 break; 135 case EVENT_TRACING_AREA_CODE_CHANGED: 136 if (mResetWhenAreaCodeChanged) { 137 resetInternal(); 138 } 139 break; 140 } 141 } 142 143 @NonNull getCarrierConfig()144 private PersistableBundle getCarrierConfig() { 145 CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() 146 .getSystemService(Context.CARRIER_CONFIG_SERVICE); 147 if (configManager != null) { 148 // If an invalid subId is used, this bundle will contain default values. 149 PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId()); 150 if (config != null) { 151 return config; 152 } 153 } 154 // Return static default defined in CarrierConfigManager. 155 return CarrierConfigManager.getDefaultConfig(); 156 } 157 onCarrierConfigChanged()158 private void onCarrierConfigChanged() { 159 PersistableBundle config = getCarrierConfig(); 160 mResetWhenAreaCodeChanged = config.getBoolean( 161 CarrierConfigManager.KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false); 162 } 163 164 /** 165 * Set the retry time and handover failure mode for the give APN types. 166 * 167 * @param apnTypes APN types 168 * @param retryElapsedTime The elapsed time that data connection for APN types should not be 169 * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist. 170 * {@link RetryManager#NO_RETRY} indicates retry should never happen. 171 */ setRetryTime(@pnType int apnTypes, @ElapsedRealtimeLong long retryElapsedTime, @DcTracker.RequestNetworkType int newRequestType)172 public void setRetryTime(@ApnType int apnTypes, @ElapsedRealtimeLong long retryElapsedTime, 173 @DcTracker.RequestNetworkType int newRequestType) { 174 sendMessage(obtainMessage(EVENT_SET_RETRY_TIME, apnTypes, newRequestType, 175 retryElapsedTime)); 176 } 177 178 /** 179 * Set the retry time and handover failure mode for the give APN types. This is running on the 180 * handler thread. 181 * 182 * @param apnTypes APN types 183 * @param retryElapsedTime The elapsed time that data connection for APN types should not be 184 * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist. 185 * {@link RetryManager#NO_RETRY} indicates retry should never happen. 186 */ setRetryTimeInternal(@pnType int apnTypes, @ElapsedRealtimeLong long retryElapsedTime, @DcTracker.RequestNetworkType int newRequestType)187 private void setRetryTimeInternal(@ApnType int apnTypes, @ElapsedRealtimeLong 188 long retryElapsedTime, @DcTracker.RequestNetworkType int newRequestType) { 189 if (retryElapsedTime < 0) { 190 retryElapsedTime = RetryManager.NO_SUGGESTED_RETRY_DELAY; 191 } 192 193 List<ThrottleStatus> changedStatuses = new ArrayList<>(); 194 while (apnTypes != 0) { 195 196 //Extract the least significant bit. 197 int apnType = apnTypes & -apnTypes; 198 199 //Update the apn throttle status 200 ThrottleStatus newStatus = createStatus(apnType, retryElapsedTime, newRequestType); 201 202 ThrottleStatus oldStatus = mThrottleStatus.get(apnType); 203 204 //Check to see if there is a change that needs to be applied 205 if (!newStatus.equals(oldStatus)) { 206 //Mark as changed status 207 changedStatuses.add(newStatus); 208 209 //Put the new status in the temp space 210 mThrottleStatus.put(apnType, newStatus); 211 } 212 213 //Remove the least significant bit. 214 apnTypes &= apnTypes - 1; 215 } 216 217 if (changedStatuses.size() > 0) { 218 sendThrottleStatusChanged(changedStatuses); 219 } 220 } 221 222 /** 223 * Get the earliest retry time for the given APN type. The time is the system's elapse time. 224 * 225 * Note the DataThrottler is running phone process's main thread, which is most of the telephony 226 * components running on. Calling this method from other threads might run into race conditions. 227 * 228 * @param apnType APN type 229 * @return The earliest retry time for APN type. The time is the system's elapse time. 230 * {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates there is no throttling for given APN 231 * type, {@link RetryManager#NO_RETRY} indicates retry should never happen. 232 */ 233 @ElapsedRealtimeLong getRetryTime(@pnType int apnType)234 public long getRetryTime(@ApnType int apnType) { 235 // This is the workaround to handle the mistake that 236 // ApnSetting.TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI. 237 if (apnType == ApnSetting.TYPE_DEFAULT) { 238 apnType &= ~(ApnSetting.TYPE_HIPRI); 239 } 240 241 ThrottleStatus status = mThrottleStatus.get(apnType); 242 if (status != null) { 243 if (status.getThrottleType() == ThrottleStatus.THROTTLE_TYPE_NONE) { 244 return RetryManager.NO_SUGGESTED_RETRY_DELAY; 245 } else { 246 return status.getThrottleExpiryTimeMillis(); 247 } 248 } 249 return RetryManager.NO_SUGGESTED_RETRY_DELAY; 250 } 251 252 /** 253 * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}. 254 */ reset()255 public void reset() { 256 sendEmptyMessage(EVENT_RESET); 257 } 258 259 /** 260 * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}. 261 */ resetInternal()262 private void resetInternal() { 263 final List<Integer> apnTypes = new ArrayList<>(); 264 for (ThrottleStatus throttleStatus : mThrottleStatus.values()) { 265 apnTypes.add(throttleStatus.getApnType()); 266 } 267 268 for (int apnType : apnTypes) { 269 setRetryTime(apnType, RetryManager.NO_SUGGESTED_RETRY_DELAY, 270 DcTracker.REQUEST_TYPE_NORMAL); 271 } 272 } 273 createStatus(@nnotation.ApnType int apnType, long retryElapsedTime, @DcTracker.RequestNetworkType int newRequestType)274 private ThrottleStatus createStatus(@Annotation.ApnType int apnType, long retryElapsedTime, 275 @DcTracker.RequestNetworkType int newRequestType) { 276 ThrottleStatus.Builder builder = new ThrottleStatus.Builder(); 277 278 if (retryElapsedTime == RetryManager.NO_SUGGESTED_RETRY_DELAY) { 279 builder 280 .setNoThrottle() 281 .setRetryType(getRetryType(newRequestType)); 282 } else if (retryElapsedTime == RetryManager.NO_RETRY) { 283 builder 284 .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY) 285 .setRetryType(ThrottleStatus.RETRY_TYPE_NONE); 286 } else { 287 builder 288 .setThrottleExpiryTimeMillis(retryElapsedTime) 289 .setRetryType(getRetryType(newRequestType)); 290 } 291 return builder 292 .setSlotIndex(mSlotIndex) 293 .setTransportType(mTransportType) 294 .setApnType(apnType) 295 .build(); 296 } 297 getRetryType(@cTracker.RequestNetworkType int newRequestType)298 private static int getRetryType(@DcTracker.RequestNetworkType int newRequestType) { 299 if (newRequestType == DcTracker.REQUEST_TYPE_NORMAL) { 300 return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION; 301 } 302 303 if (newRequestType == DcTracker.REQUEST_TYPE_HANDOVER) { 304 return ThrottleStatus.RETRY_TYPE_HANDOVER; 305 } 306 307 loge("createStatus: Unknown requestType=" + newRequestType); 308 return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION; 309 } 310 sendThrottleStatusChanged(List<ThrottleStatus> statuses)311 private void sendThrottleStatusChanged(List<ThrottleStatus> statuses) { 312 synchronized (mCallbacks) { 313 for (int i = 0; i < mCallbacks.size(); i++) { 314 mCallbacks.get(i).onThrottleStatusChanged(statuses); 315 } 316 } 317 } 318 loge(String s)319 private static void loge(String s) { 320 Rlog.e(TAG, s); 321 } 322 323 /** 324 * Reports changes to apn throttle statuses. 325 * 326 * Note: All statuses are sent when first registered. 327 * 328 * @param callback status changes callback 329 */ registerForThrottleStatusChanges(DataThrottler.Callback callback)330 public void registerForThrottleStatusChanges(DataThrottler.Callback callback) { 331 synchronized (mCallbacks) { 332 //Only add if it's not there already 333 if (!mCallbacks.contains(callback)) { 334 //Report everything the first time 335 List<ThrottleStatus> throttleStatuses = 336 new ArrayList<>(mThrottleStatus.values()); 337 callback.onThrottleStatusChanged(throttleStatuses); 338 mCallbacks.add(callback); 339 } 340 } 341 } 342 343 /** 344 * Unregister the callback 345 * @param callback the callback to unregister 346 */ unregisterForThrottleStatusChanges(DataThrottler.Callback callback)347 public void unregisterForThrottleStatusChanges(DataThrottler.Callback callback) { 348 synchronized (mCallbacks) { 349 mCallbacks.remove(callback); 350 } 351 } 352 353 /** 354 * Callback for when throttle statuses change 355 */ 356 public interface Callback { 357 /** 358 * Called whenever the throttle status of an APN has changed. 359 * 360 * Note: Called with all statuses when first registered. 361 * 362 * @param throttleStatuses the status changes 363 */ onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses)364 void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses); 365 } 366 } 367