1 /*
2  * Copyright (C) 2011 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.cellbroadcastreceiver;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.SharedPreferences;
23 import android.content.SharedPreferences.Editor;
24 import android.os.UserManager;
25 import android.preference.PreferenceManager;
26 import android.provider.Telephony;
27 import android.provider.Telephony.CellBroadcasts;
28 import android.telephony.CarrierConfigManager;
29 import android.telephony.cdma.CdmaSmsCbProgramData;
30 import android.util.Log;
31 
32 import com.android.internal.telephony.TelephonyIntents;
33 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
34 
35 public class CellBroadcastReceiver extends BroadcastReceiver {
36     private static final String TAG = "CellBroadcastReceiver";
37     static final boolean DBG = true;
38     static final boolean VDBG = false;    // STOPSHIP: change to false before ship
39 
40     // Key to access the stored reminder interval default value
41     private static final String CURRENT_INTERVAL_DEFAULT = "current_interval_default";
42 
43     // Intent actions and extras
44     public static final String CELLBROADCAST_START_CONFIG_ACTION =
45             "com.android.cellbroadcastreceiver.intent.START_CONFIG";
46     public static final String ACTION_MARK_AS_READ =
47             "com.android.cellbroadcastreceiver.intent.action.MARK_AS_READ";
48     public static final String EXTRA_DELIVERY_TIME =
49             "com.android.cellbroadcastreceiver.intent.extra.ID";
50 
51     @Override
onReceive(Context context, Intent intent)52     public void onReceive(Context context, Intent intent) {
53         onReceiveWithPrivilege(context, intent, false);
54     }
55 
onReceiveWithPrivilege(Context context, Intent intent, boolean privileged)56     protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
57         if (DBG) log("onReceive " + intent);
58 
59         String action = intent.getAction();
60 
61         if (ACTION_MARK_AS_READ.equals(action)) {
62             final long deliveryTime = intent.getLongExtra(EXTRA_DELIVERY_TIME, -1);
63             new CellBroadcastContentProvider.AsyncCellBroadcastTask(context.getContentResolver())
64                     .execute(new CellBroadcastContentProvider.CellBroadcastOperation() {
65                         @Override
66                         public boolean execute(CellBroadcastContentProvider provider) {
67                             return provider.markBroadcastRead(CellBroadcasts.DELIVERY_TIME,
68                                     deliveryTime);
69                         }
70                     });
71         } else if (TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED.equals(action)
72                 || CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
73                 || CELLBROADCAST_START_CONFIG_ACTION.equals(action)) {
74             // Todo: Add the service state check once the new get service state API is done.
75             // Do not rely on mServiceState as it gets reset to -1 time to time because
76             // the process of CellBroadcastReceiver gets killed every time once the job is done.
77             if (UserManager.get(context).isSystemUser()) {
78                 startConfigService(context.getApplicationContext());
79 
80                 // Whenever carrier changes, we need to adjust the emergency alert
81                 // reminder interval list because it might change since different
82                 // countries/carriers might have different interval settings.
83                 adjustReminderInterval(context);
84             }
85             else {
86                 Log.e(TAG, "Not system user. Ignored the intent " + action);
87             }
88         } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
89                 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
90             // If 'privileged' is false, it means that the intent was delivered to the base
91             // no-permissions receiver class.  If we get an SMS_CB_RECEIVED message that way, it
92             // means someone has tried to spoof the message by delivering it outside the normal
93             // permission-checked route, so we just ignore it.
94             if (privileged) {
95                 intent.setClass(context, CellBroadcastAlertService.class);
96                 context.startService(intent);
97             } else {
98                 loge("ignoring unprivileged action received " + action);
99             }
100         } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION
101                 .equals(action)) {
102             if (privileged) {
103                 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[])
104                         intent.getParcelableArrayExtra("program_data_list");
105                 if (programDataList != null) {
106                     handleCdmaSmsCbProgramData(context, programDataList);
107                 } else {
108                     loge("SCPD intent received with no program_data_list");
109                 }
110             } else {
111                 loge("ignoring unprivileged action received " + action);
112             }
113         } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
114             // rename registered notification channels on locale change
115             CellBroadcastAlertService.createNotificationChannels(context);
116         } else {
117             Log.w(TAG, "onReceive() unexpected action " + action);
118         }
119     }
120 
adjustReminderInterval(Context context)121     private void adjustReminderInterval(Context context) {
122         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
123         String currentIntervalDefault = sp.getString(CURRENT_INTERVAL_DEFAULT, "0");
124 
125         // If interval default changes, reset the interval to the new default value.
126         String newIntervalDefault = context.getResources().getString(
127                 R.string.alert_reminder_interval_default_value);
128         if (!newIntervalDefault.equals(currentIntervalDefault)) {
129             Log.d(TAG, "Default interval changed from " + currentIntervalDefault + " to " +
130                     newIntervalDefault);
131 
132             Editor editor = sp.edit();
133             // Reset the value to default.
134             editor.putString(
135                     CellBroadcastSettings.KEY_ALERT_REMINDER_INTERVAL, newIntervalDefault);
136             // Save the new default value.
137             editor.putString(CURRENT_INTERVAL_DEFAULT, newIntervalDefault);
138             editor.commit();
139         } else {
140             if (DBG) Log.d(TAG, "Default interval " + currentIntervalDefault + " did not change.");
141         }
142     }
143 
144     /**
145      * Handle Service Category Program Data message.
146      * TODO: Send Service Category Program Results response message to sender
147      *
148      * @param context
149      * @param programDataList
150      */
handleCdmaSmsCbProgramData(Context context, CdmaSmsCbProgramData[] programDataList)151     private void handleCdmaSmsCbProgramData(Context context,
152                                             CdmaSmsCbProgramData[] programDataList) {
153         for (CdmaSmsCbProgramData programData : programDataList) {
154             switch (programData.getOperation()) {
155                 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY:
156                     tryCdmaSetCategory(context, programData.getCategory(), true);
157                     break;
158 
159                 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY:
160                     tryCdmaSetCategory(context, programData.getCategory(), false);
161                     break;
162 
163                 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES:
164                     tryCdmaSetCategory(context,
165                             SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false);
166                     tryCdmaSetCategory(context,
167                             SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false);
168                     tryCdmaSetCategory(context,
169                             SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false);
170                     tryCdmaSetCategory(context,
171                             SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false);
172                     break;
173 
174                 default:
175                     loge("Ignoring unknown SCPD operation " + programData.getOperation());
176             }
177         }
178     }
179 
tryCdmaSetCategory(Context context, int category, boolean enable)180     private void tryCdmaSetCategory(Context context, int category, boolean enable) {
181         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
182 
183         switch (category) {
184             case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
185                 sharedPrefs.edit().putBoolean(
186                         CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable)
187                         .apply();
188                 break;
189 
190             case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
191                 sharedPrefs.edit().putBoolean(
192                         CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable)
193                         .apply();
194                 break;
195 
196             case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
197                 sharedPrefs.edit().putBoolean(
198                         CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply();
199                 break;
200 
201             case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
202                 sharedPrefs.edit().putBoolean(
203                         CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply();
204                 break;
205 
206             default:
207                 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable")
208                         + " alerts in category " + category);
209         }
210     }
211 
212     /**
213      * Tell {@link CellBroadcastConfigService} to enable the CB channels.
214      * @param context the broadcast receiver context
215      */
startConfigService(Context context)216     static void startConfigService(Context context) {
217         Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS,
218                 null, context, CellBroadcastConfigService.class);
219         Log.d(TAG, "Start Cell Broadcast configuration.");
220         context.startService(serviceIntent);
221     }
222 
log(String msg)223     private static void log(String msg) {
224         Log.d(TAG, msg);
225     }
226 
loge(String msg)227     private static void loge(String msg) {
228         Log.e(TAG, msg);
229     }
230 }
231