• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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