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.annotation.NonNull; 20 import android.app.Notification; 21 import android.app.NotificationManager; 22 import android.app.PendingIntent; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.SharedPreferences; 28 import android.content.pm.PackageManager; 29 import android.content.res.Resources; 30 import android.os.Handler; 31 import android.os.HandlerExecutor; 32 import android.os.Message; 33 import android.os.PersistableBundle; 34 import android.preference.PreferenceManager; 35 import android.provider.Settings; 36 import android.telephony.CarrierConfigManager; 37 import android.telephony.RadioAccessFamily; 38 import android.telephony.ServiceState; 39 import android.telephony.SubscriptionManager; 40 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 41 import android.telephony.TelephonyCallback; 42 import android.telephony.TelephonyManager; 43 import android.telephony.TelephonyManager.NetworkTypeBitMask; 44 45 import com.android.internal.annotations.VisibleForTesting; 46 import com.android.internal.telephony.flags.FeatureFlags; 47 import com.android.internal.telephony.util.ArrayUtils; 48 import com.android.internal.telephony.util.NotificationChannelController; 49 import com.android.telephony.Rlog; 50 51 import java.util.HashMap; 52 import java.util.Map; 53 54 /** 55 * This contains Carrier specific logic based on the states/events 56 * managed in ServiceStateTracker. 57 * {@hide} 58 */ 59 public class CarrierServiceStateTracker extends Handler { 60 private static final String LOG_TAG = "CSST"; 61 protected static final int CARRIER_EVENT_BASE = 100; 62 protected static final int CARRIER_EVENT_VOICE_REGISTRATION = CARRIER_EVENT_BASE + 1; 63 protected static final int CARRIER_EVENT_VOICE_DEREGISTRATION = CARRIER_EVENT_BASE + 2; 64 protected static final int CARRIER_EVENT_DATA_REGISTRATION = CARRIER_EVENT_BASE + 3; 65 protected static final int CARRIER_EVENT_DATA_DEREGISTRATION = CARRIER_EVENT_BASE + 4; 66 protected static final int CARRIER_EVENT_IMS_CAPABILITIES_CHANGED = CARRIER_EVENT_BASE + 5; 67 68 private static final int UNINITIALIZED_DELAY_VALUE = -1; 69 private Phone mPhone; 70 private ServiceStateTracker mSST; 71 private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>(); 72 private int mPreviousSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 73 public static final int NOTIFICATION_PREF_NETWORK = 1000; 74 public static final int NOTIFICATION_EMERGENCY_NETWORK = 1001; 75 76 77 @VisibleForTesting 78 public static final String ACTION_NEVER_ASK_AGAIN = 79 "com.android.internal.telephony.action.SILENCE_WIFI_CALLING_NOTIFICATION"; 80 public final NotificationActionReceiver mActionReceiver = new NotificationActionReceiver(); 81 82 @VisibleForTesting 83 public static final String EMERGENCY_NOTIFICATION_TAG = "EmergencyNetworkNotification"; 84 85 @VisibleForTesting 86 public static final String PREF_NETWORK_NOTIFICATION_TAG = "PrefNetworkNotification"; 87 88 private long mAllowedNetworkType = -1; 89 private AllowedNetworkTypesListener mAllowedNetworkTypesListener; 90 private TelephonyManager mTelephonyManager; 91 @NonNull private final FeatureFlags mFeatureFlags; 92 93 /** 94 * The listener for allowed network types changed 95 */ 96 @VisibleForTesting 97 public class AllowedNetworkTypesListener extends TelephonyCallback 98 implements TelephonyCallback.AllowedNetworkTypesListener { 99 @Override onAllowedNetworkTypesChanged(int reason, long newAllowedNetworkType)100 public void onAllowedNetworkTypesChanged(int reason, long newAllowedNetworkType) { 101 if (reason != TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) { 102 return; 103 } 104 105 if (mAllowedNetworkType != newAllowedNetworkType) { 106 mAllowedNetworkType = newAllowedNetworkType; 107 handleAllowedNetworkTypeChanged(); 108 } 109 } 110 } 111 CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst, @NonNull FeatureFlags featureFlags)112 public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst, 113 @NonNull FeatureFlags featureFlags) { 114 mFeatureFlags = featureFlags; 115 this.mPhone = phone; 116 this.mSST = sst; 117 mTelephonyManager = mPhone.getContext().getSystemService( 118 TelephonyManager.class).createForSubscriptionId(mPhone.getSubId()); 119 CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class); 120 if (ccm != null) { 121 ccm.registerCarrierConfigChangeListener( 122 mPhone.getContext().getMainExecutor(), 123 (slotIndex, subId, carrierId, specificCarrierId) -> { 124 if (slotIndex != mPhone.getPhoneId()) return; 125 126 Rlog.d(LOG_TAG, "onCarrierConfigChanged: slotIndex=" + slotIndex 127 + ", subId=" + subId + ", carrierId=" + carrierId); 128 129 // Only get carrier configs used for EmergencyNetworkNotification 130 // and PrefNetworkNotification 131 PersistableBundle b = 132 CarrierConfigManager.getCarrierConfigSubset( 133 mPhone.getContext(), 134 mPhone.getSubId(), 135 CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT, 136 CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, 137 CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL); 138 if (b.isEmpty()) return; 139 140 for (Map.Entry<Integer, NotificationType> entry : 141 mNotificationTypeMap.entrySet()) { 142 NotificationType notificationType = entry.getValue(); 143 notificationType.setDelay(b); 144 notificationType.setEnabled(b); 145 } 146 handleConfigChanges(); 147 }); 148 } 149 150 // Listen for subscriber changes 151 SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener( 152 new OnSubscriptionsChangedListener(this.getLooper()) { 153 @Override 154 public void onSubscriptionsChanged() { 155 int subId = mPhone.getSubId(); 156 if (mPreviousSubId != subId) { 157 mPreviousSubId = subId; 158 mTelephonyManager = mTelephonyManager.createForSubscriptionId( 159 mPhone.getSubId()); 160 registerAllowedNetworkTypesListener(); 161 } 162 } 163 }); 164 165 if (!mPhone.getContext().getPackageManager().hasSystemFeature( 166 PackageManager.FEATURE_WATCH)) { 167 registerNotificationTypes(); 168 } 169 170 mAllowedNetworkType = RadioAccessFamily.getNetworkTypeFromRaf( 171 (int) mPhone.getAllowedNetworkTypes( 172 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)); 173 mAllowedNetworkTypesListener = new AllowedNetworkTypesListener(); 174 registerAllowedNetworkTypesListener(); 175 176 if (mFeatureFlags.stopSpammingEmergencyNotification()) { 177 // register a receiver for notification actions 178 mPhone.getContext().registerReceiver( 179 mActionReceiver, 180 new IntentFilter(ACTION_NEVER_ASK_AGAIN), 181 Context.RECEIVER_NOT_EXPORTED); 182 } 183 } 184 185 /** 186 * Return preferred network mode listener 187 */ 188 @VisibleForTesting getAllowedNetworkTypesChangedListener()189 public AllowedNetworkTypesListener getAllowedNetworkTypesChangedListener() { 190 return mAllowedNetworkTypesListener; 191 } 192 registerAllowedNetworkTypesListener()193 private void registerAllowedNetworkTypesListener() { 194 int subId = mPhone.getSubId(); 195 unregisterAllowedNetworkTypesListener(); 196 if (SubscriptionManager.isValidSubscriptionId(subId)) { 197 if (mTelephonyManager != null) { 198 mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(this), 199 mAllowedNetworkTypesListener); 200 } 201 } 202 } 203 unregisterAllowedNetworkTypesListener()204 private void unregisterAllowedNetworkTypesListener() { 205 mTelephonyManager.unregisterTelephonyCallback(mAllowedNetworkTypesListener); 206 } 207 208 /** 209 * Returns mNotificationTypeMap 210 */ 211 @VisibleForTesting getNotificationTypeMap()212 public Map<Integer, NotificationType> getNotificationTypeMap() { 213 return mNotificationTypeMap; 214 } 215 registerNotificationTypes()216 private void registerNotificationTypes() { 217 mNotificationTypeMap.put(NOTIFICATION_PREF_NETWORK, 218 new PrefNetworkNotification(NOTIFICATION_PREF_NETWORK)); 219 mNotificationTypeMap.put(NOTIFICATION_EMERGENCY_NETWORK, 220 new EmergencyNetworkNotification(NOTIFICATION_EMERGENCY_NETWORK)); 221 } 222 223 @Override handleMessage(Message msg)224 public void handleMessage(Message msg) { 225 switch (msg.what) { 226 case CARRIER_EVENT_VOICE_REGISTRATION: 227 case CARRIER_EVENT_DATA_REGISTRATION: 228 case CARRIER_EVENT_VOICE_DEREGISTRATION: 229 case CARRIER_EVENT_DATA_DEREGISTRATION: 230 handleConfigChanges(); 231 break; 232 case CARRIER_EVENT_IMS_CAPABILITIES_CHANGED: 233 handleImsCapabilitiesChanged(); 234 break; 235 case NOTIFICATION_EMERGENCY_NETWORK: 236 case NOTIFICATION_PREF_NETWORK: 237 Rlog.d(LOG_TAG, "sending notification after delay: " + msg.what); 238 NotificationType notificationType = mNotificationTypeMap.get(msg.what); 239 if (notificationType != null) { 240 sendNotification(notificationType); 241 } 242 break; 243 } 244 } 245 isPhoneStillRegistered()246 private boolean isPhoneStillRegistered() { 247 if (mSST.mSS == null) { 248 return true; //something has gone wrong, return true and not show the notification. 249 } 250 return (mSST.mSS.getState() == ServiceState.STATE_IN_SERVICE 251 || mSST.mSS.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE); 252 } 253 isPhoneRegisteredForWifiCalling()254 private boolean isPhoneRegisteredForWifiCalling() { 255 Rlog.d(LOG_TAG, "isPhoneRegisteredForWifiCalling: " + mPhone.isWifiCallingEnabled()); 256 return mPhone.isWifiCallingEnabled(); 257 } 258 259 /** 260 * Returns true if the radio is off or in Airplane Mode else returns false. 261 */ 262 @VisibleForTesting isRadioOffOrAirplaneMode()263 public boolean isRadioOffOrAirplaneMode() { 264 Context context = mPhone.getContext(); 265 int airplaneMode = -1; 266 try { 267 airplaneMode = Settings.Global.getInt(context.getContentResolver(), 268 Settings.Global.AIRPLANE_MODE_ON, 0); 269 } catch (Exception e) { 270 Rlog.e(LOG_TAG, "Unable to get AIRPLACE_MODE_ON."); 271 return true; 272 } 273 return (!mSST.isRadioOn() || (airplaneMode != 0)); 274 } 275 276 /** 277 * Returns true if the preferred network is set to 'Global'. 278 */ isGlobalMode()279 private boolean isGlobalMode() { 280 int preferredNetworkSetting = -1; 281 try { 282 preferredNetworkSetting = PhoneFactory.calculatePreferredNetworkType( 283 mPhone.getPhoneId()); 284 } catch (Exception e) { 285 Rlog.e(LOG_TAG, "Unable to get PREFERRED_NETWORK_MODE."); 286 return true; 287 } 288 289 if (isNrSupported()) { 290 return (preferredNetworkSetting 291 == RadioAccessFamily.getRafFromNetworkType( 292 RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA)); 293 } else { 294 return (preferredNetworkSetting == RadioAccessFamily.getRafFromNetworkType( 295 RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)); 296 } 297 } 298 isNrSupported()299 private boolean isNrSupported() { 300 Context context = mPhone.getContext(); 301 TelephonyManager tm = ((TelephonyManager) context.getSystemService( 302 Context.TELEPHONY_SERVICE)).createForSubscriptionId(mPhone.getSubId()); 303 304 boolean isCarrierConfigEnabled = isCarrierConfigEnableNr(); 305 boolean isRadioAccessFamilySupported = checkSupportedBitmask( 306 tm.getSupportedRadioAccessFamily(), TelephonyManager.NETWORK_TYPE_BITMASK_NR); 307 boolean isNrNetworkTypeAllowed = checkSupportedBitmask( 308 tm.getAllowedNetworkTypesForReason( 309 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER), 310 TelephonyManager.NETWORK_TYPE_BITMASK_NR); 311 312 Rlog.i(LOG_TAG, "isNrSupported: " + " carrierConfigEnabled: " + isCarrierConfigEnabled 313 + ", AccessFamilySupported: " + isRadioAccessFamilySupported 314 + ", isNrNetworkTypeAllowed: " + isNrNetworkTypeAllowed); 315 316 return (isCarrierConfigEnabled && isRadioAccessFamilySupported && isNrNetworkTypeAllowed); 317 } 318 isCarrierConfigEnableNr()319 private boolean isCarrierConfigEnableNr() { 320 PersistableBundle config = 321 CarrierConfigManager.getCarrierConfigSubset( 322 mPhone.getContext(), 323 mPhone.getSubId(), 324 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); 325 if (config.isEmpty()) { 326 Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: Cannot get config " + mPhone.getSubId()); 327 return false; 328 } 329 int[] nrAvailabilities = config.getIntArray( 330 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); 331 return !ArrayUtils.isEmpty(nrAvailabilities); 332 } 333 checkSupportedBitmask(@etworkTypeBitMask long supportedBitmask, @NetworkTypeBitMask long targetBitmask)334 private boolean checkSupportedBitmask(@NetworkTypeBitMask long supportedBitmask, 335 @NetworkTypeBitMask long targetBitmask) { 336 return (targetBitmask & supportedBitmask) == targetBitmask; 337 } 338 handleConfigChanges()339 private void handleConfigChanges() { 340 for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) { 341 NotificationType notificationType = entry.getValue(); 342 evaluateSendingMessageOrCancelNotification(notificationType); 343 } 344 } 345 handleAllowedNetworkTypeChanged()346 private void handleAllowedNetworkTypeChanged() { 347 NotificationType notificationType = mNotificationTypeMap.get(NOTIFICATION_PREF_NETWORK); 348 if (notificationType != null) { 349 evaluateSendingMessageOrCancelNotification(notificationType); 350 } 351 } 352 handleImsCapabilitiesChanged()353 private void handleImsCapabilitiesChanged() { 354 NotificationType notificationType = mNotificationTypeMap 355 .get(NOTIFICATION_EMERGENCY_NETWORK); 356 if (notificationType != null) { 357 evaluateSendingMessageOrCancelNotification(notificationType); 358 } 359 } 360 evaluateSendingMessageOrCancelNotification(NotificationType notificationType)361 private void evaluateSendingMessageOrCancelNotification(NotificationType notificationType) { 362 if (evaluateSendingMessage(notificationType)) { 363 Message notificationMsg = obtainMessage(notificationType.getTypeId(), null); 364 Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId()); 365 sendMessageDelayed(notificationMsg, getDelay(notificationType)); 366 } else { 367 cancelNotification(notificationType); 368 Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId()); 369 } 370 } 371 372 /** 373 * This method adds a level of indirection, and was created so we can unit the class. 374 **/ 375 @VisibleForTesting evaluateSendingMessage(NotificationType notificationType)376 public boolean evaluateSendingMessage(NotificationType notificationType) { 377 return notificationType.sendMessage(); 378 } 379 380 /** 381 * This method adds a level of indirection, and was created so we can unit the class. 382 **/ 383 @VisibleForTesting getDelay(NotificationType notificationType)384 public int getDelay(NotificationType notificationType) { 385 return notificationType.getDelay(); 386 } 387 388 /** 389 * This method adds a level of indirection, and was created so we can unit the class. 390 **/ 391 @VisibleForTesting getNotificationBuilder(NotificationType notificationType)392 public Notification.Builder getNotificationBuilder(NotificationType notificationType) { 393 return notificationType.getNotificationBuilder(); 394 } 395 396 /** 397 * This method adds a level of indirection, and was created so we can unit the class. 398 **/ 399 @VisibleForTesting getNotificationManager(Context context)400 public NotificationManager getNotificationManager(Context context) { 401 return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 402 } 403 404 /** 405 * Post a notification to the NotificationManager for changing network type. 406 */ 407 @VisibleForTesting sendNotification(NotificationType notificationType)408 public void sendNotification(NotificationType notificationType) { 409 Context context = mPhone.getContext(); 410 411 if (!evaluateSendingMessage(notificationType)) { 412 return; 413 } 414 415 if (mFeatureFlags.stopSpammingEmergencyNotification() 416 && shouldSilenceEmrgNetNotif(notificationType, context)) { 417 Rlog.i(LOG_TAG, "sendNotification: silencing NOTIFICATION_EMERGENCY_NETWORK"); 418 return; 419 } 420 421 Notification.Builder builder = getNotificationBuilder(notificationType); 422 // set some common attributes 423 builder.setWhen(System.currentTimeMillis()) 424 .setShowWhen(true) 425 .setAutoCancel(true) 426 .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning) 427 .setColor(context.getResources().getColor( 428 com.android.internal.R.color.system_notification_accent_color)); 429 getNotificationManager(context).notify(notificationType.getNotificationTag(), 430 notificationType.getNotificationId(), builder.build()); 431 } 432 433 /** 434 * This helper checks if the user has set a flag to silence the notification permanently 435 */ shouldSilenceEmrgNetNotif(NotificationType notificationType, Context context)436 private boolean shouldSilenceEmrgNetNotif(NotificationType notificationType, Context context) { 437 return notificationType.getTypeId() == NOTIFICATION_EMERGENCY_NETWORK 438 && PreferenceManager.getDefaultSharedPreferences(context) 439 .getBoolean(ACTION_NEVER_ASK_AGAIN, false); 440 } 441 442 /** 443 * Cancel notifications if a registration is pending or has been sent. 444 **/ cancelNotification(NotificationType notificationType)445 public void cancelNotification(NotificationType notificationType) { 446 Context context = mPhone.getContext(); 447 removeMessages(notificationType.getTypeId()); 448 getNotificationManager(context).cancel( 449 notificationType.getNotificationTag(), notificationType.getNotificationId()); 450 } 451 452 /** 453 * Dispose the CarrierServiceStateTracker. 454 */ dispose()455 public void dispose() { 456 unregisterAllowedNetworkTypesListener(); 457 } 458 459 /** 460 * Class that defines the different types of notifications. 461 */ 462 public interface NotificationType { 463 464 /** 465 * decides if the message should be sent, Returns boolean 466 **/ sendMessage()467 boolean sendMessage(); 468 469 /** 470 * returns the interval by which the message is delayed. 471 **/ getDelay()472 int getDelay(); 473 474 /** sets the interval by which the message is delayed. 475 * @param bundle PersistableBundle 476 **/ setDelay(PersistableBundle bundle)477 void setDelay(PersistableBundle bundle); 478 479 /** 480 * Checks whether this Notification is enabled. 481 * @return {@code true} if this Notification is enabled, false otherwise 482 */ isEnabled()483 boolean isEnabled(); 484 485 /** 486 * Sets whether this Notification is enabled. If disabled, it will not build notification. 487 * @param bundle PersistableBundle 488 */ setEnabled(PersistableBundle bundle)489 void setEnabled(PersistableBundle bundle); 490 491 /** 492 * returns notification type id. 493 **/ getTypeId()494 int getTypeId(); 495 496 /** 497 * returns notification id. 498 **/ getNotificationId()499 int getNotificationId(); 500 501 /** 502 * returns notification tag. 503 **/ getNotificationTag()504 String getNotificationTag(); 505 506 /** 507 * returns the notification builder, for the notification to be displayed. 508 **/ getNotificationBuilder()509 Notification.Builder getNotificationBuilder(); 510 } 511 512 /** 513 * Class that defines the network notification, which is shown when the phone cannot camp on 514 * a network, and has 'preferred mode' set to global. 515 */ 516 public class PrefNetworkNotification implements NotificationType { 517 518 private final int mTypeId; 519 private int mDelay = UNINITIALIZED_DELAY_VALUE; 520 private boolean mEnabled = false; 521 PrefNetworkNotification(int typeId)522 PrefNetworkNotification(int typeId) { 523 this.mTypeId = typeId; 524 } 525 526 /** sets the interval by which the message is delayed. 527 * @param bundle PersistableBundle 528 **/ setDelay(PersistableBundle bundle)529 public void setDelay(PersistableBundle bundle) { 530 if (bundle == null) { 531 Rlog.e(LOG_TAG, "bundle is null"); 532 return; 533 } 534 this.mDelay = bundle.getInt( 535 CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT); 536 Rlog.i(LOG_TAG, "reading time to delay notification pref network: " + mDelay); 537 } 538 getDelay()539 public int getDelay() { 540 return mDelay; 541 } 542 543 /** 544 * Checks whether this Notification is enabled. 545 * @return {@code true} if this Notification is enabled, false otherwise 546 */ isEnabled()547 public boolean isEnabled() { 548 return mEnabled; 549 } 550 551 /** 552 * Sets whether this Notification is enabled. If disabled, it will not build notification. 553 * @param bundle PersistableBundle 554 */ setEnabled(PersistableBundle bundle)555 public void setEnabled(PersistableBundle bundle) { 556 if (bundle == null) { 557 Rlog.e(LOG_TAG, "bundle is null"); 558 return; 559 } 560 mEnabled = !bundle.getBoolean( 561 CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL); 562 Rlog.i(LOG_TAG, "reading enabled notification pref network: " + mEnabled); 563 } 564 getTypeId()565 public int getTypeId() { 566 return mTypeId; 567 } 568 getNotificationId()569 public int getNotificationId() { 570 return mPhone.getSubId(); 571 } 572 getNotificationTag()573 public String getNotificationTag() { 574 return PREF_NETWORK_NOTIFICATION_TAG; 575 } 576 577 /** 578 * Contains logic on sending notifications. 579 */ sendMessage()580 public boolean sendMessage() { 581 Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: " 582 + "," + mEnabled + "," + isPhoneStillRegistered() + "," + mDelay 583 + "," + isGlobalMode() + "," + mSST.isRadioOn()); 584 if (!mEnabled || mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() 585 || isGlobalMode() || isRadioOffOrAirplaneMode()) { 586 return false; 587 } 588 return true; 589 } 590 591 /** 592 * Builds a partial notificaiton builder, and returns it. 593 */ getNotificationBuilder()594 public Notification.Builder getNotificationBuilder() { 595 Context context = mPhone.getContext(); 596 Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS); 597 notificationIntent.putExtra("expandable", true); 598 PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent, 599 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE); 600 Resources res = SubscriptionManager.getResourcesForSubId(context, mPhone.getSubId()); 601 CharSequence title = res.getText( 602 com.android.internal.R.string.NetworkPreferenceSwitchTitle); 603 CharSequence details = res.getText( 604 com.android.internal.R.string.NetworkPreferenceSwitchSummary); 605 return new Notification.Builder(context) 606 .setContentTitle(title) 607 .setStyle(new Notification.BigTextStyle().bigText(details)) 608 .setContentText(details) 609 .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT) 610 .setContentIntent(settingsIntent); 611 } 612 } 613 614 /** 615 * Class that defines the emergency notification, which is shown when Wi-Fi Calling is 616 * available. 617 */ 618 public class EmergencyNetworkNotification implements NotificationType { 619 620 private final int mTypeId; 621 private int mDelay = UNINITIALIZED_DELAY_VALUE; 622 EmergencyNetworkNotification(int typeId)623 EmergencyNetworkNotification(int typeId) { 624 this.mTypeId = typeId; 625 } 626 627 /** sets the interval by which the message is delayed. 628 * @param bundle PersistableBundle 629 **/ setDelay(PersistableBundle bundle)630 public void setDelay(PersistableBundle bundle) { 631 if (bundle == null) { 632 Rlog.e(LOG_TAG, "bundle is null"); 633 return; 634 } 635 this.mDelay = bundle.getInt( 636 CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT); 637 Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay); 638 } 639 getDelay()640 public int getDelay() { 641 return mDelay; 642 } 643 644 /** 645 * Checks whether this Notification is enabled. 646 * @return {@code true} if this Notification is enabled, false otherwise 647 */ isEnabled()648 public boolean isEnabled() { 649 return true; 650 } 651 652 /** 653 * Sets whether this Notification is enabled. If disabled, it will not build notification. 654 * @param bundle PersistableBundle 655 */ setEnabled(PersistableBundle bundle)656 public void setEnabled(PersistableBundle bundle) { 657 // always allowed. There is no config to hide notifications. 658 } 659 getTypeId()660 public int getTypeId() { 661 return mTypeId; 662 } 663 getNotificationId()664 public int getNotificationId() { 665 return mPhone.getSubId(); 666 } 667 getNotificationTag()668 public String getNotificationTag() { 669 return EMERGENCY_NOTIFICATION_TAG; 670 } 671 672 /** 673 * Contains logic on sending notifications, 674 */ sendMessage()675 public boolean sendMessage() { 676 Rlog.i(LOG_TAG, "EmergencyNetworkNotification: sendMessage() w/values: " 677 + "," + mDelay + "," + isPhoneRegisteredForWifiCalling() + "," 678 + mSST.isRadioOn()); 679 if (mDelay == UNINITIALIZED_DELAY_VALUE || !isPhoneRegisteredForWifiCalling()) { 680 return false; 681 } 682 return true; 683 } 684 685 /** 686 * Builds a partial notificaiton builder, and returns it. 687 */ getNotificationBuilder()688 public Notification.Builder getNotificationBuilder() { 689 Context context = mPhone.getContext(); 690 Resources res = SubscriptionManager.getResourcesForSubId(context, mPhone.getSubId()); 691 CharSequence title = res.getText( 692 com.android.internal.R.string.EmergencyCallWarningTitle); 693 CharSequence details = res.getText( 694 com.android.internal.R.string.EmergencyCallWarningSummary); 695 if (mFeatureFlags.stopSpammingEmergencyNotification()) { 696 return new Notification.Builder(context) 697 .setContentTitle(title) 698 .setStyle(new Notification.BigTextStyle().bigText(details)) 699 .setContentText(details) 700 .setOngoing(true) 701 .setActions(createDoNotShowAgainAction(context)) 702 .setChannelId(NotificationChannelController.CHANNEL_ID_WFC); 703 } else { 704 return new Notification.Builder(context) 705 .setContentTitle(title) 706 .setStyle(new Notification.BigTextStyle().bigText(details)) 707 .setContentText(details) 708 .setOngoing(true) 709 .setChannelId(NotificationChannelController.CHANNEL_ID_WFC); 710 } 711 } 712 713 /** 714 * add a button to the notification that has a broadcast intent embedded to silence the 715 * notification 716 */ createDoNotShowAgainAction(Context c)717 private Notification.Action createDoNotShowAgainAction(Context c) { 718 final PendingIntent pendingIntent = PendingIntent.getBroadcast( 719 c, 720 0, 721 new Intent(ACTION_NEVER_ASK_AGAIN), 722 PendingIntent.FLAG_IMMUTABLE); 723 CharSequence text = "Do Not Ask Again"; 724 if (c != null && mFeatureFlags.dynamicDoNotAskAgainText()) { 725 text = c.getText(com.android.internal.R.string.emergency_calling_do_not_show_again); 726 } 727 return new Notification.Action.Builder(null, text, pendingIntent).build(); 728 } 729 } 730 731 /** 732 * This receiver listens to notification actions and can be utilized to do things like silence 733 * a notification that is spammy. 734 */ 735 public class NotificationActionReceiver extends BroadcastReceiver { 736 @Override onReceive(Context context, Intent intent)737 public void onReceive(Context context, Intent intent) { 738 if (intent.getAction().equals(ACTION_NEVER_ASK_AGAIN)) { 739 Rlog.i(LOG_TAG, "NotificationActionReceiver: ACTION_NEVER_ASK_AGAIN"); 740 dismissEmergencyCallingNotification(); 741 // insert a key to silence future notifications 742 SharedPreferences.Editor editor = 743 PreferenceManager.getDefaultSharedPreferences(context).edit(); 744 editor.putBoolean(ACTION_NEVER_ASK_AGAIN, true); 745 editor.apply(); 746 // Note: If another action is added, unregistering here should be removed. However, 747 // since there is no longer a reason to broadcasts, cleanup mActionReceiver. 748 context.unregisterReceiver(mActionReceiver); 749 } 750 } 751 752 /** 753 * Dismiss the notification when the "Do Not Ask Again" button is clicked 754 */ dismissEmergencyCallingNotification()755 private void dismissEmergencyCallingNotification() { 756 if (!mFeatureFlags.stopSpammingEmergencyNotification()) { 757 return; 758 } 759 try { 760 NotificationType t = mNotificationTypeMap.get(NOTIFICATION_EMERGENCY_NETWORK); 761 if (t != null) { 762 cancelNotification(t); 763 } 764 } catch (Exception e) { 765 Rlog.e(LOG_TAG, "dismissEmergencyCallingNotification", e); 766 } 767 } 768 } 769 } 770