• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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