1 /* 2 * Copyright (C) 2013 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.cellbroadcastservice; 18 19 import static com.android.cellbroadcastservice.CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__CDMA_SCP_HANDLING_ERROR; 20 import static com.android.cellbroadcastservice.CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK; 21 22 import android.Manifest; 23 import android.app.Activity; 24 import android.app.AppOpsManager; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.os.Bundle; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.provider.Telephony.Sms.Intents; 32 import android.telephony.cdma.CdmaSmsCbProgramData; 33 34 import java.util.ArrayList; 35 import java.util.concurrent.ConcurrentLinkedQueue; 36 import java.util.function.Consumer; 37 38 /** 39 * Handle CDMA Service Category Program Data requests and responses. 40 */ 41 public final class CdmaServiceCategoryProgramHandler extends WakeLockStateMachine { 42 43 // hold the callbacks provided from the framework, executed after SCP messages are broadcast 44 private ConcurrentLinkedQueue<Consumer<Bundle>> mScpCallback = new ConcurrentLinkedQueue<>(); 45 46 /** 47 * Create a new CDMA inbound SMS handler. 48 */ CdmaServiceCategoryProgramHandler(Context context)49 CdmaServiceCategoryProgramHandler(Context context) { 50 super("CdmaServiceCategoryProgramHandler", context, Looper.myLooper()); 51 mContext = context; 52 } 53 54 /** 55 * Create a new State machine for SCPD requests. 56 * 57 * @param context the context to use 58 * @return the new SCPD handler 59 */ makeScpHandler(Context context)60 static CdmaServiceCategoryProgramHandler makeScpHandler(Context context) { 61 CdmaServiceCategoryProgramHandler handler = new CdmaServiceCategoryProgramHandler( 62 context); 63 handler.start(); 64 return handler; 65 } 66 67 /** 68 * Handle a CDMA SCP message. 69 * 70 * @param slotIndex the index of the slot which received the message 71 * @param programData the SMS CB program data of the message 72 * @param originatingAddress the originating address of the message 73 * @param callback a callback to run after each cell broadcast receiver has handled 74 * the SCP message 75 */ onCdmaScpMessage(int slotIndex, ArrayList<CdmaSmsCbProgramData> programData, String originatingAddress, Consumer<Bundle> callback)76 public void onCdmaScpMessage(int slotIndex, ArrayList<CdmaSmsCbProgramData> programData, 77 String originatingAddress, Consumer<Bundle> callback) { 78 onCdmaCellBroadcastSms(new CdmaScpMessage(slotIndex, programData, originatingAddress, 79 callback)); 80 } 81 82 /** 83 * Class for holding parts of a CDMA Service Program Category message. 84 */ 85 private class CdmaScpMessage { 86 int mSlotIndex; 87 ArrayList<CdmaSmsCbProgramData> mProgamData; 88 String mOriginatingAddress; 89 Consumer<Bundle> mCallback; 90 CdmaScpMessage(int slotIndex, ArrayList<CdmaSmsCbProgramData> programData, String originatingAddress, Consumer<Bundle> callback)91 CdmaScpMessage(int slotIndex, ArrayList<CdmaSmsCbProgramData> programData, 92 String originatingAddress, Consumer<Bundle> callback) { 93 mSlotIndex = slotIndex; 94 mProgamData = programData; 95 mOriginatingAddress = originatingAddress; 96 mCallback = callback; 97 } 98 } 99 100 101 /** 102 * Handle Cell Broadcast messages from {@code CdmaInboundSmsHandler}. 103 * 3GPP-format Cell Broadcast messages sent from radio are handled in the subclass. 104 * 105 * @param message the message to process 106 * @return true if an ordered broadcast was sent; false on failure 107 */ 108 @Override handleSmsMessage(Message message)109 protected boolean handleSmsMessage(Message message) { 110 if (message.obj instanceof CdmaScpMessage) { 111 CdmaScpMessage cdmaScpMessage = (CdmaScpMessage) message.obj; 112 return handleServiceCategoryProgramData(cdmaScpMessage.mProgamData, 113 cdmaScpMessage.mOriginatingAddress, cdmaScpMessage.mSlotIndex, 114 cdmaScpMessage.mCallback); 115 } else { 116 final String errorMessage = 117 "handleMessage got object of type: " + message.obj.getClass().getName(); 118 loge(errorMessage); 119 CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, 120 CELL_BROADCAST_MESSAGE_ERROR__TYPE__UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK, 121 errorMessage); 122 return false; 123 } 124 } 125 126 /** 127 * Send SCPD request to CellBroadcastReceiver as an ordered broadcast. 128 * 129 * @param programData the program data of the SCP message 130 * @param originatingAddress the address of the sender 131 * @param phoneId the phoneId that the message was received on 132 * @param callback a callback to run after each broadcast 133 * @return true if an ordered broadcast was sent; false on failure 134 */ handleServiceCategoryProgramData(ArrayList<CdmaSmsCbProgramData> programData, String originatingAddress, int phoneId, Consumer<Bundle> callback)135 private boolean handleServiceCategoryProgramData(ArrayList<CdmaSmsCbProgramData> programData, 136 String originatingAddress, int phoneId, Consumer<Bundle> callback) { 137 if (programData == null) { 138 final String errorMessage = 139 "handleServiceCategoryProgramData: program data list is null!"; 140 loge(errorMessage); 141 CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, 142 CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__CDMA_SCP_EMPTY, 143 errorMessage); 144 return false; 145 } 146 147 Intent intent = new Intent(Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION); 148 intent.putExtra("sender", originatingAddress); 149 intent.putParcelableArrayListExtra("program_data", programData); 150 CellBroadcastHandler.putPhoneIdAndSubIdExtra(mContext, intent, phoneId); 151 152 String pkg = CellBroadcastHandler.getDefaultCBRPackageName(mContext, intent); 153 mReceiverCount.incrementAndGet(); 154 intent.setPackage(pkg); 155 mContext.sendOrderedBroadcast(intent, Manifest.permission.RECEIVE_SMS, 156 AppOpsManager.OPSTR_RECEIVE_SMS, mScpResultsReceiver, 157 getHandler(), Activity.RESULT_OK, null, null); 158 mScpCallback.add(callback); 159 return true; 160 } 161 162 /** 163 * Broadcast receiver to handle results of ordered broadcast. Sends the results back to the 164 * framework through a provided callback. 165 */ 166 private final BroadcastReceiver mScpResultsReceiver = new BroadcastReceiver() { 167 @Override 168 public void onReceive(Context context, Intent intent) { 169 int resultCode = getResultCode(); 170 if ((resultCode != Activity.RESULT_OK) && (resultCode != Intents.RESULT_SMS_HANDLED)) { 171 final String errorMessage = "SCP results error: result code = " + resultCode; 172 loge(errorMessage); 173 CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, 174 CELL_BROADCAST_MESSAGE_ERROR__TYPE__CDMA_SCP_HANDLING_ERROR, 175 errorMessage); 176 return; 177 } 178 Bundle extras = getResultExtras(false); 179 Consumer<Bundle> callback = mScpCallback.poll(); 180 callback.accept(extras); 181 if (DBG) log("mScpResultsReceiver finished"); 182 if (mReceiverCount.decrementAndGet() == 0) { 183 sendMessage(EVENT_BROADCAST_COMPLETE); 184 } 185 } 186 }; 187 } 188