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