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