1/*
2 * Copyright (C) 2014 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
17package com.android.server.telecom;
18
19import android.os.Handler;
20import android.os.Message;
21import android.telecom.PhoneAccountHandle;
22
23import com.android.internal.os.SomeArgs;
24import com.android.internal.telecom.IInCallAdapter;
25
26/**
27 * Receives call commands and updates from in-call app and passes them through to CallsManager.
28 * {@link InCallController} creates an instance of this class and passes it to the in-call app after
29 * binding to it. This adapter can receive commands and updates until the in-call app is unbound.
30 */
31class InCallAdapter extends IInCallAdapter.Stub {
32    private static final int MSG_ANSWER_CALL = 0;
33    private static final int MSG_REJECT_CALL = 1;
34    private static final int MSG_PLAY_DTMF_TONE = 2;
35    private static final int MSG_STOP_DTMF_TONE = 3;
36    private static final int MSG_POST_DIAL_CONTINUE = 4;
37    private static final int MSG_DISCONNECT_CALL = 5;
38    private static final int MSG_HOLD_CALL = 6;
39    private static final int MSG_UNHOLD_CALL = 7;
40    private static final int MSG_MUTE = 8;
41    private static final int MSG_SET_AUDIO_ROUTE = 9;
42    private static final int MSG_CONFERENCE = 10;
43    private static final int MSG_SPLIT_FROM_CONFERENCE = 11;
44    private static final int MSG_SWAP_WITH_BACKGROUND_CALL = 12;
45    private static final int MSG_PHONE_ACCOUNT_SELECTED = 13;
46    private static final int MSG_TURN_ON_PROXIMITY_SENSOR = 14;
47    private static final int MSG_TURN_OFF_PROXIMITY_SENSOR = 15;
48    private static final int MSG_MERGE_CONFERENCE = 16;
49    private static final int MSG_SWAP_CONFERENCE = 17;
50
51    private final class InCallAdapterHandler extends Handler {
52        @Override
53        public void handleMessage(Message msg) {
54            Call call;
55            switch (msg.what) {
56                case MSG_ANSWER_CALL: {
57                    SomeArgs args = (SomeArgs) msg.obj;
58                    try {
59                        call = mCallIdMapper.getCall(args.arg1);
60                        int videoState = (int) args.arg2;
61                        if (call != null) {
62                            mCallsManager.answerCall(call, videoState);
63                        } else {
64                            Log.w(this, "answerCall, unknown call id: %s", msg.obj);
65                        }
66                    } finally {
67                        args.recycle();
68                    }
69                    break;
70                }
71                case MSG_REJECT_CALL: {
72                    SomeArgs args = (SomeArgs) msg.obj;
73                    try {
74                        call = mCallIdMapper.getCall(args.arg1);
75                        boolean rejectWithMessage = args.argi1 == 1;
76                        String textMessage = (String) args.arg2;
77                        if (call != null) {
78                            mCallsManager.rejectCall(call, rejectWithMessage, textMessage);
79                        } else {
80                            Log.w(this, "setRingback, unknown call id: %s", args.arg1);
81                        }
82                    } finally {
83                        args.recycle();
84                    }
85                    break;
86                }
87                case MSG_PLAY_DTMF_TONE:
88                    call = mCallIdMapper.getCall(msg.obj);
89                    if (call != null) {
90                        mCallsManager.playDtmfTone(call, (char) msg.arg1);
91                    } else {
92                        Log.w(this, "playDtmfTone, unknown call id: %s", msg.obj);
93                    }
94                    break;
95                case MSG_STOP_DTMF_TONE:
96                    call = mCallIdMapper.getCall(msg.obj);
97                    if (call != null) {
98                        mCallsManager.stopDtmfTone(call);
99                    } else {
100                        Log.w(this, "stopDtmfTone, unknown call id: %s", msg.obj);
101                    }
102                    break;
103                case MSG_POST_DIAL_CONTINUE:
104                    call = mCallIdMapper.getCall(msg.obj);
105                    if (call != null) {
106                        mCallsManager.postDialContinue(call, msg.arg1 == 1);
107                    } else {
108                        Log.w(this, "postDialContinue, unknown call id: %s", msg.obj);
109                    }
110                    break;
111                case MSG_DISCONNECT_CALL:
112                    call = mCallIdMapper.getCall(msg.obj);
113                    if (call != null) {
114                        mCallsManager.disconnectCall(call);
115                    } else {
116                        Log.w(this, "disconnectCall, unknown call id: %s", msg.obj);
117                    }
118                    break;
119                case MSG_HOLD_CALL:
120                    call = mCallIdMapper.getCall(msg.obj);
121                    if (call != null) {
122                        mCallsManager.holdCall(call);
123                    } else {
124                        Log.w(this, "holdCall, unknown call id: %s", msg.obj);
125                    }
126                    break;
127                case MSG_UNHOLD_CALL:
128                    call = mCallIdMapper.getCall(msg.obj);
129                    if (call != null) {
130                        mCallsManager.unholdCall(call);
131                    } else {
132                        Log.w(this, "unholdCall, unknown call id: %s", msg.obj);
133                    }
134                    break;
135                case MSG_PHONE_ACCOUNT_SELECTED: {
136                    SomeArgs args = (SomeArgs) msg.obj;
137                    try {
138                        call = mCallIdMapper.getCall(args.arg1);
139                        if (call != null) {
140                            mCallsManager.phoneAccountSelected(call,
141                                    (PhoneAccountHandle) args.arg2, args.argi1 == 1);
142                        } else {
143                            Log.w(this, "phoneAccountSelected, unknown call id: %s", args.arg1);
144                        }
145                    } finally {
146                        args.recycle();
147                    }
148                    break;
149                }
150                case MSG_MUTE:
151                    mCallsManager.mute(msg.arg1 == 1);
152                    break;
153                case MSG_SET_AUDIO_ROUTE:
154                    mCallsManager.setAudioRoute(msg.arg1);
155                    break;
156                case MSG_CONFERENCE: {
157                    SomeArgs args = (SomeArgs) msg.obj;
158                    try {
159                        call = mCallIdMapper.getCall(args.arg1);
160                        Call otherCall = mCallIdMapper.getCall(args.arg2);
161                        if (call != null && otherCall != null) {
162                            mCallsManager.conference(call, otherCall);
163                        } else {
164                            Log.w(this, "conference, unknown call id: %s", msg.obj);
165                        }
166                    } finally {
167                        args.recycle();
168                    }
169                    break;
170                }
171                case MSG_SPLIT_FROM_CONFERENCE:
172                    call = mCallIdMapper.getCall(msg.obj);
173                    if (call != null) {
174                        call.splitFromConference();
175                    } else {
176                        Log.w(this, "splitFromConference, unknown call id: %s", msg.obj);
177                    }
178                    break;
179                case MSG_TURN_ON_PROXIMITY_SENSOR:
180                    mCallsManager.turnOnProximitySensor();
181                    break;
182                case MSG_TURN_OFF_PROXIMITY_SENSOR:
183                    mCallsManager.turnOffProximitySensor((boolean) msg.obj);
184                    break;
185                case MSG_MERGE_CONFERENCE:
186                    call = mCallIdMapper.getCall(msg.obj);
187                    if (call != null) {
188                        call.mergeConference();
189                    } else {
190                        Log.w(this, "mergeConference, unknown call id: %s", msg.obj);
191                    }
192                    break;
193                case MSG_SWAP_CONFERENCE:
194                    call = mCallIdMapper.getCall(msg.obj);
195                    if (call != null) {
196                        call.swapConference();
197                    } else {
198                        Log.w(this, "swapConference, unknown call id: %s", msg.obj);
199                    }
200                    break;
201            }
202        }
203    }
204
205    private final CallsManager mCallsManager;
206    private final Handler mHandler = new InCallAdapterHandler();
207    private final CallIdMapper mCallIdMapper;
208
209    /** Persists the specified parameters. */
210    public InCallAdapter(CallsManager callsManager, CallIdMapper callIdMapper) {
211        ThreadUtil.checkOnMainThread();
212        mCallsManager = callsManager;
213        mCallIdMapper = callIdMapper;
214    }
215
216    @Override
217    public void answerCall(String callId, int videoState) {
218        Log.d(this, "answerCall(%s,%d)", callId, videoState);
219        if (mCallIdMapper.isValidCallId(callId)) {
220            SomeArgs args = SomeArgs.obtain();
221            args.arg1 = callId;
222            args.arg2 = videoState;
223            mHandler.obtainMessage(MSG_ANSWER_CALL, args).sendToTarget();
224        }
225    }
226
227    @Override
228    public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) {
229        Log.d(this, "rejectCall(%s,%b,%s)", callId, rejectWithMessage, textMessage);
230        if (mCallIdMapper.isValidCallId(callId)) {
231            SomeArgs args = SomeArgs.obtain();
232            args.arg1 = callId;
233            args.argi1 = rejectWithMessage ? 1 : 0;
234            args.arg2 = textMessage;
235            mHandler.obtainMessage(MSG_REJECT_CALL, args).sendToTarget();
236        }
237    }
238
239    @Override
240    public void playDtmfTone(String callId, char digit) {
241        Log.d(this, "playDtmfTone(%s,%c)", callId, digit);
242        if (mCallIdMapper.isValidCallId(callId)) {
243            mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, (int) digit, 0, callId).sendToTarget();
244        }
245    }
246
247    @Override
248    public void stopDtmfTone(String callId) {
249        Log.d(this, "stopDtmfTone(%s)", callId);
250        if (mCallIdMapper.isValidCallId(callId)) {
251            mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
252        }
253    }
254
255    @Override
256    public void postDialContinue(String callId, boolean proceed) {
257        Log.d(this, "postDialContinue(%s)", callId);
258        if (mCallIdMapper.isValidCallId(callId)) {
259            mHandler.obtainMessage(MSG_POST_DIAL_CONTINUE, proceed ? 1 : 0, 0, callId).sendToTarget();
260        }
261    }
262
263    @Override
264    public void disconnectCall(String callId) {
265        Log.v(this, "disconnectCall: %s", callId);
266        if (mCallIdMapper.isValidCallId(callId)) {
267            mHandler.obtainMessage(MSG_DISCONNECT_CALL, callId).sendToTarget();
268        }
269    }
270
271    @Override
272    public void holdCall(String callId) {
273        if (mCallIdMapper.isValidCallId(callId)) {
274            mHandler.obtainMessage(MSG_HOLD_CALL, callId).sendToTarget();
275        }
276    }
277
278    @Override
279    public void unholdCall(String callId) {
280        if (mCallIdMapper.isValidCallId(callId)) {
281            mHandler.obtainMessage(MSG_UNHOLD_CALL, callId).sendToTarget();
282        }
283    }
284
285    @Override
286    public void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle,
287            boolean setDefault) {
288        if (mCallIdMapper.isValidCallId(callId)) {
289            SomeArgs args = SomeArgs.obtain();
290            args.arg1 = callId;
291            args.arg2 = accountHandle;
292            args.argi1 = setDefault? 1 : 0;
293            mHandler.obtainMessage(MSG_PHONE_ACCOUNT_SELECTED, args).sendToTarget();
294        }
295    }
296
297    @Override
298    public void mute(boolean shouldMute) {
299        mHandler.obtainMessage(MSG_MUTE, shouldMute ? 1 : 0, 0).sendToTarget();
300    }
301
302    @Override
303    public void setAudioRoute(int route) {
304        mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, route, 0).sendToTarget();
305    }
306
307    @Override
308    public void conference(String callId, String otherCallId) {
309        if (mCallIdMapper.isValidCallId(callId) &&
310                mCallIdMapper.isValidCallId(otherCallId)) {
311            SomeArgs args = SomeArgs.obtain();
312            args.arg1 = callId;
313            args.arg2 = otherCallId;
314            mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
315        }
316    }
317
318    @Override
319    public void splitFromConference(String callId) {
320        if (mCallIdMapper.isValidCallId(callId)) {
321            mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
322        }
323    }
324
325    @Override
326    public void mergeConference(String callId) {
327        if (mCallIdMapper.isValidCallId(callId)) {
328            mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();
329        }
330    }
331
332    @Override
333    public void swapConference(String callId) {
334        if (mCallIdMapper.isValidCallId(callId)) {
335            mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();
336        }
337    }
338
339    @Override
340    public void turnOnProximitySensor() {
341        mHandler.obtainMessage(MSG_TURN_ON_PROXIMITY_SENSOR).sendToTarget();
342    }
343
344    @Override
345    public void turnOffProximitySensor(boolean screenOnImmediately) {
346        mHandler.obtainMessage(MSG_TURN_OFF_PROXIMITY_SENSOR, screenOnImmediately).sendToTarget();
347    }
348}
349