1 /* 2 * Copyright 2020 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.google.android.iwlan; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.database.ContentObserver; 23 import android.net.Uri; 24 import android.net.wifi.WifiInfo; 25 import android.net.wifi.WifiManager; 26 import android.os.Handler; 27 import android.os.HandlerThread; 28 import android.os.Looper; 29 import android.support.annotation.IntDef; 30 import android.support.annotation.NonNull; 31 import android.telephony.CellInfo; 32 import android.telephony.SubscriptionManager; 33 import android.telephony.TelephonyCallback; 34 import android.telephony.TelephonyManager; 35 import android.telephony.ims.ImsManager; 36 import android.telephony.ims.ImsMmTelManager; 37 import android.util.Log; 38 import android.util.SparseArray; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 42 import com.google.android.iwlan.flags.FeatureFlags; 43 import com.google.android.iwlan.flags.FeatureFlagsImpl; 44 45 import java.util.HashSet; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.Objects; 49 import java.util.Set; 50 import java.util.concurrent.ConcurrentHashMap; 51 52 public class IwlanEventListener { 53 54 public static final int UNKNOWN_EVENT = -1; 55 56 /** On {@link IwlanCarrierConfigChangeListener#onCarrierConfigChanged} is called. */ 57 public static final int CARRIER_CONFIG_CHANGED_EVENT = 1; 58 59 /** Wifi turned off or disabled. */ 60 public static final int WIFI_DISABLE_EVENT = 2; 61 62 /** Airplane mode turned off or disabled. */ 63 public static final int APM_DISABLE_EVENT = 3; 64 65 /** Airplane mode turned on or enabled */ 66 public static final int APM_ENABLE_EVENT = 4; 67 68 /** Wifi AccessPoint changed. */ 69 public static final int WIFI_AP_CHANGED_EVENT = 5; 70 71 /** Wifi calling turned on or enabled */ 72 public static final int WIFI_CALLING_ENABLE_EVENT = 6; 73 74 /** Wifi calling turned off or disabled */ 75 public static final int WIFI_CALLING_DISABLE_EVENT = 7; 76 77 /** Cross sim calling enabled */ 78 public static final int CROSS_SIM_CALLING_ENABLE_EVENT = 8; 79 80 /** Cross sim calling disabled */ 81 public static final int CROSS_SIM_CALLING_DISABLE_EVENT = 9; 82 83 /** 84 * On {@link IwlanCarrierConfigChangeListener#onCarrierConfigChanged} is called with 85 * UNKNOWN_CARRIER_ID. 86 */ 87 public static final int CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT = 10; 88 89 /** On Cellinfo changed */ 90 public static final int CELLINFO_CHANGED_EVENT = 11; 91 92 /** On Call state changed */ 93 public static final int CALL_STATE_CHANGED_EVENT = 12; 94 95 /** On Preferred Network Type changed */ 96 public static final int PREFERRED_NETWORK_TYPE_CHANGED_EVENT = 13; 97 98 public static final int SCREEN_ON_EVENT = 14; 99 100 /* Events used and handled by IwlanDataService internally */ 101 public static final int DATA_SERVICE_INTERNAL_EVENT_BASE = 100; 102 103 /* Events used and handled by IwlanNetworkService internally */ 104 public static final int NETWORK_SERVICE_INTERNAL_EVENT_BASE = 200; 105 106 @IntDef({ 107 CARRIER_CONFIG_CHANGED_EVENT, 108 WIFI_DISABLE_EVENT, 109 APM_DISABLE_EVENT, 110 APM_ENABLE_EVENT, 111 WIFI_AP_CHANGED_EVENT, 112 WIFI_CALLING_ENABLE_EVENT, 113 WIFI_CALLING_DISABLE_EVENT, 114 CROSS_SIM_CALLING_ENABLE_EVENT, 115 CROSS_SIM_CALLING_DISABLE_EVENT, 116 CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT, 117 CELLINFO_CHANGED_EVENT, 118 CALL_STATE_CHANGED_EVENT, 119 PREFERRED_NETWORK_TYPE_CHANGED_EVENT, 120 SCREEN_ON_EVENT, 121 }) 122 @interface IwlanEventType {} 123 124 private static final String LOG_TAG = IwlanEventListener.class.getSimpleName(); 125 126 private final String SUB_TAG; 127 128 private static Boolean sIsAirplaneModeOn; 129 130 private static String sWifiSSID = ""; 131 132 private static final Map<Integer, IwlanEventListener> mInstances = new ConcurrentHashMap<>(); 133 134 private final Context mContext; 135 private final int mSlotId; 136 private int mSubId; 137 private Uri mCrossSimCallingUri; 138 private Uri mWfcEnabledUri; 139 private UserSettingContentObserver mUserSettingContentObserver; 140 private RadioInfoTelephonyCallback mTelephonyCallback; 141 142 SparseArray<Set<Handler>> eventHandlers = new SparseArray<>(); 143 144 private class UserSettingContentObserver extends ContentObserver { UserSettingContentObserver(Handler h)145 UserSettingContentObserver(Handler h) { 146 super(h); 147 } 148 149 @Override onChange(boolean selfChange, Uri uri)150 public void onChange(boolean selfChange, Uri uri) { 151 Objects.requireNonNull(mCrossSimCallingUri, "CrossSimCallingUri must not be null"); 152 Objects.requireNonNull(mWfcEnabledUri, "WfcEnabledUri must not be null"); 153 if (mCrossSimCallingUri.equals(uri)) { 154 notifyCurrentSetting(uri); 155 } else if (mWfcEnabledUri.equals(uri)) { 156 notifyCurrentSetting(uri); 157 } 158 } 159 } 160 161 private class RadioInfoTelephonyCallback extends TelephonyCallback 162 implements TelephonyCallback.CellInfoListener, 163 TelephonyCallback.CallStateListener, 164 TelephonyCallback.AllowedNetworkTypesListener { 165 @Override onCellInfoChanged(List<CellInfo> arrayCi)166 public void onCellInfoChanged(List<CellInfo> arrayCi) { 167 Log.d(LOG_TAG, "Cellinfo changed"); 168 169 for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) { 170 IwlanEventListener instance = entry.getValue(); 171 if (instance != null) { 172 instance.updateHandlers(arrayCi); 173 } 174 } 175 } 176 177 @Override onCallStateChanged(int state)178 public void onCallStateChanged(int state) { 179 Log.d( 180 LOG_TAG, 181 "Call state changed to " + callStateToString(state) + " for slot " + mSlotId); 182 183 IwlanEventListener instance = mInstances.get(mSlotId); 184 if (instance != null) { 185 instance.updateHandlers(CALL_STATE_CHANGED_EVENT, state); 186 } 187 } 188 189 @Override onAllowedNetworkTypesChanged( @elephonyManager.AllowedNetworkTypesReason int reason, @TelephonyManager.NetworkTypeBitMask long allowedNetworkType)190 public void onAllowedNetworkTypesChanged( 191 @TelephonyManager.AllowedNetworkTypesReason int reason, 192 @TelephonyManager.NetworkTypeBitMask long allowedNetworkType) { 193 if (reason != TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) { 194 return; 195 } 196 197 IwlanEventListener instance = mInstances.get(mSlotId); 198 if (instance != null) { 199 instance.updateHandlers(PREFERRED_NETWORK_TYPE_CHANGED_EVENT, allowedNetworkType); 200 } 201 } 202 } 203 204 /** Returns IwlanEventListener instance */ getInstance(@onNull Context context, int slotId)205 public static IwlanEventListener getInstance(@NonNull Context context, int slotId) { 206 return mInstances.computeIfAbsent( 207 slotId, k -> new IwlanEventListener(context, slotId, new FeatureFlagsImpl())); 208 } 209 210 @VisibleForTesting resetAllInstances()211 public static void resetAllInstances() { 212 mInstances.clear(); 213 } 214 215 /** 216 * Adds handler for the list of events. 217 * 218 * @param events lists of events for which the handler needs to be notified. 219 * @param handler handler to be called when the events happen 220 */ addEventListener(List<Integer> events, Handler handler)221 public synchronized void addEventListener(List<Integer> events, Handler handler) { 222 for (@IwlanEventType int event : events) { 223 if (eventHandlers.contains(event)) { 224 eventHandlers.get(event).add(handler); 225 } else { 226 Set<Handler> handlers = new HashSet<>(); 227 handlers.add(handler); 228 eventHandlers.append(event, handlers); 229 } 230 } 231 } 232 233 /** 234 * Removes handler for the list of events. 235 * 236 * @param events lists of events for which the handler needs to be removed. 237 * @param handler handler to be removed 238 */ removeEventListener(List<Integer> events, Handler handler)239 public synchronized void removeEventListener(List<Integer> events, Handler handler) { 240 for (int event : events) { 241 if (eventHandlers.contains(event)) { 242 Set<Handler> handlers = eventHandlers.get(event); 243 handlers.remove(handler); 244 if (handlers.isEmpty()) { 245 eventHandlers.delete(event); 246 } 247 } 248 } 249 if (eventHandlers.size() == 0) { 250 mInstances.remove(mSlotId, this); 251 } 252 } 253 254 /** 255 * Removes handler for all events it is registered 256 * 257 * @param handler handler to be removed 258 */ removeEventListener(Handler handler)259 public synchronized void removeEventListener(Handler handler) { 260 for (int i = 0; i < eventHandlers.size(); i++) { 261 Set<Handler> handlers = eventHandlers.valueAt(i); 262 handlers.remove(handler); 263 if (handlers.isEmpty()) { 264 eventHandlers.delete(eventHandlers.keyAt(i)); 265 i--; 266 } 267 } 268 if (eventHandlers.size() == 0) { 269 mInstances.remove(mSlotId, this); 270 } 271 } 272 273 /** 274 * Report a Broadcast received. Mainly used by IwlanBroadcastReceiver to report the following 275 * broadcasts: ACTION_AIRPLANE_MODE_CHANGED, WIFI_STATE_CHANGED_ACTION 276 * 277 * @param intent intent 278 */ onBroadcastReceived(Intent intent)279 public static synchronized void onBroadcastReceived(Intent intent) { 280 int event = UNKNOWN_EVENT; 281 switch (intent.getAction()) { 282 case Intent.ACTION_AIRPLANE_MODE_CHANGED: 283 Boolean isAirplaneModeOn = intent.getBooleanExtra("state", false); 284 if (sIsAirplaneModeOn != null && sIsAirplaneModeOn.equals(isAirplaneModeOn)) { 285 // no change in apm state 286 break; 287 } 288 sIsAirplaneModeOn = isAirplaneModeOn; 289 event = sIsAirplaneModeOn ? APM_ENABLE_EVENT : APM_DISABLE_EVENT; 290 for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) { 291 IwlanEventListener instance = entry.getValue(); 292 instance.updateHandlers(event); 293 } 294 break; 295 case WifiManager.WIFI_STATE_CHANGED_ACTION: 296 int wifiState = 297 intent.getIntExtra( 298 WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN); 299 if (wifiState == WifiManager.WIFI_STATE_DISABLED) { 300 event = WIFI_DISABLE_EVENT; 301 for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) { 302 IwlanEventListener instance = entry.getValue(); 303 instance.updateHandlers(event); 304 } 305 } 306 break; 307 case Intent.ACTION_SCREEN_ON: 308 mInstances.values().forEach(instance -> instance.updateHandlers(SCREEN_ON_EVENT)); 309 break; 310 } 311 } 312 313 /** 314 * Broadcast WIFI_AP_CHANGED_EVENT if Wifi SSID changed after Wifi connected. 315 * 316 * @param wifiInfo connected Wifi network's information. 317 */ onWifiConnected(WifiInfo wifiInfo)318 public static void onWifiConnected(WifiInfo wifiInfo) { 319 if (wifiInfo == null) { 320 Log.e(LOG_TAG, "wifiInfo is null"); 321 return; 322 } 323 String wifiSSID = wifiInfo.getSSID(); 324 if (wifiSSID.equals(WifiManager.UNKNOWN_SSID)) { 325 Log.e(LOG_TAG, "Could not get Wifi SSID"); 326 return; 327 } 328 329 // Check sWifiSSID is greater than 0 to avoid trigger event after device first camps on 330 // Wifi. 331 if (!sWifiSSID.isEmpty() && !sWifiSSID.equals(wifiSSID)) { 332 Log.d(LOG_TAG, "Wifi SSID changed"); 333 for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) { 334 IwlanEventListener instance = entry.getValue(); 335 if (instance != null) { 336 instance.updateHandlers(WIFI_AP_CHANGED_EVENT); 337 } 338 } 339 } 340 sWifiSSID = wifiSSID; 341 } 342 343 /** 344 * Report Carrier Config changed. Mainly used by IwlanCarrierConfigChangeListener. 345 * 346 * @param context context 347 * @param slotId slot id which carrier config is changed 348 * @param subId sub id which carrier config is changed 349 * @param carrierId carrier id 350 */ onCarrierConfigChanged( Context context, int slotId, int subId, int carrierId)351 public static synchronized void onCarrierConfigChanged( 352 Context context, int slotId, int subId, int carrierId) { 353 getInstance(context, slotId).onCarrierConfigChanged(subId, carrierId); 354 } 355 356 /** 357 * Returns the Event id of the String. String that matches the name of the event 358 * 359 * @param event String form of the event. 360 */ getUnthrottlingEvent(String event)361 public static int getUnthrottlingEvent(String event) { 362 return switch (event) { 363 case "CARRIER_CONFIG_CHANGED_EVENT" -> CARRIER_CONFIG_CHANGED_EVENT; 364 case "WIFI_DISABLE_EVENT" -> WIFI_DISABLE_EVENT; 365 case "APM_DISABLE_EVENT" -> APM_DISABLE_EVENT; 366 case "APM_ENABLE_EVENT" -> APM_ENABLE_EVENT; 367 case "WIFI_AP_CHANGED_EVENT" -> WIFI_AP_CHANGED_EVENT; 368 case "WIFI_CALLING_ENABLE_EVENT" -> WIFI_CALLING_ENABLE_EVENT; 369 case "WIFI_CALLING_DISABLE_EVENT" -> WIFI_CALLING_DISABLE_EVENT; 370 case "CROSS_SIM_CALLING_ENABLE_EVENT" -> CROSS_SIM_CALLING_ENABLE_EVENT; 371 case "CROSS_SIM_CALLING_DISABLE_EVENT" -> CROSS_SIM_CALLING_DISABLE_EVENT; 372 case "CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT" -> CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT; 373 case "CELLINFO_CHANGED_EVENT" -> CELLINFO_CHANGED_EVENT; 374 case "PREFERRED_NETWORK_TYPE_CHANGED_EVENT" -> PREFERRED_NETWORK_TYPE_CHANGED_EVENT; 375 default -> UNKNOWN_EVENT; 376 }; 377 } 378 IwlanEventListener(Context context, int slotId, FeatureFlags featureFlags)379 IwlanEventListener(Context context, int slotId, FeatureFlags featureFlags) { 380 mContext = context; 381 mSlotId = slotId; 382 mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 383 SUB_TAG = IwlanEventListener.class.getSimpleName() + "[" + slotId + "]"; 384 } 385 onCarrierConfigChanged(int subId, int carrierId)386 private void onCarrierConfigChanged(int subId, int carrierId) { 387 Log.d(SUB_TAG, "onCarrierConfigChanged"); 388 if (subId != mSubId) { 389 unregisterContentObserver(); 390 mSubId = subId; 391 registerContentObserver(); 392 } 393 notifyCurrentSetting(mCrossSimCallingUri); 394 notifyCurrentSetting(mWfcEnabledUri); 395 396 int event; 397 if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 398 event = CARRIER_CONFIG_CHANGED_EVENT; 399 registerTelephonyCallback(); 400 } else { 401 event = CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT; 402 } 403 updateHandlers(event); 404 } 405 406 /** Unregister ContentObserver. */ unregisterContentObserver()407 void unregisterContentObserver() { 408 if (mUserSettingContentObserver != null) { 409 mContext.getContentResolver().unregisterContentObserver(mUserSettingContentObserver); 410 } 411 mCrossSimCallingUri = null; 412 mWfcEnabledUri = null; 413 } 414 415 /** Initiate ContentObserver if it is not created. And, register it with the current sub id. */ registerContentObserver()416 private void registerContentObserver() { 417 if (mUserSettingContentObserver == null) { 418 HandlerThread userSettingHandlerThread = 419 new HandlerThread(IwlanNetworkService.class.getSimpleName()); 420 userSettingHandlerThread.start(); 421 Looper looper = userSettingHandlerThread.getLooper(); 422 Handler handler = new Handler(looper); 423 mUserSettingContentObserver = new UserSettingContentObserver(handler); 424 } 425 426 if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 427 return; 428 } 429 430 ContentResolver resolver = mContext.getContentResolver(); 431 // Register for CrossSimCalling setting uri 432 mCrossSimCallingUri = 433 Uri.withAppendedPath( 434 SubscriptionManager.CROSS_SIM_ENABLED_CONTENT_URI, String.valueOf(mSubId)); 435 resolver.registerContentObserver(mCrossSimCallingUri, true, mUserSettingContentObserver); 436 437 // Register for WifiCalling setting uri 438 mWfcEnabledUri = 439 Uri.withAppendedPath( 440 SubscriptionManager.WFC_ENABLED_CONTENT_URI, String.valueOf(mSubId)); 441 resolver.registerContentObserver(mWfcEnabledUri, true, mUserSettingContentObserver); 442 } 443 444 @VisibleForTesting notifyCurrentSetting(Uri uri)445 void notifyCurrentSetting(Uri uri) { 446 if (uri == null) { 447 return; 448 } 449 String uriString = uri.getPath(); 450 int subIndex = Integer.parseInt(uriString.substring(uriString.lastIndexOf('/') + 1)); 451 int slotIndex = SubscriptionManager.getSlotIndex(subIndex); 452 453 if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 454 Log.e(SUB_TAG, "Invalid slot index: " + slotIndex); 455 return; 456 } 457 458 if (uri.equals(mCrossSimCallingUri)) { 459 boolean isCstEnabled = IwlanHelper.isCrossSimCallingEnabled(mContext, slotIndex); 460 int event = 461 (isCstEnabled) 462 ? CROSS_SIM_CALLING_ENABLE_EVENT 463 : CROSS_SIM_CALLING_DISABLE_EVENT; 464 getInstance(mContext, slotIndex).updateHandlers(event); 465 } else if (uri.equals(mWfcEnabledUri)) { 466 ImsManager imsManager = mContext.getSystemService(ImsManager.class); 467 if (imsManager == null) { 468 Log.e(SUB_TAG, "Could not find ImsManager"); 469 return; 470 } 471 ImsMmTelManager imsMmTelManager = imsManager.getImsMmTelManager(subIndex); 472 if (imsMmTelManager == null) { 473 Log.e(SUB_TAG, "Could not find ImsMmTelManager"); 474 return; 475 } 476 boolean wfcEnabled = false; 477 try { 478 wfcEnabled = imsMmTelManager.isVoWiFiSettingEnabled(); 479 } catch (IllegalArgumentException e) { 480 Log.w(SUB_TAG, e.getMessage()); 481 } 482 int event = (wfcEnabled) ? WIFI_CALLING_ENABLE_EVENT : WIFI_CALLING_DISABLE_EVENT; 483 getInstance(mContext, slotIndex).updateHandlers(event); 484 } else { 485 Log.e(SUB_TAG, "Unknown Uri : " + uri); 486 } 487 } 488 489 @VisibleForTesting registerTelephonyCallback()490 void registerTelephonyCallback() { 491 Log.d(SUB_TAG, "registerTelephonyCallback"); 492 TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); 493 telephonyManager = 494 Objects.requireNonNull(telephonyManager) 495 .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 496 mTelephonyCallback = new RadioInfoTelephonyCallback(); 497 telephonyManager.registerTelephonyCallback(Runnable::run, mTelephonyCallback); 498 } 499 500 @VisibleForTesting setCrossSimCallingUri(Uri uri)501 void setCrossSimCallingUri(Uri uri) { 502 mCrossSimCallingUri = uri; 503 } 504 505 @VisibleForTesting setWfcEnabledUri(Uri uri)506 void setWfcEnabledUri(Uri uri) { 507 mWfcEnabledUri = uri; 508 } 509 510 @VisibleForTesting getTelephonyCallback()511 RadioInfoTelephonyCallback getTelephonyCallback() { 512 return mTelephonyCallback; 513 } 514 updateHandlers(int event)515 private synchronized void updateHandlers(int event) { 516 if (eventHandlers.contains(event)) { 517 Log.d(SUB_TAG, "Updating handlers for the event: " + event); 518 for (Handler handler : eventHandlers.get(event)) { 519 handler.obtainMessage(event, mSlotId, 0 /* unused */).sendToTarget(); 520 } 521 } 522 } 523 updateHandlers(List<CellInfo> arrayCi)524 private synchronized void updateHandlers(List<CellInfo> arrayCi) { 525 int event = IwlanEventListener.CELLINFO_CHANGED_EVENT; 526 if (eventHandlers.contains(event)) { 527 Log.d(SUB_TAG, "Updating handlers for the event: " + event); 528 for (Handler handler : eventHandlers.get(event)) { 529 handler.obtainMessage(event, mSlotId, 0 /* unused */, arrayCi).sendToTarget(); 530 } 531 } 532 } 533 updateHandlers(int event, int state)534 private synchronized void updateHandlers(int event, int state) { 535 if (eventHandlers.contains(event)) { 536 Log.d(SUB_TAG, "Updating handlers for the event: " + event); 537 for (Handler handler : eventHandlers.get(event)) { 538 handler.obtainMessage(event, mSlotId, state).sendToTarget(); 539 } 540 } 541 } 542 updateHandlers(int event, long allowedNetworkType)543 private synchronized void updateHandlers(int event, long allowedNetworkType) { 544 if (eventHandlers.contains(event)) { 545 Log.d(SUB_TAG, "Updating handlers for the event: " + event); 546 for (Handler handler : eventHandlers.get(event)) { 547 handler.obtainMessage(event, mSlotId, 0 /* unused */, allowedNetworkType) 548 .sendToTarget(); 549 } 550 } 551 } 552 callStateToString(int state)553 private String callStateToString(int state) { 554 return switch (state) { 555 case TelephonyManager.CALL_STATE_IDLE -> "CALL_STATE_IDLE"; 556 case TelephonyManager.CALL_STATE_RINGING -> "CALL_STATE_RINGING"; 557 case TelephonyManager.CALL_STATE_OFFHOOK -> "CALL_STATE_OFFHOOK"; 558 default -> "Unknown Call State (" + state + ")"; 559 }; 560 } 561 } 562