blob: 696f567462ff0912c0de38d9ea3e4fca3e39e159 [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
joonhunshinad228dc2022-05-20 05:54:26 +0000499 try {
500 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VOICE,
501 REGISTRATION_TECH_LTE);
502 } catch (IllegalArgumentException e) {
503 logw("setInitialProvisioningKeys: KEY_VOLTE_PROVISIONING_STATUS failed for"
504 + " subId=" + subId + ", exception: " + e.getMessage());
505 return;
506 }
507
joonhunshin2c3e4232021-11-28 07:32:01 +0000508 log(LOG_PREFIX, mSlotId,
509 "setInitialProvisioningKeys provisioning required(voice, lte) " + required);
510 if (required) {
511 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
512 CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE);
513 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
514 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
515 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
516 setProvisioningValue(KEY_VOLTE_PROVISIONING_STATUS, value);
517 }
518 }
519
520 // updating KEY_VT_PROVISIONING_STATUS
joonhunshinad228dc2022-05-20 05:54:26 +0000521 try {
522 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VIDEO,
523 REGISTRATION_TECH_LTE);
524 } catch (IllegalArgumentException e) {
525 logw("setInitialProvisioningKeys: KEY_VT_PROVISIONING_STATUS failed for"
526 + " subId=" + subId + ", exception: " + e.getMessage());
527 return;
528 }
529
joonhunshin2c3e4232021-11-28 07:32:01 +0000530 log(LOG_PREFIX, mSlotId,
531 "setInitialProvisioningKeys provisioning required(video, lte) " + required);
532 if (required) {
533 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
534 CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE);
535 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
536 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
537 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
538 setProvisioningValue(KEY_VT_PROVISIONING_STATUS, value);
539 }
540 }
541
542 // updating KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
joonhunshinad228dc2022-05-20 05:54:26 +0000543 try {
544 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VOICE,
545 REGISTRATION_TECH_IWLAN);
546 } catch (IllegalArgumentException e) {
547 logw("setInitialProvisioningKeys: KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE failed"
548 + " for subId=" + subId + ", exception: " + e.getMessage());
549 return;
550 }
551
joonhunshin2c3e4232021-11-28 07:32:01 +0000552 log(LOG_PREFIX, mSlotId,
553 "setInitialProvisioningKeys provisioning required(voice, iwlan) " + required);
554 if (required) {
555 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
556 CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN);
557 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
558 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
559 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
560 setProvisioningValue(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, value);
561 }
562 }
563 }
564 }
565
566 private final class RcsFeatureListener implements FeatureConnector.Listener<RcsFeatureManager> {
567 private static final String LOG_PREFIX = "RcsFeatureListener";
568 private FeatureConnector<RcsFeatureManager> mConnector;
569 private RcsFeatureManager mRcsFeatureManager;
570 private boolean mReady = false;
571 // stores whether the initial provisioning key value should be notified to ImsService
572 private boolean mRequiredNotify = false;
573 private int mSubId;
574 private int mSlotId;
575
576 RcsFeatureListener(int slotId) {
577 log(LOG_PREFIX, slotId, "created");
578
579 mSlotId = slotId;
580 mSubId = getSubId(slotId);
581 mConnector = mRcsFeatureConnector.create(
582 mApp, slotId, this, new HandlerExecutor(mHandler), TAG);
583 mConnector.connect();
584 }
585
586 public void setSubId(int subId) {
587 if (mRequiredNotify && mReady) {
588 mRequiredNotify = false;
589 setInitialProvisioningKeys(subId);
590 }
591 if (mSubId == subId) {
592 log(LOG_PREFIX, mSlotId, "subId is not changed");
593 return;
594 }
595
596 mSubId = subId;
597 mSlotId = getSlotId(subId);
598 }
599
600 public void destroy() {
601 log(LOG_PREFIX, mSlotId, "destroy");
602 mConnector.disconnect();
603 mConnector = null;
604 mReady = false;
605 mRcsFeatureManager = null;
606 }
607
608 @Override
609 public void connectionReady(RcsFeatureManager manager, int subId) {
610 log(LOG_PREFIX, mSlotId, "connection ready");
611 mReady = true;
612 mRcsFeatureManager = manager;
613
614 onRcsAvailable();
615 }
616
617 @Override
618 public void connectionUnavailable(int reason) {
619 log(LOG_PREFIX, mSlotId, "connection unavailable");
620 mReady = false;
621 mRcsFeatureManager = null;
622
623 // keep the callback for other reason
624 if (reason == FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED) {
625 onRcsUnavailable();
626 }
627 }
628
629 public int setProvisioningValue(int key, int value) {
630 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
631
632 if (!mReady) {
633 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
634 return retVal;
635 }
636
637 try {
638 // getConfigInterface() will return not null or throw the ImsException
639 // need not null checking
640 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
641 retVal = imsConfig.setConfig(key, value);
642 log(LOG_PREFIX, mSlotId, "setConfig called with key " + key + " value " + value);
643 } catch (ImsException e) {
644 logw(LOG_PREFIX, mSlotId,
645 "setConfig operation failed for key =" + key
646 + ", value =" + value + ". Exception:" + e.getMessage());
647 }
648 return retVal;
649 }
650
651 public int getProvisioningValue(int key) {
652 if (!mReady) {
653 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
654 return INVALID_VALUE;
655 }
656
657 int retValue = INVALID_VALUE;
658 try {
659 // getConfigInterface() will return not null or throw the ImsException
660 // need not null checking
661 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
662 retValue = imsConfig.getConfigInt(key);
663 } catch (ImsException e) {
664 logw(LOG_PREFIX, mSlotId,
665 "getConfig operation failed for key =" + key
666 + ", value =" + retValue + ". Exception:" + e.getMessage());
667 }
668 return retValue;
669 }
670
joonhunshin35d754f2022-06-24 03:29:15 +0000671 public boolean isConnectionReady() {
672 return mReady;
673 }
674
joonhunshin2c3e4232021-11-28 07:32:01 +0000675 public void onRcsAvailable() {
676 log(LOG_PREFIX, mSlotId, "onRcsAvailable");
677
678 if (isValidSubId(mSubId)) {
679 mRequiredNotify = false;
680
681 // notify provisioning key value to ImsService
682 setInitialProvisioningKeys(mSubId);
683 } else {
684 // wait until subId is valid
685 mRequiredNotify = true;
686 }
687 }
688
689 public void onRcsUnavailable() {
690 log(LOG_PREFIX, mSlotId, "onRcsUnavailable");
691
692 try {
693 // delete all callbacks reference from ProvisioningManager
694 mProvisioningCallbackManagersSlotMap.get(getSlotId(mSubId)).clear();
695 } catch (NullPointerException e) {
696 logw(LOG_PREFIX, getSlotId(mSubId), "can not find callback manager to clear");
697 }
698 }
699
700 private void setInitialProvisioningKeys(int subId) {
701 boolean required;
702 int value = ImsProvisioningLoader.STATUS_NOT_SET;
703
704 // KEY_EAB_PROVISIONING_STATUS
705 int capability = CAPABILITY_TYPE_PRESENCE_UCE;
706 // Assume that all radio techs have the same provisioning value
707 int tech = REGISTRATION_TECH_LTE;
708
joonhunshinad228dc2022-05-20 05:54:26 +0000709 try {
710 required = isRcsProvisioningRequiredForCapability(subId, capability, tech);
711 } catch (IllegalArgumentException e) {
712 logw("setInitialProvisioningKeys: KEY_EAB_PROVISIONING_STATUS failed for"
713 + " subId=" + subId + ", exception: " + e.getMessage());
714 return;
715 }
716
joonhunshin2c3e4232021-11-28 07:32:01 +0000717 if (required) {
718 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
719 capability, tech);
720 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
721 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
722 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
723 setProvisioningValue(KEY_EAB_PROVISIONING_STATUS, value);
724 }
725 }
726 }
727 }
728
729 /**
730 * Do NOT use this directly, instead use {@link #getInstance()}.
731 */
732 @VisibleForTesting
733 public ImsProvisioningController(PhoneGlobals app, int numSlot, Looper looper,
734 MmTelFeatureConnector mmTelFeatureConnector, RcsFeatureConnector rcsFeatureConnector,
735 ImsProvisioningLoader imsProvisioningLoader) {
736 log("ImsProvisioningController");
737 mApp = app;
738 mNumSlot = numSlot;
739 mHandler = new MessageHandler(looper);
740 mMmTelFeatureConnector = mmTelFeatureConnector;
741 mRcsFeatureConnector = rcsFeatureConnector;
742 mCarrierConfigManager = mApp.getSystemService(CarrierConfigManager.class);
743 mSubscriptionManager = mApp.getSystemService(SubscriptionManager.class);
744 mTelephonyRegistryManager = mApp.getSystemService(TelephonyRegistryManager.class);
745 mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
746 mSubChangedListener, mSubChangedListener.getHandlerExecutor());
747 mImsProvisioningLoader = imsProvisioningLoader;
748
joonhunshin39710022022-01-14 11:53:58 +0000749 PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
750 EVENT_MULTI_SIM_CONFIGURATION_CHANGE, null);
751
joonhunshin2c3e4232021-11-28 07:32:01 +0000752 initialize(numSlot);
753 }
754
755 private void initialize(int numSlot) {
756 for (int i = 0; i < numSlot; i++) {
757 MmTelFeatureListener m = new MmTelFeatureListener(i);
758 mMmTelFeatureListenersSlotMap.put(i, m);
759
760 RcsFeatureListener r = new RcsFeatureListener(i);
761 mRcsFeatureListenersSlotMap.put(i, r);
762
763 ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
764 mProvisioningCallbackManagersSlotMap.put(i, p);
765 }
766 }
767
joonhunshin39710022022-01-14 11:53:58 +0000768 private void onMultiSimConfigChanged(int newNumSlot) {
769 log("onMultiSimConfigChanged: NumSlot " + mNumSlot + " newNumSlot " + newNumSlot);
770
771 if (mNumSlot < newNumSlot) {
772 for (int i = mNumSlot; i < newNumSlot; i++) {
773 MmTelFeatureListener m = new MmTelFeatureListener(i);
774 mMmTelFeatureListenersSlotMap.put(i, m);
775
776 RcsFeatureListener r = new RcsFeatureListener(i);
777 mRcsFeatureListenersSlotMap.put(i, r);
778
779 ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
780 mProvisioningCallbackManagersSlotMap.put(i, p);
781 }
782 } else if (mNumSlot > newNumSlot) {
783 for (int i = (mNumSlot - 1); i > (newNumSlot - 1); i--) {
784 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(i);
785 mMmTelFeatureListenersSlotMap.remove(i);
786 m.destroy();
787
788 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(i);
789 mRcsFeatureListenersSlotMap.remove(i);
790 r.destroy();
791
792 ProvisioningCallbackManager p = mProvisioningCallbackManagersSlotMap.get(i);
793 mProvisioningCallbackManagersSlotMap.remove(i);
794 p.clear();
795 }
796 }
797
798 mNumSlot = newNumSlot;
799 }
800
joonhunshin2c3e4232021-11-28 07:32:01 +0000801 /**
802 * destroy the instance
803 */
804 @VisibleForTesting
805 public void destroy() {
806 log("destroy");
807
808 mHandler.getLooper().quit();
809
810 mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
811
812 for (int i = 0; i < mMmTelFeatureListenersSlotMap.size(); i++) {
813 mMmTelFeatureListenersSlotMap.get(i).destroy();
814 }
815 mMmTelFeatureListenersSlotMap.clear();
816
817 for (int i = 0; i < mRcsFeatureListenersSlotMap.size(); i++) {
818 mRcsFeatureListenersSlotMap.get(i).destroy();
819 }
820 mRcsFeatureListenersSlotMap.clear();
821
822 for (int i = 0; i < mProvisioningCallbackManagersSlotMap.size(); i++) {
823 mProvisioningCallbackManagersSlotMap.get(i).clear();
824 }
825 }
826
827 /**
828 * create an instance
829 */
830 @VisibleForTesting
831 public static ImsProvisioningController make(PhoneGlobals app, int numSlot) {
832 synchronized (ImsProvisioningController.class) {
833 if (sInstance == null) {
834 Rlog.i(TAG, "ImsProvisioningController created");
835 HandlerThread handlerThread = new HandlerThread(TAG);
836 handlerThread.start();
837 sInstance = new ImsProvisioningController(app, numSlot, handlerThread.getLooper(),
838 ImsManager::getConnector, RcsFeatureManager::getConnector,
839 new ImsProvisioningLoader(app));
840 }
841 }
842 return sInstance;
843 }
844
845 /**
846 * Gets a ImsProvisioningController instance
847 */
848 @VisibleForTesting
849 public static ImsProvisioningController getInstance() {
850 synchronized (ImsProvisioningController.class) {
851 return sInstance;
852 }
853 }
854
855 /**
856 * Register IFeatureProvisioningCallback from ProvisioningManager
857 */
858
859 @VisibleForTesting
860 public void addFeatureProvisioningChangedCallback(int subId,
861 IFeatureProvisioningCallback callback) {
862 if (callback == null) {
863 throw new IllegalArgumentException("provisioning callback can't be null");
864 }
865 int slotId = getSlotId(subId);
866 if (slotId < 0 || slotId >= mNumSlot) {
867 throw new IllegalArgumentException("subscription id is not available");
868 }
869
870 try {
871 mProvisioningCallbackManagersSlotMap.get(slotId).registerCallback(callback);
872 log("Feature Provisioning Callback registered.");
873 } catch (NullPointerException e) {
874 logw("can not access callback manager to add callback");
875 }
876 }
877
878 /**
879 * Remove IFeatureProvisioningCallback
880 */
881 @VisibleForTesting
882 public void removeFeatureProvisioningChangedCallback(int subId,
883 IFeatureProvisioningCallback callback) {
884 if (callback == null) {
885 throw new IllegalArgumentException("provisioning callback can't be null");
886 }
887
888 int slotId = getSlotId(subId);
889 if (slotId < 0 || slotId >= mNumSlot) {
890 throw new IllegalArgumentException("subscription id is not available");
891 }
892
893 try {
894 mProvisioningCallbackManagersSlotMap.get(slotId).unregisterCallback(callback);
895 log("Feature Provisioning Callback removed.");
896 } catch (NullPointerException e) {
897 logw("can not access callback manager to remove callback");
898 }
899 }
900
901 /**
joonhunshinf9411cb2022-01-17 07:37:15 +0000902 * return the boolean whether MmTel capability is required provisioning or not
joonhunshin2c3e4232021-11-28 07:32:01 +0000903 */
904 @VisibleForTesting
905 public boolean isImsProvisioningRequiredForCapability(int subId, int capability, int tech) {
906 // check subId
907 int slotId = getSlotId(subId);
908 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
909 loge("Fail to retrieve slotId from subId");
910 throw new IllegalArgumentException("subscribe id is invalid");
911 }
912
913 // check valid capability
914 if (!(MMTEL_CAPABILITY_MIN < capability && capability < MMTEL_CAPABILITY_MAX)) {
915 throw new IllegalArgumentException("MmTel capability '" + capability + "' is invalid");
916 }
917
918 // check valid radio tech
919 if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
920 log("Ims not matched radio tech " + tech);
921 throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
922 }
923
joonhunshin10e74ea2022-04-29 06:29:35 +0000924 // check new carrier config first KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
joonhunshin2c3e4232021-11-28 07:32:01 +0000925 boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/true);
926
joonhunshin10e74ea2022-04-29 06:29:35 +0000927 // if that returns false, check deprecated carrier config
928 // KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
929 if (!retVal && (capability == CAPABILITY_TYPE_VOICE
930 || capability == CAPABILITY_TYPE_VIDEO
931 || capability == CAPABILITY_TYPE_UT)) {
932 String key = CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL;
933 if (capability == CAPABILITY_TYPE_UT) {
934 key = CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL;
935 }
936
937 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
938 if (imsCarrierConfigs != null) {
939 retVal = imsCarrierConfigs.getBoolean(key);
940 } else {
941 retVal = CarrierConfigManager.getDefaultConfig().getBoolean(key);
942 }
943 }
944
joonhunshin2c3e4232021-11-28 07:32:01 +0000945 log("isImsProvisioningRequiredForCapability capability " + capability
946 + " tech " + tech + " return value " + retVal);
947
948 return retVal;
949 }
950
951 /**
joonhunshinf9411cb2022-01-17 07:37:15 +0000952 * return the boolean whether RCS capability is required provisioning or not
joonhunshin2c3e4232021-11-28 07:32:01 +0000953 */
954 @VisibleForTesting
955 public boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech) {
956 // check slotId and Phone object
957 int slotId = getSlotId(subId);
958 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
959 loge("Fail to retrieve slotId from subId");
960 throw new IllegalArgumentException("subscribe id is invalid");
961 }
962
963 // check valid capability
964 if (!(RCS_CAPABILITY_MIN < capability && capability < RCS_CAPABILITY_MAX)) {
965 throw new IllegalArgumentException("Rcs capability '" + capability + "' is invalid");
966 }
967
968 // check valid radio tech
969 if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
970 log("Rcs not matched radio tech " + tech);
971 throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
972 }
973
joonhunshin10e74ea2022-04-29 06:29:35 +0000974 // check new carrier config first KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
joonhunshin2c3e4232021-11-28 07:32:01 +0000975 boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/false);
976
joonhunshin10e74ea2022-04-29 06:29:35 +0000977 // if that returns false, check deprecated carrier config
978 // KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
979 if (!retVal) {
980 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
981 if (imsCarrierConfigs != null) {
982 retVal = imsCarrierConfigs.getBoolean(
983 CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL);
984 } else {
985 retVal = CarrierConfigManager.getDefaultConfig().getBoolean(
986 CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL);
987 }
988 }
989
joonhunshin2c3e4232021-11-28 07:32:01 +0000990 log("isRcsProvisioningRequiredForCapability capability " + capability
991 + " tech " + tech + " return value " + retVal);
992
993 return retVal;
994 }
995
996 /**
997 * return the provisioning status for MmTel capability in specific radio tech
998 */
999 @VisibleForTesting
1000 public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) {
1001 boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
1002 if (!mmTelProvisioned) { // provisioning not required
1003 log("getImsProvisioningStatusForCapability : not required "
1004 + " capability " + capability + " tech " + tech);
1005 return true;
1006 }
1007
1008 // read value from ImsProvisioningLoader
1009 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1010 capability, tech);
1011 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1012 // not set means initial value
1013 // read data from vendor ImsService and store that in ImsProvisioningLoader
1014 result = getValueFromImsService(subId, capability, tech);
1015 mmTelProvisioned = getBoolValue(result);
1016 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
1017 setAndNotifyMmTelProvisioningValue(subId, capability, tech, mmTelProvisioned);
1018 }
1019 } else {
1020 mmTelProvisioned = getBoolValue(result);
1021 }
1022
1023 log("getImsProvisioningStatusForCapability : "
1024 + " capability " + capability
1025 + " tech " + tech
1026 + " result " + mmTelProvisioned);
1027 return mmTelProvisioned;
1028 }
1029
1030 /**
1031 * set MmTel provisioning status in specific tech
1032 */
1033 @VisibleForTesting
1034 public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
1035 boolean isProvisioned) {
1036 boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
1037 if (!mmTelProvisioned) { // provisioning not required
1038 log("setImsProvisioningStatusForCapability : not required "
1039 + " capability " + capability + " tech " + tech);
1040 return;
1041 }
1042
1043 // write value to ImsProvisioningLoader
1044 boolean isChanged = setAndNotifyMmTelProvisioningValue(subId, capability, tech,
1045 isProvisioned);
1046 if (!isChanged) {
1047 log("status not changed mmtel capability " + capability + " tech " + tech);
1048 return;
1049 }
1050
1051 int slotId = getSlotId(subId);
1052 // find matched key from capability and tech
1053 int value = getIntValue(isProvisioned);
1054 int key = getKeyFromCapability(capability, tech);
1055 if (key != INVALID_VALUE) {
1056 log("setImsProvisioningStatusForCapability : matched key " + key);
1057 try {
1058 // set key and value to vendor ImsService for MmTel
1059 mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001060 } catch (NullPointerException e) {
1061 loge("can not access MmTelFeatureListener with capability " + capability);
1062 }
1063 }
1064 }
1065
1066 /**
1067 * return the provisioning status for RCS capability in specific radio tech
1068 */
1069 @VisibleForTesting
1070 public boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech) {
1071 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1072 if (!rcsProvisioned) { // provisioning not required
1073 log("getRcsProvisioningStatusForCapability : not required"
1074 + " capability " + capability + " tech " + tech);
1075 return true;
1076 }
1077
1078 // read data from ImsProvisioningLoader
1079 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1080 capability, tech);
1081 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1082 // not set means initial value
1083 // read data from vendor ImsService and store that in ImsProvisioningLoader
1084 result = getRcsValueFromImsService(subId, capability);
1085 rcsProvisioned = getBoolValue(result);
1086 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
1087 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, rcsProvisioned);
1088 }
1089 } else {
1090 rcsProvisioned = getBoolValue(result);
1091 }
1092
1093 log("getRcsProvisioningStatusForCapability : "
1094 + " capability " + capability
1095 + " tech " + tech
1096 + " result " + rcsProvisioned);
1097 return rcsProvisioned;
1098 }
1099
1100 /**
1101 * set RCS provisioning status in specific tech
1102 */
1103 @VisibleForTesting
1104 public void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
1105 boolean isProvisioned) {
1106 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1107 if (!rcsProvisioned) { // provisioning not required
1108 log("set rcs provisioning status but not required");
1109 return;
1110 }
1111
1112 // write status using ImsProvisioningLoader
1113 boolean isChanged = setAndNotifyRcsProvisioningValue(subId, capability, tech,
1114 isProvisioned);
1115 if (!isChanged) {
1116 log("status not changed rcs capability " + capability + " tech " + tech);
1117 return;
1118 }
1119
1120 int slotId = getSlotId(subId);
1121 int key = ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
1122 int value = getIntValue(isProvisioned);
1123 try {
joonhunshin35d754f2022-06-24 03:29:15 +00001124 // On some older devices, EAB is managed on the MmTel ImsService when the RCS
1125 // ImsService is not configured. If there is no RCS ImsService defined, fallback to
1126 // MmTel. In the rare case that we hit a race condition where the RCS ImsService has
1127 // crashed or has not come up yet, the value will be synchronized via
1128 // setInitialProvisioningKeys().
1129 if (mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1130 mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1131 }
1132
1133 // EAB provisioning status should be updated to both the Rcs and MmTel ImsService,
1134 // because the provisioning callback is listening to only MmTel provisioning key
1135 // changes.
1136 mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001137 } catch (NullPointerException e) {
1138 loge("can not access RcsFeatureListener with capability " + capability);
1139 }
1140 }
1141
1142 /**
1143 * set RCS provisioning status in specific key and value
1144 * @param key integer key, defined as one of
1145 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1146 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1147 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1148 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1149 * @param value in Integer format.
1150 * @return the result of setting the configuration value, defined as one of
1151 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1152 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1153 */
1154 @VisibleForTesting
1155 public int setProvisioningValue(int subId, int key, int value) {
1156 log("setProvisioningValue");
1157
1158 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
1159 // check key value
1160 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1161 log("not matched key " + key);
1162 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1163 }
1164
1165 // check subId
1166 int slotId = getSlotId(subId);
1167 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1168 loge("Fail to retrieve slotId from subId");
1169 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1170 }
1171
1172 try {
joonhunshin35d754f2022-06-24 03:29:15 +00001173 // set key and value to vendor ImsService for MmTel
1174 // EAB provisioning status should be updated to both the Rcs and MmTel ImsService,
1175 // because the provisioning callback is listening to only MmTel provisioning key
1176 // changes.
1177 retVal = mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1178
1179 // If the Rcs ImsService is not available, the EAB provisioning status will be written
1180 // to the MmTel ImsService for backwards compatibility. In the rare case that this is
1181 // hit due to RCS ImsService temporarily unavailable, the value will be synchronized
1182 // via setInitialProvisioningKeys() when the RCS ImsService comes back up.
1183 if (key == KEY_EAB_PROVISIONING_STATUS
1184 && mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1185 // set key and value to vendor ImsService for RCS and use retVal from RCS if
1186 // related to EAB when possible.
1187 retVal = mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001188 }
1189 } catch (NullPointerException e) {
1190 loge("can not access FeatureListener to set provisioning value");
1191 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1192 }
1193
1194 // update and notify provisioning status changed capability and tech from key
1195 updateCapabilityTechFromKey(subId, key, value);
1196
joonhunshin2c3e4232021-11-28 07:32:01 +00001197 return retVal;
1198 }
1199
1200 /**
1201 * get RCS provisioning status in specific key and value
1202 * @param key integer key, defined as one of
1203 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1204 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1205 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1206 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1207 * @return the result of setting the configuration value, defined as one of
1208 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1209 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1210 * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN}
1211 */
1212 @VisibleForTesting
1213 public int getProvisioningValue(int subId, int key) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001214 // check key value
1215 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
1216 log("not matched key " + key);
1217 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1218 }
1219
1220 // check subId
1221 int slotId = getSlotId(subId);
1222 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1223 loge("Fail to retrieve slotId from subId");
1224 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1225 }
1226
1227 // check data from ImsProvisioningLoader
1228 int capability = getCapabilityFromKey(key);
1229 int tech = getTechFromKey(key);
1230 int result;
1231 if (capability != INVALID_VALUE && tech != INVALID_VALUE) {
1232 if (key == KEY_EAB_PROVISIONING_STATUS) {
1233 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1234 capability, tech);
1235 } else {
1236 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1237 capability, tech);
1238 }
1239 if (result != ImsProvisioningLoader.STATUS_NOT_SET) {
joonhunshin6cd38ac2022-02-03 06:37:55 +00001240 log("getProvisioningValue from loader : key " + key + " result " + result);
joonhunshin2c3e4232021-11-28 07:32:01 +00001241 return result;
1242 }
1243 }
1244
1245 // get data from ImsService, update it in ImsProvisioningLoader
1246 if (key == KEY_EAB_PROVISIONING_STATUS) {
1247 result = getRcsValueFromImsService(subId, capability);
1248 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1249 logw("getProvisioningValue : fail to get data from ImsService capability"
1250 + capability);
1251 return result;
1252 }
joonhunshin6cd38ac2022-02-03 06:37:55 +00001253 log("getProvisioningValue from vendor : key " + key + " result " + result);
1254
joonhunshin2c3e4232021-11-28 07:32:01 +00001255 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, getBoolValue(result));
1256 return result;
1257 } else {
1258 result = getValueFromImsService(subId, capability, tech);
1259 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1260 logw("getProvisioningValue : fail to get data from ImsService capability"
1261 + capability);
1262 return result;
1263 }
joonhunshin6cd38ac2022-02-03 06:37:55 +00001264 log("getProvisioningValue from vendor : key " + key + " result " + result);
1265
joonhunshin2c3e4232021-11-28 07:32:01 +00001266 setAndNotifyMmTelProvisioningValue(subId, capability, tech, getBoolValue(result));
1267 return result;
1268 }
1269 }
1270
1271 /**
1272 * get the handler
1273 */
1274 @VisibleForTesting
1275 public Handler getHandler() {
1276 return mHandler;
1277 }
1278
1279 private boolean isProvisioningRequired(int subId, int capability, int tech, boolean isMmTel) {
joonhunshinf9411cb2022-01-17 07:37:15 +00001280 int[] techArray;
1281 techArray = getTechsFromCarrierConfig(subId, capability, isMmTel);
1282 if (techArray == null) {
1283 logw("isProvisioningRequired : getTechsFromCarrierConfig failed");
1284 // not exist in CarrierConfig that means provisioning is not required
joonhunshin2c3e4232021-11-28 07:32:01 +00001285 return false;
1286 }
1287
joonhunshin2c3e4232021-11-28 07:32:01 +00001288 // compare with carrier config
joonhunshinf9411cb2022-01-17 07:37:15 +00001289 if (Arrays.stream(techArray).anyMatch(keyValue -> keyValue == tech)) {
1290 // existing same tech means provisioning required
1291 return true;
joonhunshin2c3e4232021-11-28 07:32:01 +00001292 }
1293
1294 log("isProvisioningRequired : not matched capability " + capability + " tech " + tech);
1295 return false;
1296 }
1297
joonhunshin10e74ea2022-04-29 06:29:35 +00001298 private int[] getTechsFromCarrierConfig(int subId, int capability, boolean isMmTel) {
joonhunshinf9411cb2022-01-17 07:37:15 +00001299 String featureKey;
1300 String capabilityKey;
1301 if (isMmTel) {
1302 featureKey = CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE;
1303 capabilityKey = KEYS_MMTEL_CAPABILITY.get(capability);
1304 } else {
1305 featureKey = CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE;
1306 capabilityKey = KEYS_RCS_CAPABILITY.get(capability);
1307 }
joonhunshin2c3e4232021-11-28 07:32:01 +00001308
joonhunshinf9411cb2022-01-17 07:37:15 +00001309 if (capabilityKey != null) {
1310 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1311 if (imsCarrierConfigs == null) {
1312 log("getTechsFromCarrierConfig : imsCarrierConfigs null");
1313 return null;
1314 }
1315
1316 PersistableBundle provisioningBundle =
1317 imsCarrierConfigs.getPersistableBundle(featureKey);
1318 if (provisioningBundle == null) {
1319 log("getTechsFromCarrierConfig : provisioningBundle null");
1320 return null;
1321 }
1322
1323 return provisioningBundle.getIntArray(capabilityKey);
1324 }
1325
1326 return null;
joonhunshin2c3e4232021-11-28 07:32:01 +00001327 }
1328
1329 private int getValueFromImsService(int subId, int capability, int tech) {
1330 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1331
1332 // operation is based on capability
1333 switch (capability) {
1334 case CAPABILITY_TYPE_VOICE:
1335 int item = (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
1336 ? ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
1337 : ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1338 // read data from vendor ImsService
1339 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1340 .getProvisioningValue(item);
1341 break;
1342 case CAPABILITY_TYPE_VIDEO:
1343 // read data from vendor ImsService
1344 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1345 .getProvisioningValue(ProvisioningManager.KEY_VT_PROVISIONING_STATUS);
1346 break;
1347 default:
1348 log("Capability " + capability + " has been provisioning");
1349 break;
1350 }
1351
1352 return config;
1353 }
1354
1355 private int getRcsValueFromImsService(int subId, int capability) {
1356 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
joonhunshin35d754f2022-06-24 03:29:15 +00001357 int slotId = getSlotId(subId);
joonhunshin2c3e4232021-11-28 07:32:01 +00001358
joonhunshin35d754f2022-06-24 03:29:15 +00001359 if (capability != CAPABILITY_TYPE_PRESENCE_UCE) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001360 log("Capability " + capability + " has been provisioning");
joonhunshin35d754f2022-06-24 03:29:15 +00001361 return config;
1362 }
1363 try {
1364 if (mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1365 config = mRcsFeatureListenersSlotMap.get(slotId)
1366 .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1367 } else {
1368 log("Rcs ImsService is not available, "
1369 + "EAB provisioning status should be read from MmTel ImsService");
1370 config = mMmTelFeatureListenersSlotMap.get(slotId)
1371 .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1372 }
1373 } catch (NullPointerException e) {
1374 logw("can not access FeatureListener : " + e.getMessage());
joonhunshin2c3e4232021-11-28 07:32:01 +00001375 }
1376
1377 return config;
1378 }
1379
1380 private void onSubscriptionsChanged() {
1381 for (int index = 0; index < mMmTelFeatureListenersSlotMap.size(); index++) {
1382 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(index);
1383 m.setSubId(getSubId(index));
1384 }
1385 for (int index = 0; index < mRcsFeatureListenersSlotMap.size(); index++) {
1386 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(index);
1387 r.setSubId(getSubId(index));
1388 }
1389 for (int index = 0; index < mProvisioningCallbackManagersSlotMap.size(); index++) {
1390 ProvisioningCallbackManager m = mProvisioningCallbackManagersSlotMap.get(index);
1391 m.setSubId(getSubId(index));
1392 }
1393 }
1394
1395 private void updateCapabilityTechFromKey(int subId, int key, int value) {
1396 boolean isProvisioned = getBoolValue(value);
1397 int capability = getCapabilityFromKey(key);
1398 int tech = getTechFromKey(key);
1399
1400 if (capability == INVALID_VALUE || tech == INVALID_VALUE) {
1401 logw("updateCapabilityTechFromKey : unknown key " + key);
1402 return;
1403 }
1404
1405 if (key == KEY_VOLTE_PROVISIONING_STATUS
1406 || key == KEY_VT_PROVISIONING_STATUS
1407 || key == KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE) {
1408 setAndNotifyMmTelProvisioningValue(subId, capability, tech, isProvisioned);
1409 }
1410 if (key == KEY_EAB_PROVISIONING_STATUS) {
1411 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, isProvisioned);
1412 }
1413 }
1414
1415 private int getCapabilityFromKey(int key) {
1416 int capability;
1417 switch (key) {
1418 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1419 // intentional fallthrough
1420 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1421 capability = CAPABILITY_TYPE_VOICE;
1422 break;
1423 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1424 capability = CAPABILITY_TYPE_VIDEO;
1425 break;
1426 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1427 // default CAPABILITY_TYPE_PRESENCE_UCE used for KEY_EAB_PROVISIONING_STATUS
1428 capability = CAPABILITY_TYPE_PRESENCE_UCE;
1429 break;
1430 default:
1431 capability = INVALID_VALUE;
1432 break;
1433 }
1434 return capability;
1435 }
1436
1437 private int getTechFromKey(int key) {
1438 int tech;
1439 switch (key) {
1440 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1441 tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
1442 break;
1443 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1444 // intentional fallthrough
1445 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1446 // intentional fallthrough
1447 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1448 tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
1449 break;
1450 default:
1451 tech = INVALID_VALUE;
1452 break;
1453 }
1454 return tech;
1455 }
1456
1457 private int getKeyFromCapability(int capability, int tech) {
1458 int key = INVALID_VALUE;
1459 if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_IWLAN) {
1460 key = ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
1461 } else if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_LTE) {
1462 key = ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1463 } else if (capability == CAPABILITY_TYPE_VIDEO && tech == REGISTRATION_TECH_LTE) {
1464 key = ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
1465 }
1466
1467 return key;
1468 }
1469
1470 protected int getSubId(int slotId) {
1471 final int[] subIds = mSubscriptionManager.getSubscriptionIds(slotId);
1472 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1473 if (subIds != null && subIds.length >= 1) {
1474 subId = subIds[0];
1475 }
1476
1477 return subId;
1478 }
1479
1480 protected int getSlotId(int subId) {
1481 return mSubscriptionManager.getPhoneId(subId);
1482 }
1483
1484 protected ImsConfig getImsConfig(ImsManager imsManager) throws ImsException {
1485 return imsManager.getConfigInterface();
1486 }
1487
1488 protected ImsConfig getImsConfig(IImsConfig iImsConfig) {
1489 return new ImsConfig(iImsConfig);
1490 }
1491
1492 private int getIntValue(boolean isProvisioned) {
1493 return isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
1494 : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
1495 }
1496
1497 private boolean getBoolValue(int value) {
1498 return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED ? true : false;
1499 }
1500
1501 private boolean setAndNotifyMmTelProvisioningValue(int subId, int capability, int tech,
1502 boolean isProvisioned) {
1503 boolean changed = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_MMTEL,
1504 capability, tech, isProvisioned);
1505 // notify MmTel capability changed
1506 if (changed) {
1507 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1508 getSlotId(subId), 0, (Object) new FeatureProvisioningData(
1509 capability, tech, isProvisioned, /*isMmTel*/true)));
1510 }
1511
1512 return changed;
1513 }
1514
1515 private boolean setAndNotifyRcsProvisioningValue(int subId, int capability, int tech,
1516 boolean isProvisioned) {
1517 boolean isChanged = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_RCS,
1518 capability, tech, isProvisioned);
1519
1520 if (isChanged) {
1521 int slotId = getSlotId(subId);
1522
1523 // notify RCS capability changed
1524 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1525 slotId, 0, (Object) new FeatureProvisioningData(
1526 capability, tech, isProvisioned, /*isMmtel*/false)));
1527 }
1528
1529 return isChanged;
1530 }
1531
1532 private boolean setAndNotifyRcsProvisioningValueForAllTech(int subId, int capability,
1533 boolean isProvisioned) {
1534 boolean isChanged = false;
1535
1536 for (int tech : LOCAL_RADIO_TECHS) {
1537 isChanged |= setAndNotifyRcsProvisioningValue(subId, capability, tech, isProvisioned);
1538 }
1539
1540 return isChanged;
1541 }
1542
joonhunshin2c3e4232021-11-28 07:32:01 +00001543 protected boolean isValidSubId(int subId) {
1544 int slotId = getSlotId(subId);
1545 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1546 return false;
1547 }
1548
1549 return true;
1550 }
1551
1552 private void log(String s) {
1553 Rlog.d(TAG, s);
1554 }
1555
1556 private void log(String prefix, int slotId, String s) {
1557 Rlog.d(TAG, prefix + "[" + slotId + "] " + s);
1558 }
1559
1560 private void logi(String prefix, int slotId, String s) {
1561 Rlog.i(TAG, prefix + "[" + slotId + "] " + s);
1562 }
1563
1564 private void logw(String s) {
1565 Rlog.w(TAG, s);
1566 }
1567
1568 private void logw(String prefix, int slotId, String s) {
1569 Rlog.w(TAG, prefix + "[" + slotId + "] " + s);
1570 }
1571
1572 private void loge(String s) {
1573 Rlog.e(TAG, s);
1574 }
1575
1576 private void loge(String prefix, int slotId, String s) {
1577 Rlog.e(TAG, prefix + "[" + slotId + "] " + s);
1578 }
1579}