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                    mCallsManager.postDialContinue(call, msg.arg1 == 1);
106                    call = mCallIdMapper.getCall(msg.obj);
107                    if (call != null) {
108                        mCallsManager.postDialContinue(call, msg.arg1 == 1);
109                    } else {
110                        Log.w(this, "postDialContinue, unknown call id: %s", msg.obj);
111                    }
112                    break;
113                case MSG_DISCONNECT_CALL:
114                    call = mCallIdMapper.getCall(msg.obj);
115                    if (call != null) {
116                        mCallsManager.disconnectCall(call);
117                    } else {
118                        Log.w(this, "disconnectCall, unknown call id: %s", msg.obj);
119                    }
120                    break;
121                case MSG_HOLD_CALL:
122                    call = mCallIdMapper.getCall(msg.obj);
123                    if (call != null) {
124                        mCallsManager.holdCall(call);
125                    } else {
126                        Log.w(this, "holdCall, unknown call id: %s", msg.obj);
127                    }
128                    break;
129                case MSG_UNHOLD_CALL:
130                    call = mCallIdMapper.getCall(msg.obj);
131                    if (call != null) {
132                        mCallsManager.unholdCall(call);
133                    } else {
134                        Log.w(this, "unholdCall, unknown call id: %s", msg.obj);
135                    }
136                    break;
137                case MSG_PHONE_ACCOUNT_SELECTED: {
138                    SomeArgs args = (SomeArgs) msg.obj;
139                    try {
140                        call = mCallIdMapper.getCall(args.arg1);
141                        if (call != null) {
142                            mCallsManager.phoneAccountSelected(call, (PhoneAccountHandle) args.arg2);
143                        } else {
144                            Log.w(this, "phoneAccountSelected, unknown call id: %s", args.arg1);
145                        }
146                    } finally {
147                        args.recycle();
148                    }
149                    break;
150                }
151                case MSG_MUTE:
152                    mCallsManager.mute(msg.arg1 == 1);
153                    break;
154                case MSG_SET_AUDIO_ROUTE:
155                    mCallsManager.setAudioRoute(msg.arg1);
156                    break;
157                case MSG_CONFERENCE: {
158                    SomeArgs args = (SomeArgs) msg.obj;
159                    try {
160                        call = mCallIdMapper.getCall(args.arg1);
161                        Call otherCall = mCallIdMapper.getCall(args.arg2);
162                        if (call != null && otherCall != null) {
163                            mCallsManager.conference(call, otherCall);
164                        } else {
165                            Log.w(this, "conference, unknown call id: %s", msg.obj);
166                        }
167                    } finally {
168                        args.recycle();
169                    }
170                    break;
171                }
172                case MSG_SPLIT_FROM_CONFERENCE:
173                    call = mCallIdMapper.getCall(msg.obj);
174                    if (call != null) {
175                        call.splitFromConference();
176                    } else {
177                        Log.w(this, "splitFromConference, unknown call id: %s", msg.obj);
178                    }
179                    break;
180                case MSG_TURN_ON_PROXIMITY_SENSOR:
181                    mCallsManager.turnOnProximitySensor();
182                    break;
183                case MSG_TURN_OFF_PROXIMITY_SENSOR:
184                    mCallsManager.turnOffProximitySensor((boolean) msg.obj);
185                    break;
186                case MSG_MERGE_CONFERENCE:
187                    call = mCallIdMapper.getCall(msg.obj);
188                    if (call != null) {
189                        call.mergeConference();
190                    } else {
191                        Log.w(this, "mergeConference, unknown call id: %s", msg.obj);
192                    }
193                    break;
194                case MSG_SWAP_CONFERENCE:
195                    call = mCallIdMapper.getCall(msg.obj);
196                    if (call != null) {
197                        call.swapConference();
198                    } else {
199                        Log.w(this, "swapConference, unknown call id: %s", msg.obj);
200                    }
201                    break;
202            }
203        }
204    }
205
206    private final CallsManager mCallsManager;
207    private final Handler mHandler = new InCallAdapterHandler();
208    private final CallIdMapper mCallIdMapper;
209
210    /** Persists the specified parameters. */
211    public InCallAdapter(CallsManager callsManager, CallIdMapper callIdMapper) {
212        ThreadUtil.checkOnMainThread();
213        mCallsManager = callsManager;
214        mCallIdMapper = callIdMapper;
215    }
216
217    @Override
218    public void answerCall(String callId, int videoState) {
219        Log.d(this, "answerCall(%s,%d)", callId, videoState);
220        if (mCallIdMapper.isValidCallId(callId)) {
221            SomeArgs args = SomeArgs.obtain();
222            args.arg1 = callId;
223            args.arg2 = videoState;
224            mHandler.obtainMessage(MSG_ANSWER_CALL, args).sendToTarget();
225        }
226    }
227
228    @Override
229    public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) {
230        Log.d(this, "rejectCall(%s,%b,%s)", callId, rejectWithMessage, textMessage);
231        if (mCallIdMapper.isValidCallId(callId)) {
232            SomeArgs args = SomeArgs.obtain();
233            args.arg1 = callId;
234            args.argi1 = rejectWithMessage ? 1 : 0;
235            args.arg2 = textMessage;
236            mHandler.obtainMessage(MSG_REJECT_CALL, args).sendToTarget();
237        }
238    }
239
240    @Override
241    public void playDtmfTone(String callId, char digit) {
242        Log.d(this, "playDtmfTone(%s,%c)", callId, digit);
243        if (mCallIdMapper.isValidCallId(callId)) {
244            mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, (int) digit, 0, callId).sendToTarget();
245        }
246    }
247
248    @Override
249    public void stopDtmfTone(String callId) {
250        Log.d(this, "stopDtmfTone(%s)", callId);
251        if (mCallIdMapper.isValidCallId(callId)) {
252            mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
253        }
254    }
255
256    @Override
257    public void postDialContinue(String callId, boolean proceed) {
258        Log.d(this, "postDialContinue(%s)", callId);
259        if (mCallIdMapper.isValidCallId(callId)) {
260            mHandler.obtainMessage(MSG_POST_DIAL_CONTINUE, proceed ? 1 : 0, 0, callId).sendToTarget();
261        }
262    }
263
264    @Override
265    public void disconnectCall(String callId) {
266        Log.v(this, "disconnectCall: %s", callId);
267        if (mCallIdMapper.isValidCallId(callId)) {
268            mHandler.obtainMessage(MSG_DISCONNECT_CALL, callId).sendToTarget();
269        }
270    }
271
272    @Override
273    public void holdCall(String callId) {
274        if (mCallIdMapper.isValidCallId(callId)) {
275            mHandler.obtainMessage(MSG_HOLD_CALL, callId).sendToTarget();
276        }
277    }
278
279    @Override
280    public void unholdCall(String callId) {
281        if (mCallIdMapper.isValidCallId(callId)) {
282            mHandler.obtainMessage(MSG_UNHOLD_CALL, callId).sendToTarget();
283        }
284    }
285
286    @Override
287    public void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle) {
288        if (mCallIdMapper.isValidCallId(callId)) {
289            SomeArgs args = SomeArgs.obtain();
290            args.arg1 = callId;
291            args.arg2 = accountHandle;
292            mHandler.obtainMessage(MSG_PHONE_ACCOUNT_SELECTED, args).sendToTarget();
293        }
294    }
295
296    @Override
297    public void mute(boolean shouldMute) {
298        mHandler.obtainMessage(MSG_MUTE, shouldMute ? 1 : 0, 0).sendToTarget();
299    }
300
301    @Override
302    public void setAudioRoute(int route) {
303        mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, route, 0).sendToTarget();
304    }
305
306    @Override
307    public void conference(String callId, String otherCallId) {
308        if (mCallIdMapper.isValidCallId(callId) &&
309                mCallIdMapper.isValidCallId(otherCallId)) {
310            SomeArgs args = SomeArgs.obtain();
311            args.arg1 = callId;
312            args.arg2 = otherCallId;
313            mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
314        }
315    }
316
317    @Override
318    public void splitFromConference(String callId) {
319        if (mCallIdMapper.isValidCallId(callId)) {
320            mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
321        }
322    }
323
324    @Override
325    public void mergeConference(String callId) {
326        if (mCallIdMapper.isValidCallId(callId)) {
327            mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();
328        }
329    }
330
331    @Override
332    public void swapConference(String callId) {
333        if (mCallIdMapper.isValidCallId(callId)) {
334            mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();
335        }
336    }
337
338    @Override
339    public void turnOnProximitySensor() {
340        mHandler.obtainMessage(MSG_TURN_ON_PROXIMITY_SENSOR).sendToTarget();
341    }
342
343    @Override
344    public void turnOffProximitySensor(boolean screenOnImmediately) {
345        mHandler.obtainMessage(MSG_TURN_OFF_PROXIMITY_SENSOR, screenOnImmediately).sendToTarget();
346    }
347}
348