1 /*
2  * Copyright (C) 2017 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.dialer.simulator.impl;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.support.annotation.NonNull;
22 import android.support.annotation.Nullable;
23 import android.support.v7.app.AppCompatActivity;
24 import android.telecom.Connection;
25 import android.telecom.Connection.RttModifyStatus;
26 import android.telecom.DisconnectCause;
27 import com.android.dialer.common.Assert;
28 import com.android.dialer.common.LogUtil;
29 import com.android.dialer.common.concurrent.DialerExecutorComponent;
30 import com.android.dialer.common.concurrent.ThreadUtil;
31 import com.android.dialer.enrichedcall.EnrichedCallComponent;
32 import com.android.dialer.enrichedcall.EnrichedCallManager;
33 import com.android.dialer.simulator.Simulator;
34 import com.android.dialer.simulator.Simulator.Event;
35 import com.android.dialer.simulator.SimulatorComponent;
36 import com.android.dialer.simulator.SimulatorEnrichedCall;
37 
38 /** Entry point in the simulator to create voice calls. */
39 final class SimulatorVoiceCall
40     implements SimulatorConnectionService.Listener, SimulatorConnection.Listener {
41   @NonNull private final Context context;
42   @Nullable private String connectionTag;
43   private final SimulatorEnrichedCall simulatorEnrichedCall;
44 
SimulatorVoiceCall(@onNull Context context)45   SimulatorVoiceCall(@NonNull Context context) {
46     this.context = Assert.isNotNull(context);
47     simulatorEnrichedCall = SimulatorComponent.get(context).getSimulatorEnrichedCall();
48     SimulatorConnectionService.addListener(this);
49     SimulatorConnectionService.addListener(
50         new SimulatorConferenceCreator(context, Simulator.CONFERENCE_TYPE_GSM));
51   }
52 
incomingEnrichedCall()53   void incomingEnrichedCall() {
54     simulatorEnrichedCall
55         .setupIncomingEnrichedCall(Simulator.ENRICHED_CALL_INCOMING_NUMBER)
56         .addListener(
57             () -> {
58               Bundle extras = new Bundle();
59               extras.putBoolean(Simulator.IS_ENRICHED_CALL, true);
60               connectionTag =
61                   SimulatorSimCallManager.addNewIncomingCall(
62                       context,
63                       Simulator.ENRICHED_CALL_INCOMING_NUMBER,
64                       SimulatorSimCallManager.CALL_TYPE_VOICE,
65                       extras);
66             },
67             DialerExecutorComponent.get(context).uiExecutor());
68   }
69 
outgoingEnrichedCall()70   void outgoingEnrichedCall() {
71     getEnrichedCallManager().registerStateChangedListener(simulatorEnrichedCall);
72     simulatorEnrichedCall
73         .setupOutgoingEnrichedCall(Simulator.ENRICHED_CALL_OUTGOING_NUMBER)
74         .addListener(
75             () -> {
76               Bundle extras = new Bundle();
77               extras.putBoolean(Simulator.IS_ENRICHED_CALL, true);
78               connectionTag =
79                   SimulatorSimCallManager.addNewOutgoingCall(
80                       context,
81                       Simulator.ENRICHED_CALL_OUTGOING_NUMBER,
82                       SimulatorSimCallManager.CALL_TYPE_VOICE,
83                       extras);
84             },
85             DialerExecutorComponent.get(context).uiExecutor());
86   }
87 
addNewIncomingCall()88   void addNewIncomingCall() {
89     String callerId = "+44 (0) 20 7031 3000" /* Google London office */;
90     connectionTag =
91         SimulatorSimCallManager.addNewIncomingCall(
92             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE);
93   }
94 
addCustomizedIncomingCall(String callerId, int callerIdPresentation)95   void addCustomizedIncomingCall(String callerId, int callerIdPresentation) {
96     Bundle extras = new Bundle();
97     extras.putInt(Simulator.PRESENTATION_CHOICE, callerIdPresentation);
98     connectionTag =
99         SimulatorSimCallManager.addNewIncomingCall(
100             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras);
101   }
102 
addCustomizedIncomingCallWithDialog(AppCompatActivity activity)103   void addCustomizedIncomingCallWithDialog(AppCompatActivity activity) {
104     SimulatorDialogFragment.newInstance(
105             (callerId, callerIdPresentation) -> {
106               Bundle extras = new Bundle();
107               extras.putInt(Simulator.PRESENTATION_CHOICE, callerIdPresentation);
108               connectionTag =
109                   SimulatorSimCallManager.addNewIncomingCall(
110                       context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras);
111             })
112         .show(activity.getSupportFragmentManager(), "SimulatorDialog");
113   }
114 
addNewOutgoingCall()115   void addNewOutgoingCall() {
116     String callerId = "+55-31-2128-6800"; // Brazil office.
117     connectionTag =
118         SimulatorSimCallManager.addNewOutgoingCall(
119             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE);
120   }
121 
addCustomizedOutgoingCall(String callerId, int callerIdPresentation)122   void addCustomizedOutgoingCall(String callerId, int callerIdPresentation) {
123     Bundle extras = new Bundle();
124     extras.putInt(Simulator.PRESENTATION_CHOICE, callerIdPresentation);
125     connectionTag =
126         SimulatorSimCallManager.addNewIncomingCall(
127             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras);
128   }
129 
addCustomizedOutgoingCallWithDialog(AppCompatActivity activity)130   void addCustomizedOutgoingCallWithDialog(AppCompatActivity activity) {
131     SimulatorDialogFragment.newInstance(
132             (callerId, callerIdPresentation) -> {
133               Bundle extras = new Bundle();
134               extras.putInt(Simulator.PRESENTATION_CHOICE, callerIdPresentation);
135               connectionTag =
136                   SimulatorSimCallManager.addNewOutgoingCall(
137                       context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras);
138             })
139         .show(activity.getSupportFragmentManager(), "SimulatorDialog");
140   }
141 
addSpamIncomingCall()142   void addSpamIncomingCall() {
143     String callerId = "+1-661-778-3020"; /* Blacklisted custom spam number */
144     connectionTag =
145         SimulatorSimCallManager.addNewIncomingCall(
146             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE);
147   }
148 
addNewEmergencyCallBack()149   void addNewEmergencyCallBack() {
150     String callerId = "911";
151     connectionTag =
152         SimulatorSimCallManager.addNewIncomingCall(
153             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE);
154   }
155 
156   @Override
onNewOutgoingConnection(@onNull SimulatorConnection connection)157   public void onNewOutgoingConnection(@NonNull SimulatorConnection connection) {
158     if (isMyConnection(connection)) {
159       LogUtil.i("SimulatorVoiceCall.onNewOutgoingConnection", "connection created");
160       handleNewConnection(connection);
161 
162       // Telecom will force the connection to switch to Dialing when we return it. Wait until after
163       // we're returned it before changing call state.
164       ThreadUtil.postOnUiThread(connection::setActive);
165     }
166   }
167 
168   @Override
onNewIncomingConnection(@onNull SimulatorConnection connection)169   public void onNewIncomingConnection(@NonNull SimulatorConnection connection) {
170     if (isMyConnection(connection)) {
171       LogUtil.i("SimulatorVoiceCall.onNewIncomingConnection", "connection created");
172       handleNewConnection(connection);
173     }
174   }
175 
176   @Override
onConference( @onNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2)177   public void onConference(
178       @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {}
179 
handleNewConnection(@onNull SimulatorConnection connection)180   private void handleNewConnection(@NonNull SimulatorConnection connection) {
181     connection.addListener(this);
182     connection.setConnectionCapabilities(
183         connection.getConnectionCapabilities()
184             | Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL
185             | Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
186   }
187 
isMyConnection(@onNull Connection connection)188   private boolean isMyConnection(@NonNull Connection connection) {
189     return connection.getExtras().getBoolean(connectionTag);
190   }
191 
192   @Override
onEvent(@onNull SimulatorConnection connection, @NonNull Event event)193   public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
194     switch (event.type) {
195       case Event.NONE:
196         throw Assert.createIllegalStateFailException();
197       case Event.REJECT:
198         connection.setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
199         break;
200       case Event.HOLD:
201         connection.setOnHold();
202         break;
203       case Event.ANSWER:
204       case Event.UNHOLD:
205         connection.setActive();
206         break;
207       case Event.DISCONNECT:
208         connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
209         if (connection.getExtras().getBoolean(Simulator.IS_ENRICHED_CALL)) {
210           getEnrichedCallManager().unregisterStateChangedListener(simulatorEnrichedCall);
211         }
212         break;
213       case Event.SESSION_MODIFY_REQUEST:
214         ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000);
215         break;
216       case Event.START_RTT:
217         // TODO(wangqi): Add random accept/decline.
218         boolean accept = true;
219         if (accept) {
220           connection.sendRttInitiationSuccess();
221         } else {
222           connection.sendRttInitiationFailure(RttModifyStatus.SESSION_MODIFY_REQUEST_FAIL);
223         }
224         break;
225       default:
226         LogUtil.i("SimulatorVoiceCall.onEvent", "unexpected event: " + event.type);
227         break;
228     }
229   }
230 
231   @NonNull
getEnrichedCallManager()232   private EnrichedCallManager getEnrichedCallManager() {
233     return EnrichedCallComponent.get(context).getEnrichedCallManager();
234   }
235 }
236