1 /*
2  * Copyright (C) 2021 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;
18 
19 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_CHANGE;
20 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_POWER_UP;
21 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_IMS_ONLY;
22 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_NONE;
23 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_USER_CHANGE;
24 import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL;
25 import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT;
26 import static android.telephony.CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY;
27 import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW;
28 
29 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
30 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
31 
32 import android.annotation.Nullable;
33 import android.content.Context;
34 import android.content.SharedPreferences;
35 import android.os.AsyncResult;
36 import android.os.Handler;
37 import android.os.Message;
38 import android.os.PersistableBundle;
39 import android.telephony.CarrierConfigManager;
40 import android.telephony.ServiceState;
41 import android.telephony.SubscriptionManager;
42 
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.util.IndentingPrintWriter;
45 import com.android.telephony.Rlog;
46 
47 import java.io.PrintWriter;
48 
49 /**
50  * Controls the change of the user setting of the call waiting service
51  */
52 public class CallWaitingController extends Handler {
53 
54     public static final String LOG_TAG = "CallWaitingCtrl";
55     private static final boolean DBG = false; /* STOPSHIP if true */
56 
57     // Terminal-based call waiting is not supported. */
58     public static final int TERMINAL_BASED_NOT_SUPPORTED = -1;
59     // Terminal-based call waiting is supported but not activated. */
60     public static final int TERMINAL_BASED_NOT_ACTIVATED = 0;
61     // Terminal-based call waiting is supported and activated. */
62     public static final int TERMINAL_BASED_ACTIVATED = 1;
63 
64     private static final int EVENT_SET_CALL_WAITING_DONE = 1;
65     private static final int EVENT_GET_CALL_WAITING_DONE = 2;
66     private static final int EVENT_REGISTERED_TO_NETWORK = 3;
67 
68     // Class to pack mOnComplete object passed by the caller
69     private static class Cw {
70         final boolean mEnable;
71         final Message mOnComplete;
72         final boolean mImsRegistered;
73 
Cw(boolean enable, boolean imsRegistered, Message onComplete)74         Cw(boolean enable, boolean imsRegistered, Message onComplete) {
75             mEnable = enable;
76             mOnComplete = onComplete;
77             mImsRegistered = imsRegistered;
78         }
79     }
80 
81     @VisibleForTesting
82     public static final String PREFERENCE_TBCW = "terminal_based_call_waiting";
83     @VisibleForTesting
84     public static final String KEY_SUB_ID = "subId";
85     @VisibleForTesting
86     public static final String KEY_STATE = "state";
87     @VisibleForTesting
88     public static final String KEY_CS_SYNC = "cs_sync";
89 
90     private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
91             (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged(
92                     slotIndex);
93 
94     private boolean mSupportedByImsService = false;
95     private boolean mValidSubscription = false;
96 
97     // The user's last setting of terminal-based call waiting
98     private int mCallWaitingState = TERMINAL_BASED_NOT_SUPPORTED;
99 
100     private int mSyncPreference = CALL_WAITING_SYNC_NONE;
101     private int mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
102 
103     private boolean mCsEnabled = false;
104     private boolean mRegisteredForNetworkAttach = false;
105     private boolean mImsRegistered = false;
106 
107     private final GsmCdmaPhone mPhone;
108     private final ServiceStateTracker mSST;
109     private final Context mContext;
110 
111     // Constructors
CallWaitingController(GsmCdmaPhone phone)112     public CallWaitingController(GsmCdmaPhone phone) {
113         mPhone = phone;
114         mSST = phone.getServiceStateTracker();
115         mContext = phone.getContext();
116     }
117 
initialize()118     private void initialize() {
119         CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
120         if (ccm != null) {
121             // Callback directly handle carrier config change should be executed in handler thread
122             ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener);
123         } else {
124             loge("CarrierConfigLoader is not available.");
125         }
126 
127         int phoneId = mPhone.getPhoneId();
128         int subId = mPhone.getSubId();
129         SharedPreferences sp =
130                 mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
131         mLastSubId = sp.getInt(KEY_SUB_ID + phoneId, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
132         mCallWaitingState = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
133         mSyncPreference = sp.getInt(KEY_CS_SYNC + phoneId, CALL_WAITING_SYNC_NONE);
134 
135         logi("initialize phoneId=" + phoneId
136                 + ", lastSubId=" + mLastSubId + ", subId=" + subId
137                 + ", state=" + mCallWaitingState + ", sync=" + mSyncPreference
138                 + ", csEnabled=" + mCsEnabled);
139     }
140 
141     /**
142      * Returns the cached user setting.
143      *
144      * Possible values are
145      * {@link #TERMINAL_BASED_NOT_SUPPORTED},
146      * {@link #TERMINAL_BASED_NOT_ACTIVATED}, and
147      * {@link #TERMINAL_BASED_ACTIVATED}.
148      *
149      * @param forCsOnly indicates the caller expects the result for CS calls only
150      */
151     @VisibleForTesting
getTerminalBasedCallWaitingState(boolean forCsOnly)152     public synchronized int getTerminalBasedCallWaitingState(boolean forCsOnly) {
153         if (forCsOnly && (!mImsRegistered) && mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
154             return TERMINAL_BASED_NOT_SUPPORTED;
155         }
156         if (!mValidSubscription) return TERMINAL_BASED_NOT_SUPPORTED;
157         return mCallWaitingState;
158     }
159 
160     /**
161      * Serves the user's requests to interrogate the call waiting service
162      *
163      * @return true when terminal-based call waiting is supported, otherwise false
164      */
165     @VisibleForTesting
getCallWaiting(@ullable Message onComplete)166     public synchronized boolean getCallWaiting(@Nullable Message onComplete) {
167         if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false;
168 
169         logi("getCallWaiting " + mCallWaitingState);
170 
171         if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
172             // Interrogate CW in CS network
173             if (!mCsEnabled) {
174                 // skip interrogation if CS is not available and IMS is registered
175                 if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) {
176                     Cw cw = new Cw(false, isImsRegistered(), onComplete);
177                     Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw);
178                     mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp);
179                     return true;
180                 }
181             }
182         }
183 
184         if (mSyncPreference == CALL_WAITING_SYNC_NONE
185                 || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE
186                 || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
187                 || isSyncImsOnly()) {
188             sendGetCallWaitingResponse(onComplete);
189             return true;
190         } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE
191                 || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
192             Cw cw = new Cw(false, isImsRegistered(), onComplete);
193             Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw);
194             mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp);
195             return true;
196         }
197 
198         return false;
199     }
200 
201     /**
202      * Serves the user's requests to set the call waiting service
203      *
204      * @param serviceClass the target service class. Values are CommandsInterface.SERVICE_CLASS_*.
205      * @return true when terminal-based call waiting is supported, otherwise false
206      */
207     @VisibleForTesting
setCallWaiting(boolean enable, int serviceClass, @Nullable Message onComplete)208     public synchronized boolean setCallWaiting(boolean enable,
209             int serviceClass, @Nullable Message onComplete) {
210         if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false;
211 
212         if ((serviceClass & SERVICE_CLASS_VOICE) != SERVICE_CLASS_VOICE) return false;
213 
214         logi("setCallWaiting enable=" + enable + ", service=" + serviceClass);
215 
216         if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
217             // Enable CW in the CS network
218             if (!mCsEnabled && enable) {
219                 if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) {
220                     Cw cw = new Cw(true, isImsRegistered(), onComplete);
221                     Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw);
222                     mPhone.mCi.setCallWaiting(true, serviceClass, resp);
223                     return true;
224                 } else {
225                     // CS network is not available, however, IMS is registered.
226                     // Enabling the service in the CS network will be delayed.
227                     registerForNetworkAttached();
228                 }
229             }
230         }
231 
232         if (mSyncPreference == CALL_WAITING_SYNC_NONE
233                 || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE
234                 || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
235                 || isSyncImsOnly()) {
236             updateState(
237                     enable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
238 
239             sendToTarget(onComplete, null, null);
240             return true;
241         } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE
242                 || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
243             Cw cw = new Cw(enable, isImsRegistered(), onComplete);
244             Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw);
245             mPhone.mCi.setCallWaiting(enable, serviceClass, resp);
246             return true;
247         }
248 
249         return false;
250     }
251 
252     @Override
handleMessage(Message msg)253     public void handleMessage(Message msg) {
254         switch (msg.what) {
255             case EVENT_SET_CALL_WAITING_DONE:
256                 onSetCallWaitingDone((AsyncResult) msg.obj);
257                 break;
258             case EVENT_GET_CALL_WAITING_DONE:
259                 onGetCallWaitingDone((AsyncResult) msg.obj);
260                 break;
261             case EVENT_REGISTERED_TO_NETWORK:
262                 onRegisteredToNetwork();
263                 break;
264             default:
265                 break;
266         }
267     }
268 
onSetCallWaitingDone(AsyncResult ar)269     private synchronized void onSetCallWaitingDone(AsyncResult ar) {
270         if (ar.userObj == null) {
271             // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP
272             if (DBG) logd("onSetCallWaitingDone to sync on network attached");
273             if (ar.exception == null) {
274                 updateSyncState(true);
275             } else {
276                 loge("onSetCallWaitingDone e=" + ar.exception);
277             }
278             return;
279         }
280 
281         if (!(ar.userObj instanceof Cw)) {
282             // Unexpected state
283             if (DBG) logd("onSetCallWaitingDone unexpected result");
284             return;
285         }
286 
287         if (DBG) logd("onSetCallWaitingDone");
288         Cw cw = (Cw) ar.userObj;
289 
290         if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
291             // do not synchronize service state between CS and IMS
292             sendToTarget(cw.mOnComplete, ar.result, ar.exception);
293             return;
294         }
295 
296         if (ar.exception == null) {
297             if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
298                 // SYNC_FIRST_CHANGE implies cw.mEnable is true.
299                 updateSyncState(true);
300             }
301             updateState(
302                     cw.mEnable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
303         } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
304             if (cw.mImsRegistered) {
305                 // IMS is registered. Do not notify error.
306                 // SYNC_FIRST_CHANGE implies cw.mEnable is true.
307                 updateState(TERMINAL_BASED_ACTIVATED);
308                 sendToTarget(cw.mOnComplete, null, null);
309                 return;
310             }
311         }
312         sendToTarget(cw.mOnComplete, ar.result, ar.exception);
313     }
314 
onGetCallWaitingDone(AsyncResult ar)315     private synchronized void onGetCallWaitingDone(AsyncResult ar) {
316         if (ar.userObj == null) {
317             // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP
318             if (DBG) logd("onGetCallWaitingDone to sync on network attached");
319             boolean enabled = false;
320             if (ar.exception == null) {
321                 //resp[0]: 1 if enabled, 0 otherwise
322                 //resp[1]: bitwise ORs of SERVICE_CLASS_* constants
323                 int[] resp = (int[]) ar.result;
324                 if (resp != null && resp.length > 1) {
325                     enabled = (resp[0] == 1)
326                             && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE;
327                 } else {
328                     loge("onGetCallWaitingDone unexpected response");
329                 }
330             } else {
331                 loge("onGetCallWaitingDone e=" + ar.exception);
332             }
333             if (enabled) {
334                 updateSyncState(true);
335             } else {
336                 logi("onGetCallWaitingDone enabling CW service in CS network");
337                 mPhone.mCi.setCallWaiting(true, SERVICE_CLASS_VOICE,
338                         obtainMessage(EVENT_SET_CALL_WAITING_DONE));
339             }
340             unregisterForNetworkAttached();
341             return;
342         }
343 
344         if (!(ar.userObj instanceof Cw)) {
345             // Unexpected state
346             if (DBG) logd("onGetCallWaitingDone unexpected result");
347             return;
348         }
349 
350         if (DBG) logd("onGetCallWaitingDone");
351         Cw cw = (Cw) ar.userObj;
352 
353         if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
354             // do not synchronize service state between CS and IMS
355             sendToTarget(cw.mOnComplete, ar.result, ar.exception);
356             return;
357         }
358 
359         if (ar.exception == null) {
360             int[] resp = (int[]) ar.result;
361             //resp[0]: 1 if enabled, 0 otherwise
362             //resp[1]: bitwise ORs of SERVICE_CLASS_
363             if (resp == null || resp.length < 2) {
364                 logi("onGetCallWaitingDone unexpected response");
365                 if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
366                     // no exception but unexpected response, local setting is preferred.
367                     sendGetCallWaitingResponse(cw.mOnComplete);
368                 } else {
369                     sendToTarget(cw.mOnComplete, ar.result, ar.exception);
370                 }
371                 return;
372             }
373 
374             boolean enabled = resp[0] == 1
375                     && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE;
376 
377             if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
378                 updateSyncState(enabled);
379 
380                 if (!enabled && !cw.mImsRegistered) {
381                     // IMS is not registered, change the local setting
382                     logi("onGetCallWaitingDone CW in CS network is disabled.");
383                     updateState(TERMINAL_BASED_NOT_ACTIVATED);
384                 }
385 
386                 // return the user setting saved
387                 sendGetCallWaitingResponse(cw.mOnComplete);
388                 return;
389             }
390             updateState(enabled ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
391         } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
392             // Got an exception
393             if (cw.mImsRegistered) {
394                 // queryCallWaiting failed. However, IMS is registered. Do not notify error.
395                 // return the user setting saved
396                 logi("onGetCallWaitingDone get an exception, but IMS is registered");
397                 sendGetCallWaitingResponse(cw.mOnComplete);
398                 return;
399             }
400         }
401         sendToTarget(cw.mOnComplete, ar.result, ar.exception);
402     }
403 
sendToTarget(Message onComplete, Object result, Throwable exception)404     private void sendToTarget(Message onComplete, Object result, Throwable exception) {
405         if (onComplete != null) {
406             AsyncResult.forMessage(onComplete, result, exception);
407             onComplete.sendToTarget();
408         }
409     }
410 
sendGetCallWaitingResponse(Message onComplete)411     private void sendGetCallWaitingResponse(Message onComplete) {
412         if (onComplete != null) {
413             int serviceClass = SERVICE_CLASS_NONE;
414             if (mCallWaitingState == TERMINAL_BASED_ACTIVATED) {
415                 serviceClass = SERVICE_CLASS_VOICE;
416             }
417             sendToTarget(onComplete, new int[] { mCallWaitingState, serviceClass }, null);
418         }
419     }
420 
onRegisteredToNetwork()421     private synchronized void onRegisteredToNetwork() {
422         if (mCsEnabled) return;
423 
424         if (DBG) logd("onRegisteredToNetwork");
425 
426         mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE,
427                 obtainMessage(EVENT_GET_CALL_WAITING_DONE));
428     }
429 
onCarrierConfigurationChanged(int slotIndex)430     private synchronized void onCarrierConfigurationChanged(int slotIndex) {
431         if (slotIndex != mPhone.getPhoneId()) return;
432 
433         int subId = mPhone.getSubId();
434         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
435             logi("onCarrierConfigChanged invalid subId=" + subId);
436 
437             mValidSubscription = false;
438             unregisterForNetworkAttached();
439             return;
440         }
441 
442         if (!updateCarrierConfig(subId, false /* ignoreSavedState */)) {
443             return;
444         }
445 
446         logi("onCarrierConfigChanged cs_enabled=" + mCsEnabled);
447 
448         if (mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP) {
449             if (!mCsEnabled) {
450                 registerForNetworkAttached();
451             }
452         }
453     }
454 
455     /**
456      * @param ignoreSavedState only used for test
457      * @return true when succeeded.
458      */
459     @VisibleForTesting
updateCarrierConfig(int subId, boolean ignoreSavedState)460     public boolean updateCarrierConfig(int subId, boolean ignoreSavedState) {
461         mValidSubscription = true;
462 
463         PersistableBundle b =
464                 CarrierConfigManager.getCarrierConfigSubset(
465                         mContext,
466                         subId,
467                         KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY,
468                         KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT,
469                         KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL);
470         if (b.isEmpty()) return false;
471 
472         boolean supportsTerminalBased = false;
473         int[] services = b.getIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY);
474         if (services != null) {
475             for (int service : services) {
476                 if (service == SUPPLEMENTARY_SERVICE_CW) {
477                     supportsTerminalBased = true;
478                 }
479             }
480         }
481         int syncPreference = b.getInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT,
482                 CALL_WAITING_SYNC_FIRST_CHANGE);
483         boolean activated = b.getBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL);
484         int defaultState = supportsTerminalBased
485                 ? (activated ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED)
486                 : TERMINAL_BASED_NOT_SUPPORTED;
487         int savedState = getSavedState(subId);
488 
489         if (DBG) {
490             logd("updateCarrierConfig phoneId=" + mPhone.getPhoneId()
491                     + ", subId=" + subId + ", support=" + supportsTerminalBased
492                     + ", sync=" + syncPreference + ", default=" + defaultState
493                     + ", savedState=" + savedState);
494         }
495 
496         int desiredState = savedState;
497 
498         if (ignoreSavedState) {
499             desiredState = defaultState;
500         } else if ((mLastSubId != subId)
501                 && (syncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
502                         || syncPreference == CALL_WAITING_SYNC_FIRST_CHANGE)) {
503             desiredState = defaultState;
504         } else {
505             if (defaultState == TERMINAL_BASED_NOT_SUPPORTED) {
506                 desiredState = TERMINAL_BASED_NOT_SUPPORTED;
507             } else if (savedState == TERMINAL_BASED_NOT_SUPPORTED) {
508                 desiredState = defaultState;
509             }
510         }
511 
512         updateState(desiredState, syncPreference, ignoreSavedState);
513         return true;
514     }
515 
updateState(int state)516     private void updateState(int state) {
517         updateState(state, mSyncPreference, false);
518     }
519 
updateState(int state, int syncPreference, boolean ignoreSavedState)520     private void updateState(int state, int syncPreference, boolean ignoreSavedState) {
521         int subId = mPhone.getSubId();
522 
523         if (mLastSubId == subId
524                 && mCallWaitingState == state
525                 && mSyncPreference == syncPreference
526                 && (!ignoreSavedState)) {
527             return;
528         }
529 
530         int phoneId = mPhone.getPhoneId();
531 
532         logi("updateState phoneId=" + phoneId
533                 + ", subId=" + subId + ", state=" + state
534                 + ", sync=" + syncPreference + ", ignoreSavedState=" + ignoreSavedState);
535 
536         SharedPreferences sp =
537                 mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
538 
539         SharedPreferences.Editor editor = sp.edit();
540         editor.putInt(KEY_SUB_ID + phoneId, subId);
541         editor.putInt(KEY_STATE + subId, state);
542         editor.putInt(KEY_CS_SYNC + phoneId, syncPreference);
543         editor.apply();
544 
545         mCallWaitingState = state;
546         mLastSubId = subId;
547         mSyncPreference = syncPreference;
548         if (mLastSubId != subId) {
549             mCsEnabled = false;
550         }
551 
552         mPhone.setTerminalBasedCallWaitingStatus(mCallWaitingState);
553     }
554 
getSavedState(int subId)555     private int getSavedState(int subId) {
556         SharedPreferences sp =
557                 mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
558         int state = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
559 
560         logi("getSavedState subId=" + subId + ", state=" + state);
561 
562         return state;
563     }
564 
updateSyncState(boolean enabled)565     private void updateSyncState(boolean enabled) {
566         int phoneId = mPhone.getPhoneId();
567 
568         logi("updateSyncState phoneId=" + phoneId + ", enabled=" + enabled);
569 
570         mCsEnabled = enabled;
571     }
572 
573     /**
574      * @return whether the service is enabled in the CS network
575      */
576     @VisibleForTesting
getSyncState()577     public boolean getSyncState() {
578         return mCsEnabled;
579     }
580 
isCircuitSwitchedNetworkAvailable()581     private boolean isCircuitSwitchedNetworkAvailable() {
582         logi("isCircuitSwitchedNetworkAvailable="
583                 + (mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE));
584         return mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE;
585     }
586 
isImsRegistered()587     private boolean isImsRegistered() {
588         logi("isImsRegistered " + mImsRegistered);
589         return mImsRegistered;
590     }
591 
592     /**
593      * Sets the registration state of IMS service.
594      */
setImsRegistrationState(boolean registered)595     public synchronized void setImsRegistrationState(boolean registered) {
596         logi("setImsRegistrationState prev=" + mImsRegistered
597                 + ", new=" + registered);
598         mImsRegistered = registered;
599     }
600 
registerForNetworkAttached()601     private void registerForNetworkAttached() {
602         logi("registerForNetworkAttached");
603         if (mRegisteredForNetworkAttach) return;
604 
605         mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
606         mRegisteredForNetworkAttach = true;
607     }
608 
unregisterForNetworkAttached()609     private void unregisterForNetworkAttached() {
610         logi("unregisterForNetworkAttached");
611         if (!mRegisteredForNetworkAttach) return;
612 
613         mSST.unregisterForNetworkAttached(this);
614         removeMessages(EVENT_REGISTERED_TO_NETWORK);
615         mRegisteredForNetworkAttach = false;
616     }
617 
618     /**
619      * Sets whether the device supports the terminal-based call waiting.
620      * Only for test
621      */
622     @VisibleForTesting
setTerminalBasedCallWaitingSupported(boolean supported)623     public synchronized void setTerminalBasedCallWaitingSupported(boolean supported) {
624         if (mSupportedByImsService == supported) return;
625 
626         logi("setTerminalBasedCallWaitingSupported " + supported);
627 
628         mSupportedByImsService = supported;
629 
630         if (supported) {
631             initialize();
632             onCarrierConfigurationChanged(mPhone.getPhoneId());
633         } else {
634             CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
635             if (ccm != null && mCarrierConfigChangeListener != null) {
636                 ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
637             }
638             updateState(TERMINAL_BASED_NOT_SUPPORTED);
639         }
640     }
641 
642     /**
643      * Notifies that the UE has attached to the network
644      * Only for test
645      */
646     @VisibleForTesting
notifyRegisteredToNetwork()647     public void notifyRegisteredToNetwork() {
648         sendEmptyMessage(EVENT_REGISTERED_TO_NETWORK);
649     }
650 
isSyncImsOnly()651     private boolean isSyncImsOnly() {
652         return (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY && mImsRegistered);
653     }
654 
655     /**
656      * Dump this instance into a readable format for dumpsys usage.
657      */
dump(PrintWriter printWriter)658     public void dump(PrintWriter printWriter) {
659         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
660         pw.increaseIndent();
661         pw.println("CallWaitingController:");
662         pw.println(" mSupportedByImsService=" + mSupportedByImsService);
663         pw.println(" mValidSubscription=" + mValidSubscription);
664         pw.println(" mCallWaitingState=" + mCallWaitingState);
665         pw.println(" mSyncPreference=" + mSyncPreference);
666         pw.println(" mLastSubId=" + mLastSubId);
667         pw.println(" mCsEnabled=" + mCsEnabled);
668         pw.println(" mRegisteredForNetworkAttach=" + mRegisteredForNetworkAttach);
669         pw.println(" mImsRegistered=" + mImsRegistered);
670         pw.decreaseIndent();
671     }
672 
loge(String msg)673     private void loge(String msg) {
674         Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
675     }
676 
logi(String msg)677     private void logi(String msg) {
678         Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
679     }
680 
logd(String msg)681     private void logd(String msg) {
682         Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
683     }
684 }
685