blob: 7a6c3e0225989a3c8cbe7526ae67d1b11a1ae9dc [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;
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
joonhunshin6cd38ac2022-02-03 06:37:55 +0000127 * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT
joonhunshinf9411cb2022-01-17 07:37:15 +0000128 */
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,
joonhunshin6cd38ac2022-02-03 06:37:55 +0000134 CAPABILITY_TYPE_CALL_COMPOSER, Ims.KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY
joonhunshinf9411cb2022-01-17 07:37:15 +0000135 );
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}
Hakjun Choi4c6c10e2022-03-15 05:08:02 +0000205 * {{@link android.telephony.ims.ImsRcsManager.RcsImsCapabilityFlag} for RCS services}
joonhunshin2c3e4232021-11-28 07:32:01 +0000206 * {{@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
joonhunshin10e74ea2022-04-29 06:29:35 +0000892 // check new carrier config first KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
joonhunshin2c3e4232021-11-28 07:32:01 +0000893 boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/true);
894
joonhunshin10e74ea2022-04-29 06:29:35 +0000895 // if that returns false, check deprecated carrier config
896 // KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
897 if (!retVal && (capability == CAPABILITY_TYPE_VOICE
898 || capability == CAPABILITY_TYPE_VIDEO
899 || capability == CAPABILITY_TYPE_UT)) {
900 String key = CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL;
901 if (capability == CAPABILITY_TYPE_UT) {
902 key = CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL;
903 }
904
905 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
906 if (imsCarrierConfigs != null) {
907 retVal = imsCarrierConfigs.getBoolean(key);
908 } else {
909 retVal = CarrierConfigManager.getDefaultConfig().getBoolean(key);
910 }
911 }
912
joonhunshin2c3e4232021-11-28 07:32:01 +0000913 log("isImsProvisioningRequiredForCapability capability " + capability
914 + " tech " + tech + " return value " + retVal);
915
916 return retVal;
917 }
918
919 /**
joonhunshinf9411cb2022-01-17 07:37:15 +0000920 * return the boolean whether RCS capability is required provisioning or not
joonhunshin2c3e4232021-11-28 07:32:01 +0000921 */
922 @VisibleForTesting
923 public boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech) {
924 // check slotId and Phone object
925 int slotId = getSlotId(subId);
926 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
927 loge("Fail to retrieve slotId from subId");
928 throw new IllegalArgumentException("subscribe id is invalid");
929 }
930
931 // check valid capability
932 if (!(RCS_CAPABILITY_MIN < capability && capability < RCS_CAPABILITY_MAX)) {
933 throw new IllegalArgumentException("Rcs capability '" + capability + "' is invalid");
934 }
935
936 // check valid radio tech
937 if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
938 log("Rcs not matched radio tech " + tech);
939 throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
940 }
941
joonhunshin10e74ea2022-04-29 06:29:35 +0000942 // check new carrier config first KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
joonhunshin2c3e4232021-11-28 07:32:01 +0000943 boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/false);
944
joonhunshin10e74ea2022-04-29 06:29:35 +0000945 // if that returns false, check deprecated carrier config
946 // KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
947 if (!retVal) {
948 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
949 if (imsCarrierConfigs != null) {
950 retVal = imsCarrierConfigs.getBoolean(
951 CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL);
952 } else {
953 retVal = CarrierConfigManager.getDefaultConfig().getBoolean(
954 CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL);
955 }
956 }
957
joonhunshin2c3e4232021-11-28 07:32:01 +0000958 log("isRcsProvisioningRequiredForCapability capability " + capability
959 + " tech " + tech + " return value " + retVal);
960
961 return retVal;
962 }
963
964 /**
965 * return the provisioning status for MmTel capability in specific radio tech
966 */
967 @VisibleForTesting
968 public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) {
969 boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
970 if (!mmTelProvisioned) { // provisioning not required
971 log("getImsProvisioningStatusForCapability : not required "
972 + " capability " + capability + " tech " + tech);
973 return true;
974 }
975
976 // read value from ImsProvisioningLoader
977 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
978 capability, tech);
979 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
980 // not set means initial value
981 // read data from vendor ImsService and store that in ImsProvisioningLoader
982 result = getValueFromImsService(subId, capability, tech);
983 mmTelProvisioned = getBoolValue(result);
984 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
985 setAndNotifyMmTelProvisioningValue(subId, capability, tech, mmTelProvisioned);
986 }
987 } else {
988 mmTelProvisioned = getBoolValue(result);
989 }
990
991 log("getImsProvisioningStatusForCapability : "
992 + " capability " + capability
993 + " tech " + tech
994 + " result " + mmTelProvisioned);
995 return mmTelProvisioned;
996 }
997
998 /**
999 * set MmTel provisioning status in specific tech
1000 */
1001 @VisibleForTesting
1002 public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
1003 boolean isProvisioned) {
1004 boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
1005 if (!mmTelProvisioned) { // provisioning not required
1006 log("setImsProvisioningStatusForCapability : not required "
1007 + " capability " + capability + " tech " + tech);
1008 return;
1009 }
1010
1011 // write value to ImsProvisioningLoader
1012 boolean isChanged = setAndNotifyMmTelProvisioningValue(subId, capability, tech,
1013 isProvisioned);
1014 if (!isChanged) {
1015 log("status not changed mmtel capability " + capability + " tech " + tech);
1016 return;
1017 }
1018
1019 int slotId = getSlotId(subId);
1020 // find matched key from capability and tech
1021 int value = getIntValue(isProvisioned);
1022 int key = getKeyFromCapability(capability, tech);
1023 if (key != INVALID_VALUE) {
1024 log("setImsProvisioningStatusForCapability : matched key " + key);
1025 try {
1026 // set key and value to vendor ImsService for MmTel
1027 mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001028 } catch (NullPointerException e) {
1029 loge("can not access MmTelFeatureListener with capability " + capability);
1030 }
1031 }
1032 }
1033
1034 /**
1035 * return the provisioning status for RCS capability in specific radio tech
1036 */
1037 @VisibleForTesting
1038 public boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech) {
1039 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1040 if (!rcsProvisioned) { // provisioning not required
1041 log("getRcsProvisioningStatusForCapability : not required"
1042 + " capability " + capability + " tech " + tech);
1043 return true;
1044 }
1045
1046 // read data from ImsProvisioningLoader
1047 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1048 capability, tech);
1049 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1050 // not set means initial value
1051 // read data from vendor ImsService and store that in ImsProvisioningLoader
1052 result = getRcsValueFromImsService(subId, capability);
1053 rcsProvisioned = getBoolValue(result);
1054 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
1055 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, rcsProvisioned);
1056 }
1057 } else {
1058 rcsProvisioned = getBoolValue(result);
1059 }
1060
1061 log("getRcsProvisioningStatusForCapability : "
1062 + " capability " + capability
1063 + " tech " + tech
1064 + " result " + rcsProvisioned);
1065 return rcsProvisioned;
1066 }
1067
1068 /**
1069 * set RCS provisioning status in specific tech
1070 */
1071 @VisibleForTesting
1072 public void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
1073 boolean isProvisioned) {
1074 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1075 if (!rcsProvisioned) { // provisioning not required
1076 log("set rcs provisioning status but not required");
1077 return;
1078 }
1079
1080 // write status using ImsProvisioningLoader
1081 boolean isChanged = setAndNotifyRcsProvisioningValue(subId, capability, tech,
1082 isProvisioned);
1083 if (!isChanged) {
1084 log("status not changed rcs capability " + capability + " tech " + tech);
1085 return;
1086 }
1087
1088 int slotId = getSlotId(subId);
1089 int key = ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
1090 int value = getIntValue(isProvisioned);
1091 try {
1092 // set key and value to vendor ImsService for Rcs
1093 mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1094 } catch (NullPointerException e) {
1095 loge("can not access RcsFeatureListener with capability " + capability);
1096 }
1097 }
1098
1099 /**
1100 * set RCS provisioning status in specific key and value
1101 * @param key integer key, defined as one of
1102 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1103 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1104 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1105 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1106 * @param value in Integer format.
1107 * @return the result of setting the configuration value, defined as one of
1108 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1109 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1110 */
1111 @VisibleForTesting
1112 public int setProvisioningValue(int subId, int key, int value) {
1113 log("setProvisioningValue");
1114
1115 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
1116 // check key value
1117 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1118 log("not matched key " + key);
1119 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1120 }
1121
1122 // check subId
1123 int slotId = getSlotId(subId);
1124 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1125 loge("Fail to retrieve slotId from subId");
1126 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1127 }
1128
1129 try {
1130 if (key == KEY_EAB_PROVISIONING_STATUS) {
1131 // set key and value to vendor ImsService for Rcs
1132 retVal = mRcsFeatureListenersSlotMap.get(slotId)
1133 .setProvisioningValue(key, value);
1134 } else {
1135 // set key and value to vendor ImsService for MmTel
1136 retVal = mMmTelFeatureListenersSlotMap.get(slotId)
1137 .setProvisioningValue(key, value);
1138 }
1139 } catch (NullPointerException e) {
1140 loge("can not access FeatureListener to set provisioning value");
1141 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1142 }
1143
1144 // update and notify provisioning status changed capability and tech from key
1145 updateCapabilityTechFromKey(subId, key, value);
1146
joonhunshin2c3e4232021-11-28 07:32:01 +00001147 return retVal;
1148 }
1149
1150 /**
1151 * get RCS provisioning status in specific key and value
1152 * @param key integer key, defined as one of
1153 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1154 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1155 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1156 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1157 * @return the result of setting the configuration value, defined as one of
1158 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1159 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1160 * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN}
1161 */
1162 @VisibleForTesting
1163 public int getProvisioningValue(int subId, int key) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001164 // check key value
1165 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1166 log("not matched key " + key);
1167 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1168 }
1169
1170 // check subId
1171 int slotId = getSlotId(subId);
1172 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1173 loge("Fail to retrieve slotId from subId");
1174 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1175 }
1176
1177 // check data from ImsProvisioningLoader
1178 int capability = getCapabilityFromKey(key);
1179 int tech = getTechFromKey(key);
1180 int result;
1181 if (capability != INVALID_VALUE && tech != INVALID_VALUE) {
1182 if (key == KEY_EAB_PROVISIONING_STATUS) {
1183 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1184 capability, tech);
1185 } else {
1186 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1187 capability, tech);
1188 }
1189 if (result != ImsProvisioningLoader.STATUS_NOT_SET) {
joonhunshin6cd38ac2022-02-03 06:37:55 +00001190 log("getProvisioningValue from loader : key " + key + " result " + result);
joonhunshin2c3e4232021-11-28 07:32:01 +00001191 return result;
1192 }
1193 }
1194
1195 // get data from ImsService, update it in ImsProvisioningLoader
1196 if (key == KEY_EAB_PROVISIONING_STATUS) {
1197 result = getRcsValueFromImsService(subId, capability);
1198 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1199 logw("getProvisioningValue : fail to get data from ImsService capability"
1200 + capability);
1201 return result;
1202 }
joonhunshin6cd38ac2022-02-03 06:37:55 +00001203 log("getProvisioningValue from vendor : key " + key + " result " + result);
1204
joonhunshin2c3e4232021-11-28 07:32:01 +00001205 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, getBoolValue(result));
1206 return result;
1207 } else {
1208 result = getValueFromImsService(subId, capability, tech);
1209 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1210 logw("getProvisioningValue : fail to get data from ImsService capability"
1211 + capability);
1212 return result;
1213 }
joonhunshin6cd38ac2022-02-03 06:37:55 +00001214 log("getProvisioningValue from vendor : key " + key + " result " + result);
1215
joonhunshin2c3e4232021-11-28 07:32:01 +00001216 setAndNotifyMmTelProvisioningValue(subId, capability, tech, getBoolValue(result));
1217 return result;
1218 }
1219 }
1220
1221 /**
1222 * get the handler
1223 */
1224 @VisibleForTesting
1225 public Handler getHandler() {
1226 return mHandler;
1227 }
1228
1229 private boolean isProvisioningRequired(int subId, int capability, int tech, boolean isMmTel) {
joonhunshinf9411cb2022-01-17 07:37:15 +00001230 int[] techArray;
1231 techArray = getTechsFromCarrierConfig(subId, capability, isMmTel);
1232 if (techArray == null) {
1233 logw("isProvisioningRequired : getTechsFromCarrierConfig failed");
1234 // not exist in CarrierConfig that means provisioning is not required
joonhunshin2c3e4232021-11-28 07:32:01 +00001235 return false;
1236 }
1237
joonhunshin2c3e4232021-11-28 07:32:01 +00001238 // compare with carrier config
joonhunshinf9411cb2022-01-17 07:37:15 +00001239 if (Arrays.stream(techArray).anyMatch(keyValue -> keyValue == tech)) {
1240 // existing same tech means provisioning required
1241 return true;
joonhunshin2c3e4232021-11-28 07:32:01 +00001242 }
1243
1244 log("isProvisioningRequired : not matched capability " + capability + " tech " + tech);
1245 return false;
1246 }
1247
joonhunshin10e74ea2022-04-29 06:29:35 +00001248 private int[] getTechsFromCarrierConfig(int subId, int capability, boolean isMmTel) {
joonhunshinf9411cb2022-01-17 07:37:15 +00001249 String featureKey;
1250 String capabilityKey;
1251 if (isMmTel) {
1252 featureKey = CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE;
1253 capabilityKey = KEYS_MMTEL_CAPABILITY.get(capability);
1254 } else {
1255 featureKey = CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE;
1256 capabilityKey = KEYS_RCS_CAPABILITY.get(capability);
1257 }
joonhunshin2c3e4232021-11-28 07:32:01 +00001258
joonhunshinf9411cb2022-01-17 07:37:15 +00001259 if (capabilityKey != null) {
1260 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1261 if (imsCarrierConfigs == null) {
1262 log("getTechsFromCarrierConfig : imsCarrierConfigs null");
1263 return null;
1264 }
1265
1266 PersistableBundle provisioningBundle =
1267 imsCarrierConfigs.getPersistableBundle(featureKey);
1268 if (provisioningBundle == null) {
1269 log("getTechsFromCarrierConfig : provisioningBundle null");
1270 return null;
1271 }
1272
1273 return provisioningBundle.getIntArray(capabilityKey);
1274 }
1275
1276 return null;
joonhunshin2c3e4232021-11-28 07:32:01 +00001277 }
1278
1279 private int getValueFromImsService(int subId, int capability, int tech) {
1280 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1281
1282 // operation is based on capability
1283 switch (capability) {
1284 case CAPABILITY_TYPE_VOICE:
1285 int item = (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
1286 ? ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
1287 : ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1288 // read data from vendor ImsService
1289 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1290 .getProvisioningValue(item);
1291 break;
1292 case CAPABILITY_TYPE_VIDEO:
1293 // read data from vendor ImsService
1294 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1295 .getProvisioningValue(ProvisioningManager.KEY_VT_PROVISIONING_STATUS);
1296 break;
1297 default:
1298 log("Capability " + capability + " has been provisioning");
1299 break;
1300 }
1301
1302 return config;
1303 }
1304
1305 private int getRcsValueFromImsService(int subId, int capability) {
1306 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1307
1308 if (capability == CAPABILITY_TYPE_PRESENCE_UCE) {
1309 try {
1310 config = mRcsFeatureListenersSlotMap.get(getSlotId(subId))
1311 .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1312 } catch (NullPointerException e) {
1313 logw("can not access RcsFeatureListener");
1314 }
1315 } else {
1316 log("Capability " + capability + " has been provisioning");
1317 }
1318
1319 return config;
1320 }
1321
1322 private void onSubscriptionsChanged() {
1323 for (int index = 0; index < mMmTelFeatureListenersSlotMap.size(); index++) {
1324 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(index);
1325 m.setSubId(getSubId(index));
1326 }
1327 for (int index = 0; index < mRcsFeatureListenersSlotMap.size(); index++) {
1328 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(index);
1329 r.setSubId(getSubId(index));
1330 }
1331 for (int index = 0; index < mProvisioningCallbackManagersSlotMap.size(); index++) {
1332 ProvisioningCallbackManager m = mProvisioningCallbackManagersSlotMap.get(index);
1333 m.setSubId(getSubId(index));
1334 }
1335 }
1336
1337 private void updateCapabilityTechFromKey(int subId, int key, int value) {
1338 boolean isProvisioned = getBoolValue(value);
1339 int capability = getCapabilityFromKey(key);
1340 int tech = getTechFromKey(key);
1341
1342 if (capability == INVALID_VALUE || tech == INVALID_VALUE) {
1343 logw("updateCapabilityTechFromKey : unknown key " + key);
1344 return;
1345 }
1346
1347 if (key == KEY_VOLTE_PROVISIONING_STATUS
1348 || key == KEY_VT_PROVISIONING_STATUS
1349 || key == KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE) {
1350 setAndNotifyMmTelProvisioningValue(subId, capability, tech, isProvisioned);
1351 }
1352 if (key == KEY_EAB_PROVISIONING_STATUS) {
1353 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, isProvisioned);
1354 }
1355 }
1356
1357 private int getCapabilityFromKey(int key) {
1358 int capability;
1359 switch (key) {
1360 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1361 // intentional fallthrough
1362 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1363 capability = CAPABILITY_TYPE_VOICE;
1364 break;
1365 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1366 capability = CAPABILITY_TYPE_VIDEO;
1367 break;
1368 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1369 // default CAPABILITY_TYPE_PRESENCE_UCE used for KEY_EAB_PROVISIONING_STATUS
1370 capability = CAPABILITY_TYPE_PRESENCE_UCE;
1371 break;
1372 default:
1373 capability = INVALID_VALUE;
1374 break;
1375 }
1376 return capability;
1377 }
1378
1379 private int getTechFromKey(int key) {
1380 int tech;
1381 switch (key) {
1382 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1383 tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
1384 break;
1385 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1386 // intentional fallthrough
1387 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1388 // intentional fallthrough
1389 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1390 tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
1391 break;
1392 default:
1393 tech = INVALID_VALUE;
1394 break;
1395 }
1396 return tech;
1397 }
1398
1399 private int getKeyFromCapability(int capability, int tech) {
1400 int key = INVALID_VALUE;
1401 if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_IWLAN) {
1402 key = ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
1403 } else if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_LTE) {
1404 key = ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1405 } else if (capability == CAPABILITY_TYPE_VIDEO && tech == REGISTRATION_TECH_LTE) {
1406 key = ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
1407 }
1408
1409 return key;
1410 }
1411
1412 protected int getSubId(int slotId) {
1413 final int[] subIds = mSubscriptionManager.getSubscriptionIds(slotId);
1414 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1415 if (subIds != null && subIds.length >= 1) {
1416 subId = subIds[0];
1417 }
1418
1419 return subId;
1420 }
1421
1422 protected int getSlotId(int subId) {
1423 return mSubscriptionManager.getPhoneId(subId);
1424 }
1425
1426 protected ImsConfig getImsConfig(ImsManager imsManager) throws ImsException {
1427 return imsManager.getConfigInterface();
1428 }
1429
1430 protected ImsConfig getImsConfig(IImsConfig iImsConfig) {
1431 return new ImsConfig(iImsConfig);
1432 }
1433
1434 private int getIntValue(boolean isProvisioned) {
1435 return isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
1436 : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
1437 }
1438
1439 private boolean getBoolValue(int value) {
1440 return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED ? true : false;
1441 }
1442
1443 private boolean setAndNotifyMmTelProvisioningValue(int subId, int capability, int tech,
1444 boolean isProvisioned) {
1445 boolean changed = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_MMTEL,
1446 capability, tech, isProvisioned);
1447 // notify MmTel capability changed
1448 if (changed) {
1449 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1450 getSlotId(subId), 0, (Object) new FeatureProvisioningData(
1451 capability, tech, isProvisioned, /*isMmTel*/true)));
1452 }
1453
1454 return changed;
1455 }
1456
1457 private boolean setAndNotifyRcsProvisioningValue(int subId, int capability, int tech,
1458 boolean isProvisioned) {
1459 boolean isChanged = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_RCS,
1460 capability, tech, isProvisioned);
1461
1462 if (isChanged) {
1463 int slotId = getSlotId(subId);
1464
1465 // notify RCS capability changed
1466 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1467 slotId, 0, (Object) new FeatureProvisioningData(
1468 capability, tech, isProvisioned, /*isMmtel*/false)));
1469 }
1470
1471 return isChanged;
1472 }
1473
1474 private boolean setAndNotifyRcsProvisioningValueForAllTech(int subId, int capability,
1475 boolean isProvisioned) {
1476 boolean isChanged = false;
1477
1478 for (int tech : LOCAL_RADIO_TECHS) {
1479 isChanged |= setAndNotifyRcsProvisioningValue(subId, capability, tech, isProvisioned);
1480 }
1481
1482 return isChanged;
1483 }
1484
joonhunshin2c3e4232021-11-28 07:32:01 +00001485 protected boolean isValidSubId(int subId) {
1486 int slotId = getSlotId(subId);
1487 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1488 return false;
1489 }
1490
1491 return true;
1492 }
1493
1494 private void log(String s) {
1495 Rlog.d(TAG, s);
1496 }
1497
1498 private void log(String prefix, int slotId, String s) {
1499 Rlog.d(TAG, prefix + "[" + slotId + "] " + s);
1500 }
1501
1502 private void logi(String prefix, int slotId, String s) {
1503 Rlog.i(TAG, prefix + "[" + slotId + "] " + s);
1504 }
1505
1506 private void logw(String s) {
1507 Rlog.w(TAG, s);
1508 }
1509
1510 private void logw(String prefix, int slotId, String s) {
1511 Rlog.w(TAG, prefix + "[" + slotId + "] " + s);
1512 }
1513
1514 private void loge(String s) {
1515 Rlog.e(TAG, s);
1516 }
1517
1518 private void loge(String prefix, int slotId, String s) {
1519 Rlog.e(TAG, prefix + "[" + slotId + "] " + s);
1520 }
1521}