• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.phone;
18 
19 import static android.telephony.ims.ImsRcsManager.CAPABILITY_TYPE_OPTIONS_UCE;
20 import static android.telephony.ims.ImsRcsManager.CAPABILITY_TYPE_PRESENCE_UCE;
21 import static android.telephony.ims.ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
22 import static android.telephony.ims.ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
23 import static android.telephony.ims.ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
24 import static android.telephony.ims.ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
25 import static android.telephony.ims.ProvisioningManager.PROVISIONING_VALUE_DISABLED;
26 import static android.telephony.ims.ProvisioningManager.PROVISIONING_VALUE_ENABLED;
27 import static android.telephony.ims.feature.ImsFeature.FEATURE_MMTEL;
28 import static android.telephony.ims.feature.ImsFeature.FEATURE_RCS;
29 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER;
30 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
31 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
32 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
33 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
34 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
35 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
36 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
37 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_MAX;
38 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
39 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NR;
40 
41 import android.annotation.Nullable;
42 import android.content.Context;
43 import android.os.AsyncResult;
44 import android.os.Binder;
45 import android.os.Handler;
46 import android.os.HandlerThread;
47 import android.os.Looper;
48 import android.os.Message;
49 import android.os.PersistableBundle;
50 import android.os.RemoteCallbackList;
51 import android.os.RemoteException;
52 import android.telephony.CarrierConfigManager;
53 import android.telephony.CarrierConfigManager.Ims;
54 import android.telephony.SubscriptionManager;
55 import android.telephony.TelephonyRegistryManager;
56 import android.telephony.ims.ProvisioningManager;
57 import android.telephony.ims.aidl.IFeatureProvisioningCallback;
58 import android.telephony.ims.aidl.IImsConfig;
59 import android.telephony.ims.aidl.IImsConfigCallback;
60 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
61 import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
62 import android.telephony.ims.stub.ImsConfigImplBase;
63 import android.telephony.ims.stub.ImsRegistrationImplBase;
64 import android.util.SparseArray;
65 
66 import com.android.ims.FeatureConnector;
67 import com.android.ims.ImsConfig;
68 import com.android.ims.ImsException;
69 import com.android.ims.ImsManager;
70 import com.android.ims.RcsFeatureManager;
71 import com.android.internal.annotations.VisibleForTesting;
72 import com.android.internal.telephony.PhoneConfigurationManager;
73 import com.android.internal.telephony.util.HandlerExecutor;
74 import com.android.telephony.Rlog;
75 
76 import java.util.Arrays;
77 import java.util.Map;
78 import java.util.concurrent.Executor;
79 
80 /**
81  * Provides APIs for MMTEL and RCS provisioning status. This class handles provisioning status and
82  * notifies the status changing for each capability
83  * {{@link MmTelCapabilities.MmTelCapability} for MMTel services}
84  * {{@link RcsImsCapabilities.RcsImsCapabilityFlag} for RCS services}
85  */
86 public class ImsProvisioningController {
87     private static final String TAG = "ImsProvisioningController";
88     private static final int INVALID_VALUE = -1;
89 
90     private static final int EVENT_SUB_CHANGED = 1;
91     private static final int EVENT_PROVISIONING_CAPABILITY_CHANGED = 2;
92     @VisibleForTesting
93     protected static final int EVENT_MULTI_SIM_CONFIGURATION_CHANGE = 3;
94     private static final int EVENT_PROVISIONING_VALUE_CHANGED = 4;
95 
96     // Provisioning Keys that are handled via AOSP cache and not sent to the ImsService
97     private static final int[] LOCAL_IMS_CONFIG_KEYS = {
98             KEY_VOLTE_PROVISIONING_STATUS,
99             KEY_VT_PROVISIONING_STATUS,
100             KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE,
101             KEY_EAB_PROVISIONING_STATUS
102     };
103     private static final int[] LOCAL_RADIO_TECHS = {
104             REGISTRATION_TECH_LTE,
105             REGISTRATION_TECH_IWLAN,
106             REGISTRATION_TECH_CROSS_SIM,
107             REGISTRATION_TECH_NR
108     };
109 
110     private static final int MMTEL_CAPABILITY_MIN = MmTelCapabilities.CAPABILITY_TYPE_NONE;
111     private static final int MMTEL_CAPABILITY_MAX = MmTelCapabilities.CAPABILITY_TYPE_MAX;
112 
113     private static final int RCS_CAPABILITY_MIN = RcsImsCapabilities.CAPABILITY_TYPE_NONE;
114     private static final int RCS_CAPABILITY_MAX = RcsImsCapabilities.CAPABILITY_TYPE_MAX;
115 
116     private static final int[] LOCAL_MMTEL_CAPABILITY = {
117             CAPABILITY_TYPE_VOICE,
118             CAPABILITY_TYPE_VIDEO,
119             CAPABILITY_TYPE_UT,
120             CAPABILITY_TYPE_SMS,
121             CAPABILITY_TYPE_CALL_COMPOSER
122     };
123 
124     /**
125      * map the MmTelCapabilities.MmTelCapability and
126      * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VOICE_INT
127      * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VIDEO_INT
128      * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_UT_INT
129      * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_SMS_INT
130      * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT
131      */
132     private static final Map<Integer, String> KEYS_MMTEL_CAPABILITY = Map.of(
133             CAPABILITY_TYPE_VOICE, Ims.KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY,
134             CAPABILITY_TYPE_VIDEO, Ims.KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY,
135             CAPABILITY_TYPE_UT, Ims.KEY_CAPABILITY_TYPE_UT_INT_ARRAY,
136             CAPABILITY_TYPE_SMS, Ims.KEY_CAPABILITY_TYPE_SMS_INT_ARRAY,
137             CAPABILITY_TYPE_CALL_COMPOSER, Ims.KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY
138     );
139 
140     /**
141      * map the RcsImsCapabilities.RcsImsCapabilityFlag and
142      * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_OPTIONS_UCE
143      * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE
144      */
145     private static final Map<Integer, String> KEYS_RCS_CAPABILITY = Map.of(
146             CAPABILITY_TYPE_OPTIONS_UCE, Ims.KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY,
147             CAPABILITY_TYPE_PRESENCE_UCE, Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY
148     );
149 
150     /**
151      * Create a FeatureConnector for this class to use to connect to an ImsManager.
152      */
153     @VisibleForTesting
154     public interface MmTelFeatureConnector {
155         /**
156          * Create a FeatureConnector for this class to use to connect to an ImsManager.
157          * @param listener will receive ImsManager instance.
158          * @param executor that the Listener callbacks will be called on.
159          * @return A FeatureConnector
160          */
create(Context context, int slotId, String logPrefix, FeatureConnector.Listener<ImsManager> listener, Executor executor)161         FeatureConnector<ImsManager> create(Context context, int slotId,
162                 String logPrefix, FeatureConnector.Listener<ImsManager> listener,
163                 Executor executor);
164     }
165 
166     /**
167      * Create a FeatureConnector for this class to use to connect to an RcsFeatureManager.
168      */
169     @VisibleForTesting
170     public interface RcsFeatureConnector {
171         /**
172          * Create a FeatureConnector for this class to use to connect to an RcsFeatureManager.
173          * @param listener will receive RcsFeatureManager instance.
174          * @param executor that the Listener callbacks will be called on.
175          * @return A FeatureConnector
176          */
create(Context context, int slotId, FeatureConnector.Listener<RcsFeatureManager> listener, Executor executor, String logPrefix)177         FeatureConnector<RcsFeatureManager> create(Context context, int slotId,
178                 FeatureConnector.Listener<RcsFeatureManager> listener,
179                 Executor executor, String logPrefix);
180     }
181 
182     private static ImsProvisioningController sInstance;
183 
184     private final PhoneGlobals mApp;
185     private final Handler mHandler;
186     private final CarrierConfigManager mCarrierConfigManager;
187     private final SubscriptionManager mSubscriptionManager;
188     private final TelephonyRegistryManager mTelephonyRegistryManager;
189     private final MmTelFeatureConnector mMmTelFeatureConnector;
190     private final RcsFeatureConnector mRcsFeatureConnector;
191 
192     // maps a slotId to a list of MmTelFeatureListeners
193     private final SparseArray<MmTelFeatureListener> mMmTelFeatureListenersSlotMap =
194             new SparseArray<>();
195     // maps a slotId to a list of RcsFeatureListeners
196     private final SparseArray<RcsFeatureListener> mRcsFeatureListenersSlotMap =
197             new SparseArray<>();
198     // map a slotId to a list of ProvisioningCallbackManager
199     private final SparseArray<ProvisioningCallbackManager> mProvisioningCallbackManagersSlotMap =
200             new SparseArray<>();
201     private final ImsProvisioningLoader mImsProvisioningLoader;
202 
203     private int mNumSlot;
204 
205     /**
206      * This class contains the provisioning status to notify changes.
207      * {{@link MmTelCapabilities.MmTelCapability} for MMTel services}
208      * {{@link android.telephony.ims.ImsRcsManager.RcsImsCapabilityFlag} for RCS services}
209      * {{@link ImsRegistrationImplBase.ImsRegistrationTech} for Registration tech}
210      */
211     private static final class FeatureProvisioningData {
212         public final int mCapability;
213         public final int mTech;
214         public final boolean mProvisioned;
215         public final boolean mIsMmTel;
216 
FeatureProvisioningData(int capability, int tech, boolean provisioned, boolean isMmTel)217         FeatureProvisioningData(int capability, int tech, boolean provisioned, boolean isMmTel) {
218             mCapability = capability;
219             mTech = tech;
220             mProvisioned = provisioned;
221             mIsMmTel = isMmTel;
222         }
223     }
224 
225     private final class MessageHandler extends Handler {
226         private static final String LOG_PREFIX = "Handler";
MessageHandler(Looper looper)227         MessageHandler(Looper looper) {
228             super(looper);
229         }
230 
231         @Override
handleMessage(Message msg)232         public void handleMessage(Message msg) {
233             switch (msg.what) {
234                 case EVENT_SUB_CHANGED:
235                     onSubscriptionsChanged();
236                     break;
237                 case EVENT_PROVISIONING_CAPABILITY_CHANGED:
238                     try {
239                         mProvisioningCallbackManagersSlotMap.get(msg.arg1)
240                                 .notifyProvisioningCapabilityChanged(
241                                         (FeatureProvisioningData) msg.obj);
242                     } catch (NullPointerException e) {
243                         logw(LOG_PREFIX, msg.arg1,
244                                 "can not find callback manager message" + msg.what);
245                     }
246                     break;
247                 case EVENT_MULTI_SIM_CONFIGURATION_CHANGE:
248                     int activeModemCount = (int) ((AsyncResult) msg.obj).result;
249                     onMultiSimConfigChanged(activeModemCount);
250                     break;
251                 case EVENT_PROVISIONING_VALUE_CHANGED:
252                     log("subId " + msg.arg1 + " changed provisioning value item : " + msg.arg2
253                             + " value : " + (int) msg.obj);
254                     updateCapabilityTechFromKey(msg.arg1, msg.arg2, (int) msg.obj);
255                     break;
256                 default:
257                     log("unknown message " + msg);
258                     break;
259             }
260         }
261     }
262 
263     private final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
264             new SubscriptionManager.OnSubscriptionsChangedListener() {
265                 @Override
266                 public void onSubscriptionsChanged() {
267                     if (!mHandler.hasMessages(EVENT_SUB_CHANGED)) {
268                         mHandler.sendEmptyMessage(EVENT_SUB_CHANGED);
269                     }
270                 }
271             };
272 
273     private final class ProvisioningCallbackManager {
274         private static final String LOG_PREFIX = "ProvisioningCallbackManager";
275         private RemoteCallbackList<IFeatureProvisioningCallback> mIFeatureProvisioningCallbackList;
276         private int mSubId;
277         private int mSlotId;
278 
ProvisioningCallbackManager(int slotId)279         ProvisioningCallbackManager(int slotId) {
280             mIFeatureProvisioningCallbackList =
281                     new RemoteCallbackList<IFeatureProvisioningCallback>();
282             mSlotId = slotId;
283             mSubId = getSubId(slotId);
284             log(LOG_PREFIX, mSlotId, "ProvisioningCallbackManager create");
285         }
286 
clear()287         public void clear() {
288             log(LOG_PREFIX, mSlotId, "ProvisioningCallbackManager clear ");
289 
290             mIFeatureProvisioningCallbackList.kill();
291 
292             // All registered callbacks are unregistered, and the list is disabled
293             // need to create again
294             mIFeatureProvisioningCallbackList =
295                     new RemoteCallbackList<IFeatureProvisioningCallback>();
296         }
297 
registerCallback(IFeatureProvisioningCallback localCallback)298         public void registerCallback(IFeatureProvisioningCallback localCallback) {
299             if (!mIFeatureProvisioningCallbackList.register(localCallback, (Object) mSubId)) {
300                 log(LOG_PREFIX, mSlotId, "registration callback fail");
301             }
302         }
303 
unregisterCallback(IFeatureProvisioningCallback localCallback)304         public void unregisterCallback(IFeatureProvisioningCallback localCallback) {
305             mIFeatureProvisioningCallbackList.unregister(localCallback);
306         }
307 
setSubId(int subId)308         public void setSubId(int subId) {
309             if (mSubId == subId) {
310                 log(LOG_PREFIX, mSlotId, "subId is not changed ");
311                 return;
312             }
313 
314             mSubId = subId;
315             mSlotId = getSlotId(subId);
316 
317             // subId changed means the registered callbacks are not available.
318             clear();
319         }
320 
hasCallblacks()321         public boolean hasCallblacks() {
322             int size = mIFeatureProvisioningCallbackList.beginBroadcast();
323             mIFeatureProvisioningCallbackList.finishBroadcast();
324 
325             return (size > 0);
326         }
327 
notifyProvisioningCapabilityChanged(FeatureProvisioningData data)328         public void notifyProvisioningCapabilityChanged(FeatureProvisioningData data) {
329             int size = mIFeatureProvisioningCallbackList.beginBroadcast();
330             for (int index = 0; index < size; index++) {
331                 try {
332                     IFeatureProvisioningCallback imsFeatureProvisioningCallback =
333                             mIFeatureProvisioningCallbackList.getBroadcastItem(index);
334 
335                     // MMTEL
336                     if (data.mIsMmTel
337                             && Arrays.stream(LOCAL_MMTEL_CAPABILITY)
338                             .anyMatch(value -> value == data.mCapability)) {
339                         imsFeatureProvisioningCallback.onFeatureProvisioningChanged(
340                                 data.mCapability, data.mTech, data.mProvisioned);
341                         logi(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
342                                 + "onFeatureProvisioningChanged"
343                                 + " capability " + data.mCapability
344                                 + " tech "  + data.mTech
345                                 + " isProvisioned " + data.mProvisioned);
346                     } else if (data.mCapability == CAPABILITY_TYPE_PRESENCE_UCE) {
347                         imsFeatureProvisioningCallback.onRcsFeatureProvisioningChanged(
348                                 data.mCapability, data.mTech, data.mProvisioned);
349                         logi(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
350                                 + "onRcsFeatureProvisioningChanged"
351                                 + " capability " + data.mCapability
352                                 + " tech "  + data.mTech
353                                 + " isProvisioned " + data.mProvisioned);
354                     } else {
355                         loge(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
356                                 + "unknown capability "
357                                 + data.mCapability);
358                     }
359                 } catch (RemoteException e) {
360                     loge(LOG_PREFIX, mSlotId,
361                             "notifyProvisioningChanged: callback #" + index + " failed");
362                 }
363             }
364             mIFeatureProvisioningCallbackList.finishBroadcast();
365         }
366     }
367 
368     private final class MmTelFeatureListener implements FeatureConnector.Listener<ImsManager> {
369         private static final String LOG_PREFIX = "MmTelFeatureListener";
370         private FeatureConnector<ImsManager> mConnector;
371         private ImsManager mImsManager;
372         private boolean mReady = false;
373         // stores whether the initial provisioning key value should be notified to ImsService
374         private boolean mRequiredNotify = false;
375         private int mSubId;
376         private int mSlotId;
377         private ConfigCallback mConfigCallback;
378 
MmTelFeatureListener(int slotId)379         MmTelFeatureListener(int slotId) {
380             log(LOG_PREFIX, slotId, "created");
381 
382             mSlotId = slotId;
383             mSubId = getSubId(slotId);
384             mConfigCallback = new ConfigCallback(mSubId);
385 
386             mConnector = mMmTelFeatureConnector.create(
387                     mApp, slotId, TAG, this, new HandlerExecutor(mHandler));
388             mConnector.connect();
389         }
390 
setSubId(int subId)391         public void setSubId(int subId) {
392             if (mRequiredNotify && mReady) {
393                 mRequiredNotify = false;
394                 setInitialProvisioningKeys(subId);
395             }
396             if (mSubId == subId) {
397                 log(LOG_PREFIX, mSlotId, "subId is not changed");
398                 return;
399             }
400 
401             mSubId = subId;
402             mSlotId = getSlotId(subId);
403             mConfigCallback.setSubId(subId);
404         }
405 
destroy()406         public void destroy() {
407             log("destroy");
408             if (mImsManager != null) {
409                 try {
410                     ImsConfig imsConfig = getImsConfig(mImsManager);
411                     if (imsConfig != null) {
412                         imsConfig.removeConfigCallback(mConfigCallback);
413                     }
414                 } catch (ImsException e) {
415                     logw(LOG_PREFIX, mSlotId, "destroy : " + e.getMessage());
416                 }
417             }
418             mConfigCallback = null;
419             mConnector.disconnect();
420             mConnector = null;
421             mReady = false;
422             mImsManager = null;
423         }
424 
getImsManager()425         public @Nullable ImsManager getImsManager() {
426             return mImsManager;
427         }
428 
429         @Override
connectionReady(ImsManager manager, int subId)430         public void connectionReady(ImsManager manager, int subId) {
431             log(LOG_PREFIX, mSlotId, "connection ready");
432             mReady = true;
433             mImsManager = manager;
434 
435             if (mImsManager != null) {
436                 try {
437                     ImsConfig imsConfig = getImsConfig(mImsManager);
438                     if (imsConfig != null) {
439                         imsConfig.addConfigCallback(mConfigCallback);
440                     }
441                 } catch (ImsException e) {
442                     logw(LOG_PREFIX, mSlotId, "addConfigCallback : " + e.getMessage());
443                 }
444             }
445 
446             onMmTelAvailable();
447         }
448 
449         @Override
connectionUnavailable(int reason)450         public void connectionUnavailable(int reason) {
451             log(LOG_PREFIX, mSlotId, "connection unavailable " + reason);
452 
453             mReady = false;
454             mImsManager = null;
455 
456             // keep the callback for other reason
457             if (reason == FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED) {
458                 onMmTelUnavailable();
459             }
460         }
461 
setProvisioningValue(int key, int value)462         public int setProvisioningValue(int key, int value) {
463             int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
464 
465             if (!mReady) {
466                 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
467                 return retVal;
468             }
469             try {
470                 // getConfigInterface() will return not null or throw the ImsException
471                 // need not null checking
472                 ImsConfig imsConfig = getImsConfig(mImsManager);
473                 retVal = imsConfig.setConfig(key, value);
474                 log(LOG_PREFIX, mSlotId, "setConfig called with key " + key + " value " + value);
475             } catch (ImsException e) {
476                 logw(LOG_PREFIX, mSlotId,
477                         "setConfig operation failed for key =" + key
478                         + ", value =" + value + ". Exception:" + e.getMessage());
479             }
480             return retVal;
481         }
482 
getProvisioningValue(int key)483         public int getProvisioningValue(int key) {
484             if (!mReady) {
485                 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
486                 return INVALID_VALUE;
487             }
488 
489             int retValue = INVALID_VALUE;
490             try {
491                 // getConfigInterface() will return not null or throw the ImsException
492                 // need not null checking
493                 ImsConfig imsConfig = getImsConfig(mImsManager);
494                 retValue = imsConfig.getConfigInt(key);
495             } catch (ImsException e) {
496                 logw(LOG_PREFIX, mSlotId,
497                         "getConfig operation failed for key =" + key
498                         + ", value =" + retValue + ". Exception:" + e.getMessage());
499             }
500             return retValue;
501         }
502 
onMmTelAvailable()503         public void onMmTelAvailable() {
504             log(LOG_PREFIX, mSlotId, "onMmTelAvailable");
505 
506             if (isValidSubId(mSubId)) {
507                 mRequiredNotify = false;
508 
509                 // notify provisioning key value to ImsService
510                 setInitialProvisioningKeys(mSubId);
511             } else {
512                 // wait until subId is valid
513                 mRequiredNotify = true;
514             }
515         }
516 
onMmTelUnavailable()517         public void onMmTelUnavailable() {
518             log(LOG_PREFIX, mSlotId, "onMmTelUnavailable");
519 
520             try {
521                 // delete all callbacks reference from ProvisioningManager
522                 mProvisioningCallbackManagersSlotMap.get(getSlotId(mSubId)).clear();
523             } catch (NullPointerException e) {
524                 logw(LOG_PREFIX, getSlotId(mSubId), "can not find callback manager to clear");
525             }
526         }
527 
setInitialProvisioningKeys(int subId)528         private void setInitialProvisioningKeys(int subId) {
529             boolean required;
530             int value = ImsProvisioningLoader.STATUS_NOT_SET;
531 
532             // updating KEY_VOLTE_PROVISIONING_STATUS
533             try {
534                 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VOICE,
535                         REGISTRATION_TECH_LTE);
536             } catch (IllegalArgumentException e) {
537                 logw("setInitialProvisioningKeys: KEY_VOLTE_PROVISIONING_STATUS failed for"
538                         + " subId=" + subId + ", exception: " + e.getMessage());
539                 return;
540             }
541 
542             log(LOG_PREFIX, mSlotId,
543                     "setInitialProvisioningKeys provisioning required(voice, lte) " + required);
544             if (required) {
545                 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
546                         CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE);
547                 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
548                     value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
549                             ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
550                     setProvisioningValue(KEY_VOLTE_PROVISIONING_STATUS, value);
551                 }
552             }
553 
554             // updating KEY_VT_PROVISIONING_STATUS
555             try {
556                 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VIDEO,
557                         REGISTRATION_TECH_LTE);
558             } catch (IllegalArgumentException e) {
559                 logw("setInitialProvisioningKeys: KEY_VT_PROVISIONING_STATUS failed for"
560                         + " subId=" + subId + ", exception: " + e.getMessage());
561                 return;
562             }
563 
564             log(LOG_PREFIX, mSlotId,
565                     "setInitialProvisioningKeys provisioning required(video, lte) " + required);
566             if (required) {
567                 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
568                         CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE);
569                 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
570                     value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
571                             ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
572                     setProvisioningValue(KEY_VT_PROVISIONING_STATUS, value);
573                 }
574             }
575 
576             // updating KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
577             try {
578                 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VOICE,
579                         REGISTRATION_TECH_IWLAN);
580             } catch (IllegalArgumentException e) {
581                 logw("setInitialProvisioningKeys: KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE failed"
582                         + " for subId=" + subId + ", exception: " + e.getMessage());
583                 return;
584             }
585 
586             log(LOG_PREFIX, mSlotId,
587                     "setInitialProvisioningKeys provisioning required(voice, iwlan) " + required);
588             if (required) {
589                 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
590                         CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN);
591                 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
592                     value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
593                             ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
594                     setProvisioningValue(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, value);
595                 }
596             }
597         }
598     }
599 
600     private final class RcsFeatureListener implements FeatureConnector.Listener<RcsFeatureManager> {
601         private static final String LOG_PREFIX = "RcsFeatureListener";
602         private FeatureConnector<RcsFeatureManager> mConnector;
603         private RcsFeatureManager mRcsFeatureManager;
604         private boolean mReady = false;
605         // stores whether the initial provisioning key value should be notified to ImsService
606         private boolean mRequiredNotify = false;
607         private int mSubId;
608         private int mSlotId;
609         private ConfigCallback mConfigCallback;
610 
RcsFeatureListener(int slotId)611         RcsFeatureListener(int slotId) {
612             log(LOG_PREFIX, slotId, "created");
613 
614             mSlotId = slotId;
615             mSubId = getSubId(slotId);
616             mConfigCallback = new ConfigCallback(mSubId);
617 
618             mConnector = mRcsFeatureConnector.create(
619                     mApp, slotId, this, new HandlerExecutor(mHandler), TAG);
620             mConnector.connect();
621         }
622 
setSubId(int subId)623         public void setSubId(int subId) {
624             if (mRequiredNotify && mReady) {
625                 mRequiredNotify = false;
626                 setInitialProvisioningKeys(subId);
627             }
628             if (mSubId == subId) {
629                 log(LOG_PREFIX, mSlotId, "subId is not changed");
630                 return;
631             }
632 
633             mSubId = subId;
634             mSlotId = getSlotId(subId);
635             mConfigCallback.setSubId(subId);
636         }
637 
destroy()638         public void destroy() {
639             log(LOG_PREFIX, mSlotId, "destroy");
640             if (mRcsFeatureManager != null) {
641                 try {
642                     ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
643                     if (imsConfig != null) {
644                         imsConfig.removeConfigCallback(mConfigCallback);
645                     }
646                 } catch (ImsException e) {
647                     logw(LOG_PREFIX, mSlotId, "destroy :" + e.getMessage());
648                 }
649             }
650             mConfigCallback = null;
651             mConnector.disconnect();
652             mConnector = null;
653             mReady = false;
654             mRcsFeatureManager = null;
655         }
656 
657         @Override
connectionReady(RcsFeatureManager manager, int subId)658         public void connectionReady(RcsFeatureManager manager, int subId) {
659             log(LOG_PREFIX, mSlotId, "connection ready");
660             mReady = true;
661             mRcsFeatureManager = manager;
662 
663             if (mRcsFeatureManager != null) {
664                 try {
665                     ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
666                     if (imsConfig != null) {
667                         imsConfig.addConfigCallback(mConfigCallback);
668                     }
669                 } catch (ImsException e) {
670                     logw(LOG_PREFIX, mSlotId, "addConfigCallback :" + e.getMessage());
671                 }
672             }
673 
674             onRcsAvailable();
675         }
676 
677         @Override
connectionUnavailable(int reason)678         public void connectionUnavailable(int reason) {
679             log(LOG_PREFIX, mSlotId, "connection unavailable");
680             mReady = false;
681             mRcsFeatureManager = null;
682 
683             // keep the callback for other reason
684             if (reason == FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED) {
685                 onRcsUnavailable();
686             }
687         }
688 
setProvisioningValue(int key, int value)689         public int setProvisioningValue(int key, int value) {
690             int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
691 
692             if (!mReady) {
693                 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
694                 return retVal;
695             }
696 
697             try {
698                 // getConfigInterface() will return not null or throw the ImsException
699                 // need not null checking
700                 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
701                 retVal = imsConfig.setConfig(key, value);
702                 log(LOG_PREFIX, mSlotId, "setConfig called with key " + key + " value " + value);
703             } catch (ImsException e) {
704                 logw(LOG_PREFIX, mSlotId,
705                         "setConfig operation failed for key =" + key
706                         + ", value =" + value + ". Exception:" + e.getMessage());
707             }
708             return retVal;
709         }
710 
getProvisioningValue(int key)711         public int getProvisioningValue(int key) {
712             if (!mReady) {
713                 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
714                 return INVALID_VALUE;
715             }
716 
717             int retValue = INVALID_VALUE;
718             try {
719                 // getConfigInterface() will return not null or throw the ImsException
720                 // need not null checking
721                 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
722                 retValue = imsConfig.getConfigInt(key);
723             } catch (ImsException e) {
724                 logw(LOG_PREFIX, mSlotId,
725                         "getConfig operation failed for key =" + key
726                         + ", value =" + retValue + ". Exception:" + e.getMessage());
727             }
728             return retValue;
729         }
730 
isConnectionReady()731         public boolean isConnectionReady() {
732             return mReady;
733         }
734 
onRcsAvailable()735         public void onRcsAvailable() {
736             log(LOG_PREFIX, mSlotId, "onRcsAvailable");
737 
738             if (isValidSubId(mSubId)) {
739                 mRequiredNotify = false;
740 
741                 // notify provisioning key value to ImsService
742                 setInitialProvisioningKeys(mSubId);
743             } else {
744                 // wait until subId is valid
745                 mRequiredNotify = true;
746             }
747         }
748 
onRcsUnavailable()749         public void onRcsUnavailable() {
750             log(LOG_PREFIX, mSlotId, "onRcsUnavailable");
751 
752             try {
753                 // delete all callbacks reference from ProvisioningManager
754                 mProvisioningCallbackManagersSlotMap.get(getSlotId(mSubId)).clear();
755             } catch (NullPointerException e) {
756                 logw(LOG_PREFIX, getSlotId(mSubId), "can not find callback manager to clear");
757             }
758         }
759 
setInitialProvisioningKeys(int subId)760         private void setInitialProvisioningKeys(int subId) {
761             boolean required;
762             int value = ImsProvisioningLoader.STATUS_NOT_SET;
763 
764             // KEY_EAB_PROVISIONING_STATUS
765             int capability = CAPABILITY_TYPE_PRESENCE_UCE;
766             // Assume that all radio techs have the same provisioning value
767             int tech = REGISTRATION_TECH_LTE;
768 
769             try {
770                 required = isRcsProvisioningRequiredForCapability(subId, capability, tech);
771             } catch (IllegalArgumentException e) {
772                 logw("setInitialProvisioningKeys: KEY_EAB_PROVISIONING_STATUS failed for"
773                         + " subId=" + subId + ", exception: " + e.getMessage());
774                 return;
775             }
776 
777             if (required) {
778                 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
779                         capability, tech);
780                 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
781                     value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
782                             ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
783                     setProvisioningValue(KEY_EAB_PROVISIONING_STATUS, value);
784                 }
785             }
786         }
787     }
788 
789     // When vendor ImsService changed provisioning data, which should be updated in AOSP.
790     // Catch the event using IImsConfigCallback.
791     private final class ConfigCallback extends IImsConfigCallback.Stub {
792         private int mSubId;
793 
ConfigCallback(int subId)794         ConfigCallback(int subId) {
795             mSubId = subId;
796         }
797 
setSubId(int subId)798         public void setSubId(int subId) {
799             mSubId = subId;
800         }
801 
802         @Override
onIntConfigChanged(int item, int value)803         public void onIntConfigChanged(int item, int value) throws RemoteException {
804             if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == item)) {
805                 return;
806             }
807 
808             final long callingIdentity = Binder.clearCallingIdentity();
809             try {
810                 if (mHandler != null) {
811                     mHandler.sendMessage(mHandler.obtainMessage(
812                             EVENT_PROVISIONING_VALUE_CHANGED, mSubId, item, (Object) value));
813                 }
814             } finally {
815                 Binder.restoreCallingIdentity(callingIdentity);
816             }
817         }
818 
819         @Override
onStringConfigChanged(int item, String value)820         public void onStringConfigChanged(int item, String value) throws RemoteException {
821             // Ignore this callback.
822         }
823     }
824 
825     /**
826      * Do NOT use this directly, instead use {@link #getInstance()}.
827      */
828     @VisibleForTesting
ImsProvisioningController(PhoneGlobals app, int numSlot, Looper looper, MmTelFeatureConnector mmTelFeatureConnector, RcsFeatureConnector rcsFeatureConnector, ImsProvisioningLoader imsProvisioningLoader)829     public ImsProvisioningController(PhoneGlobals app, int numSlot, Looper looper,
830             MmTelFeatureConnector mmTelFeatureConnector, RcsFeatureConnector rcsFeatureConnector,
831             ImsProvisioningLoader imsProvisioningLoader) {
832         log("ImsProvisioningController");
833         mApp = app;
834         mNumSlot = numSlot;
835         mHandler = new MessageHandler(looper);
836         mMmTelFeatureConnector = mmTelFeatureConnector;
837         mRcsFeatureConnector = rcsFeatureConnector;
838         mCarrierConfigManager = mApp.getSystemService(CarrierConfigManager.class);
839         mSubscriptionManager = mApp.getSystemService(SubscriptionManager.class);
840         mTelephonyRegistryManager = mApp.getSystemService(TelephonyRegistryManager.class);
841         mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
842                 mSubChangedListener, mSubChangedListener.getHandlerExecutor());
843         mImsProvisioningLoader = imsProvisioningLoader;
844 
845         PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
846                 EVENT_MULTI_SIM_CONFIGURATION_CHANGE, null);
847 
848         initialize(numSlot);
849     }
850 
initialize(int numSlot)851     private void initialize(int numSlot) {
852         for (int i = 0; i < numSlot; i++) {
853             MmTelFeatureListener m = new MmTelFeatureListener(i);
854             mMmTelFeatureListenersSlotMap.put(i, m);
855 
856             RcsFeatureListener r = new RcsFeatureListener(i);
857             mRcsFeatureListenersSlotMap.put(i, r);
858 
859             ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
860             mProvisioningCallbackManagersSlotMap.put(i, p);
861         }
862     }
863 
onMultiSimConfigChanged(int newNumSlot)864     private void onMultiSimConfigChanged(int newNumSlot) {
865         log("onMultiSimConfigChanged: NumSlot " + mNumSlot + " newNumSlot " + newNumSlot);
866 
867         if (mNumSlot < newNumSlot) {
868             for (int i = mNumSlot; i < newNumSlot; i++) {
869                 MmTelFeatureListener m = new MmTelFeatureListener(i);
870                 mMmTelFeatureListenersSlotMap.put(i, m);
871 
872                 RcsFeatureListener r = new RcsFeatureListener(i);
873                 mRcsFeatureListenersSlotMap.put(i, r);
874 
875                 ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
876                 mProvisioningCallbackManagersSlotMap.put(i, p);
877             }
878         } else if (mNumSlot > newNumSlot) {
879             for (int i = (mNumSlot - 1); i > (newNumSlot - 1); i--) {
880                 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(i);
881                 mMmTelFeatureListenersSlotMap.remove(i);
882                 m.destroy();
883 
884                 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(i);
885                 mRcsFeatureListenersSlotMap.remove(i);
886                 r.destroy();
887 
888                 ProvisioningCallbackManager p = mProvisioningCallbackManagersSlotMap.get(i);
889                 mProvisioningCallbackManagersSlotMap.remove(i);
890                 p.clear();
891             }
892         }
893 
894         mNumSlot = newNumSlot;
895     }
896 
897     /**
898      * destroy the instance
899      */
900     @VisibleForTesting
destroy()901     public void destroy() {
902         log("destroy");
903 
904         mHandler.getLooper().quit();
905 
906         mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
907 
908         for (int i = 0; i < mMmTelFeatureListenersSlotMap.size(); i++) {
909             mMmTelFeatureListenersSlotMap.get(i).destroy();
910         }
911         mMmTelFeatureListenersSlotMap.clear();
912 
913         for (int i = 0; i < mRcsFeatureListenersSlotMap.size(); i++) {
914             mRcsFeatureListenersSlotMap.get(i).destroy();
915         }
916         mRcsFeatureListenersSlotMap.clear();
917 
918         for (int i = 0; i < mProvisioningCallbackManagersSlotMap.size(); i++) {
919             mProvisioningCallbackManagersSlotMap.get(i).clear();
920         }
921     }
922 
923     /**
924      * create an instance
925      */
926     @VisibleForTesting
make(PhoneGlobals app, int numSlot)927     public static ImsProvisioningController make(PhoneGlobals app, int numSlot) {
928         synchronized (ImsProvisioningController.class) {
929             if (sInstance == null) {
930                 Rlog.i(TAG, "ImsProvisioningController created");
931                 HandlerThread handlerThread = new HandlerThread(TAG);
932                 handlerThread.start();
933                 sInstance = new ImsProvisioningController(app, numSlot, handlerThread.getLooper(),
934                         ImsManager::getConnector, RcsFeatureManager::getConnector,
935                         new ImsProvisioningLoader(app));
936             }
937         }
938         return sInstance;
939     }
940 
941     /**
942      * Gets a ImsProvisioningController instance
943      */
944     @VisibleForTesting
getInstance()945     public static ImsProvisioningController getInstance() {
946         synchronized (ImsProvisioningController.class) {
947             return sInstance;
948         }
949     }
950 
951     /**
952      * Register IFeatureProvisioningCallback from ProvisioningManager
953      */
954 
955     @VisibleForTesting
addFeatureProvisioningChangedCallback(int subId, IFeatureProvisioningCallback callback)956     public void addFeatureProvisioningChangedCallback(int subId,
957             IFeatureProvisioningCallback callback) {
958         if (callback == null) {
959             throw new IllegalArgumentException("provisioning callback can't be null");
960         }
961         int slotId = getSlotId(subId);
962         if (slotId < 0 || slotId >= mNumSlot) {
963             throw new IllegalArgumentException("subscription id is not available");
964         }
965 
966         try {
967             mProvisioningCallbackManagersSlotMap.get(slotId).registerCallback(callback);
968             log("Feature Provisioning Callback registered.");
969         } catch (NullPointerException e) {
970             logw("can not access callback manager to add callback");
971         }
972     }
973 
974     /**
975      * Remove IFeatureProvisioningCallback
976      */
977     @VisibleForTesting
removeFeatureProvisioningChangedCallback(int subId, IFeatureProvisioningCallback callback)978     public void removeFeatureProvisioningChangedCallback(int subId,
979             IFeatureProvisioningCallback callback) {
980         if (callback == null) {
981             throw new IllegalArgumentException("provisioning callback can't be null");
982         }
983 
984         int slotId = getSlotId(subId);
985         if (slotId < 0 || slotId >= mNumSlot) {
986             throw new IllegalArgumentException("subscription id is not available");
987         }
988 
989         try {
990             mProvisioningCallbackManagersSlotMap.get(slotId).unregisterCallback(callback);
991             log("Feature Provisioning Callback removed.");
992         } catch (NullPointerException e) {
993             logw("can not access callback manager to remove callback");
994         }
995     }
996 
997     /**
998      * return the boolean whether MmTel capability is required provisioning or not
999      */
1000     @VisibleForTesting
isImsProvisioningRequiredForCapability(int subId, int capability, int tech)1001     public boolean isImsProvisioningRequiredForCapability(int subId, int capability, int tech) {
1002         // check subId
1003         int slotId = getSlotId(subId);
1004         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1005             loge("Fail to retrieve slotId from subId");
1006             throw new IllegalArgumentException("subscribe id is invalid");
1007         }
1008 
1009         // check valid capability
1010         if (!(MMTEL_CAPABILITY_MIN < capability && capability < MMTEL_CAPABILITY_MAX)) {
1011             throw new IllegalArgumentException("MmTel capability '" + capability + "' is invalid");
1012         }
1013 
1014         // check valid radio tech
1015         if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
1016             log("Ims not matched radio tech " + tech);
1017             throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
1018         }
1019 
1020         // check new carrier config first KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
1021         boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/true);
1022 
1023         // if that returns false, check deprecated carrier config
1024         // KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
1025         if (!retVal && (capability == CAPABILITY_TYPE_VOICE
1026                 || capability == CAPABILITY_TYPE_VIDEO
1027                 || capability == CAPABILITY_TYPE_UT)) {
1028             String key = CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL;
1029             if (capability == CAPABILITY_TYPE_UT) {
1030                 key = CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL;
1031             }
1032 
1033             PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1034             if (imsCarrierConfigs != null) {
1035                 retVal = imsCarrierConfigs.getBoolean(key);
1036             } else {
1037                 retVal = CarrierConfigManager.getDefaultConfig().getBoolean(key);
1038             }
1039         }
1040 
1041         log("isImsProvisioningRequiredForCapability capability " + capability
1042                 + " tech " + tech + " return value " + retVal);
1043 
1044         return retVal;
1045     }
1046 
1047     /**
1048      * return the boolean whether RCS capability is required provisioning or not
1049      */
1050     @VisibleForTesting
isRcsProvisioningRequiredForCapability(int subId, int capability, int tech)1051     public boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech) {
1052         // check slotId and Phone object
1053         int slotId = getSlotId(subId);
1054         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1055             loge("Fail to retrieve slotId from subId");
1056             throw new IllegalArgumentException("subscribe id is invalid");
1057         }
1058 
1059         // check valid capability
1060         if (!(RCS_CAPABILITY_MIN < capability && capability < RCS_CAPABILITY_MAX)) {
1061             throw new IllegalArgumentException("Rcs capability '" + capability + "' is invalid");
1062         }
1063 
1064         // check valid radio tech
1065         if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
1066             log("Rcs not matched radio tech " + tech);
1067             throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
1068         }
1069 
1070         // check new carrier config first KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
1071         boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/false);
1072 
1073         // if that returns false, check deprecated carrier config
1074         // KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
1075         if (!retVal) {
1076             PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1077             if (imsCarrierConfigs != null) {
1078                 retVal = imsCarrierConfigs.getBoolean(
1079                         CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL);
1080             } else {
1081                 retVal = CarrierConfigManager.getDefaultConfig().getBoolean(
1082                         CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL);
1083             }
1084         }
1085 
1086         log("isRcsProvisioningRequiredForCapability capability " + capability
1087                 + " tech " + tech + " return value " + retVal);
1088 
1089         return retVal;
1090     }
1091 
1092     /**
1093      * return the provisioning status for MmTel capability in specific radio tech
1094      */
1095     @VisibleForTesting
getImsProvisioningStatusForCapability(int subId, int capability, int tech)1096     public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) {
1097         boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
1098         if (!mmTelProvisioned) { // provisioning not required
1099             log("getImsProvisioningStatusForCapability : not required "
1100                     + " capability " + capability + " tech " + tech);
1101             return true;
1102         }
1103 
1104         // read value from ImsProvisioningLoader
1105         int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1106                 capability, tech);
1107         if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1108             // not set means initial value
1109             // read data from vendor ImsService and store that in ImsProvisioningLoader
1110             result = getValueFromImsService(subId, capability, tech);
1111             mmTelProvisioned = getBoolValue(result);
1112             if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
1113                 setAndNotifyMmTelProvisioningValue(subId, capability, tech, mmTelProvisioned);
1114             }
1115         } else {
1116             mmTelProvisioned = getBoolValue(result);
1117         }
1118 
1119         log("getImsProvisioningStatusForCapability : "
1120                 + " capability " + capability
1121                 + " tech " + tech
1122                 + " result " + mmTelProvisioned);
1123         return mmTelProvisioned;
1124     }
1125 
1126     /**
1127      * set MmTel provisioning status in specific tech
1128      */
1129     @VisibleForTesting
setImsProvisioningStatusForCapability(int subId, int capability, int tech, boolean isProvisioned)1130     public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
1131             boolean isProvisioned) {
1132         boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
1133         if (!mmTelProvisioned) { // provisioning not required
1134             log("setImsProvisioningStatusForCapability : not required "
1135                     + " capability " + capability + " tech " + tech);
1136             return;
1137         }
1138 
1139         // write value to ImsProvisioningLoader
1140         boolean isChanged = setAndNotifyMmTelProvisioningValue(subId, capability, tech,
1141                 isProvisioned);
1142         if (!isChanged) {
1143             log("status not changed mmtel capability " + capability + " tech " + tech);
1144             return;
1145         }
1146 
1147         int slotId = getSlotId(subId);
1148         // find matched key from capability and tech
1149         int value = getIntValue(isProvisioned);
1150         int key = getKeyFromCapability(capability, tech);
1151         if (key != INVALID_VALUE) {
1152             log("setImsProvisioningStatusForCapability : matched key " + key);
1153             try {
1154                 // set key and value to vendor ImsService for MmTel
1155                 mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1156             } catch (NullPointerException e) {
1157                 loge("can not access MmTelFeatureListener with capability " + capability);
1158             }
1159         }
1160     }
1161 
1162     /**
1163      * return the provisioning status for RCS capability in specific radio tech
1164      */
1165     @VisibleForTesting
getRcsProvisioningStatusForCapability(int subId, int capability, int tech)1166     public boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech) {
1167         boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1168         if (!rcsProvisioned) { // provisioning not required
1169             log("getRcsProvisioningStatusForCapability : not required"
1170                     + " capability " + capability + " tech " + tech);
1171             return true;
1172         }
1173 
1174         // read data from ImsProvisioningLoader
1175         int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1176                 capability, tech);
1177         if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1178             // not set means initial value
1179             // read data from vendor ImsService and store that in ImsProvisioningLoader
1180             result = getRcsValueFromImsService(subId, capability);
1181             rcsProvisioned = getBoolValue(result);
1182             if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
1183                 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, rcsProvisioned);
1184             }
1185         } else {
1186             rcsProvisioned = getBoolValue(result);
1187         }
1188 
1189         log("getRcsProvisioningStatusForCapability : "
1190                 + " capability " + capability
1191                 + " tech " + tech
1192                 + " result " + rcsProvisioned);
1193         return rcsProvisioned;
1194     }
1195 
1196     /**
1197      * set RCS provisioning status in specific tech
1198      */
1199     @VisibleForTesting
setRcsProvisioningStatusForCapability(int subId, int capability, int tech, boolean isProvisioned)1200     public void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
1201             boolean isProvisioned) {
1202         boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1203         if (!rcsProvisioned) { // provisioning not required
1204             log("set rcs provisioning status but not required");
1205             return;
1206         }
1207 
1208         // write status using ImsProvisioningLoader
1209         boolean isChanged = setAndNotifyRcsProvisioningValue(subId, capability, tech,
1210                 isProvisioned);
1211         if (!isChanged) {
1212             log("status not changed rcs capability " + capability + " tech " + tech);
1213             return;
1214         }
1215 
1216         int slotId = getSlotId(subId);
1217         int key =  ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
1218         int value = getIntValue(isProvisioned);
1219         try {
1220             // On some older devices, EAB is managed on the MmTel ImsService when the RCS
1221             // ImsService is not configured. If there is no RCS ImsService defined, fallback to
1222             // MmTel. In the rare case that we hit a race condition where the RCS ImsService has
1223             // crashed or has not come up yet, the value will be synchronized via
1224             // setInitialProvisioningKeys().
1225             if (mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1226                 mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1227             }
1228 
1229             // EAB provisioning status should be updated to both the Rcs and MmTel ImsService,
1230             // because the provisioning callback is listening to only MmTel provisioning key
1231             // changes.
1232             mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1233         } catch (NullPointerException e) {
1234             loge("can not access RcsFeatureListener with capability " + capability);
1235         }
1236     }
1237 
1238     /**
1239      * set RCS provisioning status in specific key and value
1240      * @param key integer key, defined as one of
1241      * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1242      * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1243      * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1244      * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1245      * @param value in Integer format.
1246      * @return the result of setting the configuration value, defined as one of
1247      * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1248      * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1249      */
1250     @VisibleForTesting
setProvisioningValue(int subId, int key, int value)1251     public int setProvisioningValue(int subId, int key, int value) {
1252         log("setProvisioningValue");
1253 
1254         int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
1255         // check key value
1256         if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1257             log("not matched key " + key);
1258             return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1259         }
1260 
1261         // check subId
1262         int slotId = getSlotId(subId);
1263         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1264             loge("Fail to retrieve slotId from subId");
1265             return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1266         }
1267 
1268         try {
1269             // set key and value to vendor ImsService for MmTel
1270             // EAB provisioning status should be updated to both the Rcs and MmTel ImsService,
1271             // because the provisioning callback is listening to only MmTel provisioning key
1272             // changes.
1273             retVal = mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1274 
1275             // If the  Rcs ImsService is not available, the EAB provisioning status will be written
1276             // to the MmTel ImsService for backwards compatibility. In the rare case that this is
1277             // hit due to RCS ImsService temporarily unavailable, the value will be synchronized
1278             // via setInitialProvisioningKeys() when the RCS ImsService comes back up.
1279             if (key == KEY_EAB_PROVISIONING_STATUS
1280                     && mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1281                 // set key and value to vendor ImsService for RCS and use retVal from RCS if
1282                 // related to EAB when possible.
1283                 retVal = mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1284             }
1285         } catch (NullPointerException e) {
1286             loge("can not access FeatureListener to set provisioning value");
1287             return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1288         }
1289 
1290         // update and notify provisioning status changed capability and tech from key
1291         updateCapabilityTechFromKey(subId, key, value);
1292 
1293         return retVal;
1294     }
1295 
1296     /**
1297      * get RCS provisioning status in specific key and value
1298      * @param key integer key, defined as one of
1299      * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1300      * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1301      * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1302      * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1303      * @return the result of setting the configuration value, defined as one of
1304      * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1305      * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1306      * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN}
1307      */
1308     @VisibleForTesting
getProvisioningValue(int subId, int key)1309     public int getProvisioningValue(int subId, int key) {
1310         // check key value
1311         if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1312             log("not matched key " + key);
1313             return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1314         }
1315 
1316         // check subId
1317         int slotId = getSlotId(subId);
1318         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1319             loge("Fail to retrieve slotId from subId");
1320             return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1321         }
1322 
1323         // check data from ImsProvisioningLoader
1324         int capability = getCapabilityFromKey(key);
1325         int tech = getTechFromKey(key);
1326         int result;
1327         if (capability != INVALID_VALUE && tech != INVALID_VALUE) {
1328             if (key == KEY_EAB_PROVISIONING_STATUS) {
1329                 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1330                         capability, tech);
1331             } else {
1332                 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1333                         capability, tech);
1334             }
1335             if (result != ImsProvisioningLoader.STATUS_NOT_SET) {
1336                 log("getProvisioningValue from loader : key " + key + " result " + result);
1337                 return result;
1338             }
1339         }
1340 
1341         // get data from ImsService, update it in ImsProvisioningLoader
1342         if (key == KEY_EAB_PROVISIONING_STATUS) {
1343             result = getRcsValueFromImsService(subId, capability);
1344             if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1345                 logw("getProvisioningValue : fail to get data from ImsService capability"
1346                         + capability);
1347                 return result;
1348             }
1349             log("getProvisioningValue from vendor : key " + key + " result " + result);
1350 
1351             setAndNotifyRcsProvisioningValueForAllTech(subId, capability, getBoolValue(result));
1352             return result;
1353         } else {
1354             result = getValueFromImsService(subId, capability, tech);
1355             if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1356                 logw("getProvisioningValue : fail to get data from ImsService capability"
1357                         + capability);
1358                 return result;
1359             }
1360             log("getProvisioningValue from vendor : key " + key + " result " + result);
1361 
1362             setAndNotifyMmTelProvisioningValue(subId, capability, tech, getBoolValue(result));
1363             return result;
1364         }
1365     }
1366 
1367     /**
1368      * get the handler
1369      */
1370     @VisibleForTesting
getHandler()1371     public Handler getHandler() {
1372         return mHandler;
1373     }
1374 
isProvisioningRequired(int subId, int capability, int tech, boolean isMmTel)1375     private boolean isProvisioningRequired(int subId, int capability, int tech, boolean isMmTel) {
1376         int[] techArray;
1377         techArray = getTechsFromCarrierConfig(subId, capability, isMmTel);
1378         if (techArray == null) {
1379             logw("isProvisioningRequired : getTechsFromCarrierConfig failed");
1380             // not exist in CarrierConfig that means provisioning is not required
1381             return false;
1382         }
1383 
1384         // compare with carrier config
1385         if (Arrays.stream(techArray).anyMatch(keyValue -> keyValue == tech)) {
1386             // existing same tech means provisioning required
1387             return true;
1388         }
1389 
1390         log("isProvisioningRequired : not matched capability " + capability + " tech " + tech);
1391         return false;
1392     }
1393 
getTechsFromCarrierConfig(int subId, int capability, boolean isMmTel)1394     private int[] getTechsFromCarrierConfig(int subId, int capability, boolean isMmTel) {
1395         String featureKey;
1396         String capabilityKey;
1397         if (isMmTel) {
1398             featureKey = CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE;
1399             capabilityKey = KEYS_MMTEL_CAPABILITY.get(capability);
1400         } else {
1401             featureKey = CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE;
1402             capabilityKey = KEYS_RCS_CAPABILITY.get(capability);
1403         }
1404 
1405         if (capabilityKey != null) {
1406             PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1407             if (imsCarrierConfigs == null) {
1408                 log("getTechsFromCarrierConfig : imsCarrierConfigs null");
1409                 return null;
1410             }
1411 
1412             PersistableBundle provisioningBundle =
1413                     imsCarrierConfigs.getPersistableBundle(featureKey);
1414             if (provisioningBundle == null) {
1415                 log("getTechsFromCarrierConfig : provisioningBundle null");
1416                 return null;
1417             }
1418 
1419             return provisioningBundle.getIntArray(capabilityKey);
1420         }
1421 
1422         return null;
1423     }
1424 
getValueFromImsService(int subId, int capability, int tech)1425     private int getValueFromImsService(int subId, int capability, int tech) {
1426         int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1427 
1428         // operation is based on capability
1429         switch (capability) {
1430             case CAPABILITY_TYPE_VOICE:
1431                 int item = (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
1432                         ? ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
1433                         : ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1434                 // read data from vendor ImsService
1435                 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1436                         .getProvisioningValue(item);
1437                 break;
1438             case CAPABILITY_TYPE_VIDEO:
1439                 // read data from vendor ImsService
1440                 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1441                         .getProvisioningValue(ProvisioningManager.KEY_VT_PROVISIONING_STATUS);
1442                 break;
1443             default:
1444                 log("Capability " + capability + " has been provisioning");
1445                 break;
1446         }
1447 
1448         return config;
1449     }
1450 
getRcsValueFromImsService(int subId, int capability)1451     private int getRcsValueFromImsService(int subId, int capability) {
1452         int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1453         int slotId = getSlotId(subId);
1454 
1455         if (capability != CAPABILITY_TYPE_PRESENCE_UCE) {
1456             log("Capability " + capability + " has been provisioning");
1457             return config;
1458         }
1459         try {
1460             if (mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1461                 config = mRcsFeatureListenersSlotMap.get(slotId)
1462                         .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1463             } else {
1464                 log("Rcs ImsService is not available, "
1465                         + "EAB provisioning status should be read from MmTel ImsService");
1466                 config = mMmTelFeatureListenersSlotMap.get(slotId)
1467                         .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1468             }
1469         } catch (NullPointerException e) {
1470             logw("can not access FeatureListener : " + e.getMessage());
1471         }
1472 
1473         return config;
1474     }
1475 
onSubscriptionsChanged()1476     private void onSubscriptionsChanged() {
1477         for (int index = 0; index < mMmTelFeatureListenersSlotMap.size(); index++) {
1478             MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(index);
1479             m.setSubId(getSubId(index));
1480         }
1481         for (int index = 0; index < mRcsFeatureListenersSlotMap.size(); index++) {
1482             RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(index);
1483             r.setSubId(getSubId(index));
1484         }
1485         for (int index = 0; index < mProvisioningCallbackManagersSlotMap.size(); index++) {
1486             ProvisioningCallbackManager m = mProvisioningCallbackManagersSlotMap.get(index);
1487             m.setSubId(getSubId(index));
1488         }
1489     }
1490 
updateCapabilityTechFromKey(int subId, int key, int value)1491     private void  updateCapabilityTechFromKey(int subId, int key, int value) {
1492         boolean isProvisioned = getBoolValue(value);
1493         int capability = getCapabilityFromKey(key);
1494         int tech = getTechFromKey(key);
1495 
1496         if (capability == INVALID_VALUE || tech == INVALID_VALUE) {
1497             logw("updateCapabilityTechFromKey : unknown key " + key);
1498             return;
1499         }
1500 
1501         if (key == KEY_VOLTE_PROVISIONING_STATUS
1502                 || key == KEY_VT_PROVISIONING_STATUS
1503                 || key == KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE) {
1504             setAndNotifyMmTelProvisioningValue(subId, capability, tech, isProvisioned);
1505         }
1506         if (key == KEY_EAB_PROVISIONING_STATUS) {
1507             setAndNotifyRcsProvisioningValueForAllTech(subId, capability, isProvisioned);
1508         }
1509     }
1510 
getCapabilityFromKey(int key)1511     private int getCapabilityFromKey(int key) {
1512         int capability;
1513         switch (key) {
1514             case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1515                 // intentional fallthrough
1516             case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1517                 capability = CAPABILITY_TYPE_VOICE;
1518                 break;
1519             case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1520                 capability = CAPABILITY_TYPE_VIDEO;
1521                 break;
1522             case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1523                 // default CAPABILITY_TYPE_PRESENCE_UCE used for KEY_EAB_PROVISIONING_STATUS
1524                 capability = CAPABILITY_TYPE_PRESENCE_UCE;
1525                 break;
1526             default:
1527                 capability = INVALID_VALUE;
1528                 break;
1529         }
1530         return capability;
1531     }
1532 
getTechFromKey(int key)1533     private int getTechFromKey(int key) {
1534         int tech;
1535         switch (key) {
1536             case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1537                 tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
1538                 break;
1539             case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1540                 // intentional fallthrough
1541             case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1542                 // intentional fallthrough
1543             case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1544                 tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
1545                 break;
1546             default:
1547                 tech = INVALID_VALUE;
1548                 break;
1549         }
1550         return tech;
1551     }
1552 
getKeyFromCapability(int capability, int tech)1553     private int getKeyFromCapability(int capability, int tech) {
1554         int key = INVALID_VALUE;
1555         if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_IWLAN) {
1556             key = ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
1557         } else if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_LTE) {
1558             key = ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1559         } else if (capability == CAPABILITY_TYPE_VIDEO && tech == REGISTRATION_TECH_LTE) {
1560             key = ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
1561         }
1562 
1563         return key;
1564     }
1565 
getSubId(int slotId)1566     protected int getSubId(int slotId) {
1567         return SubscriptionManager.getSubscriptionId(slotId);
1568     }
1569 
getSlotId(int subId)1570     protected int getSlotId(int subId) {
1571         return mSubscriptionManager.getPhoneId(subId);
1572     }
1573 
getImsConfig(ImsManager imsManager)1574     protected ImsConfig getImsConfig(ImsManager imsManager) throws ImsException {
1575         return imsManager.getConfigInterface();
1576     }
1577 
getImsConfig(IImsConfig iImsConfig)1578     protected ImsConfig getImsConfig(IImsConfig iImsConfig) {
1579         return new ImsConfig(iImsConfig);
1580     }
1581 
getIntValue(boolean isProvisioned)1582     private int getIntValue(boolean isProvisioned) {
1583         return isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
1584                 : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
1585     }
1586 
getBoolValue(int value)1587     private boolean getBoolValue(int value) {
1588         return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED ? true : false;
1589     }
1590 
setAndNotifyMmTelProvisioningValue(int subId, int capability, int tech, boolean isProvisioned)1591     private boolean setAndNotifyMmTelProvisioningValue(int subId, int capability, int tech,
1592             boolean isProvisioned) {
1593         boolean changed = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_MMTEL,
1594                 capability, tech, isProvisioned);
1595         // notify MmTel capability changed
1596         if (changed) {
1597             mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1598                     getSlotId(subId), 0, (Object) new FeatureProvisioningData(
1599                             capability, tech, isProvisioned, /*isMmTel*/true)));
1600         }
1601 
1602         return changed;
1603     }
1604 
setAndNotifyRcsProvisioningValue(int subId, int capability, int tech, boolean isProvisioned)1605     private boolean setAndNotifyRcsProvisioningValue(int subId, int capability, int tech,
1606             boolean isProvisioned) {
1607         boolean isChanged = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_RCS,
1608                 capability, tech, isProvisioned);
1609 
1610         if (isChanged) {
1611             int slotId = getSlotId(subId);
1612 
1613             // notify RCS capability changed
1614             mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1615                     slotId, 0, (Object) new FeatureProvisioningData(
1616                             capability, tech, isProvisioned, /*isMmtel*/false)));
1617         }
1618 
1619         return isChanged;
1620     }
1621 
setAndNotifyRcsProvisioningValueForAllTech(int subId, int capability, boolean isProvisioned)1622     private boolean setAndNotifyRcsProvisioningValueForAllTech(int subId, int capability,
1623             boolean isProvisioned) {
1624         boolean isChanged = false;
1625 
1626         for (int tech : LOCAL_RADIO_TECHS) {
1627             isChanged |= setAndNotifyRcsProvisioningValue(subId, capability, tech, isProvisioned);
1628         }
1629 
1630         return isChanged;
1631     }
1632 
isValidSubId(int subId)1633     protected boolean isValidSubId(int subId) {
1634         int slotId = getSlotId(subId);
1635         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1636             return false;
1637         }
1638 
1639         return true;
1640     }
1641 
log(String s)1642     private void log(String s) {
1643         Rlog.d(TAG, s);
1644     }
1645 
log(String prefix, int slotId, String s)1646     private void log(String prefix, int slotId, String s) {
1647         Rlog.d(TAG, prefix + "[" + slotId + "] " + s);
1648     }
1649 
logi(String prefix, int slotId, String s)1650     private void logi(String prefix, int slotId, String s) {
1651         Rlog.i(TAG, prefix + "[" + slotId + "] " + s);
1652     }
1653 
logw(String s)1654     private void logw(String s) {
1655         Rlog.w(TAG, s);
1656     }
1657 
logw(String prefix, int slotId, String s)1658     private void logw(String prefix, int slotId, String s) {
1659         Rlog.w(TAG, prefix + "[" + slotId + "] " + s);
1660     }
1661 
loge(String s)1662     private void loge(String s) {
1663         Rlog.e(TAG, s);
1664     }
1665 
loge(String prefix, int slotId, String s)1666     private void loge(String prefix, int slotId, String s) {
1667         Rlog.e(TAG, prefix + "[" + slotId + "] " + s);
1668     }
1669 }
1670