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