1 /* 2 * Copyright (C) 2016 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.internal.telephony; 18 19 import android.app.Notification; 20 import android.app.NotificationManager; 21 import android.app.PendingIntent; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.res.Resources; 27 import android.database.ContentObserver; 28 import android.os.Handler; 29 import android.os.Message; 30 import android.os.PersistableBundle; 31 import android.provider.Settings; 32 import android.telephony.CarrierConfigManager; 33 import android.telephony.ServiceState; 34 import android.telephony.SubscriptionManager; 35 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 36 import android.telephony.TelephonyManager; 37 import android.telephony.TelephonyManager.NetworkTypeBitMask; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.telephony.util.NotificationChannelController; 41 import com.android.telephony.Rlog; 42 43 import java.util.HashMap; 44 import java.util.Map; 45 46 /** 47 * This contains Carrier specific logic based on the states/events 48 * managed in ServiceStateTracker. 49 * {@hide} 50 */ 51 public class CarrierServiceStateTracker extends Handler { 52 private static final String LOG_TAG = "CSST"; 53 protected static final int CARRIER_EVENT_BASE = 100; 54 protected static final int CARRIER_EVENT_VOICE_REGISTRATION = CARRIER_EVENT_BASE + 1; 55 protected static final int CARRIER_EVENT_VOICE_DEREGISTRATION = CARRIER_EVENT_BASE + 2; 56 protected static final int CARRIER_EVENT_DATA_REGISTRATION = CARRIER_EVENT_BASE + 3; 57 protected static final int CARRIER_EVENT_DATA_DEREGISTRATION = CARRIER_EVENT_BASE + 4; 58 protected static final int CARRIER_EVENT_IMS_CAPABILITIES_CHANGED = CARRIER_EVENT_BASE + 5; 59 60 private static final int UNINITIALIZED_DELAY_VALUE = -1; 61 private Phone mPhone; 62 private ServiceStateTracker mSST; 63 private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>(); 64 private int mPreviousSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 65 public static final int NOTIFICATION_PREF_NETWORK = 1000; 66 public static final int NOTIFICATION_EMERGENCY_NETWORK = 1001; 67 68 @VisibleForTesting 69 public static final String EMERGENCY_NOTIFICATION_TAG = "EmergencyNetworkNotification"; 70 71 @VisibleForTesting 72 public static final String PREF_NETWORK_NOTIFICATION_TAG = "PrefNetworkNotification"; 73 CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst)74 public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst) { 75 this.mPhone = phone; 76 this.mSST = sst; 77 phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter( 78 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 79 // Listen for subscriber changes 80 SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener( 81 new OnSubscriptionsChangedListener(this.getLooper()) { 82 @Override 83 public void onSubscriptionsChanged() { 84 int subId = mPhone.getSubId(); 85 if (mPreviousSubId != subId) { 86 mPreviousSubId = subId; 87 registerPrefNetworkModeObserver(); 88 } 89 } 90 }); 91 92 registerNotificationTypes(); 93 registerPrefNetworkModeObserver(); 94 } 95 96 private ContentObserver mPrefNetworkModeObserver = new ContentObserver(this) { 97 @Override 98 public void onChange(boolean selfChange) { 99 handlePrefNetworkModeChanged(); 100 } 101 }; 102 103 /** 104 * Return preferred network mode observer 105 */ 106 @VisibleForTesting getContentObserver()107 public ContentObserver getContentObserver() { 108 return mPrefNetworkModeObserver; 109 } 110 registerPrefNetworkModeObserver()111 private void registerPrefNetworkModeObserver() { 112 int subId = mPhone.getSubId(); 113 unregisterPrefNetworkModeObserver(); 114 if (SubscriptionManager.isValidSubscriptionId(subId)) { 115 mPhone.getContext().getContentResolver().registerContentObserver( 116 Settings.Global.getUriFor(Settings.Global.PREFERRED_NETWORK_MODE + subId), 117 true, 118 mPrefNetworkModeObserver); 119 } 120 } 121 unregisterPrefNetworkModeObserver()122 private void unregisterPrefNetworkModeObserver() { 123 mPhone.getContext().getContentResolver().unregisterContentObserver( 124 mPrefNetworkModeObserver); 125 } 126 127 /** 128 * Returns mNotificationTypeMap 129 */ 130 @VisibleForTesting getNotificationTypeMap()131 public Map<Integer, NotificationType> getNotificationTypeMap() { 132 return mNotificationTypeMap; 133 } 134 registerNotificationTypes()135 private void registerNotificationTypes() { 136 mNotificationTypeMap.put(NOTIFICATION_PREF_NETWORK, 137 new PrefNetworkNotification(NOTIFICATION_PREF_NETWORK)); 138 mNotificationTypeMap.put(NOTIFICATION_EMERGENCY_NETWORK, 139 new EmergencyNetworkNotification(NOTIFICATION_EMERGENCY_NETWORK)); 140 } 141 142 @Override handleMessage(Message msg)143 public void handleMessage(Message msg) { 144 switch (msg.what) { 145 case CARRIER_EVENT_VOICE_REGISTRATION: 146 case CARRIER_EVENT_DATA_REGISTRATION: 147 case CARRIER_EVENT_VOICE_DEREGISTRATION: 148 case CARRIER_EVENT_DATA_DEREGISTRATION: 149 handleConfigChanges(); 150 break; 151 case CARRIER_EVENT_IMS_CAPABILITIES_CHANGED: 152 handleImsCapabilitiesChanged(); 153 break; 154 case NOTIFICATION_EMERGENCY_NETWORK: 155 case NOTIFICATION_PREF_NETWORK: 156 Rlog.d(LOG_TAG, "sending notification after delay: " + msg.what); 157 NotificationType notificationType = mNotificationTypeMap.get(msg.what); 158 if (notificationType != null) { 159 sendNotification(notificationType); 160 } 161 break; 162 } 163 } 164 isPhoneStillRegistered()165 private boolean isPhoneStillRegistered() { 166 if (mSST.mSS == null) { 167 return true; //something has gone wrong, return true and not show the notification. 168 } 169 return (mSST.mSS.getState() == ServiceState.STATE_IN_SERVICE 170 || mSST.mSS.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE); 171 } 172 isPhoneRegisteredForWifiCalling()173 private boolean isPhoneRegisteredForWifiCalling() { 174 Rlog.d(LOG_TAG, "isPhoneRegisteredForWifiCalling: " + mPhone.isWifiCallingEnabled()); 175 return mPhone.isWifiCallingEnabled(); 176 } 177 178 /** 179 * Returns true if the radio is off or in Airplane Mode else returns false. 180 */ 181 @VisibleForTesting isRadioOffOrAirplaneMode()182 public boolean isRadioOffOrAirplaneMode() { 183 Context context = mPhone.getContext(); 184 int airplaneMode = -1; 185 try { 186 airplaneMode = Settings.Global.getInt(context.getContentResolver(), 187 Settings.Global.AIRPLANE_MODE_ON, 0); 188 } catch (Exception e) { 189 Rlog.e(LOG_TAG, "Unable to get AIRPLACE_MODE_ON."); 190 return true; 191 } 192 return (!mSST.isRadioOn() || (airplaneMode != 0)); 193 } 194 195 /** 196 * Returns true if the preferred network is set to 'Global'. 197 */ isGlobalMode()198 private boolean isGlobalMode() { 199 Context context = mPhone.getContext(); 200 int preferredNetworkSetting = -1; 201 try { 202 preferredNetworkSetting = 203 android.provider.Settings.Global.getInt(context.getContentResolver(), 204 android.provider.Settings.Global.PREFERRED_NETWORK_MODE 205 + mPhone.getSubId(), Phone.PREFERRED_NT_MODE); 206 } catch (Exception e) { 207 Rlog.e(LOG_TAG, "Unable to get PREFERRED_NETWORK_MODE."); 208 return true; 209 } 210 211 if (isNrSupported()) { 212 return (preferredNetworkSetting 213 == RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA); 214 } else { 215 return (preferredNetworkSetting == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA); 216 } 217 } 218 isNrSupported()219 private boolean isNrSupported() { 220 Context context = mPhone.getContext(); 221 TelephonyManager tm = ((TelephonyManager) context.getSystemService( 222 Context.TELEPHONY_SERVICE)).createForSubscriptionId(mPhone.getSubId()); 223 224 boolean isCarrierConfigEnabled = isCarrierConfigEnableNr(context); 225 boolean isRadioAccessFamilySupported = checkSupportedBitmask( 226 tm.getSupportedRadioAccessFamily(), TelephonyManager.NETWORK_TYPE_BITMASK_NR); 227 boolean isNrNetworkTypeAllowed = checkSupportedBitmask( 228 tm.getAllowedNetworkTypes(), TelephonyManager.NETWORK_TYPE_BITMASK_NR); 229 230 Rlog.i(LOG_TAG, "isNrSupported: " + " carrierConfigEnabled: " + isCarrierConfigEnabled 231 + ", AccessFamilySupported: " + isRadioAccessFamilySupported 232 + ", isNrNetworkTypeAllowed: " + isNrNetworkTypeAllowed); 233 234 return (isCarrierConfigEnabled && isRadioAccessFamilySupported && isNrNetworkTypeAllowed); 235 } 236 isCarrierConfigEnableNr(Context context)237 private boolean isCarrierConfigEnableNr(Context context) { 238 CarrierConfigManager carrierConfigManager = (CarrierConfigManager) 239 context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 240 if (carrierConfigManager == null) { 241 Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: CarrierConfigManager is null"); 242 return false; 243 } 244 PersistableBundle config = carrierConfigManager.getConfigForSubId(mPhone.getSubId()); 245 if (config == null) { 246 Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: Cannot get config " + mPhone.getSubId()); 247 return false; 248 } 249 return config.getBoolean(CarrierConfigManager.KEY_NR_ENABLED_BOOL); 250 } 251 checkSupportedBitmask(@etworkTypeBitMask long supportedBitmask, @NetworkTypeBitMask long targetBitmask)252 private boolean checkSupportedBitmask(@NetworkTypeBitMask long supportedBitmask, 253 @NetworkTypeBitMask long targetBitmask) { 254 return (targetBitmask & supportedBitmask) == targetBitmask; 255 } 256 handleConfigChanges()257 private void handleConfigChanges() { 258 for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) { 259 NotificationType notificationType = entry.getValue(); 260 evaluateSendingMessageOrCancelNotification(notificationType); 261 } 262 } 263 handlePrefNetworkModeChanged()264 private void handlePrefNetworkModeChanged() { 265 NotificationType notificationType = mNotificationTypeMap.get(NOTIFICATION_PREF_NETWORK); 266 if (notificationType != null) { 267 evaluateSendingMessageOrCancelNotification(notificationType); 268 } 269 } 270 handleImsCapabilitiesChanged()271 private void handleImsCapabilitiesChanged() { 272 NotificationType notificationType = mNotificationTypeMap 273 .get(NOTIFICATION_EMERGENCY_NETWORK); 274 if (notificationType != null) { 275 evaluateSendingMessageOrCancelNotification(notificationType); 276 } 277 } 278 evaluateSendingMessageOrCancelNotification(NotificationType notificationType)279 private void evaluateSendingMessageOrCancelNotification(NotificationType notificationType) { 280 if (evaluateSendingMessage(notificationType)) { 281 Message notificationMsg = obtainMessage(notificationType.getTypeId(), null); 282 Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId()); 283 sendMessageDelayed(notificationMsg, getDelay(notificationType)); 284 } else { 285 cancelNotification(notificationType); 286 Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId()); 287 } 288 } 289 290 /** 291 * This method adds a level of indirection, and was created so we can unit the class. 292 **/ 293 @VisibleForTesting evaluateSendingMessage(NotificationType notificationType)294 public boolean evaluateSendingMessage(NotificationType notificationType) { 295 return notificationType.sendMessage(); 296 } 297 298 /** 299 * This method adds a level of indirection, and was created so we can unit the class. 300 **/ 301 @VisibleForTesting getDelay(NotificationType notificationType)302 public int getDelay(NotificationType notificationType) { 303 return notificationType.getDelay(); 304 } 305 306 /** 307 * This method adds a level of indirection, and was created so we can unit the class. 308 **/ 309 @VisibleForTesting getNotificationBuilder(NotificationType notificationType)310 public Notification.Builder getNotificationBuilder(NotificationType notificationType) { 311 return notificationType.getNotificationBuilder(); 312 } 313 314 /** 315 * This method adds a level of indirection, and was created so we can unit the class. 316 **/ 317 @VisibleForTesting getNotificationManager(Context context)318 public NotificationManager getNotificationManager(Context context) { 319 return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 320 } 321 322 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 323 @Override 324 public void onReceive(Context context, Intent intent) { 325 CarrierConfigManager carrierConfigManager = (CarrierConfigManager) 326 context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 327 PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId()); 328 329 for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) { 330 NotificationType notificationType = entry.getValue(); 331 notificationType.setDelay(b); 332 } 333 handleConfigChanges(); 334 } 335 }; 336 337 /** 338 * Post a notification to the NotificationManager for changing network type. 339 */ 340 @VisibleForTesting sendNotification(NotificationType notificationType)341 public void sendNotification(NotificationType notificationType) { 342 if (!evaluateSendingMessage(notificationType)) { 343 return; 344 } 345 346 Context context = mPhone.getContext(); 347 Notification.Builder builder = getNotificationBuilder(notificationType); 348 // set some common attributes 349 builder.setWhen(System.currentTimeMillis()) 350 .setAutoCancel(true) 351 .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning) 352 .setColor(context.getResources().getColor( 353 com.android.internal.R.color.system_notification_accent_color)); 354 getNotificationManager(context).notify(notificationType.getNotificationTag(), 355 notificationType.getNotificationId(), builder.build()); 356 } 357 358 /** 359 * Cancel notifications if a registration is pending or has been sent. 360 **/ cancelNotification(NotificationType notificationType)361 public void cancelNotification(NotificationType notificationType) { 362 Context context = mPhone.getContext(); 363 removeMessages(notificationType.getTypeId()); 364 getNotificationManager(context).cancel( 365 notificationType.getNotificationTag(), notificationType.getNotificationId()); 366 } 367 368 /** 369 * Dispose the CarrierServiceStateTracker. 370 */ dispose()371 public void dispose() { 372 unregisterPrefNetworkModeObserver(); 373 } 374 375 /** 376 * Class that defines the different types of notifications. 377 */ 378 public interface NotificationType { 379 380 /** 381 * decides if the message should be sent, Returns boolean 382 **/ sendMessage()383 boolean sendMessage(); 384 385 /** 386 * returns the interval by which the message is delayed. 387 **/ getDelay()388 int getDelay(); 389 390 /** sets the interval by which the message is delayed. 391 * @param bundle PersistableBundle 392 **/ setDelay(PersistableBundle bundle)393 void setDelay(PersistableBundle bundle); 394 395 /** 396 * returns notification type id. 397 **/ getTypeId()398 int getTypeId(); 399 400 /** 401 * returns notification id. 402 **/ getNotificationId()403 int getNotificationId(); 404 405 /** 406 * returns notification tag. 407 **/ getNotificationTag()408 String getNotificationTag(); 409 410 /** 411 * returns the notification builder, for the notification to be displayed. 412 **/ getNotificationBuilder()413 Notification.Builder getNotificationBuilder(); 414 } 415 416 /** 417 * Class that defines the network notification, which is shown when the phone cannot camp on 418 * a network, and has 'preferred mode' set to global. 419 */ 420 public class PrefNetworkNotification implements NotificationType { 421 422 private final int mTypeId; 423 private int mDelay = UNINITIALIZED_DELAY_VALUE; 424 PrefNetworkNotification(int typeId)425 PrefNetworkNotification(int typeId) { 426 this.mTypeId = typeId; 427 } 428 429 /** sets the interval by which the message is delayed. 430 * @param bundle PersistableBundle 431 **/ setDelay(PersistableBundle bundle)432 public void setDelay(PersistableBundle bundle) { 433 if (bundle == null) { 434 Rlog.e(LOG_TAG, "bundle is null"); 435 return; 436 } 437 this.mDelay = bundle.getInt( 438 CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT); 439 Rlog.i(LOG_TAG, "reading time to delay notification pref network: " + mDelay); 440 } 441 getDelay()442 public int getDelay() { 443 return mDelay; 444 } 445 getTypeId()446 public int getTypeId() { 447 return mTypeId; 448 } 449 getNotificationId()450 public int getNotificationId() { 451 return mPhone.getSubId(); 452 } 453 getNotificationTag()454 public String getNotificationTag() { 455 return PREF_NETWORK_NOTIFICATION_TAG; 456 } 457 458 /** 459 * Contains logic on sending notifications. 460 */ sendMessage()461 public boolean sendMessage() { 462 Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: " 463 + "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode() 464 + "," + mSST.isRadioOn()); 465 if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode() 466 || isRadioOffOrAirplaneMode()) { 467 return false; 468 } 469 return true; 470 } 471 472 /** 473 * Builds a partial notificaiton builder, and returns it. 474 */ getNotificationBuilder()475 public Notification.Builder getNotificationBuilder() { 476 Context context = mPhone.getContext(); 477 Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS); 478 notificationIntent.putExtra("expandable", true); 479 PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent, 480 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE); 481 Resources res = SubscriptionManager.getResourcesForSubId(context, mPhone.getSubId()); 482 CharSequence title = res.getText( 483 com.android.internal.R.string.NetworkPreferenceSwitchTitle); 484 CharSequence details = res.getText( 485 com.android.internal.R.string.NetworkPreferenceSwitchSummary); 486 return new Notification.Builder(context) 487 .setContentTitle(title) 488 .setStyle(new Notification.BigTextStyle().bigText(details)) 489 .setContentText(details) 490 .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT) 491 .setContentIntent(settingsIntent); 492 } 493 } 494 495 /** 496 * Class that defines the emergency notification, which is shown when Wi-Fi Calling is 497 * available. 498 */ 499 public class EmergencyNetworkNotification implements NotificationType { 500 501 private final int mTypeId; 502 private int mDelay = UNINITIALIZED_DELAY_VALUE; 503 EmergencyNetworkNotification(int typeId)504 EmergencyNetworkNotification(int typeId) { 505 this.mTypeId = typeId; 506 } 507 508 /** sets the interval by which the message is delayed. 509 * @param bundle PersistableBundle 510 **/ setDelay(PersistableBundle bundle)511 public void setDelay(PersistableBundle bundle) { 512 if (bundle == null) { 513 Rlog.e(LOG_TAG, "bundle is null"); 514 return; 515 } 516 this.mDelay = bundle.getInt( 517 CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT); 518 Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay); 519 } 520 getDelay()521 public int getDelay() { 522 return mDelay; 523 } 524 getTypeId()525 public int getTypeId() { 526 return mTypeId; 527 } 528 getNotificationId()529 public int getNotificationId() { 530 return mPhone.getSubId(); 531 } 532 getNotificationTag()533 public String getNotificationTag() { 534 return EMERGENCY_NOTIFICATION_TAG; 535 } 536 537 /** 538 * Contains logic on sending notifications, 539 */ sendMessage()540 public boolean sendMessage() { 541 Rlog.i(LOG_TAG, "EmergencyNetworkNotification: sendMessage() w/values: " 542 + "," + mDelay + "," + isPhoneRegisteredForWifiCalling() + "," 543 + mSST.isRadioOn()); 544 if (mDelay == UNINITIALIZED_DELAY_VALUE || !isPhoneRegisteredForWifiCalling()) { 545 return false; 546 } 547 return true; 548 } 549 550 /** 551 * Builds a partial notificaiton builder, and returns it. 552 */ getNotificationBuilder()553 public Notification.Builder getNotificationBuilder() { 554 Context context = mPhone.getContext(); 555 Resources res = SubscriptionManager.getResourcesForSubId(context, mPhone.getSubId()); 556 CharSequence title = res.getText( 557 com.android.internal.R.string.EmergencyCallWarningTitle); 558 CharSequence details = res.getText( 559 com.android.internal.R.string.EmergencyCallWarningSummary); 560 return new Notification.Builder(context) 561 .setContentTitle(title) 562 .setStyle(new Notification.BigTextStyle().bigText(details)) 563 .setContentText(details) 564 .setFlag(Notification.FLAG_NO_CLEAR, true) 565 .setChannelId(NotificationChannelController.CHANNEL_ID_WFC); 566 } 567 } 568 } 569