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