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