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