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.support.annotation.NonNull;
20 import android.telecom.CallAudioState;
21 import android.telecom.Conference;
22 import android.telecom.Connection;
23 import android.telecom.PhoneAccountHandle;
24 import com.android.dialer.common.Assert;
25 import com.android.dialer.common.LogUtil;
26 import com.android.dialer.simulator.Simulator;
27 import com.android.dialer.simulator.Simulator.Event;
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 /**
32  * Represents a conference call. When a user merges two phone calls we create an instance of this
33  * conference object and add it to the connection service. All operations such as hold and DTMF are
34  * then performed on this object.
35  */
36 public final class SimulatorConference extends Conference implements SimulatorConnection.Listener {
37   static final int PROPERTY_GENERIC_CONFERENCE = 1 << 1;
38 
39   private final List<Listener> listeners = new ArrayList<>();
40   private final List<Event> events = new ArrayList<>();
41   private final int conferenceType;
42 
SimulatorConference( PhoneAccountHandle handle, @Simulator.ConferenceType int conferenceType)43   private SimulatorConference(
44       PhoneAccountHandle handle, @Simulator.ConferenceType int conferenceType) {
45     super(handle);
46     this.conferenceType = conferenceType;
47     setActive();
48   }
49 
newGsmConference(PhoneAccountHandle handle)50   static SimulatorConference newGsmConference(PhoneAccountHandle handle) {
51     SimulatorConference simulatorConference =
52         new SimulatorConference(handle, Simulator.CONFERENCE_TYPE_GSM);
53     simulatorConference.setConnectionCapabilities(
54         Connection.CAPABILITY_MUTE
55             | Connection.CAPABILITY_SUPPORT_HOLD
56             | Connection.CAPABILITY_HOLD
57             | Connection.CAPABILITY_MANAGE_CONFERENCE);
58     return simulatorConference;
59   }
60 
newVoLteConference(PhoneAccountHandle handle)61   static SimulatorConference newVoLteConference(PhoneAccountHandle handle) {
62     SimulatorConference simulatorConference =
63         new SimulatorConference(handle, Simulator.CONFERENCE_TYPE_VOLTE);
64     simulatorConference.setConnectionCapabilities(
65         Connection.CAPABILITY_MUTE
66             | Connection.CAPABILITY_SUPPORT_HOLD
67             | Connection.CAPABILITY_HOLD
68             | Connection.CAPABILITY_MANAGE_CONFERENCE);
69     return simulatorConference;
70   }
71 
addListener(@onNull Listener listener)72   public void addListener(@NonNull Listener listener) {
73     listeners.add(Assert.isNotNull(listener));
74   }
75 
removeListener(@onNull Listener listener)76   public void removeListener(@NonNull Listener listener) {
77     listeners.remove(Assert.isNotNull(listener));
78   }
79 
80   @NonNull
getEvents()81   public List<Event> getEvents() {
82     return events;
83   }
84 
85   @Override
onCallAudioStateChanged(CallAudioState state)86   public void onCallAudioStateChanged(CallAudioState state) {
87     LogUtil.enterBlock("SimulatorConference.onCallAudioStateChanged");
88     onEvent(new Event(Event.CALL_AUDIO_STATE_CHANGED));
89   }
90 
91   @Override
onConnectionAdded(Connection connection)92   public void onConnectionAdded(Connection connection) {
93     LogUtil.enterBlock("SimulatorConference.onConnectionAdded");
94     onEvent(
95         new Event(
96             Event.CONNECTION_ADDED, SimulatorSimCallManager.getConnectionTag(connection), null));
97     ((SimulatorConnection) connection).addListener(this);
98   }
99 
100   @Override
onDisconnect()101   public void onDisconnect() {
102     LogUtil.enterBlock("SimulatorConference.onDisconnect");
103     onEvent(new Event(Event.DISCONNECT));
104   }
105 
106   @Override
onHold()107   public void onHold() {
108     LogUtil.enterBlock("SimulatorConference.onHold");
109     onEvent(new Event(Event.HOLD));
110   }
111 
112   @Override
onMerge(Connection connection)113   public void onMerge(Connection connection) {
114     LogUtil.i("SimulatorConference.onMerge", "connection: " + connection);
115     onEvent(new Event(Event.MERGE, SimulatorSimCallManager.getConnectionTag(connection), null));
116   }
117 
118   @Override
onMerge()119   public void onMerge() {
120     LogUtil.enterBlock("SimulatorConference.onMerge");
121     onEvent(new Event(Event.MERGE));
122   }
123 
124   @Override
onPlayDtmfTone(char c)125   public void onPlayDtmfTone(char c) {
126     LogUtil.enterBlock("SimulatorConference.onPlayDtmfTone");
127     onEvent(new Event(Event.DTMF, Character.toString(c), null));
128   }
129 
130   @Override
onSeparate(Connection connection)131   public void onSeparate(Connection connection) {
132     LogUtil.i("SimulatorConference.onSeparate", "connection: " + connection);
133     onEvent(new Event(Event.SEPARATE, SimulatorSimCallManager.getConnectionTag(connection), null));
134     // if there is only 1 connection in a gsm conference, destroy the conference.
135     if (conferenceType == Simulator.CONFERENCE_TYPE_GSM && getConnections().size() == 1) {
136       removeConnection(getConnections().get(0));
137       destroy();
138     }
139   }
140 
141   @Override
onSwap()142   public void onSwap() {
143     LogUtil.enterBlock("SimulatorConference.onSwap");
144     onEvent(new Event(Event.SWAP));
145   }
146 
147   @Override
onUnhold()148   public void onUnhold() {
149     LogUtil.enterBlock("SimulatorConference.onUnhold");
150     onEvent(new Event(Event.UNHOLD));
151   }
152 
153   @Override
onEvent(@onNull SimulatorConnection connection, @NonNull Event event)154   public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
155     if (conferenceType == Simulator.CONFERENCE_TYPE_GSM) {
156       onGsmEvent(connection, event);
157     }
158   }
159 
onGsmEvent(@onNull SimulatorConnection connection, @NonNull Event event)160   private void onGsmEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
161     if (event.type == Event.STATE_CHANGE
162         && Connection.stateToString(Connection.STATE_DISCONNECTED).equals(event.data2)) {
163       removeConnection(connection);
164       connection.removeListener(this);
165       if (getConnections().size() <= 1) {
166         // When only one connection exists, it's not conference call anymore
167         setDisconnected(connection.getDisconnectCause());
168         destroy();
169       }
170     }
171   }
172 
onEvent(@onNull Event event)173   void onEvent(@NonNull Event event) {
174     events.add(Assert.isNotNull(event));
175     for (Listener listener : new ArrayList<>(listeners)) {
176       listener.onEvent(this, event);
177     }
178   }
179 
180   /** Callback for when a new event arrives. */
181   public interface Listener {
onEvent(@onNull SimulatorConference conference, @NonNull Event event)182     void onEvent(@NonNull SimulatorConference conference, @NonNull Event event);
183   }
184 }
185