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