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 17 package android.media.tv; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.graphics.Rect; 22 import android.media.PlaybackParams; 23 import android.net.Uri; 24 import android.os.Bundle; 25 import android.os.IBinder; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.util.Log; 29 import android.view.InputChannel; 30 import android.view.InputEvent; 31 import android.view.InputEventReceiver; 32 import android.view.Surface; 33 34 import com.android.internal.os.HandlerCaller; 35 import com.android.internal.os.SomeArgs; 36 37 /** 38 * Implements the internal ITvInputSession interface to convert incoming calls on to it back to 39 * calls on the public TvInputSession interface, scheduling them on the main thread of the process. 40 * 41 * @hide 42 */ 43 public class ITvInputSessionWrapper extends ITvInputSession.Stub implements HandlerCaller.Callback { 44 private static final String TAG = "TvInputSessionWrapper"; 45 46 private static final int EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS = 50; 47 private static final int EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS = 2000; 48 private static final int EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS = 5 * 1000; 49 50 private static final int DO_RELEASE = 1; 51 private static final int DO_SET_MAIN = 2; 52 private static final int DO_SET_SURFACE = 3; 53 private static final int DO_DISPATCH_SURFACE_CHANGED = 4; 54 private static final int DO_SET_STREAM_VOLUME = 5; 55 private static final int DO_TUNE = 6; 56 private static final int DO_SET_CAPTION_ENABLED = 7; 57 private static final int DO_SELECT_TRACK = 8; 58 private static final int DO_APP_PRIVATE_COMMAND = 9; 59 private static final int DO_CREATE_OVERLAY_VIEW = 10; 60 private static final int DO_RELAYOUT_OVERLAY_VIEW = 11; 61 private static final int DO_REMOVE_OVERLAY_VIEW = 12; 62 private static final int DO_UNBLOCK_CONTENT = 13; 63 private static final int DO_TIME_SHIFT_PLAY = 14; 64 private static final int DO_TIME_SHIFT_PAUSE = 15; 65 private static final int DO_TIME_SHIFT_RESUME = 16; 66 private static final int DO_TIME_SHIFT_SEEK_TO = 17; 67 private static final int DO_TIME_SHIFT_SET_PLAYBACK_PARAMS = 18; 68 private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 19; 69 private static final int DO_START_RECORDING = 20; 70 private static final int DO_STOP_RECORDING = 21; 71 private static final int DO_PAUSE_RECORDING = 22; 72 private static final int DO_RESUME_RECORDING = 23; 73 74 private final boolean mIsRecordingSession; 75 private final HandlerCaller mCaller; 76 77 private TvInputService.Session mTvInputSessionImpl; 78 private TvInputService.RecordingSession mTvInputRecordingSessionImpl; 79 80 private InputChannel mChannel; 81 private TvInputEventReceiver mReceiver; 82 ITvInputSessionWrapper(Context context, TvInputService.Session sessionImpl, InputChannel channel)83 public ITvInputSessionWrapper(Context context, TvInputService.Session sessionImpl, 84 InputChannel channel) { 85 mIsRecordingSession = false; 86 mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */); 87 mTvInputSessionImpl = sessionImpl; 88 mChannel = channel; 89 if (channel != null) { 90 mReceiver = new TvInputEventReceiver(channel, context.getMainLooper()); 91 } 92 } 93 94 // For the recording session ITvInputSessionWrapper(Context context, TvInputService.RecordingSession recordingSessionImpl)95 public ITvInputSessionWrapper(Context context, 96 TvInputService.RecordingSession recordingSessionImpl) { 97 mIsRecordingSession = true; 98 mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */); 99 mTvInputRecordingSessionImpl = recordingSessionImpl; 100 } 101 102 @Override executeMessage(Message msg)103 public void executeMessage(Message msg) { 104 if ((mIsRecordingSession && mTvInputRecordingSessionImpl == null) 105 || (!mIsRecordingSession && mTvInputSessionImpl == null)) { 106 return; 107 } 108 109 long startTime = System.nanoTime(); 110 switch (msg.what) { 111 case DO_RELEASE: { 112 if (mIsRecordingSession) { 113 mTvInputRecordingSessionImpl.release(); 114 mTvInputRecordingSessionImpl = null; 115 } else { 116 mTvInputSessionImpl.release(); 117 mTvInputSessionImpl = null; 118 if (mReceiver != null) { 119 mReceiver.dispose(); 120 mReceiver = null; 121 } 122 if (mChannel != null) { 123 mChannel.dispose(); 124 mChannel = null; 125 } 126 } 127 break; 128 } 129 case DO_SET_MAIN: { 130 mTvInputSessionImpl.setMain((Boolean) msg.obj); 131 break; 132 } 133 case DO_SET_SURFACE: { 134 mTvInputSessionImpl.setSurface((Surface) msg.obj); 135 break; 136 } 137 case DO_DISPATCH_SURFACE_CHANGED: { 138 SomeArgs args = (SomeArgs) msg.obj; 139 mTvInputSessionImpl.dispatchSurfaceChanged(args.argi1, args.argi2, args.argi3); 140 args.recycle(); 141 break; 142 } 143 case DO_SET_STREAM_VOLUME: { 144 mTvInputSessionImpl.setStreamVolume((Float) msg.obj); 145 break; 146 } 147 case DO_TUNE: { 148 SomeArgs args = (SomeArgs) msg.obj; 149 if (mIsRecordingSession) { 150 mTvInputRecordingSessionImpl.tune((Uri) args.arg1, (Bundle) args.arg2); 151 } else { 152 mTvInputSessionImpl.tune((Uri) args.arg1, (Bundle) args.arg2); 153 } 154 args.recycle(); 155 break; 156 } 157 case DO_SET_CAPTION_ENABLED: { 158 mTvInputSessionImpl.setCaptionEnabled((Boolean) msg.obj); 159 break; 160 } 161 case DO_SELECT_TRACK: { 162 SomeArgs args = (SomeArgs) msg.obj; 163 mTvInputSessionImpl.selectTrack((Integer) args.arg1, (String) args.arg2); 164 args.recycle(); 165 break; 166 } 167 case DO_APP_PRIVATE_COMMAND: { 168 SomeArgs args = (SomeArgs) msg.obj; 169 if (mIsRecordingSession) { 170 mTvInputRecordingSessionImpl.appPrivateCommand( 171 (String) args.arg1, (Bundle) args.arg2); 172 } else { 173 mTvInputSessionImpl.appPrivateCommand((String) args.arg1, (Bundle) args.arg2); 174 } 175 args.recycle(); 176 break; 177 } 178 case DO_CREATE_OVERLAY_VIEW: { 179 SomeArgs args = (SomeArgs) msg.obj; 180 mTvInputSessionImpl.createOverlayView((IBinder) args.arg1, (Rect) args.arg2); 181 args.recycle(); 182 break; 183 } 184 case DO_RELAYOUT_OVERLAY_VIEW: { 185 mTvInputSessionImpl.relayoutOverlayView((Rect) msg.obj); 186 break; 187 } 188 case DO_REMOVE_OVERLAY_VIEW: { 189 mTvInputSessionImpl.removeOverlayView(true); 190 break; 191 } 192 case DO_UNBLOCK_CONTENT: { 193 mTvInputSessionImpl.unblockContent((String) msg.obj); 194 break; 195 } 196 case DO_TIME_SHIFT_PLAY: { 197 mTvInputSessionImpl.timeShiftPlay((Uri) msg.obj); 198 break; 199 } 200 case DO_TIME_SHIFT_PAUSE: { 201 mTvInputSessionImpl.timeShiftPause(); 202 break; 203 } 204 case DO_TIME_SHIFT_RESUME: { 205 mTvInputSessionImpl.timeShiftResume(); 206 break; 207 } 208 case DO_TIME_SHIFT_SEEK_TO: { 209 mTvInputSessionImpl.timeShiftSeekTo((Long) msg.obj); 210 break; 211 } 212 case DO_TIME_SHIFT_SET_PLAYBACK_PARAMS: { 213 mTvInputSessionImpl.timeShiftSetPlaybackParams((PlaybackParams) msg.obj); 214 break; 215 } 216 case DO_TIME_SHIFT_ENABLE_POSITION_TRACKING: { 217 mTvInputSessionImpl.timeShiftEnablePositionTracking((Boolean) msg.obj); 218 break; 219 } 220 case DO_START_RECORDING: { 221 SomeArgs args = (SomeArgs) msg.obj; 222 mTvInputRecordingSessionImpl.startRecording((Uri) args.arg1, (Bundle) args.arg2); 223 break; 224 } 225 case DO_STOP_RECORDING: { 226 mTvInputRecordingSessionImpl.stopRecording(); 227 break; 228 } 229 case DO_PAUSE_RECORDING: { 230 mTvInputRecordingSessionImpl.pauseRecording((Bundle) msg.obj); 231 break; 232 } 233 case DO_RESUME_RECORDING: { 234 mTvInputRecordingSessionImpl.resumeRecording((Bundle) msg.obj); 235 break; 236 } 237 default: { 238 Log.w(TAG, "Unhandled message code: " + msg.what); 239 break; 240 } 241 } 242 long durationMs = (System.nanoTime() - startTime) / (1000 * 1000); 243 if (durationMs > EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS) { 244 Log.w(TAG, "Handling message (" + msg.what + ") took too long time (duration=" 245 + durationMs + "ms)"); 246 if (msg.what == DO_TUNE && durationMs > EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS) { 247 throw new RuntimeException("Too much time to handle tune request. (" + durationMs 248 + "ms > " + EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS + "ms) " 249 + "Consider handling the tune request in a separate thread."); 250 } 251 if (durationMs > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) { 252 throw new RuntimeException("Too much time to handle a request. (type=" + msg.what + 253 ", " + durationMs + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms)."); 254 } 255 } 256 } 257 258 @Override release()259 public void release() { 260 if (!mIsRecordingSession) { 261 mTvInputSessionImpl.scheduleOverlayViewCleanup(); 262 } 263 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE)); 264 } 265 266 @Override setMain(boolean isMain)267 public void setMain(boolean isMain) { 268 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_MAIN, isMain)); 269 } 270 271 @Override setSurface(Surface surface)272 public void setSurface(Surface surface) { 273 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface)); 274 } 275 276 @Override dispatchSurfaceChanged(int format, int width, int height)277 public void dispatchSurfaceChanged(int format, int width, int height) { 278 mCaller.executeOrSendMessage(mCaller.obtainMessageIIII(DO_DISPATCH_SURFACE_CHANGED, 279 format, width, height, 0)); 280 } 281 282 @Override setVolume(float volume)283 public final void setVolume(float volume) { 284 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_STREAM_VOLUME, volume)); 285 } 286 287 @Override tune(Uri channelUri, Bundle params)288 public void tune(Uri channelUri, Bundle params) { 289 // Clear the pending tune requests. 290 mCaller.removeMessages(DO_TUNE); 291 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_TUNE, channelUri, params)); 292 } 293 294 @Override setCaptionEnabled(boolean enabled)295 public void setCaptionEnabled(boolean enabled) { 296 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_CAPTION_ENABLED, enabled)); 297 } 298 299 @Override selectTrack(int type, String trackId)300 public void selectTrack(int type, String trackId) { 301 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_SELECT_TRACK, type, trackId)); 302 } 303 304 @Override appPrivateCommand(String action, Bundle data)305 public void appPrivateCommand(String action, Bundle data) { 306 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, 307 data)); 308 } 309 310 @Override createOverlayView(IBinder windowToken, Rect frame)311 public void createOverlayView(IBinder windowToken, Rect frame) { 312 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_OVERLAY_VIEW, windowToken, 313 frame)); 314 } 315 316 @Override relayoutOverlayView(Rect frame)317 public void relayoutOverlayView(Rect frame) { 318 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_OVERLAY_VIEW, frame)); 319 } 320 321 @Override removeOverlayView()322 public void removeOverlayView() { 323 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW)); 324 } 325 326 @Override unblockContent(String unblockedRating)327 public void unblockContent(String unblockedRating) { 328 mCaller.executeOrSendMessage(mCaller.obtainMessageO( 329 DO_UNBLOCK_CONTENT, unblockedRating)); 330 } 331 332 @Override timeShiftPlay(Uri recordedProgramUri)333 public void timeShiftPlay(Uri recordedProgramUri) { 334 mCaller.executeOrSendMessage(mCaller.obtainMessageO( 335 DO_TIME_SHIFT_PLAY, recordedProgramUri)); 336 } 337 338 @Override timeShiftPause()339 public void timeShiftPause() { 340 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_TIME_SHIFT_PAUSE)); 341 } 342 343 @Override timeShiftResume()344 public void timeShiftResume() { 345 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_TIME_SHIFT_RESUME)); 346 } 347 348 @Override timeShiftSeekTo(long timeMs)349 public void timeShiftSeekTo(long timeMs) { 350 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TIME_SHIFT_SEEK_TO, timeMs)); 351 } 352 353 @Override timeShiftSetPlaybackParams(PlaybackParams params)354 public void timeShiftSetPlaybackParams(PlaybackParams params) { 355 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TIME_SHIFT_SET_PLAYBACK_PARAMS, 356 params)); 357 } 358 359 @Override timeShiftEnablePositionTracking(boolean enable)360 public void timeShiftEnablePositionTracking(boolean enable) { 361 mCaller.executeOrSendMessage(mCaller.obtainMessageO( 362 DO_TIME_SHIFT_ENABLE_POSITION_TRACKING, enable)); 363 } 364 365 @Override startRecording(@ullable Uri programUri, @Nullable Bundle params)366 public void startRecording(@Nullable Uri programUri, @Nullable Bundle params) { 367 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_RECORDING, programUri, 368 params)); 369 } 370 371 @Override stopRecording()372 public void stopRecording() { 373 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_STOP_RECORDING)); 374 } 375 376 @Override pauseRecording(@ullable Bundle params)377 public void pauseRecording(@Nullable Bundle params) { 378 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_PAUSE_RECORDING, params)); 379 } 380 381 @Override resumeRecording(@ullable Bundle params)382 public void resumeRecording(@Nullable Bundle params) { 383 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RESUME_RECORDING, params)); 384 } 385 386 private final class TvInputEventReceiver extends InputEventReceiver { TvInputEventReceiver(InputChannel inputChannel, Looper looper)387 public TvInputEventReceiver(InputChannel inputChannel, Looper looper) { 388 super(inputChannel, looper); 389 } 390 391 @Override onInputEvent(InputEvent event)392 public void onInputEvent(InputEvent event) { 393 if (mTvInputSessionImpl == null) { 394 // The session has been finished. 395 finishInputEvent(event, false); 396 return; 397 } 398 399 int handled = mTvInputSessionImpl.dispatchInputEvent(event, this); 400 if (handled != TvInputManager.Session.DISPATCH_IN_PROGRESS) { 401 finishInputEvent(event, handled == TvInputManager.Session.DISPATCH_HANDLED); 402 } 403 } 404 } 405 } 406