/* * Copyright (C) 2011 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.cellbroadcastreceiver; import static com.android.cellbroadcastreceiver.CellBroadcastReceiver.VDBG; import android.app.IntentService; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.preference.PreferenceManager; import android.telephony.SmsManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.util.Log; import androidx.annotation.NonNull; import com.android.cellbroadcastreceiver.CellBroadcastChannelManager.CellBroadcastChannelRange; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * This service manages enabling and disabling ranges of message identifiers * that the radio should listen for. It operates independently of the other * services and runs at boot time and after exiting airplane mode. * * Note that the entire range of emergency channels is enabled. Test messages * and lower priority broadcasts are filtered out in CellBroadcastAlertService * if the user has not enabled them in settings. * * TODO: add notification to re-enable channels after a radio reset. */ public class CellBroadcastConfigService extends IntentService { private static final String TAG = "CellBroadcastConfigService"; @VisibleForTesting public static final String ACTION_ENABLE_CHANNELS = "ACTION_ENABLE_CHANNELS"; public CellBroadcastConfigService() { super(TAG); // use class name for worker thread name } @Override protected void onHandleIntent(Intent intent) { if (ACTION_ENABLE_CHANNELS.equals(intent.getAction())) { try { SubscriptionManager subManager = (SubscriptionManager) getApplicationContext() .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); if (subManager != null) { // Retrieve all the active subscription indice and enable cell broadcast // messages on all subs. The duplication detection will be done at the // frameworks. int[] subIds = getActiveSubIdList(subManager); if (subIds.length != 0) { for (int subId : subIds) { log("Enable CellBroadcast on sub " + subId); enableCellBroadcastChannels(subId); } } else { // For no sim scenario. enableCellBroadcastChannels(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); } } } catch (Exception ex) { Log.e(TAG, "exception enabling cell broadcast channels", ex); } } } @NonNull private int[] getActiveSubIdList(SubscriptionManager subMgr) { List subInfos = subMgr.getActiveSubscriptionInfoList(); int size = subInfos != null ? subInfos.size() : 0; int[] subIds = new int[size]; for (int i = 0; i < size; i++) { subIds[i] = subInfos.get(i).getSubscriptionId(); } return subIds; } /** * Enable cell broadcast messages channels. Messages can be only received on the * enabled channels. * * @param subId Subscription index */ @VisibleForTesting public void enableCellBroadcastChannels(int subId) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); Resources res = CellBroadcastSettings.getResources(this, subId); // boolean for each user preference checkbox, true for checked, false for unchecked // Note: If enableAlertsMasterToggle is false, it disables ALL emergency broadcasts // except for CMAS presidential. i.e. to receive CMAS severe alerts, both // enableAlertsMasterToggle AND enableCmasSevereAlerts must be true. boolean enableAlertsMasterToggle = prefs.getBoolean( CellBroadcastSettings.KEY_ENABLE_ALERTS_MASTER_TOGGLE, true); boolean enableEtwsAlerts = enableAlertsMasterToggle; // CMAS Presidential must be always on (See 3GPP TS 22.268 Section 6.2) regardless // user's preference boolean enablePresidential = true; boolean enableCmasExtremeAlerts = enableAlertsMasterToggle && prefs.getBoolean( CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, true); boolean enableCmasSevereAlerts = enableAlertsMasterToggle && prefs.getBoolean( CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, true); boolean enableCmasAmberAlerts = enableAlertsMasterToggle && prefs.getBoolean( CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, true); boolean enableTestAlerts = enableAlertsMasterToggle && CellBroadcastSettings.isTestAlertsToggleVisible(getApplicationContext()) && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_TEST_ALERTS, false); boolean enableAreaUpdateInfoAlerts = res.getBoolean( R.bool.config_showAreaUpdateInfoSettings) && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_AREA_UPDATE_INFO_ALERTS, false); boolean enablePublicSafetyMessagesChannelAlerts = enableAlertsMasterToggle && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_PUBLIC_SAFETY_MESSAGES, true); boolean enableStateLocalTestAlerts = enableAlertsMasterToggle && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_STATE_LOCAL_TEST_ALERTS, false); boolean enableEmergencyAlerts = enableAlertsMasterToggle && prefs.getBoolean( CellBroadcastSettings.KEY_ENABLE_EMERGENCY_ALERTS, true); boolean enableGeoFencingTriggerMessage = true; if (VDBG) { log("enableAlertsMasterToggle = " + enableAlertsMasterToggle); log("enableEtwsAlerts = " + enableEtwsAlerts); log("enablePresidential = " + enablePresidential); log("enableCmasExtremeAlerts = " + enableCmasExtremeAlerts); log("enableCmasSevereAlerts = " + enableCmasExtremeAlerts); log("enableCmasAmberAlerts = " + enableCmasAmberAlerts); log("enableTestAlerts = " + enableTestAlerts); log("enableAreaUpdateInfoAlerts = " + enableAreaUpdateInfoAlerts); log("enablePublicSafetyMessagesChannelAlerts = " + enablePublicSafetyMessagesChannelAlerts); log("enableStateLocalTestAlerts = " + enableStateLocalTestAlerts); log("enableEmergencyAlerts = " + enableEmergencyAlerts); log("enableGeoFencingTriggerMessage = " + enableGeoFencingTriggerMessage); } CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager( getApplicationContext(), subId); /** Enable CMAS series messages. */ // Enable/Disable Presidential messages. setCellBroadcastRange(subId, enablePresidential, channelManager.getCellBroadcastChannelRanges( R.array.cmas_presidential_alerts_channels_range_strings)); // Enable/Disable CMAS extreme messages. setCellBroadcastRange(subId, enableCmasExtremeAlerts, channelManager.getCellBroadcastChannelRanges( R.array.cmas_alert_extreme_channels_range_strings)); // Enable/Disable CMAS severe messages. setCellBroadcastRange(subId, enableCmasSevereAlerts, channelManager.getCellBroadcastChannelRanges( R.array.cmas_alerts_severe_range_strings)); // Enable/Disable CMAS amber alert messages. setCellBroadcastRange(subId, enableCmasAmberAlerts, channelManager.getCellBroadcastChannelRanges( R.array.cmas_amber_alerts_channels_range_strings)); // Enable/Disable test messages. setCellBroadcastRange(subId, enableTestAlerts, channelManager.getCellBroadcastChannelRanges( R.array.required_monthly_test_range_strings)); // Exercise is part of test toggle with monthly test and operator defined. some carriers // mandate to show test settings in UI but always enable exercise alert. setCellBroadcastRange(subId, enableTestAlerts || res.getBoolean(R.bool.always_enable_exercise_alert), channelManager.getCellBroadcastChannelRanges( R.array.exercise_alert_range_strings)); setCellBroadcastRange(subId, enableTestAlerts, channelManager.getCellBroadcastChannelRanges( R.array.operator_defined_alert_range_strings)); // Enable/Disable GSM ETWS messages. setCellBroadcastRange(subId, enableEtwsAlerts, channelManager.getCellBroadcastChannelRanges( R.array.etws_alerts_range_strings)); // Enable/Disable GSM ETWS test messages. setCellBroadcastRange(subId, enableTestAlerts, channelManager.getCellBroadcastChannelRanges( R.array.etws_test_alerts_range_strings)); // Enable/Disable GSM public safety messages. setCellBroadcastRange(subId, enablePublicSafetyMessagesChannelAlerts, channelManager.getCellBroadcastChannelRanges( R.array.public_safety_messages_channels_range_strings)); // Enable/Disable GSM state/local test alerts. setCellBroadcastRange(subId, enableStateLocalTestAlerts, channelManager.getCellBroadcastChannelRanges( R.array.state_local_test_alert_range_strings)); // Enable/Disable GSM geo-fencing trigger messages. setCellBroadcastRange(subId, enableGeoFencingTriggerMessage, channelManager.getCellBroadcastChannelRanges( R.array.geo_fencing_trigger_messages_range_strings)); // Enable non-CMAS series messages. setCellBroadcastRange(subId, enableEmergencyAlerts, channelManager.getCellBroadcastChannelRanges( R.array.emergency_alerts_channels_range_strings)); // Enable/Disable additional channels based on carrier specific requirement. ArrayList ranges = channelManager.getCellBroadcastChannelRanges( R.array.additional_cbs_channels_strings); for (CellBroadcastChannelRange range: ranges) { boolean enableAlerts; switch (range.mAlertType) { case AREA: enableAlerts = enableAreaUpdateInfoAlerts; break; case TEST: enableAlerts = enableTestAlerts; break; default: enableAlerts = enableAlertsMasterToggle; } setCellBroadcastRange(subId, enableAlerts, new ArrayList<>(Arrays.asList(range))); } } /** * Enable/disable cell broadcast with messages id range * @param subId Subscription index * @param enable True for enabling cell broadcast with id range, otherwise for disabling. * @param ranges Cell broadcast id ranges */ private void setCellBroadcastRange(int subId, boolean enable, List ranges) { SmsManager manager; if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { manager = SmsManager.getSmsManagerForSubscriptionId(subId); } else { manager = SmsManager.getDefault(); } if (ranges != null) { for (CellBroadcastChannelRange range: ranges) { if (enable) { manager.enableCellBroadcastRange(range.mStartId, range.mEndId, range.mRanType); } else { manager.disableCellBroadcastRange(range.mStartId, range.mEndId, range.mRanType); } } } } private static void log(String msg) { Log.d(TAG, msg); } }