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