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