/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony;

import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.PersistableBundle;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.NetworkTypeBitMask;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.telephony.Rlog;

import java.util.HashMap;
import java.util.Map;

/**
 * This contains Carrier specific logic based on the states/events
 * managed in ServiceStateTracker.
 * {@hide}
 */
public class CarrierServiceStateTracker extends Handler {
    private static final String LOG_TAG = "CSST";
    protected static final int CARRIER_EVENT_BASE = 100;
    protected static final int CARRIER_EVENT_VOICE_REGISTRATION = CARRIER_EVENT_BASE + 1;
    protected static final int CARRIER_EVENT_VOICE_DEREGISTRATION = CARRIER_EVENT_BASE + 2;
    protected static final int CARRIER_EVENT_DATA_REGISTRATION = CARRIER_EVENT_BASE + 3;
    protected static final int CARRIER_EVENT_DATA_DEREGISTRATION = CARRIER_EVENT_BASE + 4;
    protected static final int CARRIER_EVENT_IMS_CAPABILITIES_CHANGED = CARRIER_EVENT_BASE + 5;

    private static final int UNINITIALIZED_DELAY_VALUE = -1;
    private Phone mPhone;
    private ServiceStateTracker mSST;
    private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>();
    private int mPreviousSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    public static final int NOTIFICATION_PREF_NETWORK = 1000;
    public static final int NOTIFICATION_EMERGENCY_NETWORK = 1001;


    @VisibleForTesting
    public static final String ACTION_NEVER_ASK_AGAIN = "SilenceNoWifiEmrgCallingNotification";
    public final NotificationActionReceiver mActionReceiver = new NotificationActionReceiver();

    @VisibleForTesting
    public static final String EMERGENCY_NOTIFICATION_TAG = "EmergencyNetworkNotification";

    @VisibleForTesting
    public static final String PREF_NETWORK_NOTIFICATION_TAG = "PrefNetworkNotification";

    private long mAllowedNetworkType = -1;
    private AllowedNetworkTypesListener mAllowedNetworkTypesListener;
    private TelephonyManager mTelephonyManager;
    @NonNull private final FeatureFlags mFeatureFlags;

    /**
     * The listener for allowed network types changed
     */
    @VisibleForTesting
    public class AllowedNetworkTypesListener extends TelephonyCallback
            implements TelephonyCallback.AllowedNetworkTypesListener {
        @Override
        public void onAllowedNetworkTypesChanged(int reason, long newAllowedNetworkType) {
            if (reason != TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) {
                return;
            }

            if (mAllowedNetworkType != newAllowedNetworkType) {
                mAllowedNetworkType = newAllowedNetworkType;
                handleAllowedNetworkTypeChanged();
            }
        }
    }

    public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst,
            @NonNull FeatureFlags featureFlags) {
        mFeatureFlags = featureFlags;
        this.mPhone = phone;
        this.mSST = sst;
        mTelephonyManager = mPhone.getContext().getSystemService(
                TelephonyManager.class).createForSubscriptionId(mPhone.getSubId());
        CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
        ccm.registerCarrierConfigChangeListener(
                mPhone.getContext().getMainExecutor(),
                (slotIndex, subId, carrierId, specificCarrierId) -> {
                    if (slotIndex != mPhone.getPhoneId()) return;

                    Rlog.d(LOG_TAG, "onCarrierConfigChanged: slotIndex=" + slotIndex
                            + ", subId=" + subId + ", carrierId=" + carrierId);

                    // Only get carrier configs used for EmergencyNetworkNotification
                    // and PrefNetworkNotification
                    PersistableBundle b =
                            CarrierConfigManager.getCarrierConfigSubset(
                                    mPhone.getContext(),
                                    mPhone.getSubId(),
                                    CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
                                    CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT,
                                    CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL);
                    if (b.isEmpty()) return;

                    for (Map.Entry<Integer, NotificationType> entry :
                            mNotificationTypeMap.entrySet()) {
                        NotificationType notificationType = entry.getValue();
                        notificationType.setDelay(b);
                        notificationType.setEnabled(b);
                    }
                    handleConfigChanges();
                });

        // Listen for subscriber changes
        SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
                new OnSubscriptionsChangedListener(this.getLooper()) {
                    @Override
                    public void onSubscriptionsChanged() {
                        int subId = mPhone.getSubId();
                        if (mPreviousSubId != subId) {
                            mPreviousSubId = subId;
                            mTelephonyManager = mTelephonyManager.createForSubscriptionId(
                                    mPhone.getSubId());
                            registerAllowedNetworkTypesListener();
                        }
                    }
                });

        if (!mPhone.getContext().getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_WATCH)) {
            registerNotificationTypes();
        }

        mAllowedNetworkType = RadioAccessFamily.getNetworkTypeFromRaf(
                (int) mPhone.getAllowedNetworkTypes(
                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
        mAllowedNetworkTypesListener = new AllowedNetworkTypesListener();
        registerAllowedNetworkTypesListener();

        if (mFeatureFlags.stopSpammingEmergencyNotification()) {
            // register a receiver for notification actions
            mPhone.getContext().registerReceiver(
                    mActionReceiver,
                    new IntentFilter(ACTION_NEVER_ASK_AGAIN),
                    Context.RECEIVER_NOT_EXPORTED);
        }
    }

    /**
     * Return preferred network mode listener
     */
    @VisibleForTesting
    public AllowedNetworkTypesListener getAllowedNetworkTypesChangedListener() {
        return mAllowedNetworkTypesListener;
    }

    private void registerAllowedNetworkTypesListener() {
        int subId = mPhone.getSubId();
        unregisterAllowedNetworkTypesListener();
        if (SubscriptionManager.isValidSubscriptionId(subId)) {
            if (mTelephonyManager != null) {
                mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(this),
                        mAllowedNetworkTypesListener);
            }
        }
    }

    private void unregisterAllowedNetworkTypesListener() {
        mTelephonyManager.unregisterTelephonyCallback(mAllowedNetworkTypesListener);
    }

    /**
     * Returns mNotificationTypeMap
     */
    @VisibleForTesting
    public Map<Integer, NotificationType> getNotificationTypeMap() {
        return mNotificationTypeMap;
    }

    private void registerNotificationTypes() {
        mNotificationTypeMap.put(NOTIFICATION_PREF_NETWORK,
                new PrefNetworkNotification(NOTIFICATION_PREF_NETWORK));
        mNotificationTypeMap.put(NOTIFICATION_EMERGENCY_NETWORK,
                new EmergencyNetworkNotification(NOTIFICATION_EMERGENCY_NETWORK));
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case CARRIER_EVENT_VOICE_REGISTRATION:
            case CARRIER_EVENT_DATA_REGISTRATION:
            case CARRIER_EVENT_VOICE_DEREGISTRATION:
            case CARRIER_EVENT_DATA_DEREGISTRATION:
                handleConfigChanges();
                break;
            case CARRIER_EVENT_IMS_CAPABILITIES_CHANGED:
                handleImsCapabilitiesChanged();
                break;
            case NOTIFICATION_EMERGENCY_NETWORK:
            case NOTIFICATION_PREF_NETWORK:
                Rlog.d(LOG_TAG, "sending notification after delay: " + msg.what);
                NotificationType notificationType = mNotificationTypeMap.get(msg.what);
                if (notificationType != null) {
                    sendNotification(notificationType);
                }
                break;
        }
    }

    private boolean isPhoneStillRegistered() {
        if (mSST.mSS == null) {
            return true; //something has gone wrong, return true and not show the notification.
        }
        return (mSST.mSS.getState() == ServiceState.STATE_IN_SERVICE
                || mSST.mSS.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE);
    }

    private boolean isPhoneRegisteredForWifiCalling() {
        Rlog.d(LOG_TAG, "isPhoneRegisteredForWifiCalling: " + mPhone.isWifiCallingEnabled());
        return mPhone.isWifiCallingEnabled();
    }

    /**
     * Returns true if the radio is off or in Airplane Mode else returns false.
     */
    @VisibleForTesting
    public boolean isRadioOffOrAirplaneMode() {
        Context context = mPhone.getContext();
        int airplaneMode = -1;
        try {
            airplaneMode = Settings.Global.getInt(context.getContentResolver(),
                    Settings.Global.AIRPLANE_MODE_ON, 0);
        } catch (Exception e) {
            Rlog.e(LOG_TAG, "Unable to get AIRPLACE_MODE_ON.");
            return true;
        }
        return (!mSST.isRadioOn() || (airplaneMode != 0));
    }

    /**
     * Returns true if the preferred network is set to 'Global'.
     */
    private boolean isGlobalMode() {
        int preferredNetworkSetting = -1;
        try {
            preferredNetworkSetting = PhoneFactory.calculatePreferredNetworkType(
                    mPhone.getPhoneId());
        } catch (Exception e) {
            Rlog.e(LOG_TAG, "Unable to get PREFERRED_NETWORK_MODE.");
            return true;
        }

        if (isNrSupported()) {
            return (preferredNetworkSetting
                    == RadioAccessFamily.getRafFromNetworkType(
                    RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA));
        } else {
            return (preferredNetworkSetting == RadioAccessFamily.getRafFromNetworkType(
                    RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
        }
    }

    private boolean isNrSupported() {
        Context context = mPhone.getContext();
        TelephonyManager tm = ((TelephonyManager) context.getSystemService(
                Context.TELEPHONY_SERVICE)).createForSubscriptionId(mPhone.getSubId());

        boolean isCarrierConfigEnabled = isCarrierConfigEnableNr();
        boolean isRadioAccessFamilySupported = checkSupportedBitmask(
                tm.getSupportedRadioAccessFamily(), TelephonyManager.NETWORK_TYPE_BITMASK_NR);
        boolean isNrNetworkTypeAllowed = checkSupportedBitmask(
                tm.getAllowedNetworkTypesForReason(
                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER),
                TelephonyManager.NETWORK_TYPE_BITMASK_NR);

        Rlog.i(LOG_TAG, "isNrSupported: " + " carrierConfigEnabled: " + isCarrierConfigEnabled
                + ", AccessFamilySupported: " + isRadioAccessFamilySupported
                + ", isNrNetworkTypeAllowed: " + isNrNetworkTypeAllowed);

        return (isCarrierConfigEnabled && isRadioAccessFamilySupported && isNrNetworkTypeAllowed);
    }

    private boolean isCarrierConfigEnableNr() {
        PersistableBundle config =
                CarrierConfigManager.getCarrierConfigSubset(
                        mPhone.getContext(),
                        mPhone.getSubId(),
                        CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
        if (config.isEmpty()) {
            Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: Cannot get config " + mPhone.getSubId());
            return false;
        }
        int[] nrAvailabilities = config.getIntArray(
                CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
        return !ArrayUtils.isEmpty(nrAvailabilities);
    }

    private boolean checkSupportedBitmask(@NetworkTypeBitMask long supportedBitmask,
            @NetworkTypeBitMask long targetBitmask) {
        return (targetBitmask & supportedBitmask) == targetBitmask;
    }

    private void handleConfigChanges() {
        for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
            NotificationType notificationType = entry.getValue();
            evaluateSendingMessageOrCancelNotification(notificationType);
        }
    }

    private void handleAllowedNetworkTypeChanged() {
        NotificationType notificationType = mNotificationTypeMap.get(NOTIFICATION_PREF_NETWORK);
        if (notificationType != null) {
            evaluateSendingMessageOrCancelNotification(notificationType);
        }
    }

    private void handleImsCapabilitiesChanged() {
        NotificationType notificationType = mNotificationTypeMap
                .get(NOTIFICATION_EMERGENCY_NETWORK);
        if (notificationType != null) {
            evaluateSendingMessageOrCancelNotification(notificationType);
        }
    }

    private void evaluateSendingMessageOrCancelNotification(NotificationType notificationType) {
        if (evaluateSendingMessage(notificationType)) {
            Message notificationMsg = obtainMessage(notificationType.getTypeId(), null);
            Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId());
            sendMessageDelayed(notificationMsg, getDelay(notificationType));
        } else {
            cancelNotification(notificationType);
            Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId());
        }
    }

    /**
     * This method adds a level of indirection, and was created so we can unit the class.
     **/
    @VisibleForTesting
    public boolean evaluateSendingMessage(NotificationType notificationType) {
        return notificationType.sendMessage();
    }

    /**
     * This method adds a level of indirection, and was created so we can unit the class.
     **/
    @VisibleForTesting
    public int getDelay(NotificationType notificationType) {
        return notificationType.getDelay();
    }

    /**
     * This method adds a level of indirection, and was created so we can unit the class.
     **/
    @VisibleForTesting
    public Notification.Builder getNotificationBuilder(NotificationType notificationType) {
        return notificationType.getNotificationBuilder();
    }

    /**
     * This method adds a level of indirection, and was created so we can unit the class.
     **/
    @VisibleForTesting
    public NotificationManager getNotificationManager(Context context) {
        return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    }

    /**
     * Post a notification to the NotificationManager for changing network type.
     */
    @VisibleForTesting
    public void sendNotification(NotificationType notificationType) {
        Context context = mPhone.getContext();

        if (!evaluateSendingMessage(notificationType)) {
            return;
        }

        if (mFeatureFlags.stopSpammingEmergencyNotification()
                && shouldSilenceEmrgNetNotif(notificationType, context)) {
            Rlog.i(LOG_TAG, "sendNotification: silencing NOTIFICATION_EMERGENCY_NETWORK");
            return;
        }

        Notification.Builder builder = getNotificationBuilder(notificationType);
        // set some common attributes
        builder.setWhen(System.currentTimeMillis())
                .setShowWhen(true)
                .setAutoCancel(true)
                .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
                .setColor(context.getResources().getColor(
                       com.android.internal.R.color.system_notification_accent_color));
        getNotificationManager(context).notify(notificationType.getNotificationTag(),
                notificationType.getNotificationId(), builder.build());
    }

    /**
     * This helper checks if the user has set a flag to silence the notification permanently
     */
    private boolean shouldSilenceEmrgNetNotif(NotificationType notificationType, Context context) {
        return notificationType.getTypeId() == NOTIFICATION_EMERGENCY_NETWORK
                && PreferenceManager.getDefaultSharedPreferences(context)
                .getBoolean(ACTION_NEVER_ASK_AGAIN, false);
    }

    /**
     * Cancel notifications if a registration is pending or has been sent.
     **/
    public void cancelNotification(NotificationType notificationType) {
        Context context = mPhone.getContext();
        removeMessages(notificationType.getTypeId());
        getNotificationManager(context).cancel(
                notificationType.getNotificationTag(), notificationType.getNotificationId());
    }

    /**
     * Dispose the CarrierServiceStateTracker.
     */
    public void dispose() {
        unregisterAllowedNetworkTypesListener();
    }

    /**
     * Class that defines the different types of notifications.
     */
    public interface NotificationType {

        /**
         * decides if the message should be sent, Returns boolean
         **/
        boolean sendMessage();

        /**
         * returns the interval by which the message is delayed.
         **/
        int getDelay();

        /** sets the interval by which the message is delayed.
         * @param bundle PersistableBundle
        **/
        void setDelay(PersistableBundle bundle);

        /**
         * Checks whether this Notification is enabled.
         * @return {@code true} if this Notification is enabled, false otherwise
         */
        boolean isEnabled();

        /**
         * Sets whether this Notification is enabled. If disabled, it will not build notification.
         * @param bundle PersistableBundle
         */
        void setEnabled(PersistableBundle bundle);

        /**
         * returns notification type id.
         **/
        int getTypeId();

        /**
         * returns notification id.
         **/
        int getNotificationId();

        /**
         * returns notification tag.
         **/
        String getNotificationTag();

        /**
         * returns the notification builder, for the notification to be displayed.
         **/
        Notification.Builder getNotificationBuilder();
    }

    /**
     * Class that defines the network notification, which is shown when the phone cannot camp on
     * a network, and has 'preferred mode' set to global.
     */
    public class PrefNetworkNotification implements NotificationType {

        private final int mTypeId;
        private int mDelay = UNINITIALIZED_DELAY_VALUE;
        private boolean mEnabled = false;

        PrefNetworkNotification(int typeId) {
            this.mTypeId = typeId;
        }

        /** sets the interval by which the message is delayed.
         * @param bundle PersistableBundle
         **/
        public void setDelay(PersistableBundle bundle) {
            if (bundle == null) {
                Rlog.e(LOG_TAG, "bundle is null");
                return;
            }
            this.mDelay = bundle.getInt(
                    CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
            Rlog.i(LOG_TAG, "reading time to delay notification pref network: " + mDelay);
        }

        public int getDelay() {
            return mDelay;
        }

        /**
         * Checks whether this Notification is enabled.
         * @return {@code true} if this Notification is enabled, false otherwise
         */
        public boolean isEnabled() {
            return mEnabled;
        }

        /**
         * Sets whether this Notification is enabled. If disabled, it will not build notification.
         * @param bundle PersistableBundle
         */
        public void setEnabled(PersistableBundle bundle) {
            if (bundle == null) {
                Rlog.e(LOG_TAG, "bundle is null");
                return;
            }
            mEnabled = !bundle.getBoolean(
                    CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL);
            Rlog.i(LOG_TAG, "reading enabled notification pref network: " + mEnabled);
        }

        public int getTypeId() {
            return mTypeId;
        }

        public int getNotificationId() {
            return mPhone.getSubId();
        }

        public String getNotificationTag() {
            return PREF_NETWORK_NOTIFICATION_TAG;
        }

        /**
         * Contains logic on sending notifications.
         */
        public boolean sendMessage() {
            Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: "
                    + "," + mEnabled + "," + isPhoneStillRegistered() + "," + mDelay
                    + "," + isGlobalMode() + "," + mSST.isRadioOn());
            if (!mEnabled || mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered()
                    || isGlobalMode() || isRadioOffOrAirplaneMode()) {
                return false;
            }
            return true;
        }

        /**
         * Builds a partial notificaiton builder, and returns it.
         */
        public Notification.Builder getNotificationBuilder() {
            Context context = mPhone.getContext();
            Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
            notificationIntent.putExtra("expandable", true);
            PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent,
                    PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
            Resources res = SubscriptionManager.getResourcesForSubId(context, mPhone.getSubId());
            CharSequence title = res.getText(
                    com.android.internal.R.string.NetworkPreferenceSwitchTitle);
            CharSequence details = res.getText(
                    com.android.internal.R.string.NetworkPreferenceSwitchSummary);
            return new Notification.Builder(context)
                    .setContentTitle(title)
                    .setStyle(new Notification.BigTextStyle().bigText(details))
                    .setContentText(details)
                    .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT)
                    .setContentIntent(settingsIntent);
        }
    }

    /**
     * Class that defines the emergency notification, which is shown when Wi-Fi Calling is
     * available.
     */
    public class EmergencyNetworkNotification implements NotificationType {

        private final int mTypeId;
        private int mDelay = UNINITIALIZED_DELAY_VALUE;

        EmergencyNetworkNotification(int typeId) {
            this.mTypeId = typeId;
        }

        /** sets the interval by which the message is delayed.
         * @param bundle PersistableBundle
         **/
        public void setDelay(PersistableBundle bundle) {
            if (bundle == null) {
                Rlog.e(LOG_TAG, "bundle is null");
                return;
            }
            this.mDelay = bundle.getInt(
                    CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT);
            Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay);
        }

        public int getDelay() {
            return mDelay;
        }

        /**
         * Checks whether this Notification is enabled.
         * @return {@code true} if this Notification is enabled, false otherwise
         */
        public boolean isEnabled() {
            return true;
        }

        /**
         * Sets whether this Notification is enabled. If disabled, it will not build notification.
         * @param bundle PersistableBundle
         */
        public void setEnabled(PersistableBundle bundle) {
            // always allowed. There is no config to hide notifications.
        }

        public int getTypeId() {
            return mTypeId;
        }

        public int getNotificationId() {
            return mPhone.getSubId();
        }

        public String getNotificationTag() {
            return EMERGENCY_NOTIFICATION_TAG;
        }

        /**
         * Contains logic on sending notifications,
         */
        public boolean sendMessage() {
            Rlog.i(LOG_TAG, "EmergencyNetworkNotification: sendMessage() w/values: "
                    + "," + mDelay + "," + isPhoneRegisteredForWifiCalling() + ","
                    + mSST.isRadioOn());
            if (mDelay == UNINITIALIZED_DELAY_VALUE || !isPhoneRegisteredForWifiCalling()) {
                return false;
            }
            return true;
        }

        /**
         * Builds a partial notificaiton builder, and returns it.
         */
        public Notification.Builder getNotificationBuilder() {
            Context context = mPhone.getContext();
            Resources res = SubscriptionManager.getResourcesForSubId(context, mPhone.getSubId());
            CharSequence title = res.getText(
                    com.android.internal.R.string.EmergencyCallWarningTitle);
            CharSequence details = res.getText(
                    com.android.internal.R.string.EmergencyCallWarningSummary);
            if (mFeatureFlags.stopSpammingEmergencyNotification()) {
                return new Notification.Builder(context)
                        .setContentTitle(title)
                        .setStyle(new Notification.BigTextStyle().bigText(details))
                        .setContentText(details)
                        .setOngoing(true)
                        .setActions(createDoNotShowAgainAction(context))
                        .setChannelId(NotificationChannelController.CHANNEL_ID_WFC);
            } else {
                return new Notification.Builder(context)
                        .setContentTitle(title)
                        .setStyle(new Notification.BigTextStyle().bigText(details))
                        .setContentText(details)
                        .setOngoing(true)
                        .setChannelId(NotificationChannelController.CHANNEL_ID_WFC);
            }
        }

        /**
         * add a button to the notification that has a broadcast intent embedded to silence the
         * notification
         */
        private Notification.Action createDoNotShowAgainAction(Context context) {
            final PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    context,
                    0,
                    new Intent(ACTION_NEVER_ASK_AGAIN),
                    PendingIntent.FLAG_IMMUTABLE);
            return new Notification.Action.Builder(null, "Do Not Show Again",
                    pendingIntent).build();
        }
    }

    /**
     * This receiver listens to notification actions and can be utilized to do things like silence
     * a notification that is spammy.
     */
    public class NotificationActionReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(ACTION_NEVER_ASK_AGAIN)) {
                Rlog.i(LOG_TAG, "NotificationActionReceiver: ACTION_NEVER_ASK_AGAIN");
                // insert a key to silence future notifications
                SharedPreferences.Editor editor =
                        PreferenceManager.getDefaultSharedPreferences(context).edit();
                editor.putBoolean(ACTION_NEVER_ASK_AGAIN, true);
                editor.apply();
                // Note: If another action is added, unregistering here should be removed. However,
                // since there is no longer a reason to broadcasts, cleanup mActionReceiver.
                context.unregisterReceiver(mActionReceiver);
            }
        }
    }
}
