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