• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.cts;
18 
19 import android.app.Activity;
20 import android.app.Instrumentation;
21 import android.content.Context;
22 import android.cts.util.PollingCheck;
23 import android.media.PlaybackParams;
24 import android.media.tv.TvContentRating;
25 import android.media.tv.TvContract;
26 import android.media.tv.TvInputInfo;
27 import android.media.tv.TvInputManager;
28 import android.media.tv.TvRecordingClient;
29 import android.media.tv.TvTrackInfo;
30 import android.media.tv.TvView;
31 import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingSession;
32 import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingRecordingSession;
33 import android.net.Uri;
34 import android.os.Bundle;
35 import android.os.SystemClock;
36 import android.test.ActivityInstrumentationTestCase2;
37 import android.text.TextUtils;
38 import android.view.InputDevice;
39 import android.view.KeyEvent;
40 import android.view.MotionEvent;
41 import android.view.Surface;
42 import android.view.SurfaceView;
43 import android.view.View;
44 import android.widget.LinearLayout;
45 
46 import android.tv.cts.R;
47 
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.Objects;
52 import java.util.Set;
53 
54 
55 /**
56  * Test {@link android.media.tv.TvInputService}.
57  */
58 public class TvInputServiceTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
59     /** The maximum time to wait for an operation. */
60     private static final long TIME_OUT = 15000L;
61     private static final String DUMMT_TRACK_ID = "dummyTrackId";
62     private static final TvTrackInfo DUMMY_TRACK =
63             new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, DUMMT_TRACK_ID)
64             .setVideoWidth(1920).setVideoHeight(1080).setLanguage("und").build();
65     private static Bundle sDummyBundle;
66 
67     private TvView mTvView;
68     private TvRecordingClient mTvRecordingClient;
69     private Activity mActivity;
70     private Instrumentation mInstrumentation;
71     private TvInputManager mManager;
72     private TvInputInfo mStubInfo;
73     private TvInputInfo mFaultyStubInfo;
74     private final StubCallback mCallback = new StubCallback();
75     private final StubTimeShiftPositionCallback mTimeShiftPositionCallback =
76             new StubTimeShiftPositionCallback();
77     private final StubRecordingCallback mRecordingCallback = new StubRecordingCallback();
78 
79     private static class StubCallback extends TvView.TvInputCallback {
80         private int mChannelRetunedCount;
81         private int mVideoAvailableCount;
82         private int mVideoUnavailableCount;
83         private int mTrackSelectedCount;
84         private int mTrackChangedCount;
85         private int mVideoSizeChanged;
86         private int mContentAllowedCount;
87         private int mContentBlockedCount;
88         private int mTimeShiftStatusChangedCount;
89 
90         private Uri mChannelRetunedUri;
91         private Integer mVideoUnavailableReason;
92         private Integer mTrackSelectedType;
93         private String mTrackSelectedTrackId;
94         private List<TvTrackInfo> mTracksChangedTrackList;
95         private TvContentRating mContentBlockedRating;
96         private Integer mTimeShiftStatusChangedStatus;
97 
98         @Override
onChannelRetuned(String inputId, Uri channelUri)99         public void onChannelRetuned(String inputId, Uri channelUri) {
100             mChannelRetunedCount++;
101             mChannelRetunedUri = channelUri;
102         }
103 
104         @Override
onVideoAvailable(String inputId)105         public void onVideoAvailable(String inputId) {
106             mVideoAvailableCount++;
107         }
108 
109         @Override
onVideoUnavailable(String inputId, int reason)110         public void onVideoUnavailable(String inputId, int reason) {
111             mVideoUnavailableCount++;
112             mVideoUnavailableReason = reason;
113         }
114 
115         @Override
onTrackSelected(String inputId, int type, String trackId)116         public void onTrackSelected(String inputId, int type, String trackId) {
117             mTrackSelectedCount++;
118             mTrackSelectedType = type;
119             mTrackSelectedTrackId = trackId;
120         }
121 
122         @Override
onTracksChanged(String inputId, List<TvTrackInfo> trackList)123         public void onTracksChanged(String inputId, List<TvTrackInfo> trackList) {
124             mTrackChangedCount++;
125             mTracksChangedTrackList = trackList;
126         }
127 
128         @Override
onVideoSizeChanged(String inputId, int width, int height)129         public void onVideoSizeChanged(String inputId, int width, int height) {
130             mVideoSizeChanged++;
131         }
132 
133         @Override
onContentAllowed(String inputId)134         public void onContentAllowed(String inputId) {
135             mContentAllowedCount++;
136         }
137 
138         @Override
onContentBlocked(String inputId, TvContentRating rating)139         public void onContentBlocked(String inputId, TvContentRating rating) {
140             mContentBlockedCount++;
141             mContentBlockedRating = rating;
142         }
143 
144         @Override
onTimeShiftStatusChanged(String inputId, int status)145         public void onTimeShiftStatusChanged(String inputId, int status) {
146             mTimeShiftStatusChangedCount++;
147             mTimeShiftStatusChangedStatus = status;
148         }
149 
resetCounts()150         public void resetCounts() {
151             mChannelRetunedCount = 0;
152             mVideoAvailableCount = 0;
153             mVideoUnavailableCount = 0;
154             mTrackSelectedCount = 0;
155             mTrackChangedCount = 0;
156             mContentAllowedCount = 0;
157             mContentBlockedCount = 0;
158             mTimeShiftStatusChangedCount = 0;
159         }
160 
resetPassedValues()161         public void resetPassedValues() {
162             mChannelRetunedUri = null;
163             mVideoUnavailableReason = null;
164             mTrackSelectedType = null;
165             mTrackSelectedTrackId = null;
166             mTracksChangedTrackList = null;
167             mContentBlockedRating = null;
168             mTimeShiftStatusChangedStatus = null;
169         }
170     }
171 
172     private static class StubTimeShiftPositionCallback extends TvView.TimeShiftPositionCallback {
173         private int mTimeShiftStartPositionChanged;
174         private int mTimeShiftCurrentPositionChanged;
175 
176         @Override
onTimeShiftStartPositionChanged(String inputId, long timeMs)177         public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
178             mTimeShiftStartPositionChanged++;
179         }
180 
181         @Override
onTimeShiftCurrentPositionChanged(String inputId, long timeMs)182         public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
183             mTimeShiftCurrentPositionChanged++;
184         }
185 
resetCounts()186         public void resetCounts() {
187             mTimeShiftStartPositionChanged = 0;
188             mTimeShiftCurrentPositionChanged = 0;
189         }
190     }
191 
TvInputServiceTest()192     public TvInputServiceTest() {
193         super(TvViewStubActivity.class);
194     }
195 
196     @Override
setUp()197     protected void setUp() throws Exception {
198         super.setUp();
199         if (!Utils.hasTvInputFramework(getActivity())) {
200             return;
201         }
202         mActivity = getActivity();
203         mInstrumentation = getInstrumentation();
204         mTvView = (TvView) mActivity.findViewById(R.id.tvview);
205         mTvRecordingClient = new TvRecordingClient(mActivity, "TvInputServiceTest",
206                 mRecordingCallback, null);
207         mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE);
208         for (TvInputInfo info : mManager.getTvInputList()) {
209             if (info.getServiceInfo().name.equals(CountingTvInputService.class.getName())) {
210                 mStubInfo = info;
211             }
212             if (info.getServiceInfo().name.equals(FaultyTvInputService.class.getName())) {
213                 mFaultyStubInfo = info;
214             }
215             if (mStubInfo != null && mFaultyStubInfo != null) {
216                 break;
217             }
218         }
219         assertNotNull(mStubInfo);
220         mTvView.setCallback(mCallback);
221 
222         CountingTvInputService.sSession = null;
223     }
224 
testTvInputServiceSession()225     public void testTvInputServiceSession() throws Throwable {
226         if (!Utils.hasTvInputFramework(getActivity())) {
227             return;
228         }
229         initDummyBundle();
230         verifyCommandTune();
231         verifyCommandTuneWithBundle();
232         verifyCommandSendAppPrivateCommand();
233         verifyCommandSetStreamVolume();
234         verifyCommandSetCaptionEnabled();
235         verifyCommandSelectTrack();
236         verifyCommandDispatchKeyDown();
237         verifyCommandDispatchKeyMultiple();
238         verifyCommandDispatchKeyUp();
239         verifyCommandDispatchTouchEvent();
240         verifyCommandDispatchTrackballEvent();
241         verifyCommandDispatchGenericMotionEvent();
242         verifyCommandTimeShiftPause();
243         verifyCommandTimeShiftResume();
244         verifyCommandTimeShiftSeekTo();
245         verifyCommandTimeShiftSetPlaybackParams();
246         verifyCommandTimeShiftPlay();
247         verifyCommandSetTimeShiftPositionCallback();
248         verifyCommandOverlayViewSizeChanged();
249         verifyCallbackChannelRetuned();
250         verifyCallbackVideoAvailable();
251         verifyCallbackVideoUnavailable();
252         verifyCallbackTracksChanged();
253         verifyCallbackTrackSelected();
254         verifyCallbackVideoSizeChanged();
255         verifyCallbackContentAllowed();
256         verifyCallbackContentBlocked();
257         verifyCallbackTimeShiftStatusChanged();
258         verifyCallbackLayoutSurface();
259 
260         runTestOnUiThread(new Runnable() {
261             @Override
262             public void run() {
263                 mTvView.reset();
264             }
265         });
266         mInstrumentation.waitForIdleSync();
267     }
268 
testTvInputServiceRecordingSession()269     public void testTvInputServiceRecordingSession() throws Throwable {
270         if (!Utils.hasTvInputFramework(getActivity())) {
271             return;
272         }
273         initDummyBundle();
274         verifyCommandTuneForRecording();
275         verifyCallbackConnectionFailed();
276         verifyCommandTuneForRecordingWithBundle();
277         verifyCallbackTuned();
278         verifyCommandStartRecording();
279         verifyCommandStopRecording();
280         verifyCommandSendAppPrivateCommandForRecording();
281         verifyCallbackRecordingStopped();
282         verifyCallbackError();
283         verifyCommandRelease();
284         verifyCallbackDisconnected();
285     }
286 
verifyCommandTuneForRecording()287     public void verifyCommandTuneForRecording() {
288         resetCounts();
289         resetPassedValues();
290         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
291         mTvRecordingClient.tune(mStubInfo.getId(), fakeChannelUri);
292         new PollingCheck(TIME_OUT) {
293             @Override
294             protected boolean check() {
295                 final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
296                 return session != null && session.mTuneCount > 0
297                         && Objects.equals(session.mTunedChannelUri, fakeChannelUri);
298             }
299         }.run();
300     }
301 
verifyCommandTuneForRecordingWithBundle()302     public void verifyCommandTuneForRecordingWithBundle() {
303         resetCounts();
304         resetPassedValues();
305         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
306         mTvRecordingClient.tune(mStubInfo.getId(), fakeChannelUri, sDummyBundle);
307         new PollingCheck(TIME_OUT) {
308             @Override
309             protected boolean check() {
310                 final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
311                 return session != null
312                         && session.mTuneCount > 0
313                         && session.mTuneWithBundleCount > 0
314                         && Objects.equals(session.mTunedChannelUri, fakeChannelUri)
315                         && bundleEquals(session.mTuneWithBundleData, sDummyBundle);
316             }
317         }.run();
318     }
319 
verifyCommandRelease()320     public void verifyCommandRelease() {
321         resetCounts();
322         mTvRecordingClient.release();
323         new PollingCheck(TIME_OUT) {
324             @Override
325             protected boolean check() {
326                 final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
327                 return session != null && session.mReleaseCount > 0;
328             }
329         }.run();
330     }
331 
verifyCommandStartRecording()332     public void verifyCommandStartRecording() {
333         resetCounts();
334         resetPassedValues();
335         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
336         mTvRecordingClient.startRecording(fakeChannelUri);
337         new PollingCheck(TIME_OUT) {
338             @Override
339             protected boolean check() {
340                 final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
341                 return session != null
342                         && session.mStartRecordingCount > 0
343                         && Objects.equals(session.mProgramHint, fakeChannelUri);
344             }
345         }.run();
346     }
347 
verifyCommandStopRecording()348     public void verifyCommandStopRecording() {
349         resetCounts();
350         mTvRecordingClient.stopRecording();
351         new PollingCheck(TIME_OUT) {
352             @Override
353             protected boolean check() {
354                 final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
355                 return session != null && session.mStopRecordingCount > 0;
356             }
357         }.run();
358     }
359 
verifyCommandSendAppPrivateCommandForRecording()360     public void verifyCommandSendAppPrivateCommandForRecording() {
361         resetCounts();
362         resetPassedValues();
363         final String action = "android.media.tv.cts.TvInputServiceTest.privateCommand";
364         mTvRecordingClient.sendAppPrivateCommand(action, sDummyBundle);
365         new PollingCheck(TIME_OUT) {
366             @Override
367             protected boolean check() {
368                 final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
369                 return session != null
370                         && session.mAppPrivateCommandCount > 0
371                         && bundleEquals(session.mAppPrivateCommandData, sDummyBundle)
372                         && TextUtils.equals(session.mAppPrivateCommandAction, action);
373             }
374         }.run();
375     }
376 
verifyCallbackTuned()377     public void verifyCallbackTuned() {
378         resetCounts();
379         resetPassedValues();
380         final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
381         assertNotNull(session);
382         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
383         session.notifyTuned(fakeChannelUri);
384         new PollingCheck(TIME_OUT) {
385             @Override
386             protected boolean check() {
387                 return mRecordingCallback.mTunedCount > 0
388                         && Objects.equals(mRecordingCallback.mTunedChannelUri, fakeChannelUri);
389             }
390         }.run();
391     }
392 
verifyCallbackError()393     public void verifyCallbackError() {
394         resetCounts();
395         resetPassedValues();
396         final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
397         assertNotNull(session);
398         final int error = TvInputManager.RECORDING_ERROR_UNKNOWN;
399         session.notifyError(error);
400         new PollingCheck(TIME_OUT) {
401             @Override
402             protected boolean check() {
403                 return mRecordingCallback.mErrorCount > 0
404                         && mRecordingCallback.mError == error;
405             }
406         }.run();
407     }
408 
verifyCallbackRecordingStopped()409     public void verifyCallbackRecordingStopped() {
410         resetCounts();
411         resetPassedValues();
412         final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
413         assertNotNull(session);
414         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
415         session.notifyRecordingStopped(fakeChannelUri);
416         new PollingCheck(TIME_OUT) {
417             @Override
418             protected boolean check() {
419                 return mRecordingCallback.mRecordingStoppedCount > 0
420                         && Objects.equals(mRecordingCallback.mRecordedProgramUri, fakeChannelUri);
421             }
422         }.run();
423     }
424 
verifyCallbackConnectionFailed()425     public void verifyCallbackConnectionFailed() {
426         resetCounts();
427         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
428         mTvRecordingClient.tune("invalid_input_id", fakeChannelUri);
429         new PollingCheck(TIME_OUT) {
430             @Override
431             protected boolean check() {
432                 return mRecordingCallback.mConnectionFailedCount > 0;
433             }
434         }.run();
435     }
436 
verifyCallbackDisconnected()437     public void verifyCallbackDisconnected() {
438         resetCounts();
439         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
440         mTvRecordingClient.tune(mFaultyStubInfo.getId(), fakeChannelUri);
441         new PollingCheck(TIME_OUT) {
442             @Override
443             protected boolean check() {
444                 return mRecordingCallback.mDisconnectedCount > 0;
445             }
446         }.run();
447     }
448 
verifyCommandTune()449     public void verifyCommandTune() {
450         resetCounts();
451         resetPassedValues();
452         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
453         mTvView.tune(mStubInfo.getId(), fakeChannelUri);
454         mInstrumentation.waitForIdleSync();
455         new PollingCheck(TIME_OUT) {
456             @Override
457             protected boolean check() {
458                 final CountingSession session = CountingTvInputService.sSession;
459                 return session != null
460                         && session.mTuneCount > 0
461                         && session.mCreateOverlayView > 0
462                         && Objects.equals(session.mTunedChannelUri, fakeChannelUri);
463             }
464         }.run();
465     }
466 
verifyCommandTuneWithBundle()467     public void verifyCommandTuneWithBundle() {
468         resetCounts();
469         resetPassedValues();
470         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
471         mTvView.tune(mStubInfo.getId(), fakeChannelUri, sDummyBundle);
472         mInstrumentation.waitForIdleSync();
473         new PollingCheck(TIME_OUT) {
474             @Override
475             protected boolean check() {
476                 final CountingSession session = CountingTvInputService.sSession;
477                 return session != null
478                         && session.mTuneCount > 0
479                         && session.mTuneWithBundleCount > 0
480                         && Objects.equals(session.mTunedChannelUri, fakeChannelUri)
481                         && bundleEquals(session.mTuneWithBundleData, sDummyBundle);
482             }
483         }.run();
484     }
485 
verifyCommandSetStreamVolume()486     public void verifyCommandSetStreamVolume() {
487         resetCounts();
488         resetPassedValues();
489         final float volume = 0.8f;
490         mTvView.setStreamVolume(volume);
491         mInstrumentation.waitForIdleSync();
492         new PollingCheck(TIME_OUT) {
493             @Override
494             protected boolean check() {
495                 final CountingSession session = CountingTvInputService.sSession;
496                 return session != null && session.mSetStreamVolumeCount > 0
497                         && session.mStreamVolume == volume;
498             }
499         }.run();
500     }
501 
verifyCommandSetCaptionEnabled()502     public void verifyCommandSetCaptionEnabled() {
503         resetCounts();
504         resetPassedValues();
505         final boolean enable = true;
506         mTvView.setCaptionEnabled(enable);
507         mInstrumentation.waitForIdleSync();
508         new PollingCheck(TIME_OUT) {
509             @Override
510             protected boolean check() {
511                 final CountingSession session = CountingTvInputService.sSession;
512                 return session != null && session.mSetCaptionEnabledCount > 0
513                         && session.mCaptionEnabled == enable;
514             }
515         }.run();
516     }
517 
verifyCommandSelectTrack()518     public void verifyCommandSelectTrack() {
519         resetCounts();
520         resetPassedValues();
521         verifyCallbackTracksChanged();
522         final int dummyTrackType = DUMMY_TRACK.getType();
523         final String dummyTrackId = DUMMY_TRACK.getId();
524         mTvView.selectTrack(dummyTrackType, dummyTrackId);
525         mInstrumentation.waitForIdleSync();
526         new PollingCheck(TIME_OUT) {
527             @Override
528             protected boolean check() {
529                 final CountingSession session = CountingTvInputService.sSession;
530                 return session != null
531                         && session.mSelectTrackCount > 0
532                         && session.mSelectTrackType == dummyTrackType
533                         && TextUtils.equals(session.mSelectTrackId, dummyTrackId);
534             }
535         }.run();
536     }
537 
verifyCommandDispatchKeyDown()538     public void verifyCommandDispatchKeyDown() {
539         resetCounts();
540         resetPassedValues();
541         final int keyCode = KeyEvent.KEYCODE_Q;
542         final KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
543         mTvView.dispatchKeyEvent(event);
544         mInstrumentation.waitForIdleSync();
545         new PollingCheck(TIME_OUT) {
546             @Override
547             protected boolean check() {
548                 final CountingSession session = CountingTvInputService.sSession;
549                 return session != null
550                         && session.mKeyDownCount > 0
551                         && session.mKeyDownCode == keyCode
552                         && keyEventEquals(event, session.mKeyDownEvent);
553             }
554         }.run();
555     }
556 
verifyCommandDispatchKeyMultiple()557     public void verifyCommandDispatchKeyMultiple() {
558         resetCounts();
559         resetPassedValues();
560         final int keyCode = KeyEvent.KEYCODE_Q;
561         final KeyEvent event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, keyCode);
562         mTvView.dispatchKeyEvent(event);
563         mInstrumentation.waitForIdleSync();
564         new PollingCheck(TIME_OUT) {
565             @Override
566             protected boolean check() {
567                 final CountingSession session = CountingTvInputService.sSession;
568                 return session != null
569                         && session.mKeyMultipleCount > 0
570                         && session.mKeyMultipleCode == keyCode
571                         && keyEventEquals(event, session.mKeyMultipleEvent)
572                         && session.mKeyMultipleNumber == event.getRepeatCount();
573             }
574         }.run();
575     }
576 
verifyCommandDispatchKeyUp()577     public void verifyCommandDispatchKeyUp() {
578         resetCounts();
579         resetPassedValues();
580         final int keyCode = KeyEvent.KEYCODE_Q;
581         final KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
582         mTvView.dispatchKeyEvent(event);
583         mInstrumentation.waitForIdleSync();
584         new PollingCheck(TIME_OUT) {
585             @Override
586             protected boolean check() {
587                 final CountingSession session = CountingTvInputService.sSession;
588                 return session != null
589                         && session.mKeyUpCount > 0
590                         && session.mKeyUpCode == keyCode
591                         && keyEventEquals(event, session.mKeyUpEvent);
592             }
593         }.run();
594     }
595 
verifyCommandDispatchTouchEvent()596     public void verifyCommandDispatchTouchEvent() {
597         resetCounts();
598         resetPassedValues();
599         final long now = SystemClock.uptimeMillis();
600         final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
601                 1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
602         event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
603         mTvView.dispatchTouchEvent(event);
604         mInstrumentation.waitForIdleSync();
605         new PollingCheck(TIME_OUT) {
606             @Override
607             protected boolean check() {
608                 final CountingSession session = CountingTvInputService.sSession;
609                 return session != null
610                         && session.mTouchEventCount > 0
611                         && motionEventEquals(session.mTouchEvent, event);
612             }
613         }.run();
614     }
615 
verifyCommandDispatchTrackballEvent()616     public void verifyCommandDispatchTrackballEvent() {
617         resetCounts();
618         resetPassedValues();
619         final long now = SystemClock.uptimeMillis();
620         final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
621                 1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
622         event.setSource(InputDevice.SOURCE_TRACKBALL);
623         mTvView.dispatchTouchEvent(event);
624         mInstrumentation.waitForIdleSync();
625         new PollingCheck(TIME_OUT) {
626             @Override
627             protected boolean check() {
628                 final CountingSession session = CountingTvInputService.sSession;
629                 return session != null
630                         && session.mTrackballEventCount > 0
631                         && motionEventEquals(session.mTrackballEvent, event);
632             }
633         }.run();
634     }
635 
verifyCommandDispatchGenericMotionEvent()636     public void verifyCommandDispatchGenericMotionEvent() {
637         resetCounts();
638         resetPassedValues();
639         final long now = SystemClock.uptimeMillis();
640         final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
641                 1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
642         mTvView.dispatchGenericMotionEvent(event);
643         mInstrumentation.waitForIdleSync();
644         new PollingCheck(TIME_OUT) {
645             @Override
646             protected boolean check() {
647                 final CountingSession session = CountingTvInputService.sSession;
648                 return session != null
649                         && session.mGenricMotionEventCount > 0
650                         && motionEventEquals(session.mGenricMotionEvent, event);
651             }
652         }.run();
653     }
654 
verifyCommandTimeShiftPause()655     public void verifyCommandTimeShiftPause() {
656         resetCounts();
657         mTvView.timeShiftPause();
658         mInstrumentation.waitForIdleSync();
659         new PollingCheck(TIME_OUT) {
660             @Override
661             protected boolean check() {
662                 final CountingSession session = CountingTvInputService.sSession;
663                 return session != null && session.mTimeShiftPauseCount > 0;
664             }
665         }.run();
666     }
667 
verifyCommandTimeShiftResume()668     public void verifyCommandTimeShiftResume() {
669         resetCounts();
670         mTvView.timeShiftResume();
671         mInstrumentation.waitForIdleSync();
672         new PollingCheck(TIME_OUT) {
673             @Override
674             protected boolean check() {
675                 final CountingSession session = CountingTvInputService.sSession;
676                 return session != null && session.mTimeShiftResumeCount > 0;
677             }
678         }.run();
679     }
680 
verifyCommandTimeShiftSeekTo()681     public void verifyCommandTimeShiftSeekTo() {
682         resetCounts();
683         resetPassedValues();
684         final long timeMs = 0;
685         mTvView.timeShiftSeekTo(timeMs);
686         mInstrumentation.waitForIdleSync();
687         new PollingCheck(TIME_OUT) {
688             @Override
689             protected boolean check() {
690                 final CountingSession session = CountingTvInputService.sSession;
691                 return session != null && session.mTimeShiftSeekToCount > 0
692                         && session.mTimeShiftSeekTo == timeMs;
693             }
694         }.run();
695     }
696 
verifyCommandTimeShiftSetPlaybackParams()697     public void verifyCommandTimeShiftSetPlaybackParams() {
698         resetCounts();
699         resetPassedValues();
700         final PlaybackParams param = new PlaybackParams().setSpeed(2.0f)
701                 .setAudioFallbackMode(PlaybackParams.AUDIO_FALLBACK_MODE_DEFAULT);
702         mTvView.timeShiftSetPlaybackParams(param);
703         mInstrumentation.waitForIdleSync();
704         new PollingCheck(TIME_OUT) {
705             @Override
706             protected boolean check() {
707                 final CountingSession session = CountingTvInputService.sSession;
708                 return session != null && session.mTimeShiftSetPlaybackParamsCount > 0
709                         && playbackParamsEquals(session.mTimeShiftSetPlaybackParams, param);
710             }
711         }.run();
712     }
713 
verifyCommandTimeShiftPlay()714     public void verifyCommandTimeShiftPlay() {
715         resetCounts();
716         resetPassedValues();
717         final Uri fakeRecordedProgramUri = TvContract.buildRecordedProgramUri(0);
718         mTvView.timeShiftPlay(mStubInfo.getId(), fakeRecordedProgramUri);
719         mInstrumentation.waitForIdleSync();
720         new PollingCheck(TIME_OUT) {
721             @Override
722             protected boolean check() {
723                 final CountingSession session = CountingTvInputService.sSession;
724                 return session != null && session.mTimeShiftPlayCount > 0
725                         && Objects.equals(session.mRecordedProgramUri, fakeRecordedProgramUri);
726             }
727         }.run();
728     }
729 
verifyCommandSetTimeShiftPositionCallback()730     public void verifyCommandSetTimeShiftPositionCallback() {
731         resetCounts();
732         mTvView.setTimeShiftPositionCallback(mTimeShiftPositionCallback);
733         mInstrumentation.waitForIdleSync();
734         new PollingCheck(TIME_OUT) {
735             @Override
736             protected boolean check() {
737                 return mTimeShiftPositionCallback.mTimeShiftCurrentPositionChanged > 0
738                         && mTimeShiftPositionCallback.mTimeShiftStartPositionChanged > 0;
739             }
740         }.run();
741     }
742 
verifyCommandOverlayViewSizeChanged()743     public void verifyCommandOverlayViewSizeChanged() {
744         resetCounts();
745         resetPassedValues();
746         final int width = 10;
747         final int height = 20;
748         mActivity.runOnUiThread(new Runnable() {
749             public void run() {
750                 mTvView.setLayoutParams(new LinearLayout.LayoutParams(width, height));
751             }
752         });
753         mInstrumentation.waitForIdleSync();
754         new PollingCheck(TIME_OUT) {
755             @Override
756             protected boolean check() {
757                 final CountingSession session = CountingTvInputService.sSession;
758                 return session != null
759                         && session.mOverlayViewSizeChangedCount > 0
760                         && session.mOverlayViewSizeChangedWidth == width
761                         && session.mOverlayViewSizeChangedHeight == height;
762             }
763         }.run();
764     }
765 
verifyCommandSendAppPrivateCommand()766     public void verifyCommandSendAppPrivateCommand() {
767         resetCounts();
768         final String action = "android.media.tv.cts.TvInputServiceTest.privateCommand";
769         mTvView.sendAppPrivateCommand(action, sDummyBundle);
770         mInstrumentation.waitForIdleSync();
771         new PollingCheck(TIME_OUT) {
772             @Override
773             protected boolean check() {
774                 final CountingSession session = CountingTvInputService.sSession;
775                 return session != null
776                         && session.mAppPrivateCommandCount > 0
777                         && bundleEquals(session.mAppPrivateCommandData, sDummyBundle)
778                         && TextUtils.equals(session.mAppPrivateCommandAction, action);
779             }
780         }.run();
781     }
782 
verifyCallbackChannelRetuned()783     public void verifyCallbackChannelRetuned() {
784         resetCounts();
785         resetPassedValues();
786         final CountingSession session = CountingTvInputService.sSession;
787         assertNotNull(session);
788         final Uri fakeChannelUri = TvContract.buildChannelUri(0);
789         session.notifyChannelRetuned(fakeChannelUri);
790         new PollingCheck(TIME_OUT) {
791             @Override
792             protected boolean check() {
793                 return mCallback.mChannelRetunedCount > 0
794                         && Objects.equals(mCallback.mChannelRetunedUri, fakeChannelUri);
795             }
796         }.run();
797     }
798 
verifyCallbackVideoAvailable()799     public void verifyCallbackVideoAvailable() {
800         resetCounts();
801         final CountingSession session = CountingTvInputService.sSession;
802         assertNotNull(session);
803         session.notifyVideoAvailable();
804         new PollingCheck(TIME_OUT) {
805             @Override
806             protected boolean check() {
807                 return mCallback.mVideoAvailableCount > 0;
808             }
809         }.run();
810     }
811 
verifyCallbackVideoUnavailable()812     public void verifyCallbackVideoUnavailable() {
813         resetCounts();
814         resetPassedValues();
815         final CountingSession session = CountingTvInputService.sSession;
816         assertNotNull(session);
817         final int reason = TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING;
818         session.notifyVideoUnavailable(reason);
819         new PollingCheck(TIME_OUT) {
820             @Override
821             protected boolean check() {
822                 return mCallback.mVideoUnavailableCount > 0
823                         && mCallback.mVideoUnavailableReason == reason;
824             }
825         }.run();
826     }
827 
verifyCallbackTracksChanged()828     public void verifyCallbackTracksChanged() {
829         resetCounts();
830         resetPassedValues();
831         final CountingSession session = CountingTvInputService.sSession;
832         assertNotNull(session);
833         ArrayList<TvTrackInfo> tracks = new ArrayList<>();
834         tracks.add(DUMMY_TRACK);
835         session.notifyTracksChanged(tracks);
836         new PollingCheck(TIME_OUT) {
837             @Override
838             protected boolean check() {
839                 return mCallback.mTrackChangedCount > 0
840                         && Objects.equals(mCallback.mTracksChangedTrackList, tracks);
841             }
842         }.run();
843     }
844 
verifyCallbackVideoSizeChanged()845     public void verifyCallbackVideoSizeChanged() {
846         resetCounts();
847         final CountingSession session = CountingTvInputService.sSession;
848         assertNotNull(session);
849         ArrayList<TvTrackInfo> tracks = new ArrayList<>();
850         tracks.add(DUMMY_TRACK);
851         session.notifyTracksChanged(tracks);
852         new PollingCheck(TIME_OUT) {
853             @Override
854             protected boolean check() {
855                 return mCallback.mVideoSizeChanged > 0;
856             }
857         }.run();
858     }
859 
verifyCallbackTrackSelected()860     public void verifyCallbackTrackSelected() {
861         resetCounts();
862         resetPassedValues();
863         final CountingSession session = CountingTvInputService.sSession;
864         assertNotNull(session);
865         assertNotNull(DUMMY_TRACK);
866         session.notifyTrackSelected(DUMMY_TRACK.getType(), DUMMY_TRACK.getId());
867         new PollingCheck(TIME_OUT) {
868             @Override
869             protected boolean check() {
870                 return mCallback.mTrackSelectedCount > 0
871                         && mCallback.mTrackSelectedType == DUMMY_TRACK.getType()
872                         && TextUtils.equals(DUMMY_TRACK.getId(), mCallback.mTrackSelectedTrackId);
873             }
874         }.run();
875     }
876 
verifyCallbackContentAllowed()877     public void verifyCallbackContentAllowed() {
878         resetCounts();
879         final CountingSession session = CountingTvInputService.sSession;
880         assertNotNull(session);
881         session.notifyContentAllowed();
882         new PollingCheck(TIME_OUT) {
883             @Override
884             protected boolean check() {
885                 return mCallback.mContentAllowedCount > 0;
886             }
887         }.run();
888     }
889 
verifyCallbackContentBlocked()890     public void verifyCallbackContentBlocked() {
891         resetCounts();
892         resetPassedValues();
893         final CountingSession session = CountingTvInputService.sSession;
894         assertNotNull(session);
895         final TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
896                 "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
897         session.notifyContentBlocked(rating);
898         new PollingCheck(TIME_OUT) {
899             @Override
900             protected boolean check() {
901                 return mCallback.mContentBlockedCount > 0
902                         && Objects.equals(mCallback.mContentBlockedRating, rating);
903             }
904         }.run();
905     }
906 
verifyCallbackTimeShiftStatusChanged()907     public void verifyCallbackTimeShiftStatusChanged() {
908         resetCounts();
909         resetPassedValues();
910         final CountingSession session = CountingTvInputService.sSession;
911         assertNotNull(session);
912         final int status = TvInputManager.TIME_SHIFT_STATUS_AVAILABLE;
913         session.notifyTimeShiftStatusChanged(status);
914         new PollingCheck(TIME_OUT) {
915             @Override
916             protected boolean check() {
917                 return mCallback.mTimeShiftStatusChangedCount > 0
918                         && mCallback.mTimeShiftStatusChangedStatus == status;
919             }
920         }.run();
921     }
922 
verifyCallbackLayoutSurface()923     public void verifyCallbackLayoutSurface() {
924         resetCounts();
925         final int left = 10;
926         final int top = 20;
927         final int right = 30;
928         final int bottom = 40;
929         final CountingSession session = CountingTvInputService.sSession;
930         assertNotNull(session);
931         session.layoutSurface(left, top, right, bottom);
932         new PollingCheck(TIME_OUT) {
933             @Override
934             protected boolean check() {
935                 int childCount = mTvView.getChildCount();
936                 for (int i = 0; i < childCount; ++i) {
937                     View v = mTvView.getChildAt(i);
938                     if (v instanceof SurfaceView) {
939                         return v.getLeft() == left && v.getTop() == top && v.getRight() == right
940                                 && v.getBottom() == bottom;
941                     }
942                 }
943                 return false;
944             }
945         }.run();
946     }
947 
keyEventEquals(KeyEvent event, KeyEvent other)948     public static boolean keyEventEquals(KeyEvent event, KeyEvent other) {
949         if (event == other) return true;
950         if (event == null || other == null) return false;
951         return event.getDownTime() == other.getDownTime()
952                 && event.getEventTime() == other.getEventTime()
953                 && event.getAction() == other.getAction()
954                 && event.getKeyCode() == other.getKeyCode()
955                 && event.getRepeatCount() == other.getRepeatCount()
956                 && event.getMetaState() == other.getMetaState()
957                 && event.getDeviceId() == other.getDeviceId()
958                 && event.getScanCode() == other.getScanCode()
959                 && event.getFlags() == other.getFlags()
960                 && event.getSource() == other.getSource()
961                 && TextUtils.equals(event.getCharacters(), other.getCharacters());
962     }
963 
motionEventEquals(MotionEvent event, MotionEvent other)964     public static boolean motionEventEquals(MotionEvent event, MotionEvent other) {
965         if (event == other) return true;
966         if (event == null || other == null) return false;
967         return event.getDownTime() == other.getDownTime()
968                 && event.getEventTime() == other.getEventTime()
969                 && event.getAction() == other.getAction()
970                 && event.getX() == other.getX()
971                 && event.getY() == other.getY()
972                 && event.getPressure() == other.getPressure()
973                 && event.getSize() == other.getSize()
974                 && event.getMetaState() == other.getMetaState()
975                 && event.getXPrecision() == other.getXPrecision()
976                 && event.getYPrecision() == other.getYPrecision()
977                 && event.getDeviceId() == other.getDeviceId()
978                 && event.getEdgeFlags() == other.getEdgeFlags()
979                 && event.getSource() == other.getSource();
980     }
981 
playbackParamsEquals(PlaybackParams param, PlaybackParams other)982     public static boolean playbackParamsEquals(PlaybackParams param, PlaybackParams other) {
983         if (param == other) return true;
984         if (param == null || other == null) return false;
985         return param.getAudioFallbackMode() == other.getAudioFallbackMode()
986                 && param.getSpeed() == other.getSpeed();
987     }
988 
bundleEquals(Bundle b, Bundle other)989     public static boolean bundleEquals(Bundle b, Bundle other) {
990         if (b == other) return true;
991         if (b == null || other == null) return false;
992         if (b.size() != other.size()) return false;
993 
994         Set<String> keys = b.keySet();
995         for (String key : keys) {
996             if (!other.containsKey(key)) return false;
997             Object objOne = b.get(key);
998             Object objTwo = other.get(key);
999             if (!Objects.equals(objOne, objTwo)) {
1000                 return false;
1001             }
1002         }
1003         return true;
1004     }
1005 
initDummyBundle()1006     public void initDummyBundle() {
1007         sDummyBundle = new Bundle();
1008         sDummyBundle.putString("stringKey", new String("Test String"));
1009     }
1010 
resetCounts()1011     private void resetCounts() {
1012         if (CountingTvInputService.sSession != null) {
1013             CountingTvInputService.sSession.resetCounts();
1014         }
1015         if (CountingTvInputService.sRecordingSession != null) {
1016             CountingTvInputService.sRecordingSession.resetCounts();
1017         }
1018         mCallback.resetCounts();
1019         mTimeShiftPositionCallback.resetCounts();
1020         mRecordingCallback.resetCounts();
1021     }
1022 
resetPassedValues()1023     private void resetPassedValues() {
1024         if (CountingTvInputService.sSession != null) {
1025             CountingTvInputService.sSession.resetPassedValues();
1026         }
1027         if (CountingTvInputService.sRecordingSession != null) {
1028             CountingTvInputService.sRecordingSession.resetPassedValues();
1029         }
1030         mCallback.resetPassedValues();
1031         mRecordingCallback.resetPassedValues();
1032     }
1033 
1034     public static class CountingTvInputService extends StubTvInputService {
1035         static CountingSession sSession;
1036         static CountingRecordingSession sRecordingSession;
1037 
1038         @Override
onCreateSession(String inputId)1039         public Session onCreateSession(String inputId) {
1040             sSession = new CountingSession(this);
1041             sSession.setOverlayViewEnabled(true);
1042             return sSession;
1043         }
1044 
1045         @Override
onCreateRecordingSession(String inputId)1046         public RecordingSession onCreateRecordingSession(String inputId) {
1047             sRecordingSession = new CountingRecordingSession(this);
1048             return sRecordingSession;
1049         }
1050 
1051         public static class CountingSession extends Session {
1052             public volatile int mTuneCount;
1053             public volatile int mTuneWithBundleCount;
1054             public volatile int mSetStreamVolumeCount;
1055             public volatile int mSetCaptionEnabledCount;
1056             public volatile int mSelectTrackCount;
1057             public volatile int mCreateOverlayView;
1058             public volatile int mKeyDownCount;
1059             public volatile int mKeyLongPressCount;
1060             public volatile int mKeyMultipleCount;
1061             public volatile int mKeyUpCount;
1062             public volatile int mTouchEventCount;
1063             public volatile int mTrackballEventCount;
1064             public volatile int mGenricMotionEventCount;
1065             public volatile int mOverlayViewSizeChangedCount;
1066             public volatile int mTimeShiftPauseCount;
1067             public volatile int mTimeShiftResumeCount;
1068             public volatile int mTimeShiftSeekToCount;
1069             public volatile int mTimeShiftSetPlaybackParamsCount;
1070             public volatile int mTimeShiftPlayCount;
1071             public volatile long mTimeShiftGetCurrentPositionCount;
1072             public volatile long mTimeShiftGetStartPositionCount;
1073             public volatile int mAppPrivateCommandCount;
1074 
1075             public volatile String mAppPrivateCommandAction;
1076             public volatile Bundle mAppPrivateCommandData;
1077             public volatile Uri mTunedChannelUri;
1078             public volatile Bundle mTuneWithBundleData;
1079             public volatile Float mStreamVolume;
1080             public volatile Boolean mCaptionEnabled;
1081             public volatile Integer mSelectTrackType;
1082             public volatile String mSelectTrackId;
1083             public volatile Integer mKeyDownCode;
1084             public volatile KeyEvent mKeyDownEvent;
1085             public volatile Integer mKeyLongPressCode;
1086             public volatile KeyEvent mKeyLongPressEvent;
1087             public volatile Integer mKeyMultipleCode;
1088             public volatile Integer mKeyMultipleNumber;
1089             public volatile KeyEvent mKeyMultipleEvent;
1090             public volatile Integer mKeyUpCode;
1091             public volatile KeyEvent mKeyUpEvent;
1092             public volatile MotionEvent mTouchEvent;
1093             public volatile MotionEvent mTrackballEvent;
1094             public volatile MotionEvent mGenricMotionEvent;
1095             public volatile Long mTimeShiftSeekTo;
1096             public volatile PlaybackParams mTimeShiftSetPlaybackParams;
1097             public volatile Uri mRecordedProgramUri;
1098             public volatile Integer mOverlayViewSizeChangedWidth;
1099             public volatile Integer mOverlayViewSizeChangedHeight;
1100 
CountingSession(Context context)1101             CountingSession(Context context) {
1102                 super(context);
1103             }
1104 
resetCounts()1105             public void resetCounts() {
1106                 mTuneCount = 0;
1107                 mTuneWithBundleCount = 0;
1108                 mSetStreamVolumeCount = 0;
1109                 mSetCaptionEnabledCount = 0;
1110                 mSelectTrackCount = 0;
1111                 mCreateOverlayView = 0;
1112                 mKeyDownCount = 0;
1113                 mKeyLongPressCount = 0;
1114                 mKeyMultipleCount = 0;
1115                 mKeyUpCount = 0;
1116                 mTouchEventCount = 0;
1117                 mTrackballEventCount = 0;
1118                 mGenricMotionEventCount = 0;
1119                 mOverlayViewSizeChangedCount = 0;
1120                 mTimeShiftPauseCount = 0;
1121                 mTimeShiftResumeCount = 0;
1122                 mTimeShiftSeekToCount = 0;
1123                 mTimeShiftSetPlaybackParamsCount = 0;
1124                 mTimeShiftPlayCount = 0;
1125                 mTimeShiftGetCurrentPositionCount = 0;
1126                 mTimeShiftGetStartPositionCount = 0;
1127                 mAppPrivateCommandCount = 0;
1128             }
1129 
resetPassedValues()1130             public void resetPassedValues() {
1131                 mAppPrivateCommandAction = null;
1132                 mAppPrivateCommandData = null;
1133                 mTunedChannelUri = null;
1134                 mTuneWithBundleData = null;
1135                 mStreamVolume = null;
1136                 mCaptionEnabled = null;
1137                 mSelectTrackType = null;
1138                 mSelectTrackId = null;
1139                 mKeyDownCode = null;
1140                 mKeyDownEvent = null;
1141                 mKeyLongPressCode = null;
1142                 mKeyLongPressEvent = null;
1143                 mKeyMultipleCode = null;
1144                 mKeyMultipleNumber = null;
1145                 mKeyMultipleEvent = null;
1146                 mKeyUpCode = null;
1147                 mKeyUpEvent = null;
1148                 mTouchEvent = null;
1149                 mTrackballEvent = null;
1150                 mGenricMotionEvent = null;
1151                 mTimeShiftSeekTo = null;
1152                 mTimeShiftSetPlaybackParams = null;
1153                 mRecordedProgramUri = null;
1154                 mOverlayViewSizeChangedWidth = null;
1155                 mOverlayViewSizeChangedHeight = null;
1156             }
1157 
1158             @Override
onAppPrivateCommand(String action, Bundle data)1159             public void onAppPrivateCommand(String action, Bundle data) {
1160                 mAppPrivateCommandCount++;
1161                 mAppPrivateCommandAction = action;
1162                 mAppPrivateCommandData = data;
1163             }
1164 
1165             @Override
onRelease()1166             public void onRelease() {
1167             }
1168 
1169             @Override
onSetSurface(Surface surface)1170             public boolean onSetSurface(Surface surface) {
1171                 return false;
1172             }
1173 
1174             @Override
onTune(Uri channelUri)1175             public boolean onTune(Uri channelUri) {
1176                 mTuneCount++;
1177                 mTunedChannelUri = channelUri;
1178                 return false;
1179             }
1180 
1181             @Override
onTune(Uri channelUri, Bundle data)1182             public boolean onTune(Uri channelUri, Bundle data) {
1183                 mTuneWithBundleCount++;
1184                 mTuneWithBundleData = data;
1185                 // Also calls {@link #onTune(Uri)} since it will never be called if the
1186                 // implementation overrides {@link #onTune(Uri, Bundle)}.
1187                 onTune(channelUri);
1188                 return false;
1189             }
1190 
1191             @Override
onSetStreamVolume(float volume)1192             public void onSetStreamVolume(float volume) {
1193                 mSetStreamVolumeCount++;
1194                 mStreamVolume = volume;
1195             }
1196 
1197             @Override
onSetCaptionEnabled(boolean enabled)1198             public void onSetCaptionEnabled(boolean enabled) {
1199                 mSetCaptionEnabledCount++;
1200                 mCaptionEnabled = enabled;
1201             }
1202 
1203             @Override
onSelectTrack(int type, String id)1204             public boolean onSelectTrack(int type, String id) {
1205                 mSelectTrackCount++;
1206                 mSelectTrackType = type;
1207                 mSelectTrackId = id;
1208                 return false;
1209             }
1210 
1211             @Override
onCreateOverlayView()1212             public View onCreateOverlayView() {
1213                 mCreateOverlayView++;
1214                 return null;
1215             }
1216 
1217             @Override
onKeyDown(int keyCode, KeyEvent event)1218             public boolean onKeyDown(int keyCode, KeyEvent event) {
1219                 mKeyDownCount++;
1220                 mKeyDownCode = keyCode;
1221                 mKeyDownEvent = event;
1222                 return false;
1223             }
1224 
1225             @Override
onKeyLongPress(int keyCode, KeyEvent event)1226             public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1227                 mKeyLongPressCount++;
1228                 mKeyLongPressCode = keyCode;
1229                 mKeyLongPressEvent = event;
1230                 return false;
1231             }
1232 
1233             @Override
onKeyMultiple(int keyCode, int count, KeyEvent event)1234             public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1235                 mKeyMultipleCount++;
1236                 mKeyMultipleCode = keyCode;
1237                 mKeyMultipleNumber = count;
1238                 mKeyMultipleEvent = event;
1239                 return false;
1240             }
1241 
1242             @Override
onKeyUp(int keyCode, KeyEvent event)1243             public boolean onKeyUp(int keyCode, KeyEvent event) {
1244                 mKeyUpCount++;
1245                 mKeyUpCode = keyCode;
1246                 mKeyUpEvent = event;
1247                 return false;
1248             }
1249 
1250             @Override
onTouchEvent(MotionEvent event)1251             public boolean onTouchEvent(MotionEvent event) {
1252                 mTouchEventCount++;
1253                 mTouchEvent = event;
1254                 return false;
1255             }
1256 
1257             @Override
onTrackballEvent(MotionEvent event)1258             public boolean onTrackballEvent(MotionEvent event) {
1259                 mTrackballEventCount++;
1260                 mTrackballEvent = event;
1261                 return false;
1262             }
1263 
1264             @Override
onGenericMotionEvent(MotionEvent event)1265             public boolean onGenericMotionEvent(MotionEvent event) {
1266                 mGenricMotionEventCount++;
1267                 mGenricMotionEvent = event;
1268                 return false;
1269             }
1270 
1271             @Override
onTimeShiftPause()1272             public void onTimeShiftPause() {
1273                 mTimeShiftPauseCount++;
1274             }
1275 
1276             @Override
onTimeShiftResume()1277             public void onTimeShiftResume() {
1278                 mTimeShiftResumeCount++;
1279             }
1280 
1281             @Override
onTimeShiftSeekTo(long timeMs)1282             public void onTimeShiftSeekTo(long timeMs) {
1283                 mTimeShiftSeekToCount++;
1284                 mTimeShiftSeekTo = timeMs;
1285             }
1286 
1287             @Override
onTimeShiftSetPlaybackParams(PlaybackParams param)1288             public void onTimeShiftSetPlaybackParams(PlaybackParams param) {
1289                 mTimeShiftSetPlaybackParamsCount++;
1290                 mTimeShiftSetPlaybackParams = param;
1291             }
1292 
1293             @Override
onTimeShiftPlay(Uri recordedProgramUri)1294             public void onTimeShiftPlay(Uri recordedProgramUri) {
1295                 mTimeShiftPlayCount++;
1296                 mRecordedProgramUri = recordedProgramUri;
1297             }
1298 
1299             @Override
onTimeShiftGetCurrentPosition()1300             public long onTimeShiftGetCurrentPosition() {
1301                 return ++mTimeShiftGetCurrentPositionCount;
1302             }
1303 
1304             @Override
onTimeShiftGetStartPosition()1305             public long onTimeShiftGetStartPosition() {
1306                 return ++mTimeShiftGetStartPositionCount;
1307             }
1308 
1309             @Override
onOverlayViewSizeChanged(int width, int height)1310             public void onOverlayViewSizeChanged(int width, int height) {
1311                 mOverlayViewSizeChangedCount++;
1312                 mOverlayViewSizeChangedWidth = width;
1313                 mOverlayViewSizeChangedHeight = height;
1314             }
1315         }
1316 
1317         public static class CountingRecordingSession extends RecordingSession {
1318             public volatile int mTuneCount;
1319             public volatile int mTuneWithBundleCount;
1320             public volatile int mReleaseCount;
1321             public volatile int mStartRecordingCount;
1322             public volatile int mStopRecordingCount;
1323             public volatile int mAppPrivateCommandCount;
1324 
1325             public volatile Uri mTunedChannelUri;
1326             public volatile Bundle mTuneWithBundleData;
1327             public volatile Uri mProgramHint;
1328             public volatile String mAppPrivateCommandAction;
1329             public volatile Bundle mAppPrivateCommandData;
1330 
CountingRecordingSession(Context context)1331             CountingRecordingSession(Context context) {
1332                 super(context);
1333             }
1334 
resetCounts()1335             public void resetCounts() {
1336                 mTuneCount = 0;
1337                 mTuneWithBundleCount = 0;
1338                 mReleaseCount = 0;
1339                 mStartRecordingCount = 0;
1340                 mStopRecordingCount = 0;
1341                 mAppPrivateCommandCount = 0;
1342             }
1343 
resetPassedValues()1344             public void resetPassedValues() {
1345                 mTunedChannelUri = null;
1346                 mTuneWithBundleData = null;
1347                 mProgramHint = null;
1348                 mAppPrivateCommandAction = null;
1349                 mAppPrivateCommandData = null;
1350             }
1351 
1352             @Override
onTune(Uri channelUri)1353             public void onTune(Uri channelUri) {
1354                 mTuneCount++;
1355                 mTunedChannelUri = channelUri;
1356             }
1357 
1358             @Override
onTune(Uri channelUri, Bundle data)1359             public void onTune(Uri channelUri, Bundle data) {
1360                 mTuneWithBundleCount++;
1361                 mTuneWithBundleData = data;
1362                 // Also calls {@link #onTune(Uri)} since it will never be called if the
1363                 // implementation overrides {@link #onTune(Uri, Bundle)}.
1364                 onTune(channelUri);
1365             }
1366 
1367             @Override
onRelease()1368             public void onRelease() {
1369                 mReleaseCount++;
1370             }
1371 
1372             @Override
onStartRecording(Uri programHint)1373             public void onStartRecording(Uri programHint) {
1374                 mStartRecordingCount++;
1375                 mProgramHint = programHint;
1376             }
1377 
1378             @Override
onStopRecording()1379             public void onStopRecording() {
1380                 mStopRecordingCount++;
1381             }
1382 
1383             @Override
onAppPrivateCommand(String action, Bundle data)1384             public void onAppPrivateCommand(String action, Bundle data) {
1385                 mAppPrivateCommandCount++;
1386                 mAppPrivateCommandAction = action;
1387                 mAppPrivateCommandData = data;
1388             }
1389         }
1390     }
1391 
1392     private static class StubRecordingCallback extends TvRecordingClient.RecordingCallback {
1393         private int mTunedCount;
1394         private int mRecordingStoppedCount;
1395         private int mErrorCount;
1396         private int mConnectionFailedCount;
1397         private int mDisconnectedCount;
1398 
1399         private Uri mTunedChannelUri;
1400         private Uri mRecordedProgramUri;
1401         private Integer mError;
1402 
1403         @Override
onTuned(Uri channelUri)1404         public void onTuned(Uri channelUri) {
1405             mTunedCount++;
1406             mTunedChannelUri = channelUri;
1407         }
1408 
1409         @Override
onRecordingStopped(Uri recordedProgramUri)1410         public void onRecordingStopped(Uri recordedProgramUri) {
1411             mRecordingStoppedCount++;
1412             mRecordedProgramUri = recordedProgramUri;
1413         }
1414 
1415         @Override
onError(int error)1416         public void onError(int error) {
1417             mErrorCount++;
1418             mError = error;
1419         }
1420 
1421         @Override
onConnectionFailed(String inputId)1422         public void onConnectionFailed(String inputId) {
1423             mConnectionFailedCount++;
1424         }
1425 
1426         @Override
onDisconnected(String inputId)1427         public void onDisconnected(String inputId) {
1428             mDisconnectedCount++;
1429         }
1430 
resetCounts()1431         public void resetCounts() {
1432             mTunedCount = 0;
1433             mRecordingStoppedCount = 0;
1434             mErrorCount = 0;
1435             mConnectionFailedCount = 0;
1436             mDisconnectedCount = 0;
1437         }
1438 
resetPassedValues()1439         public void resetPassedValues() {
1440             mTunedChannelUri = null;
1441             mRecordedProgramUri = null;
1442             mError = null;
1443         }
1444     }
1445 }
1446