1 /* 2 * Copyright (C) 2023 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.imsphone; 18 19 import static android.telephony.CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA; 20 import static android.telephony.CarrierConfigManager.Ims.KEY_NR_SA_DISABLE_POLICY_INT; 21 import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_NONE; 22 import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED; 23 import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_WFC_ESTABLISHED; 24 import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED; 25 import static android.telephony.CarrierConfigManager.Ims.NrSaDisablePolicy; 26 import static android.telephony.CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY; 27 import static android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech; 28 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN; 29 30 import static com.android.internal.telephony.CommandsInterface.IMS_MMTEL_CAPABILITY_VOICE; 31 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.content.Context; 35 import android.os.AsyncResult; 36 import android.os.Handler; 37 import android.os.Looper; 38 import android.os.Message; 39 import android.os.PersistableBundle; 40 import android.telephony.CarrierConfigManager; 41 import android.util.Log; 42 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.internal.telephony.Call; 45 46 import java.util.Arrays; 47 import java.util.Set; 48 49 /** 50 * Enables or Disables NR-SA mode temporarily under certain conditions where WFC is established or 51 * IMS is registered over WiFi in order to improve the delay or voice mute issue when the handover 52 * from ePDG to NR is not supported in UE or network. 53 */ 54 public class ImsNrSaModeHandler extends Handler { 55 56 public static final String TAG = "ImsNrSaModeHandler"; 57 58 private static final int MSG_PRECISE_CALL_STATE_CHANGED = 101; 59 private static final int MSG_RESULT_IS_VONR_ENABLED = 102; 60 61 private final @NonNull ImsPhone mPhone; 62 private @Nullable CarrierConfigManager mCarrierConfigManager; 63 64 @FunctionalInterface 65 public interface N1ModeSetter { 66 /** Override-able for testing */ setN1ModeEnabled(boolean enabled, @Nullable Message message)67 void setN1ModeEnabled(boolean enabled, @Nullable Message message); 68 } 69 70 private N1ModeSetter mN1ModeSetter; 71 72 private @NrSaDisablePolicy int mNrSaDisablePolicy; 73 private boolean mIsNrSaDisabledForWfc; 74 private boolean mIsWifiRegistered; 75 private boolean mIsInImsCall; 76 private boolean mIsNrSaSupported; 77 private boolean mIsVoiceCapable; 78 79 private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = 80 (slotIndex, subId, carrierId, specificCarrierId) -> setNrSaDisablePolicy(subId); 81 ImsNrSaModeHandler(@onNull ImsPhone phone, Looper looper)82 public ImsNrSaModeHandler(@NonNull ImsPhone phone, Looper looper) { 83 super(looper); 84 85 mPhone = phone; 86 mN1ModeSetter = mPhone.getDefaultPhone()::setN1ModeEnabled; 87 88 mCarrierConfigManager = (CarrierConfigManager) mPhone.getContext() 89 .getSystemService(Context.CARRIER_CONFIG_SERVICE); 90 91 registerForCarrierConfigChanges(); 92 } 93 94 /** 95 * Performs any cleanup required before the ImsNrSaModeHandler is destroyed. 96 */ tearDown()97 public void tearDown() { 98 unregisterForCarrierConfigChanges(); 99 unregisterForPreciseCallStateChanges(); 100 101 if (isNrSaDisabledForWfc()) { 102 setNrSaMode(true); 103 } 104 } 105 106 /** 107 * Based on changed VoWiFi reg state and call state, handles NR SA mode if needed. 108 * It is including handover case. 109 * 110 * @param imsRadioTech The current registered RAT. 111 */ onImsRegistered( @msRegistrationTech int imsRadioTech, @NonNull Set<String> featureTags)112 public void onImsRegistered( 113 @ImsRegistrationTech int imsRadioTech, @NonNull Set<String> featureTags) { 114 if (!mIsNrSaSupported || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE) { 115 return; 116 } 117 118 Log.d(TAG, "onImsRegistered: ImsRegistrationTech = " + imsRadioTech); 119 120 final boolean isNewWifiRegistered = imsRadioTech == REGISTRATION_TECH_IWLAN; 121 if (isWifiRegistered() != isNewWifiRegistered) { 122 setWifiRegStatus(isNewWifiRegistered); 123 calculateAndControlNrSaIfNeeded(); 124 } 125 } 126 127 /** 128 * Based on changed VoWiFi reg state and call state, handles NR SA mode if needed. 129 * 130 * @param imsRadioTech The current un-registered RAT. 131 */ onImsUnregistered( @msRegistrationTech int imsRadioTech)132 public void onImsUnregistered( 133 @ImsRegistrationTech int imsRadioTech) { 134 if (!mIsNrSaSupported || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE 135 || imsRadioTech != REGISTRATION_TECH_IWLAN || !isWifiRegistered()) { 136 return; 137 } 138 139 Log.d(TAG, "onImsUnregistered : ImsRegistrationTech = " + imsRadioTech); 140 141 setWifiRegStatus(false); 142 calculateAndControlNrSaIfNeeded(); 143 } 144 145 /** 146 * Based on changed precise call state and VoWiFi reg state, handles NR SA mode if needed. 147 */ onPreciseCallStateChanged()148 public void onPreciseCallStateChanged() { 149 Log.d(TAG, "onPreciseCallStateChanged : foreground state = " 150 + mPhone.getForegroundCall().getState() + ", background state = " 151 + mPhone.getBackgroundCall().getState()); 152 153 boolean isImsCallStatusChanged = false; 154 155 if (isImsCallJustEstablished()) { 156 setImsCallStatus(true); 157 isImsCallStatusChanged = true; 158 } else if (isImsCallJustTerminated()) { 159 setImsCallStatus(false); 160 isImsCallStatusChanged = true; 161 } 162 163 if (isWifiRegistered() && isImsCallStatusChanged) { 164 calculateAndControlNrSaIfNeeded(); 165 } 166 } 167 168 /** 169 * Updates Capability. 170 */ updateImsCapability(int capabilities)171 public void updateImsCapability(int capabilities) { 172 if (!mIsNrSaSupported || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE) { 173 return; 174 } 175 176 boolean isVoiceCapable = (IMS_MMTEL_CAPABILITY_VOICE & capabilities) != 0; 177 if (mIsVoiceCapable != isVoiceCapable) { 178 mIsVoiceCapable = isVoiceCapable; 179 calculateAndControlNrSaIfNeeded(); 180 } 181 } 182 183 @Override handleMessage(Message msg)184 public void handleMessage(Message msg) { 185 AsyncResult ar; 186 187 switch (msg.what) { 188 case MSG_PRECISE_CALL_STATE_CHANGED : 189 onPreciseCallStateChanged(); 190 break; 191 case MSG_RESULT_IS_VONR_ENABLED : 192 ar = (AsyncResult) msg.obj; 193 194 if (ar.result != null) { 195 boolean vonrEnabled = ((Boolean) ar.result).booleanValue(); 196 197 Log.d(TAG, "result isVoNrEnabled = " + vonrEnabled); 198 if (isWifiCallingOngoing() && !vonrEnabled) { 199 // If still WiFi calling is ongoing and VoNR is disabled, disable NR SA. 200 setNrSaMode(false); 201 } 202 } 203 204 ar = null; 205 break; 206 default : 207 break; 208 } 209 } 210 211 /** 212 * Registers for precise call state changes. 213 */ registerForPreciseCallStateChanges()214 private void registerForPreciseCallStateChanges() { 215 mPhone.registerForPreciseCallStateChanged(this, MSG_PRECISE_CALL_STATE_CHANGED, null); 216 } 217 218 /** 219 * Unregisters for precise call state changes. 220 */ unregisterForPreciseCallStateChanges()221 private void unregisterForPreciseCallStateChanges() { 222 mPhone.unregisterForPreciseCallStateChanged(this); 223 } 224 225 /** 226 * Registers for carrier config changes. 227 */ registerForCarrierConfigChanges()228 private void registerForCarrierConfigChanges() { 229 if (mCarrierConfigManager != null) { 230 mCarrierConfigManager.registerCarrierConfigChangeListener( 231 this::post, mCarrierConfigChangeListener); 232 } 233 } 234 235 /** 236 * Unregisters for carrier config changes. 237 */ unregisterForCarrierConfigChanges()238 private void unregisterForCarrierConfigChanges() { 239 if (mCarrierConfigManager != null) { 240 mCarrierConfigManager.unregisterCarrierConfigChangeListener( 241 mCarrierConfigChangeListener); 242 } 243 } 244 setNrSaDisablePolicy(int subId)245 private void setNrSaDisablePolicy(int subId) { 246 if (mPhone.getSubId() == subId && mCarrierConfigManager != null) { 247 PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(), 248 KEY_NR_SA_DISABLE_POLICY_INT, KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); 249 int[] nrAvailabilities = bundle.getIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); 250 mIsNrSaSupported = nrAvailabilities != null 251 && Arrays.stream(nrAvailabilities).anyMatch( 252 value -> value == CARRIER_NR_AVAILABILITY_SA); 253 254 if (!mIsNrSaSupported) { 255 return; 256 } 257 258 mNrSaDisablePolicy = bundle.getInt(KEY_NR_SA_DISABLE_POLICY_INT); 259 260 Log.d(TAG, "setNrSaDisablePolicy : NrSaDisablePolicy = " + mNrSaDisablePolicy); 261 262 if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED 263 || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED) { 264 registerForPreciseCallStateChanges(); 265 } else { 266 unregisterForPreciseCallStateChanges(); 267 } 268 } 269 } 270 271 @VisibleForTesting setN1ModeSetter(N1ModeSetter setter)272 public void setN1ModeSetter(N1ModeSetter setter) { 273 mN1ModeSetter = setter; 274 } 275 setNrSaMode(boolean onOrOff)276 private void setNrSaMode(boolean onOrOff) { 277 mN1ModeSetter.setN1ModeEnabled(onOrOff, null); 278 Log.i(TAG, "setNrSaMode : " + onOrOff); 279 280 setNrSaDisabledForWfc(!onOrOff); 281 } 282 283 /** 284 * Sets WiFi reg status. 285 */ 286 @VisibleForTesting setWifiRegStatus(boolean registered)287 public void setWifiRegStatus(boolean registered) { 288 Log.d(TAG, "setWifiRegStatus : " + registered); 289 mIsWifiRegistered = registered; 290 } 291 292 /** 293 * Sets IMS call status 294 */ 295 @VisibleForTesting setImsCallStatus(boolean inImsCall)296 public void setImsCallStatus(boolean inImsCall) { 297 Log.d(TAG, "setImsCallStatus : " + inImsCall); 298 mIsInImsCall = inImsCall; 299 } 300 301 @VisibleForTesting isWifiRegistered()302 public boolean isWifiRegistered() { 303 return mIsWifiRegistered; 304 } 305 306 @VisibleForTesting isImsCallOngoing()307 public boolean isImsCallOngoing() { 308 return mIsInImsCall; 309 } 310 isNrSaDisabledForWfc()311 private boolean isNrSaDisabledForWfc() { 312 return mIsNrSaDisabledForWfc; 313 } 314 315 @VisibleForTesting setNrSaDisabledForWfc(boolean disabled)316 public void setNrSaDisabledForWfc(boolean disabled) { 317 mIsNrSaDisabledForWfc = disabled; 318 } 319 isImsCallJustEstablished()320 private boolean isImsCallJustEstablished() { 321 if (!isImsCallOngoing()) { 322 if ((mPhone.getForegroundCall().getState() == Call.State.ACTIVE) 323 || (mPhone.getBackgroundCall().getState() == Call.State.ACTIVE)) { 324 Log.d(TAG, "isImsCallJustEstablished"); 325 return true; 326 } 327 } 328 329 return false; 330 } 331 isImsCallJustTerminated()332 private boolean isImsCallJustTerminated() { 333 if (isImsCallOngoing() && (!mPhone.getForegroundCall().getState().isAlive() 334 && !mPhone.getBackgroundCall().getState().isAlive())) { 335 Log.d(TAG, "isImsCallJustTerminated"); 336 return true; 337 } 338 339 return false; 340 } 341 calculateAndControlNrSaIfNeeded()342 private void calculateAndControlNrSaIfNeeded() { 343 switch (mNrSaDisablePolicy) { 344 case NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED: 345 if (isNrSaDisabledForWfc() == isWifiRegisteredForVoice()) { 346 // NR SA is already disabled or condition is not met for disabling NR SA. 347 // So, no need for further action 348 return; 349 } 350 351 // Disable NR SA if VoWiFi registered otherwise enable 352 setNrSaMode(!isWifiRegisteredForVoice()); 353 return; 354 case NR_SA_DISABLE_POLICY_WFC_ESTABLISHED: 355 if (isNrSaDisabledForWfc() == isWifiCallingOngoing()) { 356 // NR SA is already disabled or condition is not met for disabling NR SA. 357 // So, no need for further action 358 return; 359 } 360 361 // Disable NR SA if VoWiFi call established otherwise enable 362 setNrSaMode(!isWifiCallingOngoing()); 363 return; 364 case NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED: 365 if (isNrSaDisabledForWfc() == isWifiCallingOngoing()) { 366 // NR SA is already disabled or condition is not met for disabling NR SA. 367 // So, no need for further action 368 return; 369 } 370 371 if (isWifiCallingOngoing()) { 372 // Query whether VoNR is enabled or not. 373 mPhone.getDefaultPhone().mCi.isVoNrEnabled( 374 obtainMessage(MSG_RESULT_IS_VONR_ENABLED), null); 375 return; 376 } 377 378 // Enable NR SA if there are no VoWiFi calls. 379 setNrSaMode(true); 380 return; 381 default: 382 break; 383 } 384 } 385 isWifiRegisteredForVoice()386 private boolean isWifiRegisteredForVoice() { 387 return isWifiRegistered() && mIsVoiceCapable; 388 } 389 isWifiCallingOngoing()390 private boolean isWifiCallingOngoing() { 391 return isWifiRegistered() && mIsVoiceCapable && isImsCallOngoing(); 392 } 393 } 394