1 /*
2  * Copyright (C) 2014 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.server.telecom;
18 
19 import android.app.BroadcastOptions;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.content.pm.ResolveInfo;
24 import android.os.Bundle;
25 import android.os.UserHandle;
26 import android.telecom.Log;
27 import android.widget.Toast;
28 
29 import com.android.server.telecom.ui.ConfirmCallDialogActivity;
30 import com.android.server.telecom.ui.DisconnectedCallNotifier;
31 
32 import java.util.List;
33 
34 public final class TelecomBroadcastIntentProcessor {
35     /** The action used to send SMS response for the missed call notification. */
36     public static final String ACTION_SEND_SMS_FROM_NOTIFICATION =
37             "com.android.server.telecom.ACTION_SEND_SMS_FROM_NOTIFICATION";
38 
39     /** The action used to call a handle back for the missed call notification. */
40     public static final String ACTION_CALL_BACK_FROM_NOTIFICATION =
41             "com.android.server.telecom.ACTION_CALL_BACK_FROM_NOTIFICATION";
42 
43     /** The action used to send SMS response for the disconnected call notification. */
44     public static final String ACTION_DISCONNECTED_SEND_SMS_FROM_NOTIFICATION =
45             "com.android.server.telecom.ACTION_DISCONNECTED_SEND_SMS_FROM_NOTIFICATION";
46 
47     /** The action used to call a handle back for the disconnected call notification. */
48     public static final String ACTION_DISCONNECTED_CALL_BACK_FROM_NOTIFICATION =
49             "com.android.server.telecom.ACTION_DISCONNECTED_CALL_BACK_FROM_NOTIFICATION";
50 
51     /** The action used to clear missed calls. */
52     public static final String ACTION_CLEAR_MISSED_CALLS =
53             "com.android.server.telecom.ACTION_CLEAR_MISSED_CALLS";
54 
55     /**
56      * The action used to answer the current incoming call displayed by
57      * {@link com.android.server.telecom.ui.IncomingCallNotifier}.
58      */
59     public static final String ACTION_ANSWER_FROM_NOTIFICATION =
60             "com.android.server.telecom.ACTION_ANSWER_FROM_NOTIFICATION";
61 
62     /**
63      * The action used to reject the current incoming call displayed by
64      * {@link com.android.server.telecom.ui.IncomingCallNotifier}.
65      */
66     public static final String ACTION_REJECT_FROM_NOTIFICATION =
67             "com.android.server.telecom.ACTION_REJECT_FROM_NOTIFICATION";
68 
69     /**
70      * The action used to proceed with a call being confirmed via
71      * {@link com.android.server.telecom.ui.ConfirmCallDialogActivity}.
72      */
73     public static final String ACTION_PROCEED_WITH_CALL =
74             "com.android.server.telecom.PROCEED_WITH_CALL";
75 
76     /**
77      * The action used to cancel a call being confirmed via
78      * {@link com.android.server.telecom.ui.ConfirmCallDialogActivity}.
79      */
80     public static final String ACTION_CANCEL_CALL =
81             "com.android.server.telecom.CANCEL_CALL";
82 
83     /**
84      * The action used to proceed with a redirected call being confirmed via the call redirection
85      * confirmation dialog.
86      */
87     public static final String ACTION_PLACE_REDIRECTED_CALL =
88             "com.android.server.telecom.PROCEED_WITH_REDIRECTED_CALL";
89 
90     /**
91      * The action used to confirm to proceed the call without redirection via the call redirection
92      * confirmation dialog.
93      */
94     public static final String ACTION_PLACE_UNREDIRECTED_CALL =
95             "com.android.server.telecom.PROCEED_WITH_UNREDIRECTED_CALL";
96 
97     /**
98      * The action used to cancel a redirected call being confirmed via the call redirection
99      * confirmation dialog.
100      */
101     public static final String ACTION_CANCEL_REDIRECTED_CALL =
102             "com.android.server.telecom.CANCEL_REDIRECTED_CALL";
103 
104     public static final String ACTION_HANGUP_CALL = "com.android.server.telecom.HANGUP_CALL";
105     public static final String ACTION_STOP_STREAMING =
106             "com.android.server.telecom.ACTION_STOP_STREAMING";
107 
108     public static final String EXTRA_USERHANDLE = "userhandle";
109     public static final String EXTRA_REDIRECTION_OUTGOING_CALL_ID =
110             "android.telecom.extra.REDIRECTION_OUTGOING_CALL_ID";
111     public static final String EXTRA_REDIRECTION_APP_NAME =
112             "android.telecom.extra.REDIRECTION_APP_NAME";
113 
114     private final Context mContext;
115     private final CallsManager mCallsManager;
116 
TelecomBroadcastIntentProcessor(Context context, CallsManager callsManager)117     public TelecomBroadcastIntentProcessor(Context context, CallsManager callsManager) {
118         mContext = context;
119         mCallsManager = callsManager;
120     }
121 
processIntent(Intent intent)122     public void processIntent(Intent intent) {
123         String action = intent.getAction();
124 
125         if (ACTION_SEND_SMS_FROM_NOTIFICATION.equals(action) ||
126                 ACTION_CALL_BACK_FROM_NOTIFICATION.equals(action) ||
127                 ACTION_CLEAR_MISSED_CALLS.equals(action)) {
128             Log.v(this, "Action received: %s.", action);
129             UserHandle userHandle = intent.getParcelableExtra(EXTRA_USERHANDLE);
130             if (userHandle == null) {
131                 Log.d(this, "user handle can't be null, not processing the broadcast");
132                 return;
133             }
134 
135             MissedCallNotifier missedCallNotifier = mCallsManager.getMissedCallNotifier();
136 
137             // Send an SMS from the missed call notification.
138             if (ACTION_SEND_SMS_FROM_NOTIFICATION.equals(action)) {
139                 // Close the notification shade and the notification itself.
140                 closeSystemDialogs(mContext);
141                 missedCallNotifier.clearMissedCalls(userHandle);
142                 sendSmsIntent(intent, userHandle);
143 
144                 // Call back recent caller from the missed call notification.
145             } else if (ACTION_CALL_BACK_FROM_NOTIFICATION.equals(action)) {
146                 // Close the notification shade and the notification itself.
147                 closeSystemDialogs(mContext);
148                 missedCallNotifier.clearMissedCalls(userHandle);
149                 sendCallBackIntent(intent, userHandle);
150 
151                 // Clear the missed call notification and call log entries.
152             } else if (ACTION_CLEAR_MISSED_CALLS.equals(action)) {
153                 missedCallNotifier.clearMissedCalls(userHandle);
154             }
155         } else if(ACTION_DISCONNECTED_SEND_SMS_FROM_NOTIFICATION.equals(action) ||
156                 ACTION_DISCONNECTED_CALL_BACK_FROM_NOTIFICATION.equals(action)) {
157             Log.v(this, "Action received: %s.", action);
158             UserHandle userHandle = intent.getParcelableExtra(EXTRA_USERHANDLE);
159             if (userHandle == null) {
160                 Log.d(this, "disconnect user handle can't be null, not processing the broadcast");
161                 return;
162             }
163 
164             DisconnectedCallNotifier disconnectedCallNotifier =
165                     mCallsManager.getDisconnectedCallNotifier();
166 
167             // Send an SMS from the disconnected call notification.
168             if (ACTION_DISCONNECTED_SEND_SMS_FROM_NOTIFICATION.equals(action)) {
169                 // Close the notification shade and the notification itself.
170                 closeSystemDialogs(mContext);
171                 disconnectedCallNotifier.clearNotification(userHandle);
172                 sendSmsIntent(intent, userHandle);
173 
174             // Call back recent caller from the disconnected call notification.
175             } else if (ACTION_DISCONNECTED_CALL_BACK_FROM_NOTIFICATION.equals(action)) {
176                 // Close the notification shade and the notification itself.
177                 closeSystemDialogs(mContext);
178                 disconnectedCallNotifier.clearNotification(userHandle);
179                 sendCallBackIntent(intent, userHandle);
180             }
181         } else if (ACTION_ANSWER_FROM_NOTIFICATION.equals(action)) {
182             Log.startSession("TBIP.aAFM");
183             try {
184                 // Answer the current ringing call.
185                 Call incomingCall = mCallsManager.getIncomingCallNotifier().getIncomingCall();
186                 if (incomingCall != null) {
187                     mCallsManager.answerCall(incomingCall, incomingCall.getVideoState());
188                 }
189             } finally {
190                 Log.endSession();
191             }
192         } else if (ACTION_REJECT_FROM_NOTIFICATION.equals(action)) {
193             Log.startSession("TBIP.aRFM");
194             try {
195 
196                 // Reject the current ringing call.
197                 Call incomingCall = mCallsManager.getIncomingCallNotifier().getIncomingCall();
198                 if (incomingCall != null) {
199                     mCallsManager.rejectCall(incomingCall, false /* isRejectWithMessage */, null);
200                 }
201             } finally {
202                 Log.endSession();
203             }
204         } else if (ACTION_PROCEED_WITH_CALL.equals(action)) {
205             Log.startSession("TBIP.aPWC");
206             try {
207                 String callId = intent.getStringExtra(
208                         ConfirmCallDialogActivity.EXTRA_OUTGOING_CALL_ID);
209                 mCallsManager.confirmPendingCall(callId);
210             } finally {
211                 Log.endSession();
212             }
213         } else if (ACTION_CANCEL_CALL.equals(action)) {
214             Log.startSession("TBIP.aCC");
215             try {
216                 String callId = intent.getStringExtra(
217                         ConfirmCallDialogActivity.EXTRA_OUTGOING_CALL_ID);
218                 mCallsManager.cancelPendingCall(callId);
219             } finally {
220                 Log.endSession();
221             }
222         } else if (ACTION_PLACE_REDIRECTED_CALL.equals(action)) {
223             Log.startSession("TBIP.aPRC");
224             try {
225                 mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
226                         intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
227                         ACTION_PLACE_REDIRECTED_CALL);
228             } finally {
229                 Log.endSession();
230             }
231         } else if (ACTION_PLACE_UNREDIRECTED_CALL.equals(action)) {
232             Log.startSession("TBIP.aPUC");
233             try {
234                 mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
235                         intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
236                         ACTION_PLACE_UNREDIRECTED_CALL);
237             } finally {
238                 Log.endSession();
239             }
240         } else if (ACTION_CANCEL_REDIRECTED_CALL.equals(action)) {
241             Log.startSession("TBIP.aCRC");
242             try {
243                 mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
244                         intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
245                         ACTION_CANCEL_REDIRECTED_CALL);
246             } finally {
247                 Log.endSession();
248             }
249         } else if (ACTION_HANGUP_CALL.equals(action)) {
250             Log.startSession("TBIP.aHC", "streamingDialog");
251             try {
252                 Call call = mCallsManager.getCall(intent.getData().getSchemeSpecificPart());
253                 if (call != null) {
254                     mCallsManager.disconnectCall(call);
255                 }
256             } finally {
257                 Log.endSession();
258             }
259         } else if (ACTION_STOP_STREAMING.equals(action)) {
260             Log.startSession("TBIP.aSS", "streamingDialog");
261             try {
262                 Call call = mCallsManager.getCall(intent.getData().getSchemeSpecificPart());
263                 if (call != null) {
264                     mCallsManager.stopCallStreaming(call);
265                 }
266             } finally {
267                 Log.endSession();
268             }
269         }
270     }
271 
272     /**
273      * Closes open system dialogs and the notification shade.
274      */
closeSystemDialogs(Context context)275     private void closeSystemDialogs(Context context) {
276         Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
277                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
278         Bundle options = BroadcastOptions.makeBasic()
279                 .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
280                 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
281                 .toBundle();
282         context.sendBroadcastAsUser(intent, UserHandle.ALL, null /* receiverPermission */,
283                 options);
284     }
285 
sendSmsIntent(Intent intent, UserHandle userHandle)286     private void sendSmsIntent(Intent intent, UserHandle userHandle) {
287         Intent callIntent = new Intent(Intent.ACTION_SENDTO, intent.getData());
288         callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
289         PackageManager packageManager = mContext.getPackageManager();
290         List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(
291                 callIntent, PackageManager.MATCH_DEFAULT_ONLY, userHandle.getIdentifier());
292         if (activities.size() > 0) {
293             mContext.startActivityAsUser(callIntent, userHandle);
294         } else {
295             Toast.makeText(mContext, com.android.internal.R.string.noApplications,
296                     Toast.LENGTH_SHORT).show();
297         }
298     }
299 
sendCallBackIntent(Intent intent, UserHandle userHandle)300     private void sendCallBackIntent(Intent intent, UserHandle userHandle) {
301         Intent callIntent = new Intent(Intent.ACTION_CALL, intent.getData());
302         callIntent.setFlags(
303                 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
304         mContext.startActivityAsUser(callIntent, userHandle);
305     }
306 }
307