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.telecom.Connection; 23 import android.telecom.DisconnectCause; 24 import com.android.dialer.common.Assert; 25 import com.android.dialer.common.LogUtil; 26 import com.android.dialer.common.concurrent.ThreadUtil; 27 import com.android.dialer.simulator.Simulator; 28 import com.android.dialer.simulator.Simulator.Event; 29 import com.android.dialer.simulator.SimulatorComponent; 30 import com.android.dialer.simulator.SimulatorConnectionsBank; 31 import java.util.ArrayList; 32 import java.util.Locale; 33 34 /** Creates a conference with a given number of participants. */ 35 final class SimulatorConferenceCreator 36 implements SimulatorConnectionService.Listener, 37 SimulatorConnection.Listener, 38 SimulatorConference.Listener { 39 private static final String EXTRA_CALL_COUNT = "call_count"; 40 private static final String RECONNECT = "reconnect"; 41 @NonNull private final Context context; 42 43 private final SimulatorConnectionsBank simulatorConnectionsBank; 44 45 private boolean onNewIncomingConnectionEnabled = false; 46 47 @Simulator.ConferenceType private final int conferenceType; 48 49 public SimulatorConferenceCreator( 50 @NonNull Context context, @Simulator.ConferenceType int conferenceType) { 51 this.context = Assert.isNotNull(context); 52 this.conferenceType = conferenceType; 53 simulatorConnectionsBank = SimulatorComponent.get(context).getSimulatorConnectionsBank(); 54 } 55 56 void start(int callCount) { 57 onNewIncomingConnectionEnabled = true; 58 SimulatorConnectionService.addListener(this); 59 if (conferenceType == Simulator.CONFERENCE_TYPE_VOLTE) { 60 addNextCall(callCount, true); 61 } else if (conferenceType == Simulator.CONFERENCE_TYPE_GSM) { 62 addNextCall(callCount, false); 63 } 64 } 65 /** 66 * Add a call in a process of making a conference. 67 * 68 * @param callCount the remaining number of calls to make 69 * @param reconnect whether all connections should reconnect once (connections are reconnected 70 * once in making VoLTE conference) 71 */ 72 private void addNextCall(int callCount, boolean reconnect) { 73 LogUtil.i("SimulatorConferenceCreator.addNextIncomingCall", "callCount: " + callCount); 74 if (callCount <= 0) { 75 LogUtil.i("SimulatorConferenceCreator.addNextCall", "done adding calls"); 76 if (reconnect) { 77 simulatorConnectionsBank.disconnectAllConnections(); 78 addNextCall(simulatorConnectionsBank.getConnectionTags().size(), false); 79 } else { 80 simulatorConnectionsBank.mergeAllConnections(conferenceType, context); 81 SimulatorConnectionService.removeListener(this); 82 } 83 return; 84 } 85 String callerId = String.format(Locale.US, "+1-650-234%04d", callCount); 86 Bundle extras = new Bundle(); 87 extras.putInt(EXTRA_CALL_COUNT, callCount - 1); 88 extras.putBoolean(RECONNECT, reconnect); 89 addConferenceCall(callerId, extras); 90 } 91 92 private void addConferenceCall(String number, Bundle extras) { 93 switch (conferenceType) { 94 case Simulator.CONFERENCE_TYPE_VOLTE: 95 extras.putBoolean(Simulator.IS_VOLTE, true); 96 break; 97 default: 98 break; 99 } 100 SimulatorSimCallManager.addNewIncomingCall( 101 context, number, SimulatorSimCallManager.CALL_TYPE_VOICE, extras); 102 } 103 104 @Override 105 public void onNewIncomingConnection(@NonNull SimulatorConnection connection) { 106 if (!onNewIncomingConnectionEnabled) { 107 return; 108 } 109 if (!simulatorConnectionsBank.isSimulatorConnection(connection)) { 110 LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "unknown connection"); 111 return; 112 } 113 LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "connection created"); 114 connection.addListener(this); 115 // Once the connection is active, go ahead and conference it and add the next call. 116 ThreadUtil.postDelayedOnUiThread( 117 () -> { 118 connection.setActive(); 119 addNextCall(getCallCount(connection), shouldReconnect(connection)); 120 }, 121 1000); 122 } 123 124 @Override 125 public void onNewOutgoingConnection(@NonNull SimulatorConnection connection) {} 126 127 /** 128 * This is called when the user clicks the merge button. We create the initial conference 129 * automatically but with this method we can let the user split and merge calls as desired. 130 */ 131 @Override 132 public void onConference( 133 @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) { 134 LogUtil.enterBlock("SimulatorConferenceCreator.onConference"); 135 if (!simulatorConnectionsBank.isSimulatorConnection(connection1) 136 || !simulatorConnectionsBank.isSimulatorConnection(connection2)) { 137 LogUtil.i("SimulatorConferenceCreator.onConference", "unknown connections, ignoring"); 138 return; 139 } 140 141 if (connection1.getConference() != null) { 142 connection1.getConference().addConnection(connection2); 143 } else if (connection2.getConference() != null) { 144 connection2.getConference().addConnection(connection1); 145 } else { 146 SimulatorConference conference = 147 SimulatorConference.newGsmConference( 148 SimulatorSimCallManager.getSystemPhoneAccountHandle(context)); 149 conference.addConnection(connection1); 150 conference.addConnection(connection2); 151 conference.addListener(this); 152 SimulatorConnectionService.getInstance().addConference(conference); 153 } 154 } 155 156 private static int getCallCount(@NonNull Connection connection) { 157 return connection.getExtras().getInt(EXTRA_CALL_COUNT); 158 } 159 160 private static boolean shouldReconnect(@NonNull Connection connection) { 161 return connection.getExtras().getBoolean(RECONNECT); 162 } 163 164 @Override 165 public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) { 166 switch (event.type) { 167 case Event.NONE: 168 throw Assert.createIllegalStateFailException(); 169 case Event.HOLD: 170 connection.setOnHold(); 171 break; 172 case Event.UNHOLD: 173 connection.setActive(); 174 break; 175 case Event.DISCONNECT: 176 connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); 177 break; 178 default: 179 LogUtil.i( 180 "SimulatorConferenceCreator.onEvent", "unexpected conference event: " + event.type); 181 break; 182 } 183 } 184 185 @Override 186 public void onEvent(@NonNull SimulatorConference conference, @NonNull Event event) { 187 switch (event.type) { 188 case Event.MERGE: 189 int capabilities = conference.getConnectionCapabilities(); 190 capabilities |= Connection.CAPABILITY_SWAP_CONFERENCE; 191 conference.setConnectionCapabilities(capabilities); 192 break; 193 case Event.SEPARATE: 194 SimulatorConnection connectionToRemove = 195 SimulatorSimCallManager.findConnectionByTag(event.data1); 196 conference.removeConnection(connectionToRemove); 197 break; 198 case Event.DISCONNECT: 199 for (Connection connection : new ArrayList<>(conference.getConnections())) { 200 connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); 201 } 202 conference.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); 203 break; 204 default: 205 LogUtil.i( 206 "SimulatorConferenceCreator.onEvent", "unexpected conference event: " + event.type); 207 break; 208 } 209 } 210 } 211