blob: 98a6e890ba49f3cc3d5b54426428b8c4e7bb50e8 [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
19import static android.telephony.ims.ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
20import static android.telephony.ims.ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
21import static android.telephony.ims.ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
22import static android.telephony.ims.ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
23import static android.telephony.ims.ProvisioningManager.PROVISIONING_VALUE_DISABLED;
24import static android.telephony.ims.ProvisioningManager.PROVISIONING_VALUE_ENABLED;
25import static android.telephony.ims.feature.ImsFeature.FEATURE_MMTEL;
26import static android.telephony.ims.feature.ImsFeature.FEATURE_RCS;
27import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER;
28import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
29import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
30import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
31import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
joonhunshinf9411cb2022-01-17 07:37:15 +000032import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE;
joonhunshin2c3e4232021-11-28 07:32:01 +000033import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
34import 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;
joonhunshin2c3e4232021-11-28 07:32:01 +000044import android.os.Handler;
45import android.os.HandlerThread;
46import android.os.Looper;
47import android.os.Message;
48import android.os.PersistableBundle;
49import android.os.RemoteCallbackList;
50import android.os.RemoteException;
51import android.telephony.CarrierConfigManager;
joonhunshinf9411cb2022-01-17 07:37:15 +000052import android.telephony.CarrierConfigManager.Ims;
joonhunshin2c3e4232021-11-28 07:32:01 +000053import android.telephony.SubscriptionManager;
54import android.telephony.TelephonyRegistryManager;
55import android.telephony.ims.ProvisioningManager;
56import android.telephony.ims.aidl.IFeatureProvisioningCallback;
57import android.telephony.ims.aidl.IImsConfig;
58import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
59import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
60import android.telephony.ims.stub.ImsConfigImplBase;
61import android.telephony.ims.stub.ImsRegistrationImplBase;
62import android.util.SparseArray;
63
64import com.android.ims.FeatureConnector;
65import com.android.ims.ImsConfig;
66import com.android.ims.ImsException;
67import com.android.ims.ImsManager;
68import com.android.ims.RcsFeatureManager;
69import com.android.internal.annotations.VisibleForTesting;
joonhunshin39710022022-01-14 11:53:58 +000070import com.android.internal.telephony.PhoneConfigurationManager;
joonhunshin2c3e4232021-11-28 07:32:01 +000071import com.android.internal.telephony.util.HandlerExecutor;
72import com.android.telephony.Rlog;
73
74import java.util.Arrays;
joonhunshinf9411cb2022-01-17 07:37:15 +000075import java.util.Map;
joonhunshin2c3e4232021-11-28 07:32:01 +000076import 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 */
84public 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;
joonhunshin39710022022-01-14 11:53:58 +000090 @VisibleForTesting
91 protected static final int EVENT_MULTI_SIM_CONFIGURATION_CHANGE = 3;
joonhunshin2c3e4232021-11-28 07:32:01 +000092
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 /**
joonhunshinf9411cb2022-01-17 07:37:15 +0000122 * 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_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_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 /**
joonhunshin2c3e4232021-11-28 07:32:01 +0000148 * 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 */
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 */
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 RcsImsCapabilities.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
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";
224 MessageHandler(Looper looper) {
225 super(looper);
226 }
227
228 @Override
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;
joonhunshin39710022022-01-14 11:53:58 +0000244 case EVENT_MULTI_SIM_CONFIGURATION_CHANGE:
245 int activeModemCount = (int) ((AsyncResult) msg.obj).result;
246 onMultiSimConfigChanged(activeModemCount);
247 break;
joonhunshin2c3e4232021-11-28 07:32:01 +0000248 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
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
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
290 public void registerCallback(IFeatureProvisioningCallback localCallback) {
291 if (!mIFeatureProvisioningCallbackList.register(localCallback, (Object) mSubId)) {
292 log(LOG_PREFIX, mSlotId, "registration callback fail");
293 }
294 }
295
296 public void unregisterCallback(IFeatureProvisioningCallback localCallback) {
297 mIFeatureProvisioningCallbackList.unregister(localCallback);
298 }
299
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
313 public boolean hasCallblacks() {
314 int size = mIFeatureProvisioningCallbackList.beginBroadcast();
315 mIFeatureProvisioningCallbackList.finishBroadcast();
316
317 return (size > 0);
318 }
319
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
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
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
394 public void destroy() {
395 log("destroy");
396 mConnector.disconnect();
397 mConnector = null;
398 mReady = false;
399 mImsManager = null;
400 }
401
402 public @Nullable ImsManager getImsManager() {
403 return mImsManager;
404 }
405
406 @Override
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
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
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
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
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
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
494 private void setInitialProvisioningKeys(int subId) {
495 boolean required;
496 int value = ImsProvisioningLoader.STATUS_NOT_SET;
497
498 // updating KEY_VOLTE_PROVISIONING_STATUS
499 required = isProvisioningRequired(subId, CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE,
500 /*isMmTel*/true);
501 log(LOG_PREFIX, mSlotId,
502 "setInitialProvisioningKeys provisioning required(voice, lte) " + required);
503 if (required) {
504 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
505 CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE);
506 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
507 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
508 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
509 setProvisioningValue(KEY_VOLTE_PROVISIONING_STATUS, value);
510 }
511 }
512
513 // updating KEY_VT_PROVISIONING_STATUS
514 required = isProvisioningRequired(subId, CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE,
515 /*isMmTel*/true);
516 log(LOG_PREFIX, mSlotId,
517 "setInitialProvisioningKeys provisioning required(video, lte) " + required);
518 if (required) {
519 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
520 CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE);
521 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
522 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
523 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
524 setProvisioningValue(KEY_VT_PROVISIONING_STATUS, value);
525 }
526 }
527
528 // updating KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
529 required = isProvisioningRequired(subId, CAPABILITY_TYPE_VOICE,
530 REGISTRATION_TECH_IWLAN, /*isMmTel*/true);
531 log(LOG_PREFIX, mSlotId,
532 "setInitialProvisioningKeys provisioning required(voice, iwlan) " + required);
533 if (required) {
534 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
535 CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN);
536 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
537 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
538 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
539 setProvisioningValue(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, value);
540 }
541 }
542 }
543 }
544
545 private final class RcsFeatureListener implements FeatureConnector.Listener<RcsFeatureManager> {
546 private static final String LOG_PREFIX = "RcsFeatureListener";
547 private FeatureConnector<RcsFeatureManager> mConnector;
548 private RcsFeatureManager mRcsFeatureManager;
549 private boolean mReady = false;
550 // stores whether the initial provisioning key value should be notified to ImsService
551 private boolean mRequiredNotify = false;
552 private int mSubId;
553 private int mSlotId;
554
555 RcsFeatureListener(int slotId) {
556 log(LOG_PREFIX, slotId, "created");
557
558 mSlotId = slotId;
559 mSubId = getSubId(slotId);
560 mConnector = mRcsFeatureConnector.create(
561 mApp, slotId, this, new HandlerExecutor(mHandler), TAG);
562 mConnector.connect();
563 }
564
565 public void setSubId(int subId) {
566 if (mRequiredNotify && mReady) {
567 mRequiredNotify = false;
568 setInitialProvisioningKeys(subId);
569 }
570 if (mSubId == subId) {
571 log(LOG_PREFIX, mSlotId, "subId is not changed");
572 return;
573 }
574
575 mSubId = subId;
576 mSlotId = getSlotId(subId);
577 }
578
579 public void destroy() {
580 log(LOG_PREFIX, mSlotId, "destroy");
581 mConnector.disconnect();
582 mConnector = null;
583 mReady = false;
584 mRcsFeatureManager = null;
585 }
586
587 @Override
588 public void connectionReady(RcsFeatureManager manager, int subId) {
589 log(LOG_PREFIX, mSlotId, "connection ready");
590 mReady = true;
591 mRcsFeatureManager = manager;
592
593 onRcsAvailable();
594 }
595
596 @Override
597 public void connectionUnavailable(int reason) {
598 log(LOG_PREFIX, mSlotId, "connection unavailable");
599 mReady = false;
600 mRcsFeatureManager = null;
601
602 // keep the callback for other reason
603 if (reason == FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED) {
604 onRcsUnavailable();
605 }
606 }
607
608 public int setProvisioningValue(int key, int value) {
609 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
610
611 if (!mReady) {
612 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
613 return retVal;
614 }
615
616 try {
617 // getConfigInterface() will return not null or throw the ImsException
618 // need not null checking
619 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
620 retVal = imsConfig.setConfig(key, value);
621 log(LOG_PREFIX, mSlotId, "setConfig called with key " + key + " value " + value);
622 } catch (ImsException e) {
623 logw(LOG_PREFIX, mSlotId,
624 "setConfig operation failed for key =" + key
625 + ", value =" + value + ". Exception:" + e.getMessage());
626 }
627 return retVal;
628 }
629
630 public int getProvisioningValue(int key) {
631 if (!mReady) {
632 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
633 return INVALID_VALUE;
634 }
635
636 int retValue = INVALID_VALUE;
637 try {
638 // getConfigInterface() will return not null or throw the ImsException
639 // need not null checking
640 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
641 retValue = imsConfig.getConfigInt(key);
642 } catch (ImsException e) {
643 logw(LOG_PREFIX, mSlotId,
644 "getConfig operation failed for key =" + key
645 + ", value =" + retValue + ". Exception:" + e.getMessage());
646 }
647 return retValue;
648 }
649
650 public void onRcsAvailable() {
651 log(LOG_PREFIX, mSlotId, "onRcsAvailable");
652
653 if (isValidSubId(mSubId)) {
654 mRequiredNotify = false;
655
656 // notify provisioning key value to ImsService
657 setInitialProvisioningKeys(mSubId);
658 } else {
659 // wait until subId is valid
660 mRequiredNotify = true;
661 }
662 }
663
664 public void onRcsUnavailable() {
665 log(LOG_PREFIX, mSlotId, "onRcsUnavailable");
666
667 try {
668 // delete all callbacks reference from ProvisioningManager
669 mProvisioningCallbackManagersSlotMap.get(getSlotId(mSubId)).clear();
670 } catch (NullPointerException e) {
671 logw(LOG_PREFIX, getSlotId(mSubId), "can not find callback manager to clear");
672 }
673 }
674
675 private void setInitialProvisioningKeys(int subId) {
676 boolean required;
677 int value = ImsProvisioningLoader.STATUS_NOT_SET;
678
679 // KEY_EAB_PROVISIONING_STATUS
680 int capability = CAPABILITY_TYPE_PRESENCE_UCE;
681 // Assume that all radio techs have the same provisioning value
682 int tech = REGISTRATION_TECH_LTE;
683
684 required = isProvisioningRequired(subId, capability, tech, /*isMmTel*/false);
685 if (required) {
686 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
687 capability, tech);
688 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
689 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
690 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
691 setProvisioningValue(KEY_EAB_PROVISIONING_STATUS, value);
692 }
693 }
694 }
695 }
696
697 /**
698 * Do NOT use this directly, instead use {@link #getInstance()}.
699 */
700 @VisibleForTesting
701 public ImsProvisioningController(PhoneGlobals app, int numSlot, Looper looper,
702 MmTelFeatureConnector mmTelFeatureConnector, RcsFeatureConnector rcsFeatureConnector,
703 ImsProvisioningLoader imsProvisioningLoader) {
704 log("ImsProvisioningController");
705 mApp = app;
706 mNumSlot = numSlot;
707 mHandler = new MessageHandler(looper);
708 mMmTelFeatureConnector = mmTelFeatureConnector;
709 mRcsFeatureConnector = rcsFeatureConnector;
710 mCarrierConfigManager = mApp.getSystemService(CarrierConfigManager.class);
711 mSubscriptionManager = mApp.getSystemService(SubscriptionManager.class);
712 mTelephonyRegistryManager = mApp.getSystemService(TelephonyRegistryManager.class);
713 mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
714 mSubChangedListener, mSubChangedListener.getHandlerExecutor());
715 mImsProvisioningLoader = imsProvisioningLoader;
716
joonhunshin39710022022-01-14 11:53:58 +0000717 PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
718 EVENT_MULTI_SIM_CONFIGURATION_CHANGE, null);
719
joonhunshin2c3e4232021-11-28 07:32:01 +0000720 initialize(numSlot);
721 }
722
723 private void initialize(int numSlot) {
724 for (int i = 0; i < numSlot; i++) {
725 MmTelFeatureListener m = new MmTelFeatureListener(i);
726 mMmTelFeatureListenersSlotMap.put(i, m);
727
728 RcsFeatureListener r = new RcsFeatureListener(i);
729 mRcsFeatureListenersSlotMap.put(i, r);
730
731 ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
732 mProvisioningCallbackManagersSlotMap.put(i, p);
733 }
734 }
735
joonhunshin39710022022-01-14 11:53:58 +0000736 private void onMultiSimConfigChanged(int newNumSlot) {
737 log("onMultiSimConfigChanged: NumSlot " + mNumSlot + " newNumSlot " + newNumSlot);
738
739 if (mNumSlot < newNumSlot) {
740 for (int i = mNumSlot; i < newNumSlot; i++) {
741 MmTelFeatureListener m = new MmTelFeatureListener(i);
742 mMmTelFeatureListenersSlotMap.put(i, m);
743
744 RcsFeatureListener r = new RcsFeatureListener(i);
745 mRcsFeatureListenersSlotMap.put(i, r);
746
747 ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
748 mProvisioningCallbackManagersSlotMap.put(i, p);
749 }
750 } else if (mNumSlot > newNumSlot) {
751 for (int i = (mNumSlot - 1); i > (newNumSlot - 1); i--) {
752 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(i);
753 mMmTelFeatureListenersSlotMap.remove(i);
754 m.destroy();
755
756 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(i);
757 mRcsFeatureListenersSlotMap.remove(i);
758 r.destroy();
759
760 ProvisioningCallbackManager p = mProvisioningCallbackManagersSlotMap.get(i);
761 mProvisioningCallbackManagersSlotMap.remove(i);
762 p.clear();
763 }
764 }
765
766 mNumSlot = newNumSlot;
767 }
768
joonhunshin2c3e4232021-11-28 07:32:01 +0000769 /**
770 * destroy the instance
771 */
772 @VisibleForTesting
773 public void destroy() {
774 log("destroy");
775
776 mHandler.getLooper().quit();
777
778 mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
779
780 for (int i = 0; i < mMmTelFeatureListenersSlotMap.size(); i++) {
781 mMmTelFeatureListenersSlotMap.get(i).destroy();
782 }
783 mMmTelFeatureListenersSlotMap.clear();
784
785 for (int i = 0; i < mRcsFeatureListenersSlotMap.size(); i++) {
786 mRcsFeatureListenersSlotMap.get(i).destroy();
787 }
788 mRcsFeatureListenersSlotMap.clear();
789
790 for (int i = 0; i < mProvisioningCallbackManagersSlotMap.size(); i++) {
791 mProvisioningCallbackManagersSlotMap.get(i).clear();
792 }
793 }
794
795 /**
796 * create an instance
797 */
798 @VisibleForTesting
799 public static ImsProvisioningController make(PhoneGlobals app, int numSlot) {
800 synchronized (ImsProvisioningController.class) {
801 if (sInstance == null) {
802 Rlog.i(TAG, "ImsProvisioningController created");
803 HandlerThread handlerThread = new HandlerThread(TAG);
804 handlerThread.start();
805 sInstance = new ImsProvisioningController(app, numSlot, handlerThread.getLooper(),
806 ImsManager::getConnector, RcsFeatureManager::getConnector,
807 new ImsProvisioningLoader(app));
808 }
809 }
810 return sInstance;
811 }
812
813 /**
814 * Gets a ImsProvisioningController instance
815 */
816 @VisibleForTesting
817 public static ImsProvisioningController getInstance() {
818 synchronized (ImsProvisioningController.class) {
819 return sInstance;
820 }
821 }
822
823 /**
824 * Register IFeatureProvisioningCallback from ProvisioningManager
825 */
826
827 @VisibleForTesting
828 public void addFeatureProvisioningChangedCallback(int subId,
829 IFeatureProvisioningCallback callback) {
830 if (callback == null) {
831 throw new IllegalArgumentException("provisioning callback can't be null");
832 }
833 int slotId = getSlotId(subId);
834 if (slotId < 0 || slotId >= mNumSlot) {
835 throw new IllegalArgumentException("subscription id is not available");
836 }
837
838 try {
839 mProvisioningCallbackManagersSlotMap.get(slotId).registerCallback(callback);
840 log("Feature Provisioning Callback registered.");
841 } catch (NullPointerException e) {
842 logw("can not access callback manager to add callback");
843 }
844 }
845
846 /**
847 * Remove IFeatureProvisioningCallback
848 */
849 @VisibleForTesting
850 public void removeFeatureProvisioningChangedCallback(int subId,
851 IFeatureProvisioningCallback callback) {
852 if (callback == null) {
853 throw new IllegalArgumentException("provisioning callback can't be null");
854 }
855
856 int slotId = getSlotId(subId);
857 if (slotId < 0 || slotId >= mNumSlot) {
858 throw new IllegalArgumentException("subscription id is not available");
859 }
860
861 try {
862 mProvisioningCallbackManagersSlotMap.get(slotId).unregisterCallback(callback);
863 log("Feature Provisioning Callback removed.");
864 } catch (NullPointerException e) {
865 logw("can not access callback manager to remove callback");
866 }
867 }
868
869 /**
joonhunshinf9411cb2022-01-17 07:37:15 +0000870 * return the boolean whether MmTel capability is required provisioning or not
joonhunshin2c3e4232021-11-28 07:32:01 +0000871 */
872 @VisibleForTesting
873 public boolean isImsProvisioningRequiredForCapability(int subId, int capability, int tech) {
874 // check subId
875 int slotId = getSlotId(subId);
876 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
877 loge("Fail to retrieve slotId from subId");
878 throw new IllegalArgumentException("subscribe id is invalid");
879 }
880
881 // check valid capability
882 if (!(MMTEL_CAPABILITY_MIN < capability && capability < MMTEL_CAPABILITY_MAX)) {
883 throw new IllegalArgumentException("MmTel capability '" + capability + "' is invalid");
884 }
885
886 // check valid radio tech
887 if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
888 log("Ims not matched radio tech " + tech);
889 throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
890 }
891
892 boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/true);
893
894 log("isImsProvisioningRequiredForCapability capability " + capability
895 + " tech " + tech + " return value " + retVal);
896
897 return retVal;
898 }
899
900 /**
joonhunshinf9411cb2022-01-17 07:37:15 +0000901 * return the boolean whether RCS capability is required provisioning or not
joonhunshin2c3e4232021-11-28 07:32:01 +0000902 */
903 @VisibleForTesting
904 public boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech) {
905 // check slotId and Phone object
906 int slotId = getSlotId(subId);
907 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
908 loge("Fail to retrieve slotId from subId");
909 throw new IllegalArgumentException("subscribe id is invalid");
910 }
911
912 // check valid capability
913 if (!(RCS_CAPABILITY_MIN < capability && capability < RCS_CAPABILITY_MAX)) {
914 throw new IllegalArgumentException("Rcs capability '" + capability + "' is invalid");
915 }
916
917 // check valid radio tech
918 if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
919 log("Rcs not matched radio tech " + tech);
920 throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
921 }
922
923 boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/false);
924
925 log("isRcsProvisioningRequiredForCapability capability " + capability
926 + " tech " + tech + " return value " + retVal);
927
928 return retVal;
929 }
930
931 /**
932 * return the provisioning status for MmTel capability in specific radio tech
933 */
934 @VisibleForTesting
935 public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) {
936 boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
937 if (!mmTelProvisioned) { // provisioning not required
938 log("getImsProvisioningStatusForCapability : not required "
939 + " capability " + capability + " tech " + tech);
940 return true;
941 }
942
943 // read value from ImsProvisioningLoader
944 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
945 capability, tech);
946 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
947 // not set means initial value
948 // read data from vendor ImsService and store that in ImsProvisioningLoader
949 result = getValueFromImsService(subId, capability, tech);
950 mmTelProvisioned = getBoolValue(result);
951 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
952 setAndNotifyMmTelProvisioningValue(subId, capability, tech, mmTelProvisioned);
953 }
954 } else {
955 mmTelProvisioned = getBoolValue(result);
956 }
957
958 log("getImsProvisioningStatusForCapability : "
959 + " capability " + capability
960 + " tech " + tech
961 + " result " + mmTelProvisioned);
962 return mmTelProvisioned;
963 }
964
965 /**
966 * set MmTel provisioning status in specific tech
967 */
968 @VisibleForTesting
969 public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
970 boolean isProvisioned) {
971 boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
972 if (!mmTelProvisioned) { // provisioning not required
973 log("setImsProvisioningStatusForCapability : not required "
974 + " capability " + capability + " tech " + tech);
975 return;
976 }
977
978 // write value to ImsProvisioningLoader
979 boolean isChanged = setAndNotifyMmTelProvisioningValue(subId, capability, tech,
980 isProvisioned);
981 if (!isChanged) {
982 log("status not changed mmtel capability " + capability + " tech " + tech);
983 return;
984 }
985
986 int slotId = getSlotId(subId);
987 // find matched key from capability and tech
988 int value = getIntValue(isProvisioned);
989 int key = getKeyFromCapability(capability, tech);
990 if (key != INVALID_VALUE) {
991 log("setImsProvisioningStatusForCapability : matched key " + key);
992 try {
993 // set key and value to vendor ImsService for MmTel
994 mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
995
996 // notify provisioning status changed to ImsManager
997 updateImsServiceConfig(subId);
998 } catch (NullPointerException e) {
999 loge("can not access MmTelFeatureListener with capability " + capability);
1000 }
1001 }
1002 }
1003
1004 /**
1005 * return the provisioning status for RCS capability in specific radio tech
1006 */
1007 @VisibleForTesting
1008 public boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech) {
1009 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1010 if (!rcsProvisioned) { // provisioning not required
1011 log("getRcsProvisioningStatusForCapability : not required"
1012 + " capability " + capability + " tech " + tech);
1013 return true;
1014 }
1015
1016 // read data from ImsProvisioningLoader
1017 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1018 capability, tech);
1019 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1020 // not set means initial value
1021 // read data from vendor ImsService and store that in ImsProvisioningLoader
1022 result = getRcsValueFromImsService(subId, capability);
1023 rcsProvisioned = getBoolValue(result);
1024 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
1025 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, rcsProvisioned);
1026 }
1027 } else {
1028 rcsProvisioned = getBoolValue(result);
1029 }
1030
1031 log("getRcsProvisioningStatusForCapability : "
1032 + " capability " + capability
1033 + " tech " + tech
1034 + " result " + rcsProvisioned);
1035 return rcsProvisioned;
1036 }
1037
1038 /**
1039 * set RCS provisioning status in specific tech
1040 */
1041 @VisibleForTesting
1042 public void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
1043 boolean isProvisioned) {
1044 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1045 if (!rcsProvisioned) { // provisioning not required
1046 log("set rcs provisioning status but not required");
1047 return;
1048 }
1049
1050 // write status using ImsProvisioningLoader
1051 boolean isChanged = setAndNotifyRcsProvisioningValue(subId, capability, tech,
1052 isProvisioned);
1053 if (!isChanged) {
1054 log("status not changed rcs capability " + capability + " tech " + tech);
1055 return;
1056 }
1057
1058 int slotId = getSlotId(subId);
1059 int key = ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
1060 int value = getIntValue(isProvisioned);
1061 try {
1062 // set key and value to vendor ImsService for Rcs
1063 mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1064 } catch (NullPointerException e) {
1065 loge("can not access RcsFeatureListener with capability " + capability);
1066 }
1067 }
1068
1069 /**
1070 * set RCS provisioning status in specific key and value
1071 * @param key integer key, defined as one of
1072 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1073 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1074 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1075 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1076 * @param value in Integer format.
1077 * @return the result of setting the configuration value, defined as one of
1078 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1079 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1080 */
1081 @VisibleForTesting
1082 public int setProvisioningValue(int subId, int key, int value) {
1083 log("setProvisioningValue");
1084
1085 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
1086 // check key value
1087 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1088 log("not matched key " + key);
1089 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1090 }
1091
1092 // check subId
1093 int slotId = getSlotId(subId);
1094 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1095 loge("Fail to retrieve slotId from subId");
1096 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1097 }
1098
1099 try {
1100 if (key == KEY_EAB_PROVISIONING_STATUS) {
1101 // set key and value to vendor ImsService for Rcs
1102 retVal = mRcsFeatureListenersSlotMap.get(slotId)
1103 .setProvisioningValue(key, value);
1104 } else {
1105 // set key and value to vendor ImsService for MmTel
1106 retVal = mMmTelFeatureListenersSlotMap.get(slotId)
1107 .setProvisioningValue(key, value);
1108 }
1109 } catch (NullPointerException e) {
1110 loge("can not access FeatureListener to set provisioning value");
1111 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1112 }
1113
1114 // update and notify provisioning status changed capability and tech from key
1115 updateCapabilityTechFromKey(subId, key, value);
1116
1117 if (key != KEY_EAB_PROVISIONING_STATUS) {
1118 // notify provisioning status changed to ImsManager
1119 updateImsServiceConfig(subId);
1120 }
1121
1122 return retVal;
1123 }
1124
1125 /**
1126 * get RCS provisioning status in specific key and value
1127 * @param key integer key, defined as one of
1128 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1129 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1130 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1131 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1132 * @return the result of setting the configuration value, defined as one of
1133 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1134 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1135 * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN}
1136 */
1137 @VisibleForTesting
1138 public int getProvisioningValue(int subId, int key) {
1139 log("getProvisioningValue");
1140
1141 // check key value
1142 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1143 log("not matched key " + key);
1144 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1145 }
1146
1147 // check subId
1148 int slotId = getSlotId(subId);
1149 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1150 loge("Fail to retrieve slotId from subId");
1151 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1152 }
1153
1154 // check data from ImsProvisioningLoader
1155 int capability = getCapabilityFromKey(key);
1156 int tech = getTechFromKey(key);
1157 int result;
1158 if (capability != INVALID_VALUE && tech != INVALID_VALUE) {
1159 if (key == KEY_EAB_PROVISIONING_STATUS) {
1160 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1161 capability, tech);
1162 } else {
1163 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1164 capability, tech);
1165 }
1166 if (result != ImsProvisioningLoader.STATUS_NOT_SET) {
1167 return result;
1168 }
1169 }
1170
1171 // get data from ImsService, update it in ImsProvisioningLoader
1172 if (key == KEY_EAB_PROVISIONING_STATUS) {
1173 result = getRcsValueFromImsService(subId, capability);
1174 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1175 logw("getProvisioningValue : fail to get data from ImsService capability"
1176 + capability);
1177 return result;
1178 }
1179 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, getBoolValue(result));
1180 return result;
1181 } else {
1182 result = getValueFromImsService(subId, capability, tech);
1183 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1184 logw("getProvisioningValue : fail to get data from ImsService capability"
1185 + capability);
1186 return result;
1187 }
1188 setAndNotifyMmTelProvisioningValue(subId, capability, tech, getBoolValue(result));
1189 return result;
1190 }
1191 }
1192
1193 /**
1194 * get the handler
1195 */
1196 @VisibleForTesting
1197 public Handler getHandler() {
1198 return mHandler;
1199 }
1200
1201 private boolean isProvisioningRequired(int subId, int capability, int tech, boolean isMmTel) {
joonhunshinf9411cb2022-01-17 07:37:15 +00001202 int[] techArray;
1203 techArray = getTechsFromCarrierConfig(subId, capability, isMmTel);
1204 if (techArray == null) {
1205 logw("isProvisioningRequired : getTechsFromCarrierConfig failed");
1206 // not exist in CarrierConfig that means provisioning is not required
joonhunshin2c3e4232021-11-28 07:32:01 +00001207 return false;
1208 }
1209
joonhunshin2c3e4232021-11-28 07:32:01 +00001210 // compare with carrier config
joonhunshinf9411cb2022-01-17 07:37:15 +00001211 if (Arrays.stream(techArray).anyMatch(keyValue -> keyValue == tech)) {
1212 // existing same tech means provisioning required
1213 return true;
joonhunshin2c3e4232021-11-28 07:32:01 +00001214 }
1215
1216 log("isProvisioningRequired : not matched capability " + capability + " tech " + tech);
1217 return false;
1218 }
1219
1220 @VisibleForTesting
joonhunshinf9411cb2022-01-17 07:37:15 +00001221 protected int[] getTechsFromCarrierConfig(int subId, int capability, boolean isMmTel) {
1222 String featureKey;
1223 String capabilityKey;
1224 if (isMmTel) {
1225 featureKey = CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE;
1226 capabilityKey = KEYS_MMTEL_CAPABILITY.get(capability);
1227 } else {
1228 featureKey = CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE;
1229 capabilityKey = KEYS_RCS_CAPABILITY.get(capability);
1230 }
joonhunshin2c3e4232021-11-28 07:32:01 +00001231
joonhunshinf9411cb2022-01-17 07:37:15 +00001232 if (capabilityKey != null) {
1233 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1234 if (imsCarrierConfigs == null) {
1235 log("getTechsFromCarrierConfig : imsCarrierConfigs null");
1236 return null;
1237 }
1238
1239 PersistableBundle provisioningBundle =
1240 imsCarrierConfigs.getPersistableBundle(featureKey);
1241 if (provisioningBundle == null) {
1242 log("getTechsFromCarrierConfig : provisioningBundle null");
1243 return null;
1244 }
1245
1246 return provisioningBundle.getIntArray(capabilityKey);
1247 }
1248
1249 return null;
joonhunshin2c3e4232021-11-28 07:32:01 +00001250 }
1251
1252 private int getValueFromImsService(int subId, int capability, int tech) {
1253 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1254
1255 // operation is based on capability
1256 switch (capability) {
1257 case CAPABILITY_TYPE_VOICE:
1258 int item = (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
1259 ? ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
1260 : ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1261 // read data from vendor ImsService
1262 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1263 .getProvisioningValue(item);
1264 break;
1265 case CAPABILITY_TYPE_VIDEO:
1266 // read data from vendor ImsService
1267 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1268 .getProvisioningValue(ProvisioningManager.KEY_VT_PROVISIONING_STATUS);
1269 break;
1270 default:
1271 log("Capability " + capability + " has been provisioning");
1272 break;
1273 }
1274
1275 return config;
1276 }
1277
1278 private int getRcsValueFromImsService(int subId, int capability) {
1279 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1280
1281 if (capability == CAPABILITY_TYPE_PRESENCE_UCE) {
1282 try {
1283 config = mRcsFeatureListenersSlotMap.get(getSlotId(subId))
1284 .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1285 } catch (NullPointerException e) {
1286 logw("can not access RcsFeatureListener");
1287 }
1288 } else {
1289 log("Capability " + capability + " has been provisioning");
1290 }
1291
1292 return config;
1293 }
1294
1295 private void onSubscriptionsChanged() {
1296 for (int index = 0; index < mMmTelFeatureListenersSlotMap.size(); index++) {
1297 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(index);
1298 m.setSubId(getSubId(index));
1299 }
1300 for (int index = 0; index < mRcsFeatureListenersSlotMap.size(); index++) {
1301 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(index);
1302 r.setSubId(getSubId(index));
1303 }
1304 for (int index = 0; index < mProvisioningCallbackManagersSlotMap.size(); index++) {
1305 ProvisioningCallbackManager m = mProvisioningCallbackManagersSlotMap.get(index);
1306 m.setSubId(getSubId(index));
1307 }
1308 }
1309
1310 private void updateCapabilityTechFromKey(int subId, int key, int value) {
1311 boolean isProvisioned = getBoolValue(value);
1312 int capability = getCapabilityFromKey(key);
1313 int tech = getTechFromKey(key);
1314
1315 if (capability == INVALID_VALUE || tech == INVALID_VALUE) {
1316 logw("updateCapabilityTechFromKey : unknown key " + key);
1317 return;
1318 }
1319
1320 if (key == KEY_VOLTE_PROVISIONING_STATUS
1321 || key == KEY_VT_PROVISIONING_STATUS
1322 || key == KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE) {
1323 setAndNotifyMmTelProvisioningValue(subId, capability, tech, isProvisioned);
1324 }
1325 if (key == KEY_EAB_PROVISIONING_STATUS) {
1326 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, isProvisioned);
1327 }
1328 }
1329
1330 private int getCapabilityFromKey(int key) {
1331 int capability;
1332 switch (key) {
1333 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1334 // intentional fallthrough
1335 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1336 capability = CAPABILITY_TYPE_VOICE;
1337 break;
1338 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1339 capability = CAPABILITY_TYPE_VIDEO;
1340 break;
1341 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1342 // default CAPABILITY_TYPE_PRESENCE_UCE used for KEY_EAB_PROVISIONING_STATUS
1343 capability = CAPABILITY_TYPE_PRESENCE_UCE;
1344 break;
1345 default:
1346 capability = INVALID_VALUE;
1347 break;
1348 }
1349 return capability;
1350 }
1351
1352 private int getTechFromKey(int key) {
1353 int tech;
1354 switch (key) {
1355 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1356 tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
1357 break;
1358 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1359 // intentional fallthrough
1360 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1361 // intentional fallthrough
1362 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1363 tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
1364 break;
1365 default:
1366 tech = INVALID_VALUE;
1367 break;
1368 }
1369 return tech;
1370 }
1371
1372 private int getKeyFromCapability(int capability, int tech) {
1373 int key = INVALID_VALUE;
1374 if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_IWLAN) {
1375 key = ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
1376 } else if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_LTE) {
1377 key = ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1378 } else if (capability == CAPABILITY_TYPE_VIDEO && tech == REGISTRATION_TECH_LTE) {
1379 key = ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
1380 }
1381
1382 return key;
1383 }
1384
1385 protected int getSubId(int slotId) {
1386 final int[] subIds = mSubscriptionManager.getSubscriptionIds(slotId);
1387 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1388 if (subIds != null && subIds.length >= 1) {
1389 subId = subIds[0];
1390 }
1391
1392 return subId;
1393 }
1394
1395 protected int getSlotId(int subId) {
1396 return mSubscriptionManager.getPhoneId(subId);
1397 }
1398
1399 protected ImsConfig getImsConfig(ImsManager imsManager) throws ImsException {
1400 return imsManager.getConfigInterface();
1401 }
1402
1403 protected ImsConfig getImsConfig(IImsConfig iImsConfig) {
1404 return new ImsConfig(iImsConfig);
1405 }
1406
1407 private int getIntValue(boolean isProvisioned) {
1408 return isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
1409 : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
1410 }
1411
1412 private boolean getBoolValue(int value) {
1413 return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED ? true : false;
1414 }
1415
1416 private boolean setAndNotifyMmTelProvisioningValue(int subId, int capability, int tech,
1417 boolean isProvisioned) {
1418 boolean changed = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_MMTEL,
1419 capability, tech, isProvisioned);
1420 // notify MmTel capability changed
1421 if (changed) {
1422 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1423 getSlotId(subId), 0, (Object) new FeatureProvisioningData(
1424 capability, tech, isProvisioned, /*isMmTel*/true)));
1425 }
1426
1427 return changed;
1428 }
1429
1430 private boolean setAndNotifyRcsProvisioningValue(int subId, int capability, int tech,
1431 boolean isProvisioned) {
1432 boolean isChanged = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_RCS,
1433 capability, tech, isProvisioned);
1434
1435 if (isChanged) {
1436 int slotId = getSlotId(subId);
1437
1438 // notify RCS capability changed
1439 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1440 slotId, 0, (Object) new FeatureProvisioningData(
1441 capability, tech, isProvisioned, /*isMmtel*/false)));
1442 }
1443
1444 return isChanged;
1445 }
1446
1447 private boolean setAndNotifyRcsProvisioningValueForAllTech(int subId, int capability,
1448 boolean isProvisioned) {
1449 boolean isChanged = false;
1450
1451 for (int tech : LOCAL_RADIO_TECHS) {
1452 isChanged |= setAndNotifyRcsProvisioningValue(subId, capability, tech, isProvisioned);
1453 }
1454
1455 return isChanged;
1456 }
1457
1458 private void updateImsServiceConfig(int subId) {
1459 try {
1460 ImsManager imsManager = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1461 .getImsManager();
1462 imsManager.updateImsServiceConfig();
1463 log("updateImsServiceConfig");
1464 } catch (NullPointerException e) {
1465 loge("updateImsServiceConfig : ImsService not ready");
1466 }
1467 }
1468
1469 protected boolean isValidSubId(int subId) {
1470 int slotId = getSlotId(subId);
1471 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1472 return false;
1473 }
1474
1475 return true;
1476 }
1477
1478 private void log(String s) {
1479 Rlog.d(TAG, s);
1480 }
1481
1482 private void log(String prefix, int slotId, String s) {
1483 Rlog.d(TAG, prefix + "[" + slotId + "] " + s);
1484 }
1485
1486 private void logi(String prefix, int slotId, String s) {
1487 Rlog.i(TAG, prefix + "[" + slotId + "] " + s);
1488 }
1489
1490 private void logw(String s) {
1491 Rlog.w(TAG, s);
1492 }
1493
1494 private void logw(String prefix, int slotId, String s) {
1495 Rlog.w(TAG, prefix + "[" + slotId + "] " + s);
1496 }
1497
1498 private void loge(String s) {
1499 Rlog.e(TAG, s);
1500 }
1501
1502 private void loge(String prefix, int slotId, String s) {
1503 Rlog.e(TAG, prefix + "[" + slotId + "] " + s);
1504 }
1505}