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