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