• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.pm.PackageManager;
24 import android.os.RemoteException;
25 import android.os.ServiceManager;
26 import android.os.UserHandle;
27 import android.preference.PreferenceManager;
28 import android.provider.Telephony;
29 import android.telephony.CellBroadcastMessage;
30 import android.telephony.PhoneStateListener;
31 import android.telephony.ServiceState;
32 import android.telephony.TelephonyManager;
33 import android.telephony.cdma.CdmaSmsCbProgramData;
34 import android.util.Log;
35 
36 import com.android.internal.telephony.ITelephony;
37 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
38 
39 public class CellBroadcastReceiver extends BroadcastReceiver {
40     private static final String TAG = "CellBroadcastReceiver";
41     static final boolean DBG = true;    // STOPSHIP: change to false before ship
42 
43     private static final String GET_LATEST_CB_AREA_INFO_ACTION =
44             "android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO";
45 
46     @Override
onReceive(Context context, Intent intent)47     public void onReceive(Context context, Intent intent) {
48         onReceiveWithPrivilege(context, intent, false);
49     }
50 
onReceiveWithPrivilege(Context context, Intent intent, boolean privileged)51     protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
52         if (DBG) log("onReceive " + intent);
53 
54         String action = intent.getAction();
55 
56         if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
57             if (DBG) log("Registering for ServiceState updates");
58             TelephonyManager tm = (TelephonyManager) context.getSystemService(
59                     Context.TELEPHONY_SERVICE);
60             tm.listen(new ServiceStateListener(context.getApplicationContext()),
61                     PhoneStateListener.LISTEN_SERVICE_STATE);
62         } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
63             boolean airplaneModeOn = intent.getBooleanExtra("state", false);
64             if (DBG) log("airplaneModeOn: " + airplaneModeOn);
65             if (!airplaneModeOn) {
66                 startConfigService(context);
67             }
68         } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
69                 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
70             // If 'privileged' is false, it means that the intent was delivered to the base
71             // no-permissions receiver class.  If we get an SMS_CB_RECEIVED message that way, it
72             // means someone has tried to spoof the message by delivering it outside the normal
73             // permission-checked route, so we just ignore it.
74             if (privileged) {
75                 intent.setClass(context, CellBroadcastAlertService.class);
76                 context.startService(intent);
77             } else {
78                 loge("ignoring unprivileged action received " + action);
79             }
80         } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION
81                 .equals(action)) {
82             if (privileged) {
83                 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[])
84                         intent.getParcelableArrayExtra("program_data_list");
85                 if (programDataList != null) {
86                     handleCdmaSmsCbProgramData(context, programDataList);
87                 } else {
88                     loge("SCPD intent received with no program_data_list");
89                 }
90             } else {
91                 loge("ignoring unprivileged action received " + action);
92             }
93         } else if (GET_LATEST_CB_AREA_INFO_ACTION.equals(action)) {
94             if (privileged) {
95                 CellBroadcastMessage message = CellBroadcastReceiverApp.getLatestAreaInfo();
96                 if (message != null) {
97                     Intent areaInfoIntent = new Intent(
98                             CellBroadcastAlertService.CB_AREA_INFO_RECEIVED_ACTION);
99                     areaInfoIntent.putExtra("message", message);
100                     context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL,
101                             android.Manifest.permission.READ_PHONE_STATE);
102                 }
103             } else {
104                 Log.e(TAG, "caller missing READ_PHONE_STATE permission, returning");
105             }
106         } else {
107             Log.w(TAG, "onReceive() unexpected action " + action);
108         }
109     }
110 
111     /**
112      * Handle Service Category Program Data message.
113      * TODO: Send Service Category Program Results response message to sender
114      *
115      * @param context
116      * @param programDataList
117      */
handleCdmaSmsCbProgramData(Context context, CdmaSmsCbProgramData[] programDataList)118     private void handleCdmaSmsCbProgramData(Context context,
119             CdmaSmsCbProgramData[] programDataList) {
120         for (CdmaSmsCbProgramData programData : programDataList) {
121             switch (programData.getOperation()) {
122                 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY:
123                     tryCdmaSetCategory(context, programData.getCategory(), true);
124                     break;
125 
126                 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY:
127                     tryCdmaSetCategory(context, programData.getCategory(), false);
128                     break;
129 
130                 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES:
131                     tryCdmaSetCategory(context,
132                             SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false);
133                     tryCdmaSetCategory(context,
134                             SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false);
135                     tryCdmaSetCategory(context,
136                             SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false);
137                     tryCdmaSetCategory(context,
138                             SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false);
139                     break;
140 
141                 default:
142                     loge("Ignoring unknown SCPD operation " + programData.getOperation());
143             }
144         }
145     }
146 
tryCdmaSetCategory(Context context, int category, boolean enable)147     private void tryCdmaSetCategory(Context context, int category, boolean enable) {
148         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
149 
150         switch (category) {
151             case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
152                 sharedPrefs.edit().putBoolean(
153                         CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable)
154                         .apply();
155                 break;
156 
157             case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
158                 sharedPrefs.edit().putBoolean(
159                         CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable)
160                         .apply();
161                 break;
162 
163             case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
164                 sharedPrefs.edit().putBoolean(
165                         CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply();
166                 break;
167 
168             case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
169                 sharedPrefs.edit().putBoolean(
170                         CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply();
171                 break;
172 
173             default:
174                 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable")
175                         + " alerts in category " + category);
176         }
177     }
178 
179     /**
180      * Tell {@link CellBroadcastConfigService} to enable the CB channels.
181      * @param context the broadcast receiver context
182      */
startConfigService(Context context)183     static void startConfigService(Context context) {
184         Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS,
185                 null, context, CellBroadcastConfigService.class);
186         context.startService(serviceIntent);
187     }
188 
189     /**
190      * @return true if the phone is a CDMA phone type
191      */
phoneIsCdma()192     static boolean phoneIsCdma() {
193         boolean isCdma = false;
194         try {
195             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
196             if (phone != null) {
197                 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
198             }
199         } catch (RemoteException e) {
200             Log.w(TAG, "phone.getActivePhoneType() failed", e);
201         }
202         return isCdma;
203     }
204 
205     private static class ServiceStateListener extends PhoneStateListener {
206         private final Context mContext;
207         private int mServiceState = -1;
208 
ServiceStateListener(Context context)209         ServiceStateListener(Context context) {
210             mContext = context;
211         }
212 
213         @Override
onServiceStateChanged(ServiceState ss)214         public void onServiceStateChanged(ServiceState ss) {
215             int newState = ss.getState();
216             if (newState != mServiceState) {
217                 Log.d(TAG, "Service state changed! " + newState + " Full: " + ss);
218                 mServiceState = newState;
219                 if (newState == ServiceState.STATE_IN_SERVICE ||
220                         newState == ServiceState.STATE_EMERGENCY_ONLY) {
221                     startConfigService(mContext);
222                 }
223             }
224         }
225     }
226 
log(String msg)227     private static void log(String msg) {
228         Log.d(TAG, msg);
229     }
230 
loge(String msg)231     private static void loge(String msg) {
232         Log.e(TAG, msg);
233     }
234 }
235