/* * 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.cellbroadcastservice; import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERR_SCP_EMPTY; import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERR_SCP_HANDLING; import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERR_UNEXPECTED_SPC_MSG_FROM_FWK; import static com.android.cellbroadcastservice.CellBroadcastMetrics.RPT_SPC; import static com.android.cellbroadcastservice.CellBroadcastMetrics.SRC_CBS; 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.Looper; import android.os.Message; import android.provider.Telephony.Sms.Intents; import android.telephony.cdma.CdmaSmsCbProgramData; import java.util.ArrayList; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; /** * Handle CDMA Service Category Program Data requests and responses. */ public final class CdmaServiceCategoryProgramHandler extends WakeLockStateMachine { // hold the callbacks provided from the framework, executed after SCP messages are broadcast private ConcurrentLinkedQueue> mScpCallback = new ConcurrentLinkedQueue<>(); /** * Create a new CDMA inbound SMS handler. */ CdmaServiceCategoryProgramHandler(Context context) { super("CdmaServiceCategoryProgramHandler", context, Looper.myLooper()); mContext = context; } /** * Create a new State machine for SCPD requests. * * @param context the context to use * @return the new SCPD handler */ static CdmaServiceCategoryProgramHandler makeScpHandler(Context context) { CdmaServiceCategoryProgramHandler handler = new CdmaServiceCategoryProgramHandler( context); handler.start(); return handler; } /** * Handle a CDMA SCP message. * * @param slotIndex the index of the slot which received the message * @param programData the SMS CB program data of the message * @param originatingAddress the originating address of the message * @param callback a callback to run after each cell broadcast receiver has handled * the SCP message */ public void onCdmaScpMessage(int slotIndex, ArrayList programData, String originatingAddress, Consumer callback) { onCdmaCellBroadcastSms(new CdmaScpMessage(slotIndex, programData, originatingAddress, callback)); } /** * Class for holding parts of a CDMA Service Program Category message. */ private class CdmaScpMessage { int mSlotIndex; ArrayList mProgamData; String mOriginatingAddress; Consumer mCallback; CdmaScpMessage(int slotIndex, ArrayList programData, String originatingAddress, Consumer callback) { mSlotIndex = slotIndex; mProgamData = programData; mOriginatingAddress = originatingAddress; mCallback = callback; } } /** * 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 CdmaScpMessage) { CdmaScpMessage cdmaScpMessage = (CdmaScpMessage) message.obj; CellBroadcastServiceMetrics.getInstance() .logMessageReported(mContext, RPT_SPC, SRC_CBS, 0, 0); return handleServiceCategoryProgramData(cdmaScpMessage.mProgamData, cdmaScpMessage.mOriginatingAddress, cdmaScpMessage.mSlotIndex, cdmaScpMessage.mCallback); } else { final String errorMessage = "handleMessage got object of type: " + message.obj.getClass().getName(); loge(errorMessage); CellBroadcastServiceMetrics.getInstance().logMessageError( ERR_UNEXPECTED_SPC_MSG_FROM_FWK, errorMessage); return false; } } /** * Send SCPD request to CellBroadcastReceiver as an ordered broadcast. * * @param programData the program data of the SCP message * @param originatingAddress the address of the sender * @param phoneId the phoneId that the message was received on * @param callback a callback to run after each broadcast * @return true if an ordered broadcast was sent; false on failure */ private boolean handleServiceCategoryProgramData(ArrayList programData, String originatingAddress, int phoneId, Consumer callback) { if (programData == null) { final String errorMessage = "handleServiceCategoryProgramData: program data list is null!"; loge(errorMessage); CellBroadcastServiceMetrics.getInstance().logMessageError(ERR_SCP_EMPTY, errorMessage); return false; } Intent intent = new Intent(Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION); intent.putExtra("sender", originatingAddress); intent.putParcelableArrayListExtra("program_data", programData); CellBroadcastHandler.putPhoneIdAndSubIdExtra(mContext, intent, phoneId); String pkg = CellBroadcastHandler.getDefaultCBRPackageName(mContext, intent); mReceiverCount.incrementAndGet(); intent.setPackage(pkg); mContext.sendOrderedBroadcast(intent, Manifest.permission.RECEIVE_SMS, AppOpsManager.OPSTR_RECEIVE_SMS, mScpResultsReceiver, getHandler(), Activity.RESULT_OK, null, null); mScpCallback.add(callback); return true; } /** * Broadcast receiver to handle results of ordered broadcast. Sends the results back to the * framework through a provided callback. */ private final BroadcastReceiver mScpResultsReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int resultCode = getResultCode(); if ((resultCode != Activity.RESULT_OK) && (resultCode != Intents.RESULT_SMS_HANDLED)) { final String errorMessage = "SCP results error: result code = " + resultCode; loge(errorMessage); CellBroadcastServiceMetrics.getInstance().logMessageError( ERR_SCP_HANDLING, errorMessage); return; } Bundle extras = getResultExtras(false); Consumer callback = mScpCallback.poll(); callback.accept(extras); if (DBG) log("mScpResultsReceiver finished"); if (mReceiverCount.decrementAndGet() == 0) { sendMessage(EVENT_BROADCAST_COMPLETE); } } }; }