blob: 54129119d342d58ac42b7599433cde020c486139 [file] [log] [blame]
/*
* Copyright (C) 2013 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.internal.telephony.cdma;
import android.Manifest;
import android.app.Activity;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.provider.Telephony.Sms.Intents;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionManager;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.telephony.cdma.CdmaSmsCbProgramResults;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.WakeLockStateMachine;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
/**
* Handle CDMA Service Category Program Data requests and responses.
*/
public final class CdmaServiceCategoryProgramHandler extends WakeLockStateMachine {
final CommandsInterface mCi;
/**
* Create a new CDMA inbound SMS handler.
*/
CdmaServiceCategoryProgramHandler(Context context, CommandsInterface commandsInterface) {
super("CdmaServiceCategoryProgramHandler", context, null);
mContext = context;
mCi = commandsInterface;
}
/**
* Create a new State machine for SCPD requests.
* @param context the context to use
* @param commandsInterface the radio commands interface
* @return the new SCPD handler
*/
static CdmaServiceCategoryProgramHandler makeScpHandler(Context context,
CommandsInterface commandsInterface) {
CdmaServiceCategoryProgramHandler handler = new CdmaServiceCategoryProgramHandler(
context, commandsInterface);
handler.start();
return handler;
}
/**
* Handle Cell Broadcast messages from {@code CdmaInboundSmsHandler}.
* 3GPP-format Cell Broadcast messages sent from radio are handled in the subclass.
*
* @param message the message to process
* @return true if an ordered broadcast was sent; false on failure
*/
@Override
protected boolean handleSmsMessage(Message message) {
if (message.obj instanceof SmsMessage) {
return handleServiceCategoryProgramData((SmsMessage) message.obj);
} else {
loge("handleMessage got object of type: " + message.obj.getClass().getName());
return false;
}
}
/**
* Send SCPD request to CellBroadcastReceiver as an ordered broadcast.
* @param sms the CDMA SmsMessage containing the SCPD request
* @return true if an ordered broadcast was sent; false on failure
*/
private boolean handleServiceCategoryProgramData(SmsMessage sms) {
ArrayList<CdmaSmsCbProgramData> programDataList = sms.getSmsCbProgramData();
if (programDataList == null) {
loge("handleServiceCategoryProgramData: program data list is null!");
return false;
}
Intent intent = new Intent(Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION);
intent.putExtra("sender", sms.getOriginatingAddress());
intent.putParcelableArrayListExtra("program_data", programDataList);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
String[] pkgs = mContext.getResources().getStringArray(
com.android.internal.R.array.config_defaultCellBroadcastReceiverPkgs);
mReceiverCount.addAndGet(pkgs.length);
for (String pkg : pkgs) {
intent.setPackage(pkg);
mContext.sendOrderedBroadcast(intent, Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, mScpResultsReceiver,
getHandler(), Activity.RESULT_OK, null, null);
}
return true;
}
/**
* Broadcast receiver to handle results of ordered broadcast. Sends the SCPD results
* as a reply SMS, then sends a message to state machine to transition to idle.
*/
private final BroadcastReceiver mScpResultsReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
sendScpResults();
if (DBG) log("mScpResultsReceiver finished");
if (mReceiverCount.decrementAndGet() == 0) {
sendMessage(EVENT_BROADCAST_COMPLETE);
}
}
private void sendScpResults() {
int resultCode = getResultCode();
if ((resultCode != Activity.RESULT_OK) && (resultCode != Intents.RESULT_SMS_HANDLED)) {
loge("SCP results error: result code = " + resultCode);
return;
}
Bundle extras = getResultExtras(false);
if (extras == null) {
loge("SCP results error: missing extras");
return;
}
String sender = extras.getString("sender");
if (sender == null) {
loge("SCP results error: missing sender extra.");
return;
}
ArrayList<CdmaSmsCbProgramResults> results
= extras.getParcelableArrayList("results");
if (results == null) {
loge("SCP results error: missing results extra.");
return;
}
BearerData bData = new BearerData();
bData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
bData.messageId = SmsMessage.getNextMessageId();
bData.serviceCategoryProgramResults = results;
byte[] encodedBearerData = BearerData.encode(bData);
ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
DataOutputStream dos = new DataOutputStream(baos);
try {
dos.writeInt(SmsEnvelope.TELESERVICE_SCPT);
dos.writeInt(0); //servicePresent
dos.writeInt(0); //serviceCategory
CdmaSmsAddress destAddr = CdmaSmsAddress.parse(
PhoneNumberUtils.cdmaCheckAndProcessPlusCodeForSms(sender));
dos.write(destAddr.digitMode);
dos.write(destAddr.numberMode);
dos.write(destAddr.ton); // number_type
dos.write(destAddr.numberPlan);
dos.write(destAddr.numberOfDigits);
dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits
// Subaddress is not supported.
dos.write(0); //subaddressType
dos.write(0); //subaddr_odd
dos.write(0); //subaddr_nbr_of_digits
dos.write(encodedBearerData.length);
dos.write(encodedBearerData, 0, encodedBearerData.length);
// Ignore the RIL response. TODO: implement retry if SMS send fails.
mCi.sendCdmaSms(baos.toByteArray(), null);
} catch (IOException e) {
loge("exception creating SCP results PDU", e);
} finally {
try {
dos.close();
} catch (IOException ignored) {
}
}
}
};
}