• 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.Bundle;
25 import android.os.RemoteException;
26 import android.os.ServiceManager;
27 import android.os.UserHandle;
28 import android.preference.PreferenceManager;
29 import android.provider.Telephony;
30 import android.telephony.CellBroadcastMessage;
31 import android.telephony.ServiceState;
32 import android.telephony.SubscriptionInfo;
33 import android.telephony.SubscriptionManager;
34 import android.telephony.TelephonyManager;
35 import android.telephony.cdma.CdmaSmsCbProgramData;
36 import android.util.Log;
37 import com.android.internal.telephony.PhoneConstants;
38 
39 import com.android.internal.telephony.ITelephony;
40 import com.android.internal.telephony.IccCardConstants;
41 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
42 import com.android.internal.telephony.TelephonyIntents;
43 import com.android.internal.telephony.uicc.IccCardProxy;
44 
45 import java.util.List;
46 
47 public class CellBroadcastReceiver extends BroadcastReceiver {
48     private static final String TAG = "CellBroadcastReceiver";
49     static final boolean DBG = false;    // STOPSHIP: change to false before ship
50     private static int mServiceState = -1;
51     private static final String GET_LATEST_CB_AREA_INFO_ACTION =
52             "android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO";
53 
54     @Override
onReceive(Context context, Intent intent)55     public void onReceive(Context context, Intent intent) {
56         onReceiveWithPrivilege(context, intent, false);
57     }
58 
onReceiveWithPrivilege(Context context, Intent intent, boolean privileged)59     protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
60         if (DBG) log("onReceive " + intent);
61 
62         String action = intent.getAction();
63 
64         if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
65             if (DBG) log("Intent ACTION_SERVICE_STATE_CHANGED");
66             int subId = intent.getExtras().getInt(PhoneConstants.SUBSCRIPTION_KEY);
67             Log.d(TAG, "subscriptionId = " + subId);
68             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
69                 return;
70             }
71             ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
72             int newState = serviceState.getState();
73             if (newState != mServiceState) {
74                 Log.d(TAG, "Service state changed! " + newState + " Full: " + serviceState +
75                         " Current state=" + mServiceState);
76                 mServiceState = newState;
77 
78                 if (((newState == ServiceState.STATE_IN_SERVICE) ||
79                         (newState == ServiceState.STATE_EMERGENCY_ONLY)) &&
80                         (UserHandle.myUserId() == UserHandle.USER_OWNER)) {
81                     startConfigService(context.getApplicationContext(), subId);
82                 }
83             }
84         } else if (IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED.equals(action)){
85             String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
86             if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
87                 List<SubscriptionInfo> subscriptionInfoList = SubscriptionManager.from(
88                         context).getActiveSubscriptionInfoList();
89                 if (subscriptionInfoList != null) {
90                     for (SubscriptionInfo subInfo : subscriptionInfoList) {
91                         startConfigService(context, subInfo.getSubscriptionId());
92                     }
93                 }
94             }
95         } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
96                 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
97             // If 'privileged' is false, it means that the intent was delivered to the base
98             // no-permissions receiver class.  If we get an SMS_CB_RECEIVED message that way, it
99             // means someone has tried to spoof the message by delivering it outside the normal
100             // permission-checked route, so we just ignore it.
101             if (privileged) {
102                 intent.setClass(context, CellBroadcastAlertService.class);
103                 context.startService(intent);
104             } else {
105                 loge("ignoring unprivileged action received " + action);
106             }
107         } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION
108                 .equals(action)) {
109             if (privileged) {
110                 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[])
111                         intent.getParcelableArrayExtra("program_data_list");
112                 if (programDataList != null) {
113                     int subId = intent.getExtras().getInt(PhoneConstants.SUBSCRIPTION_KEY);
114                     Log.d(TAG, "subscriptionId = " + subId);
115                     handleCdmaSmsCbProgramData(context, programDataList, subId);
116                 } else {
117                     loge("SCPD intent received with no program_data_list");
118                 }
119             } else {
120                 loge("ignoring unprivileged action received " + action);
121             }
122         } else if (GET_LATEST_CB_AREA_INFO_ACTION.equals(action)) {
123             if (privileged) {
124                 CellBroadcastMessage message = CellBroadcastReceiverApp.getLatestAreaInfo();
125                 if (message != null) {
126                     Intent areaInfoIntent = new Intent(
127                             CellBroadcastAlertService.CB_AREA_INFO_RECEIVED_ACTION);
128                     areaInfoIntent.putExtra("message", message);
129                     // Send broadcast twice, once for apps that have PRIVILEGED permission and once
130                     // for those that have the runtime one
131                     context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL,
132                             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
133                     context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL,
134                             android.Manifest.permission.READ_PHONE_STATE);
135                 }
136             } else {
137                 Log.e(TAG, "caller missing READ_PHONE_STATE permission, returning");
138             }
139         } else {
140             Log.w(TAG, "onReceive() unexpected action " + action);
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, int subId)151     private void handleCdmaSmsCbProgramData(Context context,
152             CdmaSmsCbProgramData[] programDataList, int subId) {
153         for (CdmaSmsCbProgramData programData : programDataList) {
154             switch (programData.getOperation()) {
155                 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY:
156                     tryCdmaSetCategory(context, programData.getCategory(), true, subId);
157                     break;
158 
159                 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY:
160                     tryCdmaSetCategory(context, programData.getCategory(), false, subId);
161                     break;
162 
163                 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES:
164                     tryCdmaSetCategory(context,
165                             SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false, subId);
166                     tryCdmaSetCategory(context,
167                             SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false, subId);
168                     tryCdmaSetCategory(context,
169                             SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false,
170                             subId);
171                     tryCdmaSetCategory(context,
172                             SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false, subId);
173                     break;
174 
175                 default:
176                     loge("Ignoring unknown SCPD operation " + programData.getOperation());
177             }
178         }
179     }
180 
tryCdmaSetCategory(Context context, int category, boolean enable, int subId)181     private void tryCdmaSetCategory(Context context, int category, boolean enable, int subId) {
182         switch (category) {
183             case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
184                 SubscriptionManager.setSubscriptionProperty(subId,
185                         SubscriptionManager.CB_EXTREME_THREAT_ALERT,
186                         (enable ? "1" : "0"));
187                 break;
188 
189             case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
190                 SubscriptionManager.setSubscriptionProperty(subId,
191                         SubscriptionManager.CB_SEVERE_THREAT_ALERT,
192                         (enable ? "1" : "0"));
193                 break;
194 
195             case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
196                 SubscriptionManager.setSubscriptionProperty(subId,
197                         SubscriptionManager.CB_AMBER_ALERT,
198                         (enable ? "1" : "0"));
199                 break;
200 
201             case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
202                 SubscriptionManager.setSubscriptionProperty(subId,
203                         SubscriptionManager.CB_CMAS_TEST_ALERT,
204                         (enable ? "1" : "0"));
205                 break;
206 
207             default:
208                 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable")
209                         + " alerts in category " + category);
210         }
211     }
212 
213     /**
214      * Tell {@link CellBroadcastConfigService} to enable the CB channels.
215      * @param context the broadcast receiver context
216      */
startConfigService(Context context, int subId)217     static void startConfigService(Context context, int subId) {
218         Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS,
219                 null, context, CellBroadcastConfigService.class);
220         serviceIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
221         context.startService(serviceIntent);
222     }
223 
224     /**
225      * @return true if the phone is a CDMA phone type
226      */
phoneIsCdma(int subId)227     static boolean phoneIsCdma(int subId) {
228         boolean isCdma = false;
229         try {
230             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
231             if (phone != null) {
232                 isCdma = (phone.getActivePhoneTypeForSubscriber(subId) ==
233                         TelephonyManager.PHONE_TYPE_CDMA);
234             }
235         } catch (RemoteException e) {
236             Log.w(TAG, "phone.getActivePhoneType() failed", e);
237         }
238         return isCdma;
239     }
240 
log(String msg)241     private static void log(String msg) {
242         Log.d(TAG, msg);
243     }
244 
loge(String msg)245     private static void loge(String msg) {
246         Log.e(TAG, msg);
247     }
248 }
249