• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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