blob: ea60633141d18596eba7e99fb3ff57f5ab9e7a86 [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;
joonhunshin1724dd62022-08-30 07:41:20 +000044import android.os.Binder;
joonhunshin2c3e4232021-11-28 07:32:01 +000045import android.os.Handler;
46import android.os.HandlerThread;
47import android.os.Looper;
48import android.os.Message;
49import android.os.PersistableBundle;
50import android.os.RemoteCallbackList;
51import android.os.RemoteException;
Brad Ebinger0df4fdf2024-07-19 17:26:12 -070052import android.telephony.AnomalyReporter;
joonhunshin2c3e4232021-11-28 07:32:01 +000053import android.telephony.CarrierConfigManager;
joonhunshinf9411cb2022-01-17 07:37:15 +000054import android.telephony.CarrierConfigManager.Ims;
joonhunshin2c3e4232021-11-28 07:32:01 +000055import android.telephony.SubscriptionManager;
Brad Ebinger0df4fdf2024-07-19 17:26:12 -070056import android.telephony.TelephonyManager;
joonhunshin2c3e4232021-11-28 07:32:01 +000057import android.telephony.TelephonyRegistryManager;
58import android.telephony.ims.ProvisioningManager;
59import android.telephony.ims.aidl.IFeatureProvisioningCallback;
60import android.telephony.ims.aidl.IImsConfig;
joonhunshin1724dd62022-08-30 07:41:20 +000061import android.telephony.ims.aidl.IImsConfigCallback;
joonhunshin2c3e4232021-11-28 07:32:01 +000062import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
63import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
64import android.telephony.ims.stub.ImsConfigImplBase;
65import android.telephony.ims.stub.ImsRegistrationImplBase;
66import android.util.SparseArray;
67
68import com.android.ims.FeatureConnector;
69import com.android.ims.ImsConfig;
70import com.android.ims.ImsException;
71import com.android.ims.ImsManager;
72import com.android.ims.RcsFeatureManager;
73import com.android.internal.annotations.VisibleForTesting;
joonhunshin39710022022-01-14 11:53:58 +000074import com.android.internal.telephony.PhoneConfigurationManager;
joonhunshin711a8872024-03-19 10:04:08 +000075import com.android.internal.telephony.flags.FeatureFlags;
joonhunshin2c3e4232021-11-28 07:32:01 +000076import com.android.internal.telephony.util.HandlerExecutor;
77import com.android.telephony.Rlog;
78
79import java.util.Arrays;
joonhunshinf9411cb2022-01-17 07:37:15 +000080import java.util.Map;
Brad Ebinger0df4fdf2024-07-19 17:26:12 -070081import java.util.UUID;
joonhunshin2c3e4232021-11-28 07:32:01 +000082import java.util.concurrent.Executor;
83
84/**
85 * Provides APIs for MMTEL and RCS provisioning status. This class handles provisioning status and
86 * notifies the status changing for each capability
87 * {{@link MmTelCapabilities.MmTelCapability} for MMTel services}
88 * {{@link RcsImsCapabilities.RcsImsCapabilityFlag} for RCS services}
89 */
90public class ImsProvisioningController {
91 private static final String TAG = "ImsProvisioningController";
92 private static final int INVALID_VALUE = -1;
93
94 private static final int EVENT_SUB_CHANGED = 1;
95 private static final int EVENT_PROVISIONING_CAPABILITY_CHANGED = 2;
joonhunshin39710022022-01-14 11:53:58 +000096 @VisibleForTesting
97 protected static final int EVENT_MULTI_SIM_CONFIGURATION_CHANGE = 3;
joonhunshin1724dd62022-08-30 07:41:20 +000098 private static final int EVENT_PROVISIONING_VALUE_CHANGED = 4;
joonhunshin711a8872024-03-19 10:04:08 +000099 private static final int EVENT_NOTIFY_INIT_PROVISIONED_VALUE = 5;
joonhunshin2c3e4232021-11-28 07:32:01 +0000100
101 // Provisioning Keys that are handled via AOSP cache and not sent to the ImsService
102 private static final int[] LOCAL_IMS_CONFIG_KEYS = {
103 KEY_VOLTE_PROVISIONING_STATUS,
104 KEY_VT_PROVISIONING_STATUS,
105 KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE,
106 KEY_EAB_PROVISIONING_STATUS
107 };
108 private static final int[] LOCAL_RADIO_TECHS = {
109 REGISTRATION_TECH_LTE,
110 REGISTRATION_TECH_IWLAN,
111 REGISTRATION_TECH_CROSS_SIM,
112 REGISTRATION_TECH_NR
113 };
114
115 private static final int MMTEL_CAPABILITY_MIN = MmTelCapabilities.CAPABILITY_TYPE_NONE;
116 private static final int MMTEL_CAPABILITY_MAX = MmTelCapabilities.CAPABILITY_TYPE_MAX;
117
118 private static final int RCS_CAPABILITY_MIN = RcsImsCapabilities.CAPABILITY_TYPE_NONE;
119 private static final int RCS_CAPABILITY_MAX = RcsImsCapabilities.CAPABILITY_TYPE_MAX;
120
121 private static final int[] LOCAL_MMTEL_CAPABILITY = {
122 CAPABILITY_TYPE_VOICE,
123 CAPABILITY_TYPE_VIDEO,
124 CAPABILITY_TYPE_UT,
125 CAPABILITY_TYPE_SMS,
126 CAPABILITY_TYPE_CALL_COMPOSER
127 };
128
joonhunshin711a8872024-03-19 10:04:08 +0000129 private static final int[] LOCAL_RCS_CAPABILITY = {
130 CAPABILITY_TYPE_OPTIONS_UCE,
131 CAPABILITY_TYPE_PRESENCE_UCE
132 };
133
joonhunshin2c3e4232021-11-28 07:32:01 +0000134 /**
joonhunshinf9411cb2022-01-17 07:37:15 +0000135 * map the MmTelCapabilities.MmTelCapability and
136 * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VOICE_INT
137 * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VIDEO_INT
138 * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_UT_INT
139 * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_SMS_INT
joonhunshin6cd38ac2022-02-03 06:37:55 +0000140 * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT
joonhunshinf9411cb2022-01-17 07:37:15 +0000141 */
142 private static final Map<Integer, String> KEYS_MMTEL_CAPABILITY = Map.of(
143 CAPABILITY_TYPE_VOICE, Ims.KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY,
144 CAPABILITY_TYPE_VIDEO, Ims.KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY,
145 CAPABILITY_TYPE_UT, Ims.KEY_CAPABILITY_TYPE_UT_INT_ARRAY,
146 CAPABILITY_TYPE_SMS, Ims.KEY_CAPABILITY_TYPE_SMS_INT_ARRAY,
joonhunshin6cd38ac2022-02-03 06:37:55 +0000147 CAPABILITY_TYPE_CALL_COMPOSER, Ims.KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY
joonhunshinf9411cb2022-01-17 07:37:15 +0000148 );
149
150 /**
151 * map the RcsImsCapabilities.RcsImsCapabilityFlag and
152 * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_OPTIONS_UCE
153 * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE
154 */
155 private static final Map<Integer, String> KEYS_RCS_CAPABILITY = Map.of(
156 CAPABILITY_TYPE_OPTIONS_UCE, Ims.KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY,
157 CAPABILITY_TYPE_PRESENCE_UCE, Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY
158 );
159
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700160 private static final UUID VOLTE_PROVISIONING_ANOMALY =
161 UUID.fromString("f5f90e4d-3d73-4f63-a0f9-cbe1941ca57c");
162 private static final String VOLTE_PROVISIONING_ANOMALY_DESC = "VoLTE is Not Provisioned";
163
joonhunshinf9411cb2022-01-17 07:37:15 +0000164 /**
joonhunshin2c3e4232021-11-28 07:32:01 +0000165 * Create a FeatureConnector for this class to use to connect to an ImsManager.
166 */
167 @VisibleForTesting
168 public interface MmTelFeatureConnector {
169 /**
170 * Create a FeatureConnector for this class to use to connect to an ImsManager.
171 * @param listener will receive ImsManager instance.
172 * @param executor that the Listener callbacks will be called on.
173 * @return A FeatureConnector
174 */
175 FeatureConnector<ImsManager> create(Context context, int slotId,
176 String logPrefix, FeatureConnector.Listener<ImsManager> listener,
177 Executor executor);
178 }
179
180 /**
181 * Create a FeatureConnector for this class to use to connect to an RcsFeatureManager.
182 */
183 @VisibleForTesting
184 public interface RcsFeatureConnector {
185 /**
186 * Create a FeatureConnector for this class to use to connect to an RcsFeatureManager.
187 * @param listener will receive RcsFeatureManager instance.
188 * @param executor that the Listener callbacks will be called on.
189 * @return A FeatureConnector
190 */
191 FeatureConnector<RcsFeatureManager> create(Context context, int slotId,
192 FeatureConnector.Listener<RcsFeatureManager> listener,
193 Executor executor, String logPrefix);
194 }
195
196 private static ImsProvisioningController sInstance;
197
198 private final PhoneGlobals mApp;
199 private final Handler mHandler;
200 private final CarrierConfigManager mCarrierConfigManager;
201 private final SubscriptionManager mSubscriptionManager;
202 private final TelephonyRegistryManager mTelephonyRegistryManager;
203 private final MmTelFeatureConnector mMmTelFeatureConnector;
204 private final RcsFeatureConnector mRcsFeatureConnector;
205
206 // maps a slotId to a list of MmTelFeatureListeners
207 private final SparseArray<MmTelFeatureListener> mMmTelFeatureListenersSlotMap =
208 new SparseArray<>();
209 // maps a slotId to a list of RcsFeatureListeners
210 private final SparseArray<RcsFeatureListener> mRcsFeatureListenersSlotMap =
211 new SparseArray<>();
212 // map a slotId to a list of ProvisioningCallbackManager
213 private final SparseArray<ProvisioningCallbackManager> mProvisioningCallbackManagersSlotMap =
214 new SparseArray<>();
215 private final ImsProvisioningLoader mImsProvisioningLoader;
joonhunshin711a8872024-03-19 10:04:08 +0000216 private final FeatureFlags mFeatureFlags;
joonhunshin2c3e4232021-11-28 07:32:01 +0000217
218 private int mNumSlot;
219
220 /**
221 * This class contains the provisioning status to notify changes.
222 * {{@link MmTelCapabilities.MmTelCapability} for MMTel services}
Hakjun Choi4c6c10e2022-03-15 05:08:02 +0000223 * {{@link android.telephony.ims.ImsRcsManager.RcsImsCapabilityFlag} for RCS services}
joonhunshin2c3e4232021-11-28 07:32:01 +0000224 * {{@link ImsRegistrationImplBase.ImsRegistrationTech} for Registration tech}
225 */
226 private static final class FeatureProvisioningData {
227 public final int mCapability;
228 public final int mTech;
229 public final boolean mProvisioned;
230 public final boolean mIsMmTel;
231
232 FeatureProvisioningData(int capability, int tech, boolean provisioned, boolean isMmTel) {
233 mCapability = capability;
234 mTech = tech;
235 mProvisioned = provisioned;
236 mIsMmTel = isMmTel;
237 }
238 }
239
240 private final class MessageHandler extends Handler {
241 private static final String LOG_PREFIX = "Handler";
242 MessageHandler(Looper looper) {
243 super(looper);
244 }
245
246 @Override
247 public void handleMessage(Message msg) {
248 switch (msg.what) {
249 case EVENT_SUB_CHANGED:
250 onSubscriptionsChanged();
251 break;
252 case EVENT_PROVISIONING_CAPABILITY_CHANGED:
253 try {
254 mProvisioningCallbackManagersSlotMap.get(msg.arg1)
255 .notifyProvisioningCapabilityChanged(
256 (FeatureProvisioningData) msg.obj);
257 } catch (NullPointerException e) {
258 logw(LOG_PREFIX, msg.arg1,
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700259 "can not find callback manager, message" + msg.what);
joonhunshin2c3e4232021-11-28 07:32:01 +0000260 }
261 break;
joonhunshin39710022022-01-14 11:53:58 +0000262 case EVENT_MULTI_SIM_CONFIGURATION_CHANGE:
263 int activeModemCount = (int) ((AsyncResult) msg.obj).result;
264 onMultiSimConfigChanged(activeModemCount);
265 break;
joonhunshin1724dd62022-08-30 07:41:20 +0000266 case EVENT_PROVISIONING_VALUE_CHANGED:
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700267 logAttr("ImsConfig", "EVENT_PROVISIONING_VALUE_CHANGED", msg.arg1,
268 "changed provisioning value, item : " + msg.arg2
joonhunshin1724dd62022-08-30 07:41:20 +0000269 + " value : " + (int) msg.obj);
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700270 updateCapabilityTechFromKey("ImsConfig[" + msg.arg1 + "]",
271 msg.arg1, msg.arg2, (int) msg.obj);
joonhunshin1724dd62022-08-30 07:41:20 +0000272 break;
joonhunshin711a8872024-03-19 10:04:08 +0000273 case EVENT_NOTIFY_INIT_PROVISIONED_VALUE:
274 int slotId = msg.arg1;
275 int subId = msg.arg2;
276 IFeatureProvisioningCallback callback =
277 (IFeatureProvisioningCallback) msg.obj;
278 log("slotId " + slotId + " subId " + subId
279 + " callback " + (callback != null));
280
281 // Notify MmTel Provisioning Status
282 notifyMmTelProvisioningStatus(slotId, subId, callback);
283 notifyRcsProvisioningStatus(slotId, subId, callback);
284 break;
joonhunshin2c3e4232021-11-28 07:32:01 +0000285 default:
286 log("unknown message " + msg);
287 break;
288 }
289 }
290 }
291
292 private final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
293 new SubscriptionManager.OnSubscriptionsChangedListener() {
294 @Override
295 public void onSubscriptionsChanged() {
296 if (!mHandler.hasMessages(EVENT_SUB_CHANGED)) {
297 mHandler.sendEmptyMessage(EVENT_SUB_CHANGED);
298 }
299 }
300 };
301
302 private final class ProvisioningCallbackManager {
303 private static final String LOG_PREFIX = "ProvisioningCallbackManager";
304 private RemoteCallbackList<IFeatureProvisioningCallback> mIFeatureProvisioningCallbackList;
305 private int mSubId;
306 private int mSlotId;
307
308 ProvisioningCallbackManager(int slotId) {
309 mIFeatureProvisioningCallbackList =
310 new RemoteCallbackList<IFeatureProvisioningCallback>();
311 mSlotId = slotId;
312 mSubId = getSubId(slotId);
313 log(LOG_PREFIX, mSlotId, "ProvisioningCallbackManager create");
314 }
315
316 public void clear() {
317 log(LOG_PREFIX, mSlotId, "ProvisioningCallbackManager clear ");
318
319 mIFeatureProvisioningCallbackList.kill();
320
321 // All registered callbacks are unregistered, and the list is disabled
322 // need to create again
323 mIFeatureProvisioningCallbackList =
324 new RemoteCallbackList<IFeatureProvisioningCallback>();
325 }
326
327 public void registerCallback(IFeatureProvisioningCallback localCallback) {
328 if (!mIFeatureProvisioningCallbackList.register(localCallback, (Object) mSubId)) {
329 log(LOG_PREFIX, mSlotId, "registration callback fail");
330 }
331 }
332
333 public void unregisterCallback(IFeatureProvisioningCallback localCallback) {
334 mIFeatureProvisioningCallbackList.unregister(localCallback);
335 }
336
337 public void setSubId(int subId) {
338 if (mSubId == subId) {
339 log(LOG_PREFIX, mSlotId, "subId is not changed ");
340 return;
341 }
342
343 mSubId = subId;
344 mSlotId = getSlotId(subId);
345
346 // subId changed means the registered callbacks are not available.
347 clear();
348 }
349
350 public boolean hasCallblacks() {
351 int size = mIFeatureProvisioningCallbackList.beginBroadcast();
352 mIFeatureProvisioningCallbackList.finishBroadcast();
353
354 return (size > 0);
355 }
356
357 public void notifyProvisioningCapabilityChanged(FeatureProvisioningData data) {
358 int size = mIFeatureProvisioningCallbackList.beginBroadcast();
359 for (int index = 0; index < size; index++) {
360 try {
361 IFeatureProvisioningCallback imsFeatureProvisioningCallback =
362 mIFeatureProvisioningCallbackList.getBroadcastItem(index);
363
364 // MMTEL
365 if (data.mIsMmTel
366 && Arrays.stream(LOCAL_MMTEL_CAPABILITY)
367 .anyMatch(value -> value == data.mCapability)) {
368 imsFeatureProvisioningCallback.onFeatureProvisioningChanged(
369 data.mCapability, data.mTech, data.mProvisioned);
370 logi(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
371 + "onFeatureProvisioningChanged"
372 + " capability " + data.mCapability
373 + " tech " + data.mTech
374 + " isProvisioned " + data.mProvisioned);
375 } else if (data.mCapability == CAPABILITY_TYPE_PRESENCE_UCE) {
376 imsFeatureProvisioningCallback.onRcsFeatureProvisioningChanged(
377 data.mCapability, data.mTech, data.mProvisioned);
378 logi(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
379 + "onRcsFeatureProvisioningChanged"
380 + " capability " + data.mCapability
381 + " tech " + data.mTech
382 + " isProvisioned " + data.mProvisioned);
383 } else {
384 loge(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
385 + "unknown capability "
386 + data.mCapability);
387 }
388 } catch (RemoteException e) {
389 loge(LOG_PREFIX, mSlotId,
390 "notifyProvisioningChanged: callback #" + index + " failed");
391 }
392 }
393 mIFeatureProvisioningCallbackList.finishBroadcast();
394 }
395 }
396
397 private final class MmTelFeatureListener implements FeatureConnector.Listener<ImsManager> {
398 private static final String LOG_PREFIX = "MmTelFeatureListener";
399 private FeatureConnector<ImsManager> mConnector;
400 private ImsManager mImsManager;
401 private boolean mReady = false;
402 // stores whether the initial provisioning key value should be notified to ImsService
403 private boolean mRequiredNotify = false;
404 private int mSubId;
405 private int mSlotId;
joonhunshin1724dd62022-08-30 07:41:20 +0000406 private ConfigCallback mConfigCallback;
joonhunshin2c3e4232021-11-28 07:32:01 +0000407
408 MmTelFeatureListener(int slotId) {
409 log(LOG_PREFIX, slotId, "created");
410
411 mSlotId = slotId;
412 mSubId = getSubId(slotId);
joonhunshin1724dd62022-08-30 07:41:20 +0000413 mConfigCallback = new ConfigCallback(mSubId);
414
joonhunshin2c3e4232021-11-28 07:32:01 +0000415 mConnector = mMmTelFeatureConnector.create(
416 mApp, slotId, TAG, this, new HandlerExecutor(mHandler));
417 mConnector.connect();
418 }
419
420 public void setSubId(int subId) {
421 if (mRequiredNotify && mReady) {
422 mRequiredNotify = false;
423 setInitialProvisioningKeys(subId);
424 }
425 if (mSubId == subId) {
426 log(LOG_PREFIX, mSlotId, "subId is not changed");
427 return;
428 }
429
430 mSubId = subId;
joonhunshin1724dd62022-08-30 07:41:20 +0000431 mConfigCallback.setSubId(subId);
joonhunshin2c3e4232021-11-28 07:32:01 +0000432 }
433
434 public void destroy() {
435 log("destroy");
joonhunshin1724dd62022-08-30 07:41:20 +0000436 if (mImsManager != null) {
437 try {
438 ImsConfig imsConfig = getImsConfig(mImsManager);
439 if (imsConfig != null) {
440 imsConfig.removeConfigCallback(mConfigCallback);
441 }
442 } catch (ImsException e) {
443 logw(LOG_PREFIX, mSlotId, "destroy : " + e.getMessage());
444 }
445 }
446 mConfigCallback = null;
joonhunshin2c3e4232021-11-28 07:32:01 +0000447 mConnector.disconnect();
448 mConnector = null;
449 mReady = false;
450 mImsManager = null;
451 }
452
453 public @Nullable ImsManager getImsManager() {
454 return mImsManager;
455 }
456
457 @Override
458 public void connectionReady(ImsManager manager, int subId) {
459 log(LOG_PREFIX, mSlotId, "connection ready");
460 mReady = true;
461 mImsManager = manager;
462
joonhunshin1724dd62022-08-30 07:41:20 +0000463 if (mImsManager != null) {
464 try {
465 ImsConfig imsConfig = getImsConfig(mImsManager);
466 if (imsConfig != null) {
467 imsConfig.addConfigCallback(mConfigCallback);
468 }
469 } catch (ImsException e) {
470 logw(LOG_PREFIX, mSlotId, "addConfigCallback : " + e.getMessage());
471 }
472 }
473
joonhunshin2c3e4232021-11-28 07:32:01 +0000474 onMmTelAvailable();
475 }
476
477 @Override
478 public void connectionUnavailable(int reason) {
479 log(LOG_PREFIX, mSlotId, "connection unavailable " + reason);
480
481 mReady = false;
482 mImsManager = null;
483
484 // keep the callback for other reason
485 if (reason == FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED) {
486 onMmTelUnavailable();
487 }
488 }
489
490 public int setProvisioningValue(int key, int value) {
491 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
492
493 if (!mReady) {
494 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
495 return retVal;
496 }
497 try {
498 // getConfigInterface() will return not null or throw the ImsException
499 // need not null checking
500 ImsConfig imsConfig = getImsConfig(mImsManager);
501 retVal = imsConfig.setConfig(key, value);
502 log(LOG_PREFIX, mSlotId, "setConfig called with key " + key + " value " + value);
503 } catch (ImsException e) {
504 logw(LOG_PREFIX, mSlotId,
505 "setConfig operation failed for key =" + key
506 + ", value =" + value + ". Exception:" + e.getMessage());
507 }
508 return retVal;
509 }
510
511 public int getProvisioningValue(int key) {
512 if (!mReady) {
513 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
514 return INVALID_VALUE;
515 }
516
517 int retValue = INVALID_VALUE;
518 try {
519 // getConfigInterface() will return not null or throw the ImsException
520 // need not null checking
521 ImsConfig imsConfig = getImsConfig(mImsManager);
522 retValue = imsConfig.getConfigInt(key);
523 } catch (ImsException e) {
524 logw(LOG_PREFIX, mSlotId,
525 "getConfig operation failed for key =" + key
526 + ", value =" + retValue + ". Exception:" + e.getMessage());
527 }
528 return retValue;
529 }
530
531 public void onMmTelAvailable() {
532 log(LOG_PREFIX, mSlotId, "onMmTelAvailable");
533
534 if (isValidSubId(mSubId)) {
535 mRequiredNotify = false;
536
537 // notify provisioning key value to ImsService
538 setInitialProvisioningKeys(mSubId);
joonhunshin711a8872024-03-19 10:04:08 +0000539
540 if (mFeatureFlags.notifyInitialImsProvisioningStatus()) {
541 // Notify MmTel provisioning value based on capability and radio tech.
joonhunshin0e690c02024-06-04 06:43:21 +0000542 ProvisioningCallbackManager p =
543 mProvisioningCallbackManagersSlotMap.get(mSlotId);
544 if (p != null && p.hasCallblacks()) {
joonhunshin711a8872024-03-19 10:04:08 +0000545 notifyMmTelProvisioningStatus(mSlotId, mSubId, null);
546 }
547 }
joonhunshin2c3e4232021-11-28 07:32:01 +0000548 } else {
549 // wait until subId is valid
550 mRequiredNotify = true;
551 }
552 }
553
554 public void onMmTelUnavailable() {
555 log(LOG_PREFIX, mSlotId, "onMmTelUnavailable");
556
557 try {
558 // delete all callbacks reference from ProvisioningManager
559 mProvisioningCallbackManagersSlotMap.get(getSlotId(mSubId)).clear();
560 } catch (NullPointerException e) {
561 logw(LOG_PREFIX, getSlotId(mSubId), "can not find callback manager to clear");
562 }
563 }
564
565 private void setInitialProvisioningKeys(int subId) {
566 boolean required;
567 int value = ImsProvisioningLoader.STATUS_NOT_SET;
568
569 // updating KEY_VOLTE_PROVISIONING_STATUS
joonhunshinad228dc2022-05-20 05:54:26 +0000570 try {
571 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VOICE,
572 REGISTRATION_TECH_LTE);
573 } catch (IllegalArgumentException e) {
574 logw("setInitialProvisioningKeys: KEY_VOLTE_PROVISIONING_STATUS failed for"
575 + " subId=" + subId + ", exception: " + e.getMessage());
576 return;
577 }
578
joonhunshin2c3e4232021-11-28 07:32:01 +0000579 log(LOG_PREFIX, mSlotId,
580 "setInitialProvisioningKeys provisioning required(voice, lte) " + required);
581 if (required) {
582 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
583 CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE);
584 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
585 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
586 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
587 setProvisioningValue(KEY_VOLTE_PROVISIONING_STATUS, value);
588 }
589 }
590
591 // updating KEY_VT_PROVISIONING_STATUS
joonhunshinad228dc2022-05-20 05:54:26 +0000592 try {
593 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VIDEO,
594 REGISTRATION_TECH_LTE);
595 } catch (IllegalArgumentException e) {
596 logw("setInitialProvisioningKeys: KEY_VT_PROVISIONING_STATUS failed for"
597 + " subId=" + subId + ", exception: " + e.getMessage());
598 return;
599 }
600
joonhunshin2c3e4232021-11-28 07:32:01 +0000601 log(LOG_PREFIX, mSlotId,
602 "setInitialProvisioningKeys provisioning required(video, lte) " + required);
603 if (required) {
604 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
605 CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE);
606 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
607 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
608 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
609 setProvisioningValue(KEY_VT_PROVISIONING_STATUS, value);
610 }
611 }
612
613 // updating KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
joonhunshinad228dc2022-05-20 05:54:26 +0000614 try {
615 required = isImsProvisioningRequiredForCapability(subId, CAPABILITY_TYPE_VOICE,
616 REGISTRATION_TECH_IWLAN);
617 } catch (IllegalArgumentException e) {
618 logw("setInitialProvisioningKeys: KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE failed"
619 + " for subId=" + subId + ", exception: " + e.getMessage());
620 return;
621 }
622
joonhunshin2c3e4232021-11-28 07:32:01 +0000623 log(LOG_PREFIX, mSlotId,
624 "setInitialProvisioningKeys provisioning required(voice, iwlan) " + required);
625 if (required) {
626 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
627 CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN);
628 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
629 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
630 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
631 setProvisioningValue(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, value);
632 }
633 }
634 }
635 }
636
637 private final class RcsFeatureListener implements FeatureConnector.Listener<RcsFeatureManager> {
638 private static final String LOG_PREFIX = "RcsFeatureListener";
639 private FeatureConnector<RcsFeatureManager> mConnector;
640 private RcsFeatureManager mRcsFeatureManager;
641 private boolean mReady = false;
642 // stores whether the initial provisioning key value should be notified to ImsService
643 private boolean mRequiredNotify = false;
644 private int mSubId;
645 private int mSlotId;
joonhunshin1724dd62022-08-30 07:41:20 +0000646 private ConfigCallback mConfigCallback;
joonhunshin2c3e4232021-11-28 07:32:01 +0000647
648 RcsFeatureListener(int slotId) {
649 log(LOG_PREFIX, slotId, "created");
650
651 mSlotId = slotId;
652 mSubId = getSubId(slotId);
joonhunshin1724dd62022-08-30 07:41:20 +0000653 mConfigCallback = new ConfigCallback(mSubId);
654
joonhunshin2c3e4232021-11-28 07:32:01 +0000655 mConnector = mRcsFeatureConnector.create(
656 mApp, slotId, this, new HandlerExecutor(mHandler), TAG);
657 mConnector.connect();
658 }
659
660 public void setSubId(int subId) {
661 if (mRequiredNotify && mReady) {
662 mRequiredNotify = false;
663 setInitialProvisioningKeys(subId);
664 }
665 if (mSubId == subId) {
666 log(LOG_PREFIX, mSlotId, "subId is not changed");
667 return;
668 }
669
670 mSubId = subId;
joonhunshin1724dd62022-08-30 07:41:20 +0000671 mConfigCallback.setSubId(subId);
joonhunshin2c3e4232021-11-28 07:32:01 +0000672 }
673
674 public void destroy() {
675 log(LOG_PREFIX, mSlotId, "destroy");
joonhunshin1724dd62022-08-30 07:41:20 +0000676 if (mRcsFeatureManager != null) {
677 try {
678 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
679 if (imsConfig != null) {
680 imsConfig.removeConfigCallback(mConfigCallback);
681 }
682 } catch (ImsException e) {
683 logw(LOG_PREFIX, mSlotId, "destroy :" + e.getMessage());
684 }
685 }
686 mConfigCallback = null;
joonhunshin2c3e4232021-11-28 07:32:01 +0000687 mConnector.disconnect();
688 mConnector = null;
689 mReady = false;
690 mRcsFeatureManager = null;
691 }
692
693 @Override
694 public void connectionReady(RcsFeatureManager manager, int subId) {
695 log(LOG_PREFIX, mSlotId, "connection ready");
696 mReady = true;
697 mRcsFeatureManager = manager;
698
joonhunshin1724dd62022-08-30 07:41:20 +0000699 if (mRcsFeatureManager != null) {
700 try {
701 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
702 if (imsConfig != null) {
703 imsConfig.addConfigCallback(mConfigCallback);
704 }
705 } catch (ImsException e) {
706 logw(LOG_PREFIX, mSlotId, "addConfigCallback :" + e.getMessage());
707 }
708 }
709
joonhunshin2c3e4232021-11-28 07:32:01 +0000710 onRcsAvailable();
711 }
712
713 @Override
714 public void connectionUnavailable(int reason) {
715 log(LOG_PREFIX, mSlotId, "connection unavailable");
716 mReady = false;
717 mRcsFeatureManager = null;
718
719 // keep the callback for other reason
720 if (reason == FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED) {
721 onRcsUnavailable();
722 }
723 }
724
725 public int setProvisioningValue(int key, int value) {
726 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
727
728 if (!mReady) {
729 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
730 return retVal;
731 }
732
733 try {
734 // getConfigInterface() will return not null or throw the ImsException
735 // need not null checking
736 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
737 retVal = imsConfig.setConfig(key, value);
738 log(LOG_PREFIX, mSlotId, "setConfig called with key " + key + " value " + value);
739 } catch (ImsException e) {
740 logw(LOG_PREFIX, mSlotId,
741 "setConfig operation failed for key =" + key
742 + ", value =" + value + ". Exception:" + e.getMessage());
743 }
744 return retVal;
745 }
746
747 public int getProvisioningValue(int key) {
748 if (!mReady) {
749 loge(LOG_PREFIX, mSlotId, "service is Unavailable");
750 return INVALID_VALUE;
751 }
752
753 int retValue = INVALID_VALUE;
754 try {
755 // getConfigInterface() will return not null or throw the ImsException
756 // need not null checking
757 ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
758 retValue = imsConfig.getConfigInt(key);
759 } catch (ImsException e) {
760 logw(LOG_PREFIX, mSlotId,
761 "getConfig operation failed for key =" + key
762 + ", value =" + retValue + ". Exception:" + e.getMessage());
763 }
764 return retValue;
765 }
766
joonhunshin35d754f2022-06-24 03:29:15 +0000767 public boolean isConnectionReady() {
768 return mReady;
769 }
770
joonhunshin2c3e4232021-11-28 07:32:01 +0000771 public void onRcsAvailable() {
772 log(LOG_PREFIX, mSlotId, "onRcsAvailable");
773
774 if (isValidSubId(mSubId)) {
775 mRequiredNotify = false;
776
777 // notify provisioning key value to ImsService
778 setInitialProvisioningKeys(mSubId);
joonhunshin711a8872024-03-19 10:04:08 +0000779
780 if (mFeatureFlags.notifyInitialImsProvisioningStatus()) {
joonhunshin0e690c02024-06-04 06:43:21 +0000781 ProvisioningCallbackManager p =
782 mProvisioningCallbackManagersSlotMap.get(mSlotId);
783 if (p != null && p.hasCallblacks()) {
joonhunshin711a8872024-03-19 10:04:08 +0000784 // Notify RCS provisioning value based on capability and radio tech.
785 notifyRcsProvisioningStatus(mSlotId, mSubId, null);
786 }
787 }
joonhunshin2c3e4232021-11-28 07:32:01 +0000788 } else {
789 // wait until subId is valid
790 mRequiredNotify = true;
791 }
792 }
793
794 public void onRcsUnavailable() {
795 log(LOG_PREFIX, mSlotId, "onRcsUnavailable");
796
797 try {
798 // delete all callbacks reference from ProvisioningManager
799 mProvisioningCallbackManagersSlotMap.get(getSlotId(mSubId)).clear();
800 } catch (NullPointerException e) {
801 logw(LOG_PREFIX, getSlotId(mSubId), "can not find callback manager to clear");
802 }
803 }
804
805 private void setInitialProvisioningKeys(int subId) {
806 boolean required;
807 int value = ImsProvisioningLoader.STATUS_NOT_SET;
808
809 // KEY_EAB_PROVISIONING_STATUS
810 int capability = CAPABILITY_TYPE_PRESENCE_UCE;
811 // Assume that all radio techs have the same provisioning value
812 int tech = REGISTRATION_TECH_LTE;
813
joonhunshinad228dc2022-05-20 05:54:26 +0000814 try {
815 required = isRcsProvisioningRequiredForCapability(subId, capability, tech);
816 } catch (IllegalArgumentException e) {
817 logw("setInitialProvisioningKeys: KEY_EAB_PROVISIONING_STATUS failed for"
818 + " subId=" + subId + ", exception: " + e.getMessage());
819 return;
820 }
821
joonhunshin2c3e4232021-11-28 07:32:01 +0000822 if (required) {
823 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
824 capability, tech);
825 if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
826 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
827 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
828 setProvisioningValue(KEY_EAB_PROVISIONING_STATUS, value);
829 }
830 }
831 }
832 }
833
joonhunshin1724dd62022-08-30 07:41:20 +0000834 // When vendor ImsService changed provisioning data, which should be updated in AOSP.
835 // Catch the event using IImsConfigCallback.
836 private final class ConfigCallback extends IImsConfigCallback.Stub {
837 private int mSubId;
838
839 ConfigCallback(int subId) {
840 mSubId = subId;
841 }
842
843 public void setSubId(int subId) {
844 mSubId = subId;
845 }
846
847 @Override
848 public void onIntConfigChanged(int item, int value) throws RemoteException {
849 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == item)) {
850 return;
851 }
852
853 final long callingIdentity = Binder.clearCallingIdentity();
854 try {
855 if (mHandler != null) {
856 mHandler.sendMessage(mHandler.obtainMessage(
857 EVENT_PROVISIONING_VALUE_CHANGED, mSubId, item, (Object) value));
858 }
859 } finally {
860 Binder.restoreCallingIdentity(callingIdentity);
861 }
862 }
863
864 @Override
865 public void onStringConfigChanged(int item, String value) throws RemoteException {
866 // Ignore this callback.
867 }
868 }
869
joonhunshin2c3e4232021-11-28 07:32:01 +0000870 /**
871 * Do NOT use this directly, instead use {@link #getInstance()}.
872 */
873 @VisibleForTesting
874 public ImsProvisioningController(PhoneGlobals app, int numSlot, Looper looper,
875 MmTelFeatureConnector mmTelFeatureConnector, RcsFeatureConnector rcsFeatureConnector,
joonhunshin711a8872024-03-19 10:04:08 +0000876 ImsProvisioningLoader imsProvisioningLoader, FeatureFlags featureFlags) {
joonhunshin2c3e4232021-11-28 07:32:01 +0000877 log("ImsProvisioningController");
878 mApp = app;
879 mNumSlot = numSlot;
880 mHandler = new MessageHandler(looper);
881 mMmTelFeatureConnector = mmTelFeatureConnector;
882 mRcsFeatureConnector = rcsFeatureConnector;
883 mCarrierConfigManager = mApp.getSystemService(CarrierConfigManager.class);
884 mSubscriptionManager = mApp.getSystemService(SubscriptionManager.class);
885 mTelephonyRegistryManager = mApp.getSystemService(TelephonyRegistryManager.class);
886 mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
Nathan Harold4d544b62023-06-21 13:48:40 -0700887 mSubChangedListener, mHandler::post);
joonhunshin2c3e4232021-11-28 07:32:01 +0000888 mImsProvisioningLoader = imsProvisioningLoader;
joonhunshin711a8872024-03-19 10:04:08 +0000889 mFeatureFlags = featureFlags;
joonhunshin2c3e4232021-11-28 07:32:01 +0000890
joonhunshin39710022022-01-14 11:53:58 +0000891 PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
892 EVENT_MULTI_SIM_CONFIGURATION_CHANGE, null);
893
joonhunshin2c3e4232021-11-28 07:32:01 +0000894 initialize(numSlot);
895 }
896
897 private void initialize(int numSlot) {
898 for (int i = 0; i < numSlot; i++) {
899 MmTelFeatureListener m = new MmTelFeatureListener(i);
900 mMmTelFeatureListenersSlotMap.put(i, m);
901
902 RcsFeatureListener r = new RcsFeatureListener(i);
903 mRcsFeatureListenersSlotMap.put(i, r);
904
905 ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
906 mProvisioningCallbackManagersSlotMap.put(i, p);
907 }
908 }
909
joonhunshin39710022022-01-14 11:53:58 +0000910 private void onMultiSimConfigChanged(int newNumSlot) {
911 log("onMultiSimConfigChanged: NumSlot " + mNumSlot + " newNumSlot " + newNumSlot);
912
913 if (mNumSlot < newNumSlot) {
914 for (int i = mNumSlot; i < newNumSlot; i++) {
915 MmTelFeatureListener m = new MmTelFeatureListener(i);
916 mMmTelFeatureListenersSlotMap.put(i, m);
917
918 RcsFeatureListener r = new RcsFeatureListener(i);
919 mRcsFeatureListenersSlotMap.put(i, r);
920
921 ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
922 mProvisioningCallbackManagersSlotMap.put(i, p);
923 }
924 } else if (mNumSlot > newNumSlot) {
925 for (int i = (mNumSlot - 1); i > (newNumSlot - 1); i--) {
926 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(i);
927 mMmTelFeatureListenersSlotMap.remove(i);
928 m.destroy();
929
930 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(i);
931 mRcsFeatureListenersSlotMap.remove(i);
932 r.destroy();
933
934 ProvisioningCallbackManager p = mProvisioningCallbackManagersSlotMap.get(i);
935 mProvisioningCallbackManagersSlotMap.remove(i);
936 p.clear();
937 }
938 }
939
940 mNumSlot = newNumSlot;
941 }
942
joonhunshin2c3e4232021-11-28 07:32:01 +0000943 /**
944 * destroy the instance
945 */
946 @VisibleForTesting
947 public void destroy() {
948 log("destroy");
949
950 mHandler.getLooper().quit();
951
952 mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
953
954 for (int i = 0; i < mMmTelFeatureListenersSlotMap.size(); i++) {
955 mMmTelFeatureListenersSlotMap.get(i).destroy();
956 }
957 mMmTelFeatureListenersSlotMap.clear();
958
959 for (int i = 0; i < mRcsFeatureListenersSlotMap.size(); i++) {
960 mRcsFeatureListenersSlotMap.get(i).destroy();
961 }
962 mRcsFeatureListenersSlotMap.clear();
963
964 for (int i = 0; i < mProvisioningCallbackManagersSlotMap.size(); i++) {
965 mProvisioningCallbackManagersSlotMap.get(i).clear();
966 }
967 }
968
969 /**
970 * create an instance
971 */
972 @VisibleForTesting
joonhunshin711a8872024-03-19 10:04:08 +0000973 public static ImsProvisioningController make(PhoneGlobals app, int numSlot,
974 FeatureFlags featureFlags) {
joonhunshin2c3e4232021-11-28 07:32:01 +0000975 synchronized (ImsProvisioningController.class) {
976 if (sInstance == null) {
977 Rlog.i(TAG, "ImsProvisioningController created");
978 HandlerThread handlerThread = new HandlerThread(TAG);
979 handlerThread.start();
980 sInstance = new ImsProvisioningController(app, numSlot, handlerThread.getLooper(),
981 ImsManager::getConnector, RcsFeatureManager::getConnector,
joonhunshin711a8872024-03-19 10:04:08 +0000982 new ImsProvisioningLoader(app), featureFlags);
joonhunshin2c3e4232021-11-28 07:32:01 +0000983 }
984 }
985 return sInstance;
986 }
987
988 /**
989 * Gets a ImsProvisioningController instance
990 */
991 @VisibleForTesting
992 public static ImsProvisioningController getInstance() {
993 synchronized (ImsProvisioningController.class) {
994 return sInstance;
995 }
996 }
997
998 /**
999 * Register IFeatureProvisioningCallback from ProvisioningManager
1000 */
1001
1002 @VisibleForTesting
1003 public void addFeatureProvisioningChangedCallback(int subId,
1004 IFeatureProvisioningCallback callback) {
1005 if (callback == null) {
1006 throw new IllegalArgumentException("provisioning callback can't be null");
1007 }
1008 int slotId = getSlotId(subId);
1009 if (slotId < 0 || slotId >= mNumSlot) {
1010 throw new IllegalArgumentException("subscription id is not available");
1011 }
1012
1013 try {
1014 mProvisioningCallbackManagersSlotMap.get(slotId).registerCallback(callback);
1015 log("Feature Provisioning Callback registered.");
joonhunshin711a8872024-03-19 10:04:08 +00001016
1017 if (mFeatureFlags.notifyInitialImsProvisioningStatus()) {
1018 mHandler.sendMessage(mHandler.obtainMessage(EVENT_NOTIFY_INIT_PROVISIONED_VALUE,
1019 getSlotId(subId), subId, (Object) callback));
1020 }
joonhunshin2c3e4232021-11-28 07:32:01 +00001021 } catch (NullPointerException e) {
1022 logw("can not access callback manager to add callback");
1023 }
1024 }
1025
1026 /**
1027 * Remove IFeatureProvisioningCallback
1028 */
1029 @VisibleForTesting
1030 public void removeFeatureProvisioningChangedCallback(int subId,
1031 IFeatureProvisioningCallback callback) {
1032 if (callback == null) {
1033 throw new IllegalArgumentException("provisioning callback can't be null");
1034 }
1035
1036 int slotId = getSlotId(subId);
1037 if (slotId < 0 || slotId >= mNumSlot) {
1038 throw new IllegalArgumentException("subscription id is not available");
1039 }
1040
1041 try {
1042 mProvisioningCallbackManagersSlotMap.get(slotId).unregisterCallback(callback);
1043 log("Feature Provisioning Callback removed.");
1044 } catch (NullPointerException e) {
1045 logw("can not access callback manager to remove callback");
1046 }
1047 }
1048
1049 /**
joonhunshinf9411cb2022-01-17 07:37:15 +00001050 * return the boolean whether MmTel capability is required provisioning or not
joonhunshin2c3e4232021-11-28 07:32:01 +00001051 */
1052 @VisibleForTesting
1053 public boolean isImsProvisioningRequiredForCapability(int subId, int capability, int tech) {
1054 // check subId
1055 int slotId = getSlotId(subId);
1056 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1057 loge("Fail to retrieve slotId from subId");
1058 throw new IllegalArgumentException("subscribe id is invalid");
1059 }
1060
1061 // check valid capability
1062 if (!(MMTEL_CAPABILITY_MIN < capability && capability < MMTEL_CAPABILITY_MAX)) {
1063 throw new IllegalArgumentException("MmTel capability '" + capability + "' is invalid");
1064 }
1065
1066 // check valid radio tech
1067 if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
1068 log("Ims not matched radio tech " + tech);
1069 throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
1070 }
1071
joonhunshin10e74ea2022-04-29 06:29:35 +00001072 // check new carrier config first KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
joonhunshin2c3e4232021-11-28 07:32:01 +00001073 boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/true);
1074
joonhunshin10e74ea2022-04-29 06:29:35 +00001075 // if that returns false, check deprecated carrier config
1076 // KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
1077 if (!retVal && (capability == CAPABILITY_TYPE_VOICE
1078 || capability == CAPABILITY_TYPE_VIDEO
1079 || capability == CAPABILITY_TYPE_UT)) {
1080 String key = CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL;
1081 if (capability == CAPABILITY_TYPE_UT) {
1082 key = CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL;
1083 }
1084
1085 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1086 if (imsCarrierConfigs != null) {
1087 retVal = imsCarrierConfigs.getBoolean(key);
1088 } else {
1089 retVal = CarrierConfigManager.getDefaultConfig().getBoolean(key);
1090 }
1091 }
1092
joonhunshin2c3e4232021-11-28 07:32:01 +00001093 log("isImsProvisioningRequiredForCapability capability " + capability
1094 + " tech " + tech + " return value " + retVal);
1095
1096 return retVal;
1097 }
1098
1099 /**
joonhunshinf9411cb2022-01-17 07:37:15 +00001100 * return the boolean whether RCS capability is required provisioning or not
joonhunshin2c3e4232021-11-28 07:32:01 +00001101 */
1102 @VisibleForTesting
1103 public boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech) {
1104 // check slotId and Phone object
1105 int slotId = getSlotId(subId);
1106 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1107 loge("Fail to retrieve slotId from subId");
1108 throw new IllegalArgumentException("subscribe id is invalid");
1109 }
1110
1111 // check valid capability
1112 if (!(RCS_CAPABILITY_MIN < capability && capability < RCS_CAPABILITY_MAX)) {
1113 throw new IllegalArgumentException("Rcs capability '" + capability + "' is invalid");
1114 }
1115
1116 // check valid radio tech
1117 if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
1118 log("Rcs not matched radio tech " + tech);
1119 throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
1120 }
1121
joonhunshin10e74ea2022-04-29 06:29:35 +00001122 // check new carrier config first KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
joonhunshin2c3e4232021-11-28 07:32:01 +00001123 boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/false);
1124
joonhunshin10e74ea2022-04-29 06:29:35 +00001125 // if that returns false, check deprecated carrier config
1126 // KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
1127 if (!retVal) {
1128 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1129 if (imsCarrierConfigs != null) {
1130 retVal = imsCarrierConfigs.getBoolean(
1131 CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL);
1132 } else {
1133 retVal = CarrierConfigManager.getDefaultConfig().getBoolean(
1134 CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL);
1135 }
1136 }
1137
joonhunshin2c3e4232021-11-28 07:32:01 +00001138 log("isRcsProvisioningRequiredForCapability capability " + capability
1139 + " tech " + tech + " return value " + retVal);
1140
1141 return retVal;
1142 }
1143
1144 /**
1145 * return the provisioning status for MmTel capability in specific radio tech
1146 */
1147 @VisibleForTesting
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001148 public boolean getImsProvisioningStatusForCapability(String attributionPackage, int subId,
1149 int capability, int tech) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001150 boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
1151 if (!mmTelProvisioned) { // provisioning not required
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001152 logAttr(attributionPackage, "getImsProvisioningStatusForCapability", subId,
1153 " not required, capability " + capability + " tech " + tech);
joonhunshin2c3e4232021-11-28 07:32:01 +00001154 return true;
1155 }
1156
1157 // read value from ImsProvisioningLoader
1158 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1159 capability, tech);
1160 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1161 // not set means initial value
1162 // read data from vendor ImsService and store that in ImsProvisioningLoader
1163 result = getValueFromImsService(subId, capability, tech);
1164 mmTelProvisioned = getBoolValue(result);
1165 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001166 setAndNotifyMmTelProvisioningValue(attributionPackage, subId, capability, tech,
1167 mmTelProvisioned);
joonhunshin2c3e4232021-11-28 07:32:01 +00001168 }
1169 } else {
1170 mmTelProvisioned = getBoolValue(result);
1171 }
1172
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001173 logAttr(attributionPackage, "getImsProvisioningStatusForCapability", subId,
1174 " capability " + capability
joonhunshin2c3e4232021-11-28 07:32:01 +00001175 + " tech " + tech
1176 + " result " + mmTelProvisioned);
1177 return mmTelProvisioned;
1178 }
1179
1180 /**
1181 * set MmTel provisioning status in specific tech
1182 */
1183 @VisibleForTesting
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001184 public void setImsProvisioningStatusForCapability(String attributionPackage, int subId,
1185 int capability, int tech, boolean isProvisioned) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001186 boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
1187 if (!mmTelProvisioned) { // provisioning not required
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001188 logAttr(attributionPackage, "setImsProvisioningStatusForCapability", subId,
1189 "not required, capability " + capability + " tech " + tech);
joonhunshin2c3e4232021-11-28 07:32:01 +00001190 return;
1191 }
1192
1193 // write value to ImsProvisioningLoader
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001194 boolean isChanged = setAndNotifyMmTelProvisioningValue(attributionPackage, subId,
1195 capability, tech, isProvisioned);
joonhunshin2c3e4232021-11-28 07:32:01 +00001196 if (!isChanged) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001197 logAttr(attributionPackage, "setImsProvisioningStatusForCapability", subId,
1198 "status not changed, capability " + capability + " tech " + tech);
joonhunshin2c3e4232021-11-28 07:32:01 +00001199 return;
1200 }
1201
1202 int slotId = getSlotId(subId);
1203 // find matched key from capability and tech
1204 int value = getIntValue(isProvisioned);
1205 int key = getKeyFromCapability(capability, tech);
1206 if (key != INVALID_VALUE) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001207 logAttr(attributionPackage, "setImsProvisioningStatusForCapability", subId,
1208 "matched key " + key);
joonhunshin2c3e4232021-11-28 07:32:01 +00001209 try {
1210 // set key and value to vendor ImsService for MmTel
1211 mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001212 } catch (NullPointerException e) {
1213 loge("can not access MmTelFeatureListener with capability " + capability);
1214 }
1215 }
1216 }
1217
1218 /**
1219 * return the provisioning status for RCS capability in specific radio tech
1220 */
1221 @VisibleForTesting
1222 public boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech) {
1223 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1224 if (!rcsProvisioned) { // provisioning not required
1225 log("getRcsProvisioningStatusForCapability : not required"
1226 + " capability " + capability + " tech " + tech);
1227 return true;
1228 }
1229
1230 // read data from ImsProvisioningLoader
1231 int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1232 capability, tech);
1233 if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
1234 // not set means initial value
1235 // read data from vendor ImsService and store that in ImsProvisioningLoader
1236 result = getRcsValueFromImsService(subId, capability);
1237 rcsProvisioned = getBoolValue(result);
1238 if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
1239 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, rcsProvisioned);
1240 }
1241 } else {
1242 rcsProvisioned = getBoolValue(result);
1243 }
1244
1245 log("getRcsProvisioningStatusForCapability : "
1246 + " capability " + capability
1247 + " tech " + tech
1248 + " result " + rcsProvisioned);
1249 return rcsProvisioned;
1250 }
1251
1252 /**
1253 * set RCS provisioning status in specific tech
1254 */
1255 @VisibleForTesting
1256 public void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
1257 boolean isProvisioned) {
1258 boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
1259 if (!rcsProvisioned) { // provisioning not required
1260 log("set rcs provisioning status but not required");
1261 return;
1262 }
1263
1264 // write status using ImsProvisioningLoader
1265 boolean isChanged = setAndNotifyRcsProvisioningValue(subId, capability, tech,
1266 isProvisioned);
1267 if (!isChanged) {
1268 log("status not changed rcs capability " + capability + " tech " + tech);
1269 return;
1270 }
1271
1272 int slotId = getSlotId(subId);
1273 int key = ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
1274 int value = getIntValue(isProvisioned);
1275 try {
joonhunshin35d754f2022-06-24 03:29:15 +00001276 // On some older devices, EAB is managed on the MmTel ImsService when the RCS
1277 // ImsService is not configured. If there is no RCS ImsService defined, fallback to
1278 // MmTel. In the rare case that we hit a race condition where the RCS ImsService has
1279 // crashed or has not come up yet, the value will be synchronized via
1280 // setInitialProvisioningKeys().
1281 if (mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1282 mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1283 }
1284
1285 // EAB provisioning status should be updated to both the Rcs and MmTel ImsService,
1286 // because the provisioning callback is listening to only MmTel provisioning key
1287 // changes.
1288 mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001289 } catch (NullPointerException e) {
1290 loge("can not access RcsFeatureListener with capability " + capability);
1291 }
1292 }
1293
1294 /**
1295 * set RCS provisioning status in specific key and value
1296 * @param key integer key, defined as one of
1297 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1298 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1299 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1300 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1301 * @param value in Integer format.
1302 * @return the result of setting the configuration value, defined as one of
1303 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1304 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1305 */
1306 @VisibleForTesting
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001307 public int setProvisioningValue(String attributionPackage, int subId, int key, int value) {
1308 logAttr(attributionPackage, "setProvisioningValue", subId, key + ": " + value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001309
1310 int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
1311 // check key value
1312 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001313 logAttr(attributionPackage, "setProvisioningValue", subId,
1314 "not matched key " + key);
joonhunshin2c3e4232021-11-28 07:32:01 +00001315 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1316 }
1317
1318 // check subId
1319 int slotId = getSlotId(subId);
1320 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001321 logAttrE(attributionPackage, "setProvisioningValue", subId,
1322 "Fail to retrieve slotId from subId");
joonhunshin2c3e4232021-11-28 07:32:01 +00001323 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1324 }
1325
1326 try {
joonhunshin35d754f2022-06-24 03:29:15 +00001327 // set key and value to vendor ImsService for MmTel
1328 // EAB provisioning status should be updated to both the Rcs and MmTel ImsService,
1329 // because the provisioning callback is listening to only MmTel provisioning key
1330 // changes.
1331 retVal = mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
1332
1333 // If the Rcs ImsService is not available, the EAB provisioning status will be written
1334 // to the MmTel ImsService for backwards compatibility. In the rare case that this is
1335 // hit due to RCS ImsService temporarily unavailable, the value will be synchronized
1336 // via setInitialProvisioningKeys() when the RCS ImsService comes back up.
1337 if (key == KEY_EAB_PROVISIONING_STATUS
1338 && mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1339 // set key and value to vendor ImsService for RCS and use retVal from RCS if
1340 // related to EAB when possible.
1341 retVal = mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001342 }
1343 } catch (NullPointerException e) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001344 logAttrE(attributionPackage, "setProvisioningValue", subId,
1345 "can not access FeatureListener to set provisioning value");
joonhunshin2c3e4232021-11-28 07:32:01 +00001346 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
1347 }
1348
1349 // update and notify provisioning status changed capability and tech from key
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001350 updateCapabilityTechFromKey(attributionPackage, subId, key, value);
joonhunshin2c3e4232021-11-28 07:32:01 +00001351
joonhunshin2c3e4232021-11-28 07:32:01 +00001352 return retVal;
1353 }
1354
1355 /**
1356 * get RCS provisioning status in specific key and value
1357 * @param key integer key, defined as one of
1358 * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
1359 * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
1360 * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
1361 * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
1362 * @return the result of setting the configuration value, defined as one of
1363 * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
1364 * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
1365 * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN}
1366 */
1367 @VisibleForTesting
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001368 public int getProvisioningValue(String attributionPackage, int subId, int key) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001369 // check key value
1370 if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001371 logAttr(attributionPackage, "getProvisioningValue", subId,
1372 "not matched key " + key);
joonhunshin2c3e4232021-11-28 07:32:01 +00001373 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1374 }
1375
1376 // check subId
1377 int slotId = getSlotId(subId);
1378 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001379 logAttrE(attributionPackage, "getProvisioningValue", subId,
1380 "Fail to retrieve slotId from subId");
joonhunshin2c3e4232021-11-28 07:32:01 +00001381 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1382 }
1383
1384 // check data from ImsProvisioningLoader
1385 int capability = getCapabilityFromKey(key);
1386 int tech = getTechFromKey(key);
1387 int result;
1388 if (capability != INVALID_VALUE && tech != INVALID_VALUE) {
1389 if (key == KEY_EAB_PROVISIONING_STATUS) {
1390 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1391 capability, tech);
1392 } else {
1393 result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1394 capability, tech);
1395 }
1396 if (result != ImsProvisioningLoader.STATUS_NOT_SET) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001397 logAttr(attributionPackage, "getProvisioningValue", subId,
1398 "cache hit : key=" + key + ": value=" + result);
joonhunshin2c3e4232021-11-28 07:32:01 +00001399 return result;
1400 }
1401 }
1402
1403 // get data from ImsService, update it in ImsProvisioningLoader
1404 if (key == KEY_EAB_PROVISIONING_STATUS) {
1405 result = getRcsValueFromImsService(subId, capability);
1406 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001407 logAttrW(attributionPackage, "getProvisioningValue", subId,
1408 "fail to get data from ImsService, capability=" + capability);
joonhunshin2c3e4232021-11-28 07:32:01 +00001409 return result;
1410 }
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001411 logAttr(attributionPackage, "getProvisioningValue", subId,
1412 "cache miss, get from RCS - key=" + key + ": value=" + result);
joonhunshin6cd38ac2022-02-03 06:37:55 +00001413
joonhunshin2c3e4232021-11-28 07:32:01 +00001414 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, getBoolValue(result));
1415 return result;
1416 } else {
1417 result = getValueFromImsService(subId, capability, tech);
1418 if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001419 logAttrW(attributionPackage, "getProvisioningValue", subId,
1420 "fail to get data from ImsService, capability=" + capability);
joonhunshin2c3e4232021-11-28 07:32:01 +00001421 return result;
1422 }
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001423 logAttr(attributionPackage, "getProvisioningValue", subId,
1424 "cache miss, get from MMTEL - key=" + key + ": value=" + result);
joonhunshin6cd38ac2022-02-03 06:37:55 +00001425
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001426 setAndNotifyMmTelProvisioningValue(attributionPackage, subId, capability, tech,
1427 getBoolValue(result));
joonhunshin2c3e4232021-11-28 07:32:01 +00001428 return result;
1429 }
1430 }
1431
1432 /**
1433 * get the handler
1434 */
1435 @VisibleForTesting
1436 public Handler getHandler() {
1437 return mHandler;
1438 }
1439
1440 private boolean isProvisioningRequired(int subId, int capability, int tech, boolean isMmTel) {
joonhunshinf9411cb2022-01-17 07:37:15 +00001441 int[] techArray;
1442 techArray = getTechsFromCarrierConfig(subId, capability, isMmTel);
1443 if (techArray == null) {
1444 logw("isProvisioningRequired : getTechsFromCarrierConfig failed");
1445 // not exist in CarrierConfig that means provisioning is not required
joonhunshin2c3e4232021-11-28 07:32:01 +00001446 return false;
1447 }
1448
joonhunshin2c3e4232021-11-28 07:32:01 +00001449 // compare with carrier config
joonhunshinf9411cb2022-01-17 07:37:15 +00001450 if (Arrays.stream(techArray).anyMatch(keyValue -> keyValue == tech)) {
1451 // existing same tech means provisioning required
1452 return true;
joonhunshin2c3e4232021-11-28 07:32:01 +00001453 }
1454
1455 log("isProvisioningRequired : not matched capability " + capability + " tech " + tech);
1456 return false;
1457 }
1458
joonhunshin10e74ea2022-04-29 06:29:35 +00001459 private int[] getTechsFromCarrierConfig(int subId, int capability, boolean isMmTel) {
joonhunshinf9411cb2022-01-17 07:37:15 +00001460 String featureKey;
1461 String capabilityKey;
1462 if (isMmTel) {
1463 featureKey = CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE;
1464 capabilityKey = KEYS_MMTEL_CAPABILITY.get(capability);
1465 } else {
1466 featureKey = CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE;
1467 capabilityKey = KEYS_RCS_CAPABILITY.get(capability);
1468 }
joonhunshin2c3e4232021-11-28 07:32:01 +00001469
joonhunshinf9411cb2022-01-17 07:37:15 +00001470 if (capabilityKey != null) {
1471 PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
1472 if (imsCarrierConfigs == null) {
1473 log("getTechsFromCarrierConfig : imsCarrierConfigs null");
1474 return null;
1475 }
1476
1477 PersistableBundle provisioningBundle =
1478 imsCarrierConfigs.getPersistableBundle(featureKey);
1479 if (provisioningBundle == null) {
1480 log("getTechsFromCarrierConfig : provisioningBundle null");
1481 return null;
1482 }
1483
1484 return provisioningBundle.getIntArray(capabilityKey);
1485 }
1486
1487 return null;
joonhunshin2c3e4232021-11-28 07:32:01 +00001488 }
1489
1490 private int getValueFromImsService(int subId, int capability, int tech) {
1491 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
1492
1493 // operation is based on capability
1494 switch (capability) {
1495 case CAPABILITY_TYPE_VOICE:
1496 int item = (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
1497 ? ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
1498 : ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1499 // read data from vendor ImsService
1500 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1501 .getProvisioningValue(item);
1502 break;
1503 case CAPABILITY_TYPE_VIDEO:
1504 // read data from vendor ImsService
1505 config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
1506 .getProvisioningValue(ProvisioningManager.KEY_VT_PROVISIONING_STATUS);
1507 break;
1508 default:
1509 log("Capability " + capability + " has been provisioning");
1510 break;
1511 }
1512
1513 return config;
1514 }
1515
1516 private int getRcsValueFromImsService(int subId, int capability) {
1517 int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
joonhunshin35d754f2022-06-24 03:29:15 +00001518 int slotId = getSlotId(subId);
joonhunshin2c3e4232021-11-28 07:32:01 +00001519
joonhunshin35d754f2022-06-24 03:29:15 +00001520 if (capability != CAPABILITY_TYPE_PRESENCE_UCE) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001521 log("Capability " + capability + " has been provisioning");
joonhunshin35d754f2022-06-24 03:29:15 +00001522 return config;
1523 }
1524 try {
1525 if (mRcsFeatureListenersSlotMap.get(slotId).isConnectionReady()) {
1526 config = mRcsFeatureListenersSlotMap.get(slotId)
1527 .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1528 } else {
1529 log("Rcs ImsService is not available, "
1530 + "EAB provisioning status should be read from MmTel ImsService");
1531 config = mMmTelFeatureListenersSlotMap.get(slotId)
1532 .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
1533 }
1534 } catch (NullPointerException e) {
1535 logw("can not access FeatureListener : " + e.getMessage());
joonhunshin2c3e4232021-11-28 07:32:01 +00001536 }
1537
1538 return config;
1539 }
1540
1541 private void onSubscriptionsChanged() {
1542 for (int index = 0; index < mMmTelFeatureListenersSlotMap.size(); index++) {
1543 MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(index);
1544 m.setSubId(getSubId(index));
1545 }
1546 for (int index = 0; index < mRcsFeatureListenersSlotMap.size(); index++) {
1547 RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(index);
1548 r.setSubId(getSubId(index));
1549 }
1550 for (int index = 0; index < mProvisioningCallbackManagersSlotMap.size(); index++) {
1551 ProvisioningCallbackManager m = mProvisioningCallbackManagersSlotMap.get(index);
1552 m.setSubId(getSubId(index));
1553 }
1554 }
1555
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001556 private void updateCapabilityTechFromKey(String attributionPackage, int subId, int key,
1557 int value) {
joonhunshin2c3e4232021-11-28 07:32:01 +00001558 boolean isProvisioned = getBoolValue(value);
1559 int capability = getCapabilityFromKey(key);
1560 int tech = getTechFromKey(key);
1561
1562 if (capability == INVALID_VALUE || tech == INVALID_VALUE) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001563 logAttrW(attributionPackage, "updateCapabilityTechFromKey", subId,
1564 "unknown key " + key);
joonhunshin2c3e4232021-11-28 07:32:01 +00001565 return;
1566 }
1567
1568 if (key == KEY_VOLTE_PROVISIONING_STATUS
1569 || key == KEY_VT_PROVISIONING_STATUS
1570 || key == KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001571 setAndNotifyMmTelProvisioningValue(attributionPackage, subId, capability, tech,
1572 isProvisioned);
joonhunshin2c3e4232021-11-28 07:32:01 +00001573 }
1574 if (key == KEY_EAB_PROVISIONING_STATUS) {
1575 setAndNotifyRcsProvisioningValueForAllTech(subId, capability, isProvisioned);
1576 }
1577 }
1578
1579 private int getCapabilityFromKey(int key) {
1580 int capability;
1581 switch (key) {
1582 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1583 // intentional fallthrough
1584 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1585 capability = CAPABILITY_TYPE_VOICE;
1586 break;
1587 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1588 capability = CAPABILITY_TYPE_VIDEO;
1589 break;
1590 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1591 // default CAPABILITY_TYPE_PRESENCE_UCE used for KEY_EAB_PROVISIONING_STATUS
1592 capability = CAPABILITY_TYPE_PRESENCE_UCE;
1593 break;
1594 default:
1595 capability = INVALID_VALUE;
1596 break;
1597 }
1598 return capability;
1599 }
1600
1601 private int getTechFromKey(int key) {
1602 int tech;
1603 switch (key) {
1604 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
1605 tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
1606 break;
1607 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
1608 // intentional fallthrough
1609 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
1610 // intentional fallthrough
1611 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
1612 tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
1613 break;
1614 default:
1615 tech = INVALID_VALUE;
1616 break;
1617 }
1618 return tech;
1619 }
1620
1621 private int getKeyFromCapability(int capability, int tech) {
1622 int key = INVALID_VALUE;
1623 if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_IWLAN) {
1624 key = ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
1625 } else if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_LTE) {
1626 key = ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
1627 } else if (capability == CAPABILITY_TYPE_VIDEO && tech == REGISTRATION_TECH_LTE) {
1628 key = ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
1629 }
1630
1631 return key;
1632 }
1633
1634 protected int getSubId(int slotId) {
Jack Yudfce4d62023-02-25 15:15:01 -08001635 return SubscriptionManager.getSubscriptionId(slotId);
joonhunshin2c3e4232021-11-28 07:32:01 +00001636 }
1637
1638 protected int getSlotId(int subId) {
1639 return mSubscriptionManager.getPhoneId(subId);
1640 }
1641
1642 protected ImsConfig getImsConfig(ImsManager imsManager) throws ImsException {
1643 return imsManager.getConfigInterface();
1644 }
1645
1646 protected ImsConfig getImsConfig(IImsConfig iImsConfig) {
1647 return new ImsConfig(iImsConfig);
1648 }
1649
1650 private int getIntValue(boolean isProvisioned) {
1651 return isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
1652 : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
1653 }
1654
1655 private boolean getBoolValue(int value) {
1656 return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED ? true : false;
1657 }
1658
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001659 // If VoLTE is not provisioned, generate an anomaly report as this is not expected.
1660 private void checkProvisioningValueForAnomaly(String attributionPackage, int subId,
1661 int capability, int tech, boolean isProvisioned) {
1662 if (isProvisioned) return;
1663 boolean isVolte = capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_LTE;
1664 if (!isVolte) return;
1665 // We have hit the condition where VoLTE has been de-provisioned
1666 int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
1667 TelephonyManager manager = mApp.getSystemService(TelephonyManager.class);
1668 if (manager != null) {
1669 carrierId = manager.createForSubscriptionId(subId).getSimCarrierId();
1670 }
1671 logAttrW(attributionPackage, "checkProvisioningValueForAnomaly", subId,
1672 "VoLTE provisioning disabled");
1673 AnomalyReporter.reportAnomaly(VOLTE_PROVISIONING_ANOMALY,
1674 VOLTE_PROVISIONING_ANOMALY_DESC, carrierId);
1675 }
1676
1677 private boolean setAndNotifyMmTelProvisioningValue(String attributionPackage, int subId,
1678 int capability, int tech,
joonhunshin2c3e4232021-11-28 07:32:01 +00001679 boolean isProvisioned) {
1680 boolean changed = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_MMTEL,
1681 capability, tech, isProvisioned);
1682 // notify MmTel capability changed
1683 if (changed) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001684 checkProvisioningValueForAnomaly(attributionPackage, subId, capability, tech,
1685 isProvisioned);
joonhunshin2c3e4232021-11-28 07:32:01 +00001686 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1687 getSlotId(subId), 0, (Object) new FeatureProvisioningData(
1688 capability, tech, isProvisioned, /*isMmTel*/true)));
1689 }
1690
1691 return changed;
1692 }
1693
1694 private boolean setAndNotifyRcsProvisioningValue(int subId, int capability, int tech,
1695 boolean isProvisioned) {
1696 boolean isChanged = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_RCS,
1697 capability, tech, isProvisioned);
1698
1699 if (isChanged) {
1700 int slotId = getSlotId(subId);
1701
1702 // notify RCS capability changed
1703 mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
1704 slotId, 0, (Object) new FeatureProvisioningData(
1705 capability, tech, isProvisioned, /*isMmtel*/false)));
1706 }
1707
1708 return isChanged;
1709 }
1710
1711 private boolean setAndNotifyRcsProvisioningValueForAllTech(int subId, int capability,
1712 boolean isProvisioned) {
1713 boolean isChanged = false;
1714
1715 for (int tech : LOCAL_RADIO_TECHS) {
1716 isChanged |= setAndNotifyRcsProvisioningValue(subId, capability, tech, isProvisioned);
1717 }
1718
1719 return isChanged;
1720 }
1721
joonhunshin2c3e4232021-11-28 07:32:01 +00001722 protected boolean isValidSubId(int subId) {
1723 int slotId = getSlotId(subId);
1724 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
1725 return false;
1726 }
1727
1728 return true;
1729 }
1730
joonhunshin711a8872024-03-19 10:04:08 +00001731 private void notifyMmTelProvisioningStatus(int slotId, int subId,
1732 @Nullable IFeatureProvisioningCallback callback) {
1733 int value = ImsProvisioningLoader.STATUS_NOT_SET;
1734 int[] techArray;
1735 for (int capability : LOCAL_MMTEL_CAPABILITY) {
1736 techArray = getTechsFromCarrierConfig(subId, capability, /*isMmTle*/true);
1737 if (techArray == null) {
1738 continue;
1739 }
1740
1741 for (int radioTech : techArray) {
1742 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
1743 capability, radioTech);
1744 if (value == ImsProvisioningLoader.STATUS_NOT_SET) {
1745 // Not yet provisioned
1746 continue;
1747 }
1748
1749 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
1750 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
1751
1752 // Notify all registered callbacks
1753 if (callback == null) {
1754 mProvisioningCallbackManagersSlotMap.get(slotId)
1755 .notifyProvisioningCapabilityChanged(
1756 new FeatureProvisioningData(
1757 capability,
1758 radioTech,
1759 getBoolValue(value),
1760 /*isMmTle*/true));
1761 } else {
1762 try {
1763 callback.onFeatureProvisioningChanged(capability, radioTech,
1764 getBoolValue(value));
1765 } catch (RemoteException e) {
1766 logw("notifyMmTelProvisioningStatus callback is not available");
1767 }
1768 }
1769 }
1770 }
1771 }
1772
1773 private void notifyRcsProvisioningStatus(int slotId, int subId,
1774 @Nullable IFeatureProvisioningCallback callback) {
1775 int value = ImsProvisioningLoader.STATUS_NOT_SET;
1776 int[] techArray;
1777 for (int capability : LOCAL_RCS_CAPABILITY) {
1778 techArray = getTechsFromCarrierConfig(subId, capability, /*isMmTle*/false);
1779 if (techArray == null) {
1780 continue;
1781 }
1782
1783 for (int radioTech : techArray) {
1784 value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
1785 capability, radioTech);
1786 if (value == ImsProvisioningLoader.STATUS_NOT_SET) {
1787 // Not yet provisioned
1788 continue;
1789 }
1790
1791 value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
1792 ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
1793
1794 // Notify all registered callbacks
1795 if (callback == null) {
1796 mProvisioningCallbackManagersSlotMap.get(slotId)
1797 .notifyProvisioningCapabilityChanged(
1798 new FeatureProvisioningData(
1799 capability,
1800 radioTech,
1801 getBoolValue(value),
1802 /*isMmTle*/false));
1803 } else {
1804 try {
1805 callback.onRcsFeatureProvisioningChanged(capability, radioTech,
1806 getBoolValue(value));
1807 } catch (RemoteException e) {
1808 logw("notifyRcsProvisioningStatus callback is not available");
1809 }
1810 }
1811 }
1812 }
1813 }
1814
Brad Ebinger0df4fdf2024-07-19 17:26:12 -07001815 private void logAttr(String attr, String prefix, int subId, String log) {
1816 Rlog.d(TAG, prefix + "[" + subId + "]: " + log + ", attr = [" + attr + "]");
1817 }
1818
1819 private void logAttrW(String attr, String prefix, int subId, String log) {
1820 Rlog.w(TAG, prefix + "[" + subId + "]: " + log + ", attr = [" + attr + "]");
1821 }
1822
1823 private void logAttrE(String attr, String prefix, int subId, String log) {
1824 Rlog.e(TAG, prefix + "[" + subId + "]: " + log + ", attr = [" + attr + "]");
1825 }
1826
joonhunshin2c3e4232021-11-28 07:32:01 +00001827 private void log(String s) {
1828 Rlog.d(TAG, s);
1829 }
1830
1831 private void log(String prefix, int slotId, String s) {
1832 Rlog.d(TAG, prefix + "[" + slotId + "] " + s);
1833 }
1834
1835 private void logi(String prefix, int slotId, String s) {
1836 Rlog.i(TAG, prefix + "[" + slotId + "] " + s);
1837 }
1838
1839 private void logw(String s) {
1840 Rlog.w(TAG, s);
1841 }
1842
1843 private void logw(String prefix, int slotId, String s) {
1844 Rlog.w(TAG, prefix + "[" + slotId + "] " + s);
1845 }
1846
1847 private void loge(String s) {
1848 Rlog.e(TAG, s);
1849 }
1850
1851 private void loge(String prefix, int slotId, String s) {
1852 Rlog.e(TAG, prefix + "[" + slotId + "] " + s);
1853 }
1854}