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