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.ims.internal;
18
19import android.net.Uri;
20import android.os.Handler;
21import android.os.IBinder;
22import android.os.Looper;
23import android.os.Message;
24import android.os.RegistrantList;
25import android.os.RemoteException;
26import android.telecom.Connection;
27import android.telecom.VideoProfile;
28import android.view.Surface;
29
30import com.android.internal.os.SomeArgs;
31
32import java.util.Collections;
33import java.util.Set;
34import java.util.concurrent.ConcurrentHashMap;
35
36/**
37 * Subclass implementation of {@link Connection.VideoProvider}. This intermediates and
38 * communicates with the actual implementation of the video call provider in the IMS service; it is
39 * in essence, a wrapper around the IMS's video call provider implementation.
40 *
41 * This class maintains a binder by which the ImsVideoCallProvider's implementation can communicate
42 * its intent to invoke callbacks. In this class, the message across this binder is handled, and
43 * the superclass's methods are used to execute the callbacks.
44 *
45 * @hide
46 */
47public class ImsVideoCallProviderWrapper extends Connection.VideoProvider {
48
49    public interface ImsVideoProviderWrapperCallback {
50        void onReceiveSessionModifyResponse(int status, VideoProfile requestProfile,
51                VideoProfile responseProfile);
52    }
53
54    private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
55    private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
56    private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
57    private static final int MSG_CHANGE_PEER_DIMENSIONS = 4;
58    private static final int MSG_CHANGE_CALL_DATA_USAGE = 5;
59    private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 6;
60    private static final int MSG_CHANGE_VIDEO_QUALITY = 7;
61
62    private final IImsVideoCallProvider mVideoCallProvider;
63    private final ImsVideoCallCallback mBinder;
64    private RegistrantList mDataUsageUpdateRegistrants = new RegistrantList();
65    private final Set<ImsVideoProviderWrapperCallback> mCallbacks = Collections.newSetFromMap(
66            new ConcurrentHashMap<ImsVideoProviderWrapperCallback, Boolean>(8, 0.9f, 1));
67
68    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
69        @Override
70        public void binderDied() {
71            mVideoCallProvider.asBinder().unlinkToDeath(this, 0);
72        }
73    };
74
75    /**
76     * IImsVideoCallCallback stub implementation.
77     */
78    private final class ImsVideoCallCallback extends IImsVideoCallCallback.Stub {
79        @Override
80        public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
81            mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST,
82                    VideoProfile).sendToTarget();
83        }
84
85        @Override
86        public void receiveSessionModifyResponse(
87                int status, VideoProfile requestProfile, VideoProfile responseProfile) {
88            SomeArgs args = SomeArgs.obtain();
89            args.arg1 = status;
90            args.arg2 = requestProfile;
91            args.arg3 = responseProfile;
92            mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
93        }
94
95        @Override
96        public void handleCallSessionEvent(int event) {
97            mHandler.obtainMessage(MSG_HANDLE_CALL_SESSION_EVENT, event).sendToTarget();
98        }
99
100        @Override
101        public void changePeerDimensions(int width, int height) {
102            SomeArgs args = SomeArgs.obtain();
103            args.arg1 = width;
104            args.arg2 = height;
105            mHandler.obtainMessage(MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
106        }
107
108        @Override
109        public void changeVideoQuality(int videoQuality) {
110            mHandler.obtainMessage(MSG_CHANGE_VIDEO_QUALITY, videoQuality, 0).sendToTarget();
111        }
112
113        @Override
114        public void changeCallDataUsage(long dataUsage) {
115            mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, dataUsage).sendToTarget();
116        }
117
118        @Override
119        public void changeCameraCapabilities(
120                VideoProfile.CameraCapabilities cameraCapabilities) {
121            mHandler.obtainMessage(MSG_CHANGE_CAMERA_CAPABILITIES,
122                    cameraCapabilities).sendToTarget();
123        }
124    }
125
126    public void registerForDataUsageUpdate(Handler h, int what, Object obj) {
127        mDataUsageUpdateRegistrants.addUnique(h, what, obj);
128    }
129
130    public void unregisterForDataUsageUpdate(Handler h) {
131        mDataUsageUpdateRegistrants.remove(h);
132    }
133
134    public void addImsVideoProviderCallback(ImsVideoProviderWrapperCallback callback) {
135        mCallbacks.add(callback);
136    }
137
138    public void removeImsVideoProviderCallback(ImsVideoProviderWrapperCallback callback) {
139        mCallbacks.remove(callback);
140    }
141
142    /** Default handler used to consolidate binder method calls onto a single thread. */
143    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
144        @Override
145        public void handleMessage(Message msg) {
146            SomeArgs args;
147            switch (msg.what) {
148                case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
149                    receiveSessionModifyRequest((VideoProfile) msg.obj);
150                    break;
151                case MSG_RECEIVE_SESSION_MODIFY_RESPONSE:
152                    args = (SomeArgs) msg.obj;
153                    try {
154                        int status = (int) args.arg1;
155                        VideoProfile requestProfile = (VideoProfile) args.arg2;
156                        VideoProfile responseProfile = (VideoProfile) args.arg3;
157
158                        receiveSessionModifyResponse(status, requestProfile, responseProfile);
159
160                        // Notify any local Telephony components interested in upgrade responses.
161                        for (ImsVideoProviderWrapperCallback callback : mCallbacks) {
162                            if (callback != null) {
163                                callback.onReceiveSessionModifyResponse(status, requestProfile,
164                                        responseProfile);
165                            }
166                        }
167                    } finally {
168                        args.recycle();
169                    }
170                    break;
171                case MSG_HANDLE_CALL_SESSION_EVENT:
172                    handleCallSessionEvent((int) msg.obj);
173                    break;
174                case MSG_CHANGE_PEER_DIMENSIONS:
175                    args = (SomeArgs) msg.obj;
176                    try {
177                        int width = (int) args.arg1;
178                        int height = (int) args.arg2;
179                        changePeerDimensions(width, height);
180                    } finally {
181                        args.recycle();
182                    }
183                    break;
184                case MSG_CHANGE_CALL_DATA_USAGE:
185                    // TODO: We should use callback in the future.
186                    setCallDataUsage((long) msg.obj);
187                    mDataUsageUpdateRegistrants.notifyResult(msg.obj);
188                    break;
189                case MSG_CHANGE_CAMERA_CAPABILITIES:
190                    changeCameraCapabilities((VideoProfile.CameraCapabilities) msg.obj);
191                    break;
192                case MSG_CHANGE_VIDEO_QUALITY:
193                    changeVideoQuality(msg.arg1);
194                    break;
195                default:
196                    break;
197            }
198        }
199    };
200
201    /**
202     * Instantiates an instance of the ImsVideoCallProvider, taking in the binder for IMS's video
203     * call provider implementation.
204     *
205     * @param VideoProvider
206     */
207    public ImsVideoCallProviderWrapper(IImsVideoCallProvider VideoProvider)
208            throws RemoteException {
209        mVideoCallProvider = VideoProvider;
210        mVideoCallProvider.asBinder().linkToDeath(mDeathRecipient, 0);
211
212        mBinder = new ImsVideoCallCallback();
213        mVideoCallProvider.setCallback(mBinder);
214    }
215
216    /** @inheritDoc */
217    public void onSetCamera(String cameraId) {
218        try {
219            mVideoCallProvider.setCamera(cameraId);
220        } catch (RemoteException e) {
221        }
222    }
223
224    /** @inheritDoc */
225    public void onSetPreviewSurface(Surface surface) {
226        try {
227            mVideoCallProvider.setPreviewSurface(surface);
228        } catch (RemoteException e) {
229        }
230    }
231
232    /** @inheritDoc */
233    public void onSetDisplaySurface(Surface surface) {
234        try {
235            mVideoCallProvider.setDisplaySurface(surface);
236        } catch (RemoteException e) {
237        }
238    }
239
240    /** @inheritDoc */
241    public void onSetDeviceOrientation(int rotation) {
242        try {
243            mVideoCallProvider.setDeviceOrientation(rotation);
244        } catch (RemoteException e) {
245        }
246    }
247
248    /** @inheritDoc */
249    public void onSetZoom(float value) {
250        try {
251            mVideoCallProvider.setZoom(value);
252        } catch (RemoteException e) {
253        }
254    }
255
256    /** @inheritDoc */
257    public void onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
258        try {
259            mVideoCallProvider.sendSessionModifyRequest(fromProfile, toProfile);
260        } catch (RemoteException e) {
261        }
262    }
263
264    /** @inheritDoc */
265    public void onSendSessionModifyResponse(VideoProfile responseProfile) {
266        try {
267            mVideoCallProvider.sendSessionModifyResponse(responseProfile);
268        } catch (RemoteException e) {
269        }
270    }
271
272    /** @inheritDoc */
273    public void onRequestCameraCapabilities() {
274        try {
275            mVideoCallProvider.requestCameraCapabilities();
276        } catch (RemoteException e) {
277        }
278    }
279
280    /** @inheritDoc */
281    public void onRequestConnectionDataUsage() {
282        try {
283            mVideoCallProvider.requestCallDataUsage();
284        } catch (RemoteException e) {
285        }
286    }
287
288    /** @inheritDoc */
289    public void onSetPauseImage(Uri uri) {
290        try {
291            mVideoCallProvider.setPauseImage(uri);
292        } catch (RemoteException e) {
293        }
294    }
295}
296