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