746 lines
29 KiB
Java
746 lines
29 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|
|
}
|