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