blob: 54fc547be698c8eb040c5ec0b5b3c36f9cebb715 [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
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);
joonhunshin2c3e4232021-11-28 07:32:01 +0000995 } catch (NullPointerException e) {
996 loge("can not access MmTelFeatureListener with capability " + capability);
997 }
998 }
999 }
1000
1001 /**
1002 * return the provisioning status for RCS capability in specific radio tech
1003 */
1004 @VisibleForTesting
1005 public boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech) {
1006 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1007 if (!rcsProvisioned) { // provisioning not required
1008 log("getRcsProvisioningStatusForCapability : not required"
1009 + " capability " + capability + " tech " + tech);
1010 return true;
1011 }
1012
1013 // read data from ImsProvisioningLoader
1014 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1015 capability, tech);
1016 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1017 // not set means initial value
1018 // read data from vendor ImsService and store that in ImsProvisioningLoader
1019 result = getRcsValueFromImsService(subId, capability);
1020 rcsProvisioned = getBoolValue(result);
1021 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
1022 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, rcsProvisioned);
1023 }
1024 } else {
1025 rcsProvisioned = getBoolValue(result);
1026 }
1027
1028 log("getRcsProvisioningStatusForCapability : "
1029 + " capability " + capability
1030 + " tech " + tech
1031 + " result " + rcsProvisioned);
1032 return rcsProvisioned;
1033 }
1034
1035 /**
1036 * set RCS provisioning status in specific tech
1037 */
1038 @VisibleForTesting
1039 public void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
1040 boolean isProvisioned) {
1041 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1042 if (!rcsProvisioned) { // provisioning not required
1043 log("set rcs provisioning status but not required");
1044 return;
1045 }
1046
1047 // write status using ImsProvisioningLoader
1048 boolean isChanged = setAndNotifyRcsProvisioningValue(subId, capability, tech,
1049 isProvisioned);
1050 if (!isChanged) {
1051 log("status not changed rcs capability " + capability + " tech " + tech);
1052 return;
1053 }
1054
1055 int slotId = getSlotId(subId);
1056 int key = ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
1057 int value = getIntValue(isProvisioned);
1058 try {
1059 // set key and value to vendor ImsService for Rcs
1060 mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1061 } catch (NullPointerException e) {
1062 loge("can not access RcsFeatureListener with capability " + capability);
1063 }
1064 }
1065
1066 /**
1067 * set RCS provisioning status in specific key and value
1068 * @param key integer key, defined as one of
1069 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1070 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1071 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1072 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1073 * @param value in Integer format.
1074 * @return the result of setting the configuration value, defined as one of
1075 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1076 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1077 */
1078 @VisibleForTesting
1079 public int setProvisioningValue(int subId, int key, int value) {
1080 log("setProvisioningValue");
1081
1082 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
1083 // check key value
1084 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1085 log("not matched key " + key);
1086 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1087 }
1088
1089 // check subId
1090 int slotId = getSlotId(subId);
1091 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1092 loge("Fail to retrieve slotId from subId");
1093 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1094 }
1095
1096 try {
1097 if (key == KEY_EAB_PROVISIONING_STATUS) {
1098 // set key and value to vendor ImsService for Rcs
1099 retVal = mRcsFeatureListenersSlotMap.get(slotId)
1100 .setProvisioningValue(key, value);
1101 } else {
1102 // set key and value to vendor ImsService for MmTel
1103 retVal = mMmTelFeatureListenersSlotMap.get(slotId)
1104 .setProvisioningValue(key, value);
1105 }
1106 } catch (NullPointerException e) {
1107 loge("can not access FeatureListener to set provisioning value");
1108 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1109 }
1110
1111 // update and notify provisioning status changed capability and tech from key
1112 updateCapabilityTechFromKey(subId, key, value);
1113
joonhunshin2c3e4232021-11-28 07:32:01 +00001114 return retVal;
1115 }
1116
1117 /**
1118 * get RCS provisioning status in specific key and value
1119 * @param key integer key, defined as one of
1120 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1121 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1122 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1123 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1124 * @return the result of setting the configuration value, defined as one of
1125 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1126 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1127 * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN}
1128 */
1129 @VisibleForTesting
1130 public int getProvisioningValue(int subId, int key) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001131 // check key value
1132 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1133 log("not matched key " + key);
1134 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1135 }
1136
1137 // check subId
1138 int slotId = getSlotId(subId);
1139 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1140 loge("Fail to retrieve slotId from subId");
1141 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1142 }
1143
1144 // check data from ImsProvisioningLoader
1145 int capability = getCapabilityFromKey(key);
1146 int tech = getTechFromKey(key);
1147 int result;
1148 if (capability != INVALID_VALUE && tech != INVALID_VALUE) {
1149 if (key == KEY_EAB_PROVISIONING_STATUS) {
1150 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1151 capability, tech);
1152 } else {
1153 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1154 capability, tech);
1155 }
1156 if (result != ImsProvisioningLoader.STATUS_NOT_SET) {
joonhunshin6cd38ac2022-02-03 06:37:55 +00001157 log("getProvisioningValue from loader : key " + key + " result " + result);
joonhunshin2c3e4232021-11-28 07:32:01 +00001158 return result;
1159 }
1160 }
1161
1162 // get data from ImsService, update it in ImsProvisioningLoader
1163 if (key == KEY_EAB_PROVISIONING_STATUS) {
1164 result = getRcsValueFromImsService(subId, capability);
1165 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1166 logw("getProvisioningValue : fail to get data from ImsService capability"
1167 + capability);
1168 return result;
1169 }
joonhunshin6cd38ac2022-02-03 06:37:55 +00001170 log("getProvisioningValue from vendor : key " + key + " result " + result);
1171
joonhunshin2c3e4232021-11-28 07:32:01 +00001172 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, getBoolValue(result));
1173 return result;
1174 } else {
1175 result = getValueFromImsService(subId, capability, tech);
1176 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1177 logw("getProvisioningValue : fail to get data from ImsService capability"
1178 + capability);
1179 return result;
1180 }
joonhunshin6cd38ac2022-02-03 06:37:55 +00001181 log("getProvisioningValue from vendor : key " + key + " result " + result);
1182
joonhunshin2c3e4232021-11-28 07:32:01 +00001183 setAndNotifyMmTelProvisioningValue(subId, capability, tech, getBoolValue(result));
1184 return result;
1185 }
1186 }
1187
1188 /**
1189 * get the handler
1190 */
1191 @VisibleForTesting
1192 public Handler getHandler() {
1193 return mHandler;
1194 }
1195
1196 private boolean isProvisioningRequired(int subId, int capability, int tech, boolean isMmTel) {
joonhunshinf9411cb2022-01-17 07:37:15 +00001197 int[] techArray;
1198 techArray = getTechsFromCarrierConfig(subId, capability, isMmTel);
1199 if (techArray == null) {
1200 logw("isProvisioningRequired : getTechsFromCarrierConfig failed");
1201 // not exist in CarrierConfig that means provisioning is not required
joonhunshin2c3e4232021-11-28 07:32:01 +00001202 return false;
1203 }
1204
joonhunshin2c3e4232021-11-28 07:32:01 +00001205 // compare with carrier config
joonhunshinf9411cb2022-01-17 07:37:15 +00001206 if (Arrays.stream(techArray).anyMatch(keyValue -> keyValue == tech)) {
1207 // existing same tech means provisioning required
1208 return true;
joonhunshin2c3e4232021-11-28 07:32:01 +00001209 }
1210
1211 log("isProvisioningRequired : not matched capability " + capability + " tech " + tech);
1212 return false;
1213 }
1214
1215 @VisibleForTesting
joonhunshinf9411cb2022-01-17 07:37:15 +00001216 protected int[] getTechsFromCarrierConfig(int subId, int capability, boolean isMmTel) {
1217 String featureKey;
1218 String capabilityKey;
1219 if (isMmTel) {
1220 featureKey = CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE;
1221 capabilityKey = KEYS_MMTEL_CAPABILITY.get(capability);
1222 } else {
1223 featureKey = CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE;
1224 capabilityKey = KEYS_RCS_CAPABILITY.get(capability);
1225 }
joonhunshin2c3e4232021-11-28 07:32:01 +00001226
joonhunshinf9411cb2022-01-17 07:37:15 +00001227 if (capabilityKey != null) {
1228 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1229 if (imsCarrierConfigs == null) {
1230 log("getTechsFromCarrierConfig : imsCarrierConfigs null");
1231 return null;
1232 }
1233
1234 PersistableBundle provisioningBundle =
1235 imsCarrierConfigs.getPersistableBundle(featureKey);
1236 if (provisioningBundle == null) {
1237 log("getTechsFromCarrierConfig : provisioningBundle null");
1238 return null;
1239 }
1240
1241 return provisioningBundle.getIntArray(capabilityKey);
1242 }
1243
1244 return null;
joonhunshin2c3e4232021-11-28 07:32:01 +00001245 }
1246
1247 private int getValueFromImsService(int subId, int capability, int tech) {
1248 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1249
1250 // operation is based on capability
1251 switch (capability) {
1252 case CAPABILITY_TYPE_VOICE:
1253 int item = (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
1254 ? ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
1255 : ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1256 // read data from vendor ImsService
1257 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1258 .getProvisioningValue(item);
1259 break;
1260 case CAPABILITY_TYPE_VIDEO:
1261 // read data from vendor ImsService
1262 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1263 .getProvisioningValue(ProvisioningManager.KEY_VT_PROVISIONING_STATUS);
1264 break;
1265 default:
1266 log("Capability " + capability + " has been provisioning");
1267 break;
1268 }
1269
1270 return config;
1271 }
1272
1273 private int getRcsValueFromImsService(int subId, int capability) {
1274 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1275
1276 if (capability == CAPABILITY_TYPE_PRESENCE_UCE) {
1277 try {
1278 config = mRcsFeatureListenersSlotMap.get(getSlotId(subId))
1279 .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1280 } catch (NullPointerException e) {
1281 logw("can not access RcsFeatureListener");
1282 }
1283 } else {
1284 log("Capability " + capability + " has been provisioning");
1285 }
1286
1287 return config;
1288 }
1289
1290 private void onSubscriptionsChanged() {
1291 for (int index = 0; index < mMmTelFeatureListenersSlotMap.size(); index++) {
1292 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(index);
1293 m.setSubId(getSubId(index));
1294 }
1295 for (int index = 0; index < mRcsFeatureListenersSlotMap.size(); index++) {
1296 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(index);
1297 r.setSubId(getSubId(index));
1298 }
1299 for (int index = 0; index < mProvisioningCallbackManagersSlotMap.size(); index++) {
1300 ProvisioningCallbackManager m = mProvisioningCallbackManagersSlotMap.get(index);
1301 m.setSubId(getSubId(index));
1302 }
1303 }
1304
1305 private void updateCapabilityTechFromKey(int subId, int key, int value) {
1306 boolean isProvisioned = getBoolValue(value);
1307 int capability = getCapabilityFromKey(key);
1308 int tech = getTechFromKey(key);
1309
1310 if (capability == INVALID_VALUE || tech == INVALID_VALUE) {
1311 logw("updateCapabilityTechFromKey : unknown key " + key);
1312 return;
1313 }
1314
1315 if (key == KEY_VOLTE_PROVISIONING_STATUS
1316 || key == KEY_VT_PROVISIONING_STATUS
1317 || key == KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE) {
1318 setAndNotifyMmTelProvisioningValue(subId, capability, tech, isProvisioned);
1319 }
1320 if (key == KEY_EAB_PROVISIONING_STATUS) {
1321 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, isProvisioned);
1322 }
1323 }
1324
1325 private int getCapabilityFromKey(int key) {
1326 int capability;
1327 switch (key) {
1328 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1329 // intentional fallthrough
1330 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1331 capability = CAPABILITY_TYPE_VOICE;
1332 break;
1333 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1334 capability = CAPABILITY_TYPE_VIDEO;
1335 break;
1336 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1337 // default CAPABILITY_TYPE_PRESENCE_UCE used for KEY_EAB_PROVISIONING_STATUS
1338 capability = CAPABILITY_TYPE_PRESENCE_UCE;
1339 break;
1340 default:
1341 capability = INVALID_VALUE;
1342 break;
1343 }
1344 return capability;
1345 }
1346
1347 private int getTechFromKey(int key) {
1348 int tech;
1349 switch (key) {
1350 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1351 tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
1352 break;
1353 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1354 // intentional fallthrough
1355 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1356 // intentional fallthrough
1357 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1358 tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
1359 break;
1360 default:
1361 tech = INVALID_VALUE;
1362 break;
1363 }
1364 return tech;
1365 }
1366
1367 private int getKeyFromCapability(int capability, int tech) {
1368 int key = INVALID_VALUE;
1369 if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_IWLAN) {
1370 key = ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
1371 } else if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_LTE) {
1372 key = ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1373 } else if (capability == CAPABILITY_TYPE_VIDEO && tech == REGISTRATION_TECH_LTE) {
1374 key = ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
1375 }
1376
1377 return key;
1378 }
1379
1380 protected int getSubId(int slotId) {
1381 final int[] subIds = mSubscriptionManager.getSubscriptionIds(slotId);
1382 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1383 if (subIds != null && subIds.length >= 1) {
1384 subId = subIds[0];
1385 }
1386
1387 return subId;
1388 }
1389
1390 protected int getSlotId(int subId) {
1391 return mSubscriptionManager.getPhoneId(subId);
1392 }
1393
1394 protected ImsConfig getImsConfig(ImsManager imsManager) throws ImsException {
1395 return imsManager.getConfigInterface();
1396 }
1397
1398 protected ImsConfig getImsConfig(IImsConfig iImsConfig) {
1399 return new ImsConfig(iImsConfig);
1400 }
1401
1402 private int getIntValue(boolean isProvisioned) {
1403 return isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
1404 : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
1405 }
1406
1407 private boolean getBoolValue(int value) {
1408 return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED ? true : false;
1409 }
1410
1411 private boolean setAndNotifyMmTelProvisioningValue(int subId, int capability, int tech,
1412 boolean isProvisioned) {
1413 boolean changed = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_MMTEL,
1414 capability, tech, isProvisioned);
1415 // notify MmTel capability changed
1416 if (changed) {
1417 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1418 getSlotId(subId), 0, (Object) new FeatureProvisioningData(
1419 capability, tech, isProvisioned, /*isMmTel*/true)));
1420 }
1421
1422 return changed;
1423 }
1424
1425 private boolean setAndNotifyRcsProvisioningValue(int subId, int capability, int tech,
1426 boolean isProvisioned) {
1427 boolean isChanged = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_RCS,
1428 capability, tech, isProvisioned);
1429
1430 if (isChanged) {
1431 int slotId = getSlotId(subId);
1432
1433 // notify RCS capability changed
1434 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1435 slotId, 0, (Object) new FeatureProvisioningData(
1436 capability, tech, isProvisioned, /*isMmtel*/false)));
1437 }
1438
1439 return isChanged;
1440 }
1441
1442 private boolean setAndNotifyRcsProvisioningValueForAllTech(int subId, int capability,
1443 boolean isProvisioned) {
1444 boolean isChanged = false;
1445
1446 for (int tech : LOCAL_RADIO_TECHS) {
1447 isChanged |= setAndNotifyRcsProvisioningValue(subId, capability, tech, isProvisioned);
1448 }
1449
1450 return isChanged;
1451 }
1452
joonhunshin2c3e4232021-11-28 07:32:01 +00001453 protected boolean isValidSubId(int subId) {
1454 int slotId = getSlotId(subId);
1455 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1456 return false;
1457 }
1458
1459 return true;
1460 }
1461
1462 private void log(String s) {
1463 Rlog.d(TAG, s);
1464 }
1465
1466 private void log(String prefix, int slotId, String s) {
1467 Rlog.d(TAG, prefix + "[" + slotId + "] " + s);
1468 }
1469
1470 private void logi(String prefix, int slotId, String s) {
1471 Rlog.i(TAG, prefix + "[" + slotId + "] " + s);
1472 }
1473
1474 private void logw(String s) {
1475 Rlog.w(TAG, s);
1476 }
1477
1478 private void logw(String prefix, int slotId, String s) {
1479 Rlog.w(TAG, prefix + "[" + slotId + "] " + s);
1480 }
1481
1482 private void loge(String s) {
1483 Rlog.e(TAG, s);
1484 }
1485
1486 private void loge(String prefix, int slotId, String s) {
1487 Rlog.e(TAG, prefix + "[" + slotId + "] " + s);
1488 }
1489}