blob: d776b8a5c77e59040ea10b1952fe2e59f48d9963 [file] [log] [blame]
/*
* 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 android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.provider.Telephony;
import android.telephony.CellBroadcastMessage;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.util.Log;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.uicc.IccCardProxy;
import java.util.List;
public class CellBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "CellBroadcastReceiver";
static final boolean DBG = true; // STOPSHIP: change to false before ship
private int[] mServiceState = null;
private static final String GET_LATEST_CB_AREA_INFO_ACTION =
"android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO";
@Override
public void onReceive(Context context, Intent intent) {
onReceiveWithPrivilege(context, intent, false);
}
protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
if (DBG) log("onReceive " + intent);
String action = intent.getAction();
if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
if (DBG) log("Intent ACTION_SERVICE_STATE_CHANGED");
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
Log.d(TAG, "subscriptionId = " + subId);
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
return;
}
ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
int newState = serviceState.getState();
SubscriptionInfo subInfo = SubscriptionManager.from(context).
getActiveSubscriptionInfo(subId);
if (subInfo == null) {
loge("subId is not active:" + subId);
return;
}
int slotId = subInfo.getSimSlotIndex();
if (mServiceState == null) {
int phoneCount = TelephonyManager.getDefault().getPhoneCount();
mServiceState = new int[phoneCount];
for (int i = 0; i < phoneCount; i++) {
mServiceState[i] = ServiceState.STATE_OUT_OF_SERVICE;
}
}
if (newState != mServiceState[slotId]) {
Log.d(TAG, "Service state changed! " + newState + " Full: " + serviceState +
" Current state=" + mServiceState[slotId]);
mServiceState[slotId] = newState;
if (((newState == ServiceState.STATE_IN_SERVICE) ||
(newState == ServiceState.STATE_EMERGENCY_ONLY)) &&
(UserHandle.myUserId() == UserHandle.USER_OWNER)) {
startConfigService(context.getApplicationContext(), subId);
}
}
} else if (IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED.equals(action)){
String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
List<SubscriptionInfo> subscriptionInfoList = SubscriptionManager.from(
context).getActiveSubscriptionInfoList();
if (subscriptionInfoList != null) {
for (SubscriptionInfo subInfo : subscriptionInfoList) {
startConfigService(context, subInfo.getSubscriptionId());
}
}
}
} else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
// If 'privileged' is false, it means that the intent was delivered to the base
// no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it
// means someone has tried to spoof the message by delivering it outside the normal
// permission-checked route, so we just ignore it.
if (privileged) {
intent.setClass(context, CellBroadcastAlertService.class);
context.startService(intent);
} else {
loge("ignoring unprivileged action received " + action);
}
} else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION
.equals(action)) {
if (privileged) {
CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[])
intent.getParcelableArrayExtra("program_data_list");
if (programDataList != null) {
int subId = intent.getExtras().getInt(PhoneConstants.SUBSCRIPTION_KEY);
Log.d(TAG, "subscriptionId = " + subId);
handleCdmaSmsCbProgramData(context, programDataList, subId);
} else {
loge("SCPD intent received with no program_data_list");
}
} else {
loge("ignoring unprivileged action received " + action);
}
} else if (GET_LATEST_CB_AREA_INFO_ACTION.equals(action)) {
if (privileged) {
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.getDefaultSmsSubId());
CellBroadcastMessage message = CellBroadcastReceiverApp.getLatestAreaInfo(subId);
Log.d(TAG, "onReceive GET_LATEST_CB_AREA_INFO_ACTION subId :" + subId
+ "message :" + message);
Intent areaInfoIntent = new Intent(
CellBroadcastAlertService.CB_AREA_INFO_RECEIVED_ACTION);
areaInfoIntent.putExtra("message", message);
// Send broadcast twice, once for apps that have PRIVILEGED permission and once
// for those that have the runtime one
context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL,
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL,
android.Manifest.permission.READ_PHONE_STATE);
} else {
Log.e(TAG, "caller missing READ_PHONE_STATE permission, returning");
}
} else {
Log.w(TAG, "onReceive() unexpected action " + action);
}
}
/**
* Handle Service Category Program Data message.
* TODO: Send Service Category Program Results response message to sender
*
* @param context
* @param programDataList
*/
private void handleCdmaSmsCbProgramData(Context context,
CdmaSmsCbProgramData[] programDataList, int subId) {
for (CdmaSmsCbProgramData programData : programDataList) {
switch (programData.getOperation()) {
case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY:
tryCdmaSetCategory(context, programData.getCategory(), true, subId);
break;
case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY:
tryCdmaSetCategory(context, programData.getCategory(), false, subId);
break;
case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES:
tryCdmaSetCategory(context,
SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false, subId);
tryCdmaSetCategory(context,
SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false, subId);
tryCdmaSetCategory(context,
SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false,
subId);
tryCdmaSetCategory(context,
SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false, subId);
break;
default:
loge("Ignoring unknown SCPD operation " + programData.getOperation());
}
}
}
private void tryCdmaSetCategory(Context context, int category, boolean enable, int subId) {
switch (category) {
case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
SubscriptionManager.setSubscriptionProperty(subId,
SubscriptionManager.CB_EXTREME_THREAT_ALERT,
(enable ? "1" : "0"));
break;
case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
SubscriptionManager.setSubscriptionProperty(subId,
SubscriptionManager.CB_SEVERE_THREAT_ALERT,
(enable ? "1" : "0"));
break;
case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
SubscriptionManager.setSubscriptionProperty(subId,
SubscriptionManager.CB_AMBER_ALERT,
(enable ? "1" : "0"));
break;
case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
SubscriptionManager.setSubscriptionProperty(subId,
SubscriptionManager.CB_CMAS_TEST_ALERT,
(enable ? "1" : "0"));
break;
default:
Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable")
+ " alerts in category " + category);
}
}
/**
* Tell {@link CellBroadcastConfigService} to enable the CB channels.
* @param context the broadcast receiver context
*/
static void startConfigService(Context context, int subId) {
Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS,
null, context, CellBroadcastConfigService.class);
serviceIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
context.startService(serviceIntent);
}
/**
* @return true if the phone is a CDMA phone type
*/
static boolean phoneIsCdma(int subId) {
boolean isCdma = false;
try {
ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
if (phone != null) {
isCdma = (phone.getActivePhoneTypeForSubscriber(subId) ==
TelephonyManager.PHONE_TYPE_CDMA);
}
} catch (RemoteException e) {
Log.w(TAG, "phone.getActivePhoneType() failed", e);
}
return isCdma;
}
private static void log(String msg) {
Log.d(TAG, msg);
}
private static void loge(String msg) {
Log.e(TAG, msg);
}
}