Home | History | Annotate | Download | only in tv
      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, int displayId) {
    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