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