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.os.RemoteException;
24 import android.os.ServiceManager;
25 import android.os.UserHandle;
26 import android.os.UserManager;
27 import android.preference.PreferenceManager;
28 import android.provider.Telephony;
29 import android.telephony.CellBroadcastMessage;
30 import android.telephony.ServiceState;
31 import android.telephony.SubscriptionManager;
32 import android.telephony.TelephonyManager;
33 import android.telephony.cdma.CdmaSmsCbProgramData;
34 import android.util.Log;
35 
36 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
37 import com.android.internal.telephony.TelephonyIntents;
38 
39 public class CellBroadcastReceiver extends BroadcastReceiver {
40     private static final String TAG = "CellBroadcastReceiver";
41     static final boolean DBG = false;    // STOPSHIP: change to false before ship
42     private static int mServiceState = -1;
43 
44     public static final String CELLBROADCAST_START_CONFIG_ACTION =
45             "android.cellbroadcastreceiver.START_CONFIG";
46 
47     @Override
onReceive(Context context, Intent intent)48     public void onReceive(Context context, Intent intent) {
49         onReceiveWithPrivilege(context, intent, false);
50     }
51 
onReceiveWithPrivilege(Context context, Intent intent, boolean privileged)52     protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
53         if (DBG) log("onReceive " + intent);
54 
55         String action = intent.getAction();
56 
57         if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
58             if (DBG) log("Intent: " + action);
59             ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
60             if (serviceState != null) {
61                 int newState = serviceState.getState();
62                 if (newState != mServiceState) {
63                     Log.d(TAG, "Service state changed! " + newState + " Full: " + serviceState +
64                             " Current state=" + mServiceState);
65                     mServiceState = newState;
66                     if (((newState == ServiceState.STATE_IN_SERVICE) ||
67                             (newState == ServiceState.STATE_EMERGENCY_ONLY)) &&
68                             (UserManager.get(context).isSystemUser())) {
69                         startConfigService(context.getApplicationContext());
70                     }
71                 }
72             }
73         } else if (TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED.equals(action) ||
74                 CELLBROADCAST_START_CONFIG_ACTION.equals(action)) {
75             // Todo: Add the service state check once the new get service state API is done.
76             // Do not rely on mServiceState as it gets reset to -1 time to time because
77             // the process of CellBroadcastReceiver gets killed every time once the job is done.
78             if (UserManager.get(context).isSystemUser()) {
79                 startConfigService(context.getApplicationContext());
80             }
81             else {
82                 Log.e(TAG, "Not system user. Ignored the intent " + action);
83             }
84         } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
85                 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
86             // If 'privileged' is false, it means that the intent was delivered to the base
87             // no-permissions receiver class.  If we get an SMS_CB_RECEIVED message that way, it
88             // means someone has tried to spoof the message by delivering it outside the normal
89             // permission-checked route, so we just ignore it.
90             if (privileged) {
91                 intent.setClass(context, CellBroadcastAlertService.class);
92                 context.startService(intent);
93             } else {
94                 loge("ignoring unprivileged action received " + action);
95             }
96         } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION
97                 .equals(action)) {
98             if (privileged) {
99                 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[])
100                         intent.getParcelableArrayExtra("program_data_list");
101                 if (programDataList != null) {
102                     handleCdmaSmsCbProgramData(context, programDataList);
103                 } else {
104                     loge("SCPD intent received with no program_data_list");
105                 }
106             } else {
107                 loge("ignoring unprivileged action received " + action);
108             }
109         } else {
110             Log.w(TAG, "onReceive() unexpected action " + action);
111         }
112     }
113 
114     /**
115      * Handle Service Category Program Data message.
116      * TODO: Send Service Category Program Results response message to sender
117      *
118      * @param context
119      * @param programDataList
120      */
handleCdmaSmsCbProgramData(Context context, CdmaSmsCbProgramData[] programDataList)121     private void handleCdmaSmsCbProgramData(Context context,
122                                             CdmaSmsCbProgramData[] programDataList) {
123         for (CdmaSmsCbProgramData programData : programDataList) {
124             switch (programData.getOperation()) {
125                 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY:
126                     tryCdmaSetCategory(context, programData.getCategory(), true);
127                     break;
128 
129                 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY:
130                     tryCdmaSetCategory(context, programData.getCategory(), false);
131                     break;
132 
133                 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES:
134                     tryCdmaSetCategory(context,
135                             SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false);
136                     tryCdmaSetCategory(context,
137                             SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false);
138                     tryCdmaSetCategory(context,
139                             SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false);
140                     tryCdmaSetCategory(context,
141                             SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false);
142                     break;
143 
144                 default:
145                     loge("Ignoring unknown SCPD operation " + programData.getOperation());
146             }
147         }
148     }
149 
tryCdmaSetCategory(Context context, int category, boolean enable)150     private void tryCdmaSetCategory(Context context, int category, boolean enable) {
151         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
152 
153         switch (category) {
154             case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
155                 sharedPrefs.edit().putBoolean(
156                         CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable)
157                         .apply();
158                 break;
159 
160             case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
161                 sharedPrefs.edit().putBoolean(
162                         CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable)
163                         .apply();
164                 break;
165 
166             case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
167                 sharedPrefs.edit().putBoolean(
168                         CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply();
169                 break;
170 
171             case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
172                 sharedPrefs.edit().putBoolean(
173                         CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply();
174                 break;
175 
176             default:
177                 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable")
178                         + " alerts in category " + category);
179         }
180     }
181 
182     /**
183      * Tell {@link CellBroadcastConfigService} to enable the CB channels.
184      * @param context the broadcast receiver context
185      */
startConfigService(Context context)186     static void startConfigService(Context context) {
187         Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS,
188                 null, context, CellBroadcastConfigService.class);
189         Log.d(TAG, "Start Cell Broadcast configuration.");
190         context.startService(serviceIntent);
191     }
192 
193     /**
194      * @return true if the phone is a CDMA phone type
195      */
phoneIsCdma()196     static boolean phoneIsCdma() {
197         boolean isCdma = false;
198 
199         int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
200         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
201             subId = SubscriptionManager.getDefaultSubscriptionId();
202         }
203 
204         TelephonyManager tm = TelephonyManager.getDefault();
205         if (tm != null) {
206             isCdma = (tm.getCurrentPhoneType(subId) == TelephonyManager.PHONE_TYPE_CDMA);
207         }
208         return isCdma;
209     }
210 
log(String msg)211     private static void log(String msg) {
212         Log.d(TAG, msg);
213     }
214 
loge(String msg)215     private static void loge(String msg) {
216         Log.e(TAG, msg);
217     }
218 }
219