1 /* 2 * Copyright (C) 2022 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 17 package com.android.telephony.qns; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.os.Handler; 22 import android.os.HandlerThread; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.telephony.SubscriptionManager; 26 import android.telephony.ims.ProvisioningManager; 27 import android.text.TextUtils; 28 import android.util.Log; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 32 import java.util.concurrent.ConcurrentHashMap; 33 34 class QnsProvisioningListener { 35 36 private static final long REG_CALLBACK_DELAY = 2000L; // 3sec 37 private static final int REG_CALLBACK_RETRY = 10; // 10 times 38 private static final int EVENT_BASE = 11000; 39 private static final int EVENT_REGISTER_PROVISIONING_CALLBACK = EVENT_BASE + 1; 40 private static final int EVENT_CALLBACK_REGISTERED = EVENT_BASE + 3; 41 private static final int EVENT_NOTIFY_PROVISION_INFO_CHANGED = EVENT_BASE + 4; 42 private static final int EVENT_IMS_STATE_CHANGED = EVENT_BASE + 5; 43 private final String mLogTag; 44 private final Context mContext; 45 private final int mSlotIndex; 46 private final QnsProvisioningInfo mProvisioningInfo; 47 private final QnsImsManager mQnsImsManager; 48 @VisibleForTesting QnsProvisioningHandler mQnsProvisioningHandler; 49 50 private final QnsProvisioningCallback mQnsProvisioningCallback; 51 private final QnsRegistrantList mRegistrantList; 52 private ProvisioningManager mProvisioningManager; 53 private boolean mIsProvisioningCallbackRegistered; 54 QnsProvisioningListener(Context context, QnsImsManager imsManager, int slotIndex)55 QnsProvisioningListener(Context context, QnsImsManager imsManager, int slotIndex) { 56 mSlotIndex = slotIndex; 57 mLogTag = QnsProvisioningListener.class.getSimpleName() + "_" + mSlotIndex; 58 mContext = context; 59 mQnsImsManager = imsManager; 60 mProvisioningInfo = new QnsProvisioningInfo(); 61 mQnsProvisioningCallback = new QnsProvisioningCallback(); 62 mIsProvisioningCallbackRegistered = false; 63 mRegistrantList = new QnsRegistrantList(); 64 65 HandlerThread handlerThread = new HandlerThread(mLogTag); 66 handlerThread.start(); 67 mQnsProvisioningHandler = new QnsProvisioningHandler(handlerThread.getLooper()); 68 69 registerProvisioningCallback(); 70 mQnsImsManager.registerImsStateChanged(mQnsProvisioningHandler, EVENT_IMS_STATE_CHANGED); 71 } 72 close()73 void close() { 74 mQnsImsManager.unregisterImsStateChanged(mQnsProvisioningHandler); 75 mRegistrantList.removeAll(); 76 mProvisioningInfo.clear(); 77 unregisterProvisioningCallback(); 78 } 79 registerProvisioningCallback()80 private void registerProvisioningCallback() { 81 // checks if the callback is already registered 82 if (mIsProvisioningCallbackRegistered) { 83 log("registerProvisioningCallback: already registered."); 84 return; 85 } 86 87 // checks for validation subscription id. 88 int subId = QnsUtils.getSubId(mContext, mSlotIndex); 89 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 90 log("registerProvisioningCallback failed Invalid Subscription Id"); 91 return; 92 } 93 94 log("on registering provisioning callback"); 95 96 try { 97 // checks ImsException for ims not supported or unavailable. 98 if (mQnsImsManager.getImsServiceState() != 2) { // STATE_READY 99 throw new Exception(); 100 } 101 102 // create provisioning manager. 103 if (mProvisioningManager == null) { 104 mProvisioningManager = ProvisioningManager.createForSubscriptionId(subId); 105 } 106 107 // Register provisioning changed callback 108 mProvisioningManager.registerProvisioningChangedCallback( 109 mContext.getMainExecutor(), mQnsProvisioningCallback); 110 111 // Set the provisioning callback is registered. 112 mIsProvisioningCallbackRegistered = true; 113 114 log("registered provisioning callback"); 115 116 mQnsProvisioningHandler.sendProvisioningCallbackRegistered(); 117 } catch (Exception e) { 118 loge("registerProvisioningCallback error: " + e); 119 120 // Unregister the callback 121 unregisterProvisioningCallback(); 122 123 // Retry registering provisioning callback. 124 if (!mIsProvisioningCallbackRegistered) { 125 mQnsProvisioningHandler.sendRegisterProvisioningCallback(); 126 } 127 } 128 } 129 unregisterProvisioningCallback()130 private void unregisterProvisioningCallback() { 131 log("unregisterProvisioningCallback"); 132 133 if (mProvisioningManager != null) { 134 try { 135 mProvisioningManager.unregisterProvisioningChangedCallback( 136 mQnsProvisioningCallback); 137 } catch (Exception e) { 138 loge("unregisterProvisioningCallback error:" + e); 139 } 140 } 141 if (mIsProvisioningCallbackRegistered) { 142 mIsProvisioningCallbackRegistered = false; 143 } 144 if (mProvisioningManager != null) { 145 mProvisioningManager = null; 146 } 147 } 148 149 /** 150 * Register an event for Provisioning value changed. 151 * 152 * @param h the Handler to get event. 153 * @param what the event. 154 * @param userObj user object. 155 * @param notifyImmediately set true if you want to notify immediately. 156 */ registerProvisioningItemInfoChanged( Handler h, int what, Object userObj, boolean notifyImmediately)157 void registerProvisioningItemInfoChanged( 158 Handler h, int what, Object userObj, boolean notifyImmediately) { 159 if (h != null) { 160 QnsRegistrant r = new QnsRegistrant(h, what, userObj); 161 mRegistrantList.add(r); 162 if (notifyImmediately) { 163 r.notifyRegistrant( 164 new QnsAsyncResult(null, new QnsProvisioningInfo(mProvisioningInfo), null)); 165 } 166 } 167 } 168 169 /** 170 * Unregister an event for Provisioning value changed. 171 * 172 * @param h the handler to get event. 173 */ unregisterProvisioningItemInfoChanged(Handler h)174 void unregisterProvisioningItemInfoChanged(Handler h) { 175 if (h != null) { 176 mRegistrantList.remove(h); 177 } 178 } 179 getLastProvisioningWfcRoamingEnabledInfo()180 boolean getLastProvisioningWfcRoamingEnabledInfo() { 181 try { 182 return mProvisioningInfo.getIntegerItem( 183 ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE) 184 != 0; 185 } catch (Exception e) { 186 return false; 187 } 188 } 189 notifyProvisioningItemInfoChanged(@onNull QnsProvisioningInfo info)190 private void notifyProvisioningItemInfoChanged(@NonNull QnsProvisioningInfo info) { 191 log("notify ProvisioningItemInfo:" + info); 192 mRegistrantList.notifyRegistrants(new QnsAsyncResult(null, info, null)); 193 } 194 loadDefaultItems()195 private void loadDefaultItems() { 196 synchronized (mProvisioningInfo) { 197 loadIntegerItem(ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS); 198 loadIntegerItem(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE); 199 loadIntegerItem(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE); 200 loadIntegerItem(ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE); 201 loadIntegerItem(ProvisioningManager.KEY_LTE_THRESHOLD_1); 202 loadIntegerItem(ProvisioningManager.KEY_LTE_THRESHOLD_2); 203 loadIntegerItem(ProvisioningManager.KEY_LTE_THRESHOLD_3); 204 loadIntegerItem(ProvisioningManager.KEY_1X_THRESHOLD); 205 loadIntegerItem(ProvisioningManager.KEY_WIFI_THRESHOLD_A); 206 loadIntegerItem(ProvisioningManager.KEY_WIFI_THRESHOLD_B); 207 loadIntegerItem(ProvisioningManager.KEY_LTE_EPDG_TIMER_SEC); 208 loadIntegerItem(ProvisioningManager.KEY_WIFI_EPDG_TIMER_SEC); 209 loadIntegerItem(ProvisioningManager.KEY_1X_EPDG_TIMER_SEC); 210 loadStringItem(ProvisioningManager.KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID); 211 if (mProvisioningInfo.isUpdated()) { 212 mQnsProvisioningHandler.sendNotifyProvisioningInfoChanged(); 213 } 214 } 215 } 216 loadIntegerItem(int item)217 private void loadIntegerItem(int item) { 218 try { 219 int value = mProvisioningManager.getProvisioningIntValue(item); 220 log("loadIntegerItem item:" + item + " value:" + value); 221 mProvisioningInfo.setIntegerItem(item, value); 222 } catch (Exception e) { 223 loge("got exception e:" + e); 224 } 225 } 226 loadStringItem(int item)227 private void loadStringItem(int item) { 228 try { 229 String value = mProvisioningManager.getProvisioningStringValue(item); 230 log("loadStringItem item:" + item + " value:" + value); 231 mProvisioningInfo.setStringItem(item, value); 232 } catch (Exception e) { 233 loge("got exception e:" + e); 234 } 235 } 236 log(String s)237 protected void log(String s) { 238 Log.d(mLogTag, s); 239 } 240 loge(String s)241 protected void loge(String s) { 242 Log.e(mLogTag, s); 243 } 244 245 static class QnsProvisioningInfo { 246 247 private final ConcurrentHashMap<Integer, Integer> mIntegerItems; 248 private final ConcurrentHashMap<Integer, String> mStringItems; 249 private boolean mUpdated; 250 QnsProvisioningInfo()251 QnsProvisioningInfo() { 252 mIntegerItems = new ConcurrentHashMap<>(); 253 mStringItems = new ConcurrentHashMap<>(); 254 mUpdated = false; 255 } 256 QnsProvisioningInfo(QnsProvisioningInfo info)257 QnsProvisioningInfo(QnsProvisioningInfo info) { 258 mIntegerItems = new ConcurrentHashMap<>(); 259 mStringItems = new ConcurrentHashMap<>(); 260 mIntegerItems.putAll(info.mIntegerItems); 261 mStringItems.putAll(info.mStringItems); 262 mUpdated = info.mUpdated; 263 } 264 265 @Override toString()266 public String toString() { 267 return "QnsProvisioningInfo{" 268 + "mIntegerItems=" 269 + mIntegerItems 270 + ", mStringItems=" 271 + mStringItems 272 + ", mUpdated=" 273 + mUpdated 274 + '}'; 275 } 276 hasItem(int item)277 boolean hasItem(int item) { 278 return mIntegerItems.get(item) != null || mStringItems.get(item) != null; 279 } 280 setIntegerItem(int item, int value)281 private void setIntegerItem(int item, int value) { 282 if (value == ProvisioningManager.PROVISIONING_RESULT_UNKNOWN 283 || (!isValueZeroValidItem(item) 284 && value == ProvisioningManager.PROVISIONING_VALUE_DISABLED)) { 285 if (mIntegerItems.remove(item) != null) { 286 markUpdated(true); 287 } 288 return; 289 } 290 if (getIntegerItem(item) != null && getIntegerItem(item) == value) { 291 return; 292 } 293 mIntegerItems.put(item, value); 294 markUpdated(true); 295 } 296 getIntegerItem(int item)297 Integer getIntegerItem(int item) { 298 return mIntegerItems.get(item); 299 } 300 isValueZeroValidItem(int key)301 private boolean isValueZeroValidItem(int key) { 302 switch (key) { 303 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS: 304 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE: 305 case ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE: 306 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE: 307 case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID: 308 return true; 309 case ProvisioningManager.KEY_LTE_THRESHOLD_1: 310 case ProvisioningManager.KEY_LTE_THRESHOLD_2: 311 case ProvisioningManager.KEY_LTE_THRESHOLD_3: 312 case ProvisioningManager.KEY_1X_THRESHOLD: 313 case ProvisioningManager.KEY_WIFI_THRESHOLD_A: 314 case ProvisioningManager.KEY_WIFI_THRESHOLD_B: 315 case ProvisioningManager.KEY_LTE_EPDG_TIMER_SEC: 316 case ProvisioningManager.KEY_WIFI_EPDG_TIMER_SEC: 317 case ProvisioningManager.KEY_1X_EPDG_TIMER_SEC: 318 return false; 319 } 320 return true; 321 } 322 setStringItem(int item, String value)323 private void setStringItem(int item, String value) { 324 if (ProvisioningManager.STRING_QUERY_RESULT_ERROR_GENERIC.equals(value) 325 || ProvisioningManager.STRING_QUERY_RESULT_ERROR_NOT_READY.equals(value)) { 326 if (mStringItems.remove(item) != null) { 327 markUpdated(true); 328 } 329 return; 330 } 331 if (TextUtils.equals(value, getStringItem(item))) { 332 return; 333 } 334 mStringItems.put(item, value); 335 markUpdated(true); 336 } 337 getStringItem(int item)338 String getStringItem(int item) { 339 return mStringItems.get(item); 340 } 341 clear()342 void clear() { 343 mIntegerItems.clear(); 344 mStringItems.clear(); 345 markUpdated(false); 346 } 347 markUpdated(boolean bUpdated)348 void markUpdated(boolean bUpdated) { 349 mUpdated = bUpdated; 350 } 351 isUpdated()352 boolean isUpdated() { 353 return mUpdated; 354 } 355 equalsIntegerItem(QnsProvisioningInfo info, int key)356 boolean equalsIntegerItem(QnsProvisioningInfo info, int key) { 357 Integer my = getIntegerItem(key); 358 Integer other = info.getIntegerItem(key); 359 if (my == null && other == null) { 360 return true; 361 } else if (my != null && other != null) { 362 int myvalue = my; 363 int othervalue = other; 364 return myvalue == othervalue; 365 } 366 return false; 367 } 368 } 369 370 private class QnsProvisioningCallback extends ProvisioningManager.Callback { 371 /** Constructor */ QnsProvisioningCallback()372 QnsProvisioningCallback() {} 373 374 /** 375 * Called when a provisioning item has changed. 376 * 377 * @param item the IMS provisioning key constant, as defined by the OEM. 378 * @param value the new integer value of the IMS provisioning key. 379 */ 380 @Override onProvisioningIntChanged(int item, int value)381 public void onProvisioningIntChanged(int item, int value) { 382 synchronized (mProvisioningInfo) { 383 mProvisioningInfo.setIntegerItem(item, value); 384 if (mProvisioningInfo.isUpdated()) { 385 mQnsProvisioningHandler.sendNotifyProvisioningInfoChanged(); 386 } 387 } 388 } 389 390 /** 391 * Called when a provisioning item has changed. 392 * 393 * @param item the IMS provisioning key constant, as defined by the OEM. 394 * @param value the new String value of the IMS configuration constant. 395 */ 396 @Override onProvisioningStringChanged(int item, String value)397 public void onProvisioningStringChanged(int item, String value) { 398 synchronized (mProvisioningInfo) { 399 mProvisioningInfo.setStringItem(item, value); 400 if (mProvisioningInfo.isUpdated()) { 401 mQnsProvisioningHandler.sendNotifyProvisioningInfoChanged(); 402 } 403 } 404 } 405 } 406 407 @VisibleForTesting 408 class QnsProvisioningHandler extends Handler { 409 private int mRetryRegisterProvisioningCallbackCount; 410 QnsProvisioningHandler(Looper looper)411 QnsProvisioningHandler(Looper looper) { 412 super(looper); 413 mRetryRegisterProvisioningCallbackCount = REG_CALLBACK_RETRY; 414 } 415 resetRetryRegisterProvisioningCallbackCount()416 void resetRetryRegisterProvisioningCallbackCount() { 417 mRetryRegisterProvisioningCallbackCount = REG_CALLBACK_RETRY; 418 } 419 420 @Override handleMessage(Message message)421 public void handleMessage(Message message) { 422 log("message what:" + message.what); 423 QnsAsyncResult ar = (QnsAsyncResult) message.obj; 424 switch (message.what) { 425 case EVENT_IMS_STATE_CHANGED: 426 if (ar != null) { 427 QnsImsManager.ImsState state = (QnsImsManager.ImsState) ar.mResult; 428 if (state.isImsAvailable()) { 429 log("ImsState is changed to available"); 430 unregisterProvisioningCallback(); 431 resetRetryRegisterProvisioningCallbackCount(); 432 registerProvisioningCallback(); 433 } else { 434 log("ImsState is changed to unavailable"); 435 clearLastProvisioningInfo(); 436 } 437 } 438 break; 439 case EVENT_REGISTER_PROVISIONING_CALLBACK: 440 registerProvisioningCallback(); 441 break; 442 case EVENT_CALLBACK_REGISTERED: 443 loadDefaultItems(); 444 break; 445 case EVENT_NOTIFY_PROVISION_INFO_CHANGED: 446 synchronized (mProvisioningInfo) { 447 if (mProvisioningInfo.isUpdated()) { 448 notifyProvisioningItemInfoChanged( 449 new QnsProvisioningInfo(mProvisioningInfo)); 450 mProvisioningInfo.markUpdated(false); 451 } 452 } 453 break; 454 default: 455 break; 456 } 457 } 458 sendRegisterProvisioningCallback()459 void sendRegisterProvisioningCallback() { 460 mRetryRegisterProvisioningCallbackCount--; 461 if (mRetryRegisterProvisioningCallbackCount > 0) { 462 removeMessages(EVENT_REGISTER_PROVISIONING_CALLBACK); 463 Message msg = obtainMessage(EVENT_REGISTER_PROVISIONING_CALLBACK); 464 sendMessageDelayed(msg, REG_CALLBACK_DELAY); 465 } 466 } 467 sendProvisioningCallbackRegistered()468 void sendProvisioningCallbackRegistered() { 469 removeMessages(EVENT_REGISTER_PROVISIONING_CALLBACK); 470 resetRetryRegisterProvisioningCallbackCount(); 471 Message msg = obtainMessage(EVENT_CALLBACK_REGISTERED); 472 sendMessage(msg); 473 } 474 sendNotifyProvisioningInfoChanged()475 void sendNotifyProvisioningInfoChanged() { 476 removeMessages(EVENT_NOTIFY_PROVISION_INFO_CHANGED); 477 Message msg = obtainMessage(EVENT_NOTIFY_PROVISION_INFO_CHANGED); 478 sendMessageDelayed(msg, 100); 479 } 480 } 481 clearLastProvisioningInfo()482 private void clearLastProvisioningInfo() { 483 synchronized (mProvisioningInfo) { 484 mProvisioningInfo.clear(); 485 } 486 } 487 } 488