blob: d47c5edfcd87bb05744321272abd1ec910d07356 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.tv.cts;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.media.PlaybackParams;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvRecordingClient;
import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingSession;
import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingRecordingSession;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.text.TextUtils;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.widget.LinearLayout;
import android.tv.cts.R;
import com.android.compatibility.common.util.PollingCheck;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* Test {@link android.media.tv.TvInputService}.
*/
public class TvInputServiceTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
/** The maximum time to wait for an operation. */
private static final long TIME_OUT = 15000L;
private static final String DUMMT_TRACK_ID = "dummyTrackId";
private static final TvTrackInfo DUMMY_TRACK =
new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, DUMMT_TRACK_ID)
.setVideoWidth(1920).setVideoHeight(1080).setLanguage("und").build();
private static Bundle sDummyBundle;
private TvView mTvView;
private TvRecordingClient mTvRecordingClient;
private Activity mActivity;
private Instrumentation mInstrumentation;
private TvInputManager mManager;
private TvInputInfo mStubInfo;
private TvInputInfo mFaultyStubInfo;
private final StubCallback mCallback = new StubCallback();
private final StubTimeShiftPositionCallback mTimeShiftPositionCallback =
new StubTimeShiftPositionCallback();
private final StubRecordingCallback mRecordingCallback = new StubRecordingCallback();
private static class StubCallback extends TvView.TvInputCallback {
private int mChannelRetunedCount;
private int mVideoAvailableCount;
private int mVideoUnavailableCount;
private int mTrackSelectedCount;
private int mTrackChangedCount;
private int mVideoSizeChanged;
private int mContentAllowedCount;
private int mContentBlockedCount;
private int mTimeShiftStatusChangedCount;
private Uri mChannelRetunedUri;
private Integer mVideoUnavailableReason;
private Integer mTrackSelectedType;
private String mTrackSelectedTrackId;
private List<TvTrackInfo> mTracksChangedTrackList;
private TvContentRating mContentBlockedRating;
private Integer mTimeShiftStatusChangedStatus;
@Override
public void onChannelRetuned(String inputId, Uri channelUri) {
mChannelRetunedCount++;
mChannelRetunedUri = channelUri;
}
@Override
public void onVideoAvailable(String inputId) {
mVideoAvailableCount++;
}
@Override
public void onVideoUnavailable(String inputId, int reason) {
mVideoUnavailableCount++;
mVideoUnavailableReason = reason;
}
@Override
public void onTrackSelected(String inputId, int type, String trackId) {
mTrackSelectedCount++;
mTrackSelectedType = type;
mTrackSelectedTrackId = trackId;
}
@Override
public void onTracksChanged(String inputId, List<TvTrackInfo> trackList) {
mTrackChangedCount++;
mTracksChangedTrackList = trackList;
}
@Override
public void onVideoSizeChanged(String inputId, int width, int height) {
mVideoSizeChanged++;
}
@Override
public void onContentAllowed(String inputId) {
mContentAllowedCount++;
}
@Override
public void onContentBlocked(String inputId, TvContentRating rating) {
mContentBlockedCount++;
mContentBlockedRating = rating;
}
@Override
public void onTimeShiftStatusChanged(String inputId, int status) {
mTimeShiftStatusChangedCount++;
mTimeShiftStatusChangedStatus = status;
}
public void resetCounts() {
mChannelRetunedCount = 0;
mVideoAvailableCount = 0;
mVideoUnavailableCount = 0;
mTrackSelectedCount = 0;
mTrackChangedCount = 0;
mContentAllowedCount = 0;
mContentBlockedCount = 0;
mTimeShiftStatusChangedCount = 0;
}
public void resetPassedValues() {
mChannelRetunedUri = null;
mVideoUnavailableReason = null;
mTrackSelectedType = null;
mTrackSelectedTrackId = null;
mTracksChangedTrackList = null;
mContentBlockedRating = null;
mTimeShiftStatusChangedStatus = null;
}
}
private static class StubTimeShiftPositionCallback extends TvView.TimeShiftPositionCallback {
private int mTimeShiftStartPositionChanged;
private int mTimeShiftCurrentPositionChanged;
@Override
public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
mTimeShiftStartPositionChanged++;
}
@Override
public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
mTimeShiftCurrentPositionChanged++;
}
public void resetCounts() {
mTimeShiftStartPositionChanged = 0;
mTimeShiftCurrentPositionChanged = 0;
}
}
public TvInputServiceTest() {
super(TvViewStubActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
mActivity = getActivity();
mInstrumentation = getInstrumentation();
mTvView = (TvView) mActivity.findViewById(R.id.tvview);
mTvRecordingClient = new TvRecordingClient(mActivity, "TvInputServiceTest",
mRecordingCallback, null);
mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE);
for (TvInputInfo info : mManager.getTvInputList()) {
if (info.getServiceInfo().name.equals(CountingTvInputService.class.getName())) {
mStubInfo = info;
}
if (info.getServiceInfo().name.equals(FaultyTvInputService.class.getName())) {
mFaultyStubInfo = info;
}
if (mStubInfo != null && mFaultyStubInfo != null) {
break;
}
}
assertNotNull(mStubInfo);
mTvView.setCallback(mCallback);
CountingTvInputService.sSession = null;
CountingTvInputService.sTvInputSessionId = null;
}
public void testTvInputServiceSession() throws Throwable {
if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
initDummyBundle();
verifyCommandTune();
verifyCommandTuneWithBundle();
verifyCommandSendAppPrivateCommand();
verifyCommandSetStreamVolume();
verifyCommandSetCaptionEnabled();
verifyCommandSelectTrack();
verifyCommandDispatchKeyDown();
verifyCommandDispatchKeyMultiple();
verifyCommandDispatchKeyUp();
verifyCommandDispatchTouchEvent();
verifyCommandDispatchTrackballEvent();
verifyCommandDispatchGenericMotionEvent();
verifyCommandTimeShiftPause();
verifyCommandTimeShiftResume();
verifyCommandTimeShiftSeekTo();
verifyCommandTimeShiftSetPlaybackParams();
verifyCommandTimeShiftPlay();
verifyCommandSetTimeShiftPositionCallback();
verifyCommandOverlayViewSizeChanged();
verifyCallbackChannelRetuned();
verifyCallbackVideoAvailable();
verifyCallbackVideoUnavailable();
verifyCallbackTracksChanged();
verifyCallbackTrackSelected();
verifyCallbackVideoSizeChanged();
verifyCallbackContentAllowed();
verifyCallbackContentBlocked();
verifyCallbackTimeShiftStatusChanged();
verifyCallbackLayoutSurface();
runTestOnUiThread(new Runnable() {
@Override
public void run() {
mTvView.reset();
}
});
mInstrumentation.waitForIdleSync();
}
public void testTvInputServiceRecordingSession() throws Throwable {
if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
initDummyBundle();
verifyCommandTuneForRecording();
verifyCallbackConnectionFailed();
verifyCommandTuneForRecordingWithBundle();
verifyCallbackTuned();
verifyCommandStartRecording();
verifyCommandStartRecordingWithBundle();
verifyCommandStopRecording();
verifyCommandSendAppPrivateCommandForRecording();
verifyCallbackRecordingStopped();
verifyCallbackError();
verifyCommandRelease();
verifyCallbackDisconnected();
}
public void verifyCommandTuneForRecording() {
resetCounts();
resetPassedValues();
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
mTvRecordingClient.tune(mStubInfo.getId(), fakeChannelUri);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
final String tvInputSessionId = CountingTvInputService.sTvInputSessionId;
return session != null && session.mTuneCount > 0
&& tvInputSessionId != null
&& Objects.equals(session.mTunedChannelUri, fakeChannelUri);
}
}.run();
}
public void verifyCommandTuneForRecordingWithBundle() {
resetCounts();
resetPassedValues();
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
mTvRecordingClient.tune(mStubInfo.getId(), fakeChannelUri, sDummyBundle);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
final String tvInputSessionId = CountingTvInputService.sTvInputSessionId;
return session != null
&& tvInputSessionId != null
&& session.mTuneCount > 0
&& session.mTuneWithBundleCount > 0
&& Objects.equals(session.mTunedChannelUri, fakeChannelUri)
&& bundleEquals(session.mTuneWithBundleData, sDummyBundle);
}
}.run();
}
public void verifyCommandRelease() {
resetCounts();
mTvRecordingClient.release();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
return session != null && session.mReleaseCount > 0;
}
}.run();
}
public void verifyCommandStartRecording() {
resetCounts();
resetPassedValues();
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
mTvRecordingClient.startRecording(fakeChannelUri);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
return session != null
&& session.mStartRecordingCount > 0
&& Objects.equals(session.mProgramHint, fakeChannelUri);
}
}.run();
}
public void verifyCommandStartRecordingWithBundle() {
resetCounts();
resetPassedValues();
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
mTvRecordingClient.startRecording(fakeChannelUri, sDummyBundle);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
return session != null
&& session.mStartRecordingCount > 0
&& session.mStartRecordingWithBundleCount > 0
&& Objects.equals(session.mProgramHint, fakeChannelUri)
&& bundleEquals(session.mStartRecordingWithBundleData, sDummyBundle);
}
}.run();
}
public void verifyCommandStopRecording() {
resetCounts();
mTvRecordingClient.stopRecording();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
return session != null && session.mStopRecordingCount > 0;
}
}.run();
}
public void verifyCommandSendAppPrivateCommandForRecording() {
resetCounts();
resetPassedValues();
final String action = "android.media.tv.cts.TvInputServiceTest.privateCommand";
mTvRecordingClient.sendAppPrivateCommand(action, sDummyBundle);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
return session != null
&& session.mAppPrivateCommandCount > 0
&& bundleEquals(session.mAppPrivateCommandData, sDummyBundle)
&& TextUtils.equals(session.mAppPrivateCommandAction, action);
}
}.run();
}
public void verifyCallbackTuned() {
resetCounts();
resetPassedValues();
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
assertNotNull(session);
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
session.notifyTuned(fakeChannelUri);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mRecordingCallback.mTunedCount > 0
&& Objects.equals(mRecordingCallback.mTunedChannelUri, fakeChannelUri);
}
}.run();
}
public void verifyCallbackError() {
resetCounts();
resetPassedValues();
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
assertNotNull(session);
final int error = TvInputManager.RECORDING_ERROR_UNKNOWN;
session.notifyError(error);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mRecordingCallback.mErrorCount > 0
&& mRecordingCallback.mError == error;
}
}.run();
}
public void verifyCallbackRecordingStopped() {
resetCounts();
resetPassedValues();
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
assertNotNull(session);
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
session.notifyRecordingStopped(fakeChannelUri);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mRecordingCallback.mRecordingStoppedCount > 0
&& Objects.equals(mRecordingCallback.mRecordedProgramUri, fakeChannelUri);
}
}.run();
}
public void verifyCallbackConnectionFailed() {
resetCounts();
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
mTvRecordingClient.tune("invalid_input_id", fakeChannelUri);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mRecordingCallback.mConnectionFailedCount > 0;
}
}.run();
}
public void verifyCallbackDisconnected() {
resetCounts();
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
mTvRecordingClient.tune(mFaultyStubInfo.getId(), fakeChannelUri);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mRecordingCallback.mDisconnectedCount > 0;
}
}.run();
}
public void verifyCommandTune() {
resetCounts();
resetPassedValues();
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
mTvView.tune(mStubInfo.getId(), fakeChannelUri);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
final String tvInputSessionId = CountingTvInputService.sTvInputSessionId;
return session != null
&& tvInputSessionId != null
&& session.mTuneCount > 0
&& session.mCreateOverlayView > 0
&& Objects.equals(session.mTunedChannelUri, fakeChannelUri);
}
}.run();
}
public void verifyCommandTuneWithBundle() {
resetCounts();
resetPassedValues();
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
mTvView.tune(mStubInfo.getId(), fakeChannelUri, sDummyBundle);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
final String tvInputSessionId = CountingTvInputService.sTvInputSessionId;
return session != null
&& tvInputSessionId != null
&& session.mTuneCount > 0
&& session.mTuneWithBundleCount > 0
&& Objects.equals(session.mTunedChannelUri, fakeChannelUri)
&& bundleEquals(session.mTuneWithBundleData, sDummyBundle);
}
}.run();
}
public void verifyCommandSetStreamVolume() {
resetCounts();
resetPassedValues();
final float volume = 0.8f;
mTvView.setStreamVolume(volume);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null && session.mSetStreamVolumeCount > 0
&& session.mStreamVolume == volume;
}
}.run();
}
public void verifyCommandSetCaptionEnabled() {
resetCounts();
resetPassedValues();
final boolean enable = true;
mTvView.setCaptionEnabled(enable);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null && session.mSetCaptionEnabledCount > 0
&& session.mCaptionEnabled == enable;
}
}.run();
}
public void verifyCommandSelectTrack() {
resetCounts();
resetPassedValues();
verifyCallbackTracksChanged();
final int dummyTrackType = DUMMY_TRACK.getType();
final String dummyTrackId = DUMMY_TRACK.getId();
mTvView.selectTrack(dummyTrackType, dummyTrackId);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mSelectTrackCount > 0
&& session.mSelectTrackType == dummyTrackType
&& TextUtils.equals(session.mSelectTrackId, dummyTrackId);
}
}.run();
}
public void verifyCommandDispatchKeyDown() {
resetCounts();
resetPassedValues();
final int keyCode = KeyEvent.KEYCODE_Q;
final KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
mTvView.dispatchKeyEvent(event);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mKeyDownCount > 0
&& session.mKeyDownCode == keyCode
&& keyEventEquals(event, session.mKeyDownEvent);
}
}.run();
}
public void verifyCommandDispatchKeyMultiple() {
resetCounts();
resetPassedValues();
final int keyCode = KeyEvent.KEYCODE_Q;
final KeyEvent event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, keyCode);
mTvView.dispatchKeyEvent(event);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mKeyMultipleCount > 0
&& session.mKeyMultipleCode == keyCode
&& keyEventEquals(event, session.mKeyMultipleEvent)
&& session.mKeyMultipleNumber == event.getRepeatCount();
}
}.run();
}
public void verifyCommandDispatchKeyUp() {
resetCounts();
resetPassedValues();
final int keyCode = KeyEvent.KEYCODE_Q;
final KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
mTvView.dispatchKeyEvent(event);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mKeyUpCount > 0
&& session.mKeyUpCode == keyCode
&& keyEventEquals(event, session.mKeyUpEvent);
}
}.run();
}
public void verifyCommandDispatchTouchEvent() {
resetCounts();
resetPassedValues();
final long now = SystemClock.uptimeMillis();
final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
mTvView.dispatchTouchEvent(event);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mTouchEventCount > 0
&& motionEventEquals(session.mTouchEvent, event);
}
}.run();
}
public void verifyCommandDispatchTrackballEvent() {
resetCounts();
resetPassedValues();
final long now = SystemClock.uptimeMillis();
final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
event.setSource(InputDevice.SOURCE_TRACKBALL);
mTvView.dispatchTouchEvent(event);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mTrackballEventCount > 0
&& motionEventEquals(session.mTrackballEvent, event);
}
}.run();
}
public void verifyCommandDispatchGenericMotionEvent() {
resetCounts();
resetPassedValues();
final long now = SystemClock.uptimeMillis();
final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
mTvView.dispatchGenericMotionEvent(event);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mGenricMotionEventCount > 0
&& motionEventEquals(session.mGenricMotionEvent, event);
}
}.run();
}
public void verifyCommandTimeShiftPause() {
resetCounts();
mTvView.timeShiftPause();
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null && session.mTimeShiftPauseCount > 0;
}
}.run();
}
public void verifyCommandTimeShiftResume() {
resetCounts();
mTvView.timeShiftResume();
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null && session.mTimeShiftResumeCount > 0;
}
}.run();
}
public void verifyCommandTimeShiftSeekTo() {
resetCounts();
resetPassedValues();
final long timeMs = 0;
mTvView.timeShiftSeekTo(timeMs);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null && session.mTimeShiftSeekToCount > 0
&& session.mTimeShiftSeekTo == timeMs;
}
}.run();
}
public void verifyCommandTimeShiftSetPlaybackParams() {
resetCounts();
resetPassedValues();
final PlaybackParams param = new PlaybackParams().setSpeed(2.0f)
.setAudioFallbackMode(PlaybackParams.AUDIO_FALLBACK_MODE_DEFAULT);
mTvView.timeShiftSetPlaybackParams(param);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null && session.mTimeShiftSetPlaybackParamsCount > 0
&& playbackParamsEquals(session.mTimeShiftSetPlaybackParams, param);
}
}.run();
}
public void verifyCommandTimeShiftPlay() {
resetCounts();
resetPassedValues();
final Uri fakeRecordedProgramUri = TvContract.buildRecordedProgramUri(0);
mTvView.timeShiftPlay(mStubInfo.getId(), fakeRecordedProgramUri);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null && session.mTimeShiftPlayCount > 0
&& Objects.equals(session.mRecordedProgramUri, fakeRecordedProgramUri);
}
}.run();
}
public void verifyCommandSetTimeShiftPositionCallback() {
resetCounts();
mTvView.setTimeShiftPositionCallback(mTimeShiftPositionCallback);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mTimeShiftPositionCallback.mTimeShiftCurrentPositionChanged > 0
&& mTimeShiftPositionCallback.mTimeShiftStartPositionChanged > 0;
}
}.run();
}
public void verifyCommandOverlayViewSizeChanged() {
resetCounts();
resetPassedValues();
final int width = 10;
final int height = 20;
mActivity.runOnUiThread(new Runnable() {
public void run() {
mTvView.setLayoutParams(new LinearLayout.LayoutParams(width, height));
}
});
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mOverlayViewSizeChangedCount > 0
&& session.mOverlayViewSizeChangedWidth == width
&& session.mOverlayViewSizeChangedHeight == height;
}
}.run();
}
public void verifyCommandSendAppPrivateCommand() {
resetCounts();
final String action = "android.media.tv.cts.TvInputServiceTest.privateCommand";
mTvView.sendAppPrivateCommand(action, sDummyBundle);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
final CountingSession session = CountingTvInputService.sSession;
return session != null
&& session.mAppPrivateCommandCount > 0
&& bundleEquals(session.mAppPrivateCommandData, sDummyBundle)
&& TextUtils.equals(session.mAppPrivateCommandAction, action);
}
}.run();
}
public void verifyCallbackChannelRetuned() {
resetCounts();
resetPassedValues();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
final Uri fakeChannelUri = TvContract.buildChannelUri(0);
session.notifyChannelRetuned(fakeChannelUri);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mChannelRetunedCount > 0
&& Objects.equals(mCallback.mChannelRetunedUri, fakeChannelUri);
}
}.run();
}
public void verifyCallbackVideoAvailable() {
resetCounts();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyVideoAvailable();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mVideoAvailableCount > 0;
}
}.run();
}
public void verifyCallbackVideoUnavailable() {
resetCounts();
resetPassedValues();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
final int reason = TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING;
session.notifyVideoUnavailable(reason);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mVideoUnavailableCount > 0
&& mCallback.mVideoUnavailableReason == reason;
}
}.run();
}
public void verifyCallbackTracksChanged() {
resetCounts();
resetPassedValues();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
ArrayList<TvTrackInfo> tracks = new ArrayList<>();
tracks.add(DUMMY_TRACK);
session.notifyTracksChanged(tracks);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mTrackChangedCount > 0
&& Objects.equals(mCallback.mTracksChangedTrackList, tracks);
}
}.run();
}
public void verifyCallbackVideoSizeChanged() {
resetCounts();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
ArrayList<TvTrackInfo> tracks = new ArrayList<>();
tracks.add(DUMMY_TRACK);
session.notifyTracksChanged(tracks);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mVideoSizeChanged > 0;
}
}.run();
}
public void verifyCallbackTrackSelected() {
resetCounts();
resetPassedValues();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
assertNotNull(DUMMY_TRACK);
session.notifyTrackSelected(DUMMY_TRACK.getType(), DUMMY_TRACK.getId());
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mTrackSelectedCount > 0
&& mCallback.mTrackSelectedType == DUMMY_TRACK.getType()
&& TextUtils.equals(DUMMY_TRACK.getId(), mCallback.mTrackSelectedTrackId);
}
}.run();
}
public void verifyCallbackContentAllowed() {
resetCounts();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyContentAllowed();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mContentAllowedCount > 0;
}
}.run();
}
public void verifyCallbackContentBlocked() {
resetCounts();
resetPassedValues();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
final TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
"US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
session.notifyContentBlocked(rating);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mContentBlockedCount > 0
&& Objects.equals(mCallback.mContentBlockedRating, rating);
}
}.run();
}
public void verifyCallbackTimeShiftStatusChanged() {
resetCounts();
resetPassedValues();
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
final int status = TvInputManager.TIME_SHIFT_STATUS_AVAILABLE;
session.notifyTimeShiftStatusChanged(status);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
return mCallback.mTimeShiftStatusChangedCount > 0
&& mCallback.mTimeShiftStatusChangedStatus == status;
}
}.run();
}
public void verifyCallbackLayoutSurface() {
resetCounts();
final int left = 10;
final int top = 20;
final int right = 30;
final int bottom = 40;
final CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.layoutSurface(left, top, right, bottom);
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
int childCount = mTvView.getChildCount();
for (int i = 0; i < childCount; ++i) {
View v = mTvView.getChildAt(i);
if (v instanceof SurfaceView) {
return v.getLeft() == left && v.getTop() == top && v.getRight() == right
&& v.getBottom() == bottom;
}
}
return false;
}
}.run();
}
public static boolean keyEventEquals(KeyEvent event, KeyEvent other) {
if (event == other) return true;
if (event == null || other == null) return false;
return event.getDownTime() == other.getDownTime()
&& event.getEventTime() == other.getEventTime()
&& event.getAction() == other.getAction()
&& event.getKeyCode() == other.getKeyCode()
&& event.getRepeatCount() == other.getRepeatCount()
&& event.getMetaState() == other.getMetaState()
&& event.getDeviceId() == other.getDeviceId()
&& event.getScanCode() == other.getScanCode()
&& event.getFlags() == other.getFlags()
&& event.getSource() == other.getSource()
&& TextUtils.equals(event.getCharacters(), other.getCharacters());
}
public static boolean motionEventEquals(MotionEvent event, MotionEvent other) {
if (event == other) return true;
if (event == null || other == null) return false;
return event.getDownTime() == other.getDownTime()
&& event.getEventTime() == other.getEventTime()
&& event.getAction() == other.getAction()
&& event.getX() == other.getX()
&& event.getY() == other.getY()
&& event.getPressure() == other.getPressure()
&& event.getSize() == other.getSize()
&& event.getMetaState() == other.getMetaState()
&& event.getXPrecision() == other.getXPrecision()
&& event.getYPrecision() == other.getYPrecision()
&& event.getDeviceId() == other.getDeviceId()
&& event.getEdgeFlags() == other.getEdgeFlags()
&& event.getSource() == other.getSource();
}
public static boolean playbackParamsEquals(PlaybackParams param, PlaybackParams other) {
if (param == other) return true;
if (param == null || other == null) return false;
return param.getAudioFallbackMode() == other.getAudioFallbackMode()
&& param.getSpeed() == other.getSpeed();
}
public static boolean bundleEquals(Bundle b, Bundle other) {
if (b == other) return true;
if (b == null || other == null) return false;
if (b.size() != other.size()) return false;
Set<String> keys = b.keySet();
for (String key : keys) {
if (!other.containsKey(key)) return false;
Object objOne = b.get(key);
Object objTwo = other.get(key);
if (!Objects.equals(objOne, objTwo)) {
return false;
}
}
return true;
}
public void initDummyBundle() {
sDummyBundle = new Bundle();
sDummyBundle.putString("stringKey", new String("Test String"));
}
private void resetCounts() {
if (CountingTvInputService.sSession != null) {
CountingTvInputService.sSession.resetCounts();
}
if (CountingTvInputService.sRecordingSession != null) {
CountingTvInputService.sRecordingSession.resetCounts();
}
mCallback.resetCounts();
mTimeShiftPositionCallback.resetCounts();
mRecordingCallback.resetCounts();
}
private void resetPassedValues() {
if (CountingTvInputService.sSession != null) {
CountingTvInputService.sSession.resetPassedValues();
}
if (CountingTvInputService.sRecordingSession != null) {
CountingTvInputService.sRecordingSession.resetPassedValues();
}
mCallback.resetPassedValues();
mRecordingCallback.resetPassedValues();
}
public static class CountingTvInputService extends StubTvInputService {
static CountingSession sSession;
static CountingRecordingSession sRecordingSession;
static String sTvInputSessionId;
@Override
public Session onCreateSession(String inputId) {
sSession = new CountingSession(this);
sSession.setOverlayViewEnabled(true);
return sSession;
}
@Override
public RecordingSession onCreateRecordingSession(String inputId) {
sRecordingSession = new CountingRecordingSession(this);
return sRecordingSession;
}
@Override
public Session onCreateSession(String inputId, String tvInputSessionId) {
sTvInputSessionId = tvInputSessionId;
return onCreateSession(inputId);
}
@Override
public RecordingSession onCreateRecordingSession(String inputId, String tvInputSessionId) {
sTvInputSessionId = tvInputSessionId;
return onCreateRecordingSession(inputId);
}
public static class CountingSession extends Session {
public volatile int mTuneCount;
public volatile int mTuneWithBundleCount;
public volatile int mSetStreamVolumeCount;
public volatile int mSetCaptionEnabledCount;
public volatile int mSelectTrackCount;
public volatile int mCreateOverlayView;
public volatile int mKeyDownCount;
public volatile int mKeyLongPressCount;
public volatile int mKeyMultipleCount;
public volatile int mKeyUpCount;
public volatile int mTouchEventCount;
public volatile int mTrackballEventCount;
public volatile int mGenricMotionEventCount;
public volatile int mOverlayViewSizeChangedCount;
public volatile int mTimeShiftPauseCount;
public volatile int mTimeShiftResumeCount;
public volatile int mTimeShiftSeekToCount;
public volatile int mTimeShiftSetPlaybackParamsCount;
public volatile int mTimeShiftPlayCount;
public volatile long mTimeShiftGetCurrentPositionCount;
public volatile long mTimeShiftGetStartPositionCount;
public volatile int mAppPrivateCommandCount;
public volatile String mAppPrivateCommandAction;
public volatile Bundle mAppPrivateCommandData;
public volatile Uri mTunedChannelUri;
public volatile Bundle mTuneWithBundleData;
public volatile Float mStreamVolume;
public volatile Boolean mCaptionEnabled;
public volatile Integer mSelectTrackType;
public volatile String mSelectTrackId;
public volatile Integer mKeyDownCode;
public volatile KeyEvent mKeyDownEvent;
public volatile Integer mKeyLongPressCode;
public volatile KeyEvent mKeyLongPressEvent;
public volatile Integer mKeyMultipleCode;
public volatile Integer mKeyMultipleNumber;
public volatile KeyEvent mKeyMultipleEvent;
public volatile Integer mKeyUpCode;
public volatile KeyEvent mKeyUpEvent;
public volatile MotionEvent mTouchEvent;
public volatile MotionEvent mTrackballEvent;
public volatile MotionEvent mGenricMotionEvent;
public volatile Long mTimeShiftSeekTo;
public volatile PlaybackParams mTimeShiftSetPlaybackParams;
public volatile Uri mRecordedProgramUri;
public volatile Integer mOverlayViewSizeChangedWidth;
public volatile Integer mOverlayViewSizeChangedHeight;
CountingSession(Context context) {
super(context);
}
public void resetCounts() {
mTuneCount = 0;
mTuneWithBundleCount = 0;
mSetStreamVolumeCount = 0;
mSetCaptionEnabledCount = 0;
mSelectTrackCount = 0;
mCreateOverlayView = 0;
mKeyDownCount = 0;
mKeyLongPressCount = 0;
mKeyMultipleCount = 0;
mKeyUpCount = 0;
mTouchEventCount = 0;
mTrackballEventCount = 0;
mGenricMotionEventCount = 0;
mOverlayViewSizeChangedCount = 0;
mTimeShiftPauseCount = 0;
mTimeShiftResumeCount = 0;
mTimeShiftSeekToCount = 0;
mTimeShiftSetPlaybackParamsCount = 0;
mTimeShiftPlayCount = 0;
mTimeShiftGetCurrentPositionCount = 0;
mTimeShiftGetStartPositionCount = 0;
mAppPrivateCommandCount = 0;
}
public void resetPassedValues() {
mAppPrivateCommandAction = null;
mAppPrivateCommandData = null;
mTunedChannelUri = null;
mTuneWithBundleData = null;
mStreamVolume = null;
mCaptionEnabled = null;
mSelectTrackType = null;
mSelectTrackId = null;
mKeyDownCode = null;
mKeyDownEvent = null;
mKeyLongPressCode = null;
mKeyLongPressEvent = null;
mKeyMultipleCode = null;
mKeyMultipleNumber = null;
mKeyMultipleEvent = null;
mKeyUpCode = null;
mKeyUpEvent = null;
mTouchEvent = null;
mTrackballEvent = null;
mGenricMotionEvent = null;
mTimeShiftSeekTo = null;
mTimeShiftSetPlaybackParams = null;
mRecordedProgramUri = null;
mOverlayViewSizeChangedWidth = null;
mOverlayViewSizeChangedHeight = null;
}
@Override
public void onAppPrivateCommand(String action, Bundle data) {
mAppPrivateCommandCount++;
mAppPrivateCommandAction = action;
mAppPrivateCommandData = data;
}
@Override
public void onRelease() {
}
@Override
public boolean onSetSurface(Surface surface) {
return false;
}
@Override
public boolean onTune(Uri channelUri) {
mTuneCount++;
mTunedChannelUri = channelUri;
return false;
}
@Override
public boolean onTune(Uri channelUri, Bundle data) {
mTuneWithBundleCount++;
mTuneWithBundleData = data;
// Also calls {@link #onTune(Uri)} since it will never be called if the
// implementation overrides {@link #onTune(Uri, Bundle)}.
onTune(channelUri);
return false;
}
@Override
public void onSetStreamVolume(float volume) {
mSetStreamVolumeCount++;
mStreamVolume = volume;
}
@Override
public void onSetCaptionEnabled(boolean enabled) {
mSetCaptionEnabledCount++;
mCaptionEnabled = enabled;
}
@Override
public boolean onSelectTrack(int type, String id) {
mSelectTrackCount++;
mSelectTrackType = type;
mSelectTrackId = id;
return false;
}
@Override
public View onCreateOverlayView() {
mCreateOverlayView++;
return null;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mKeyDownCount++;
mKeyDownCode = keyCode;
mKeyDownEvent = event;
return false;
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
mKeyLongPressCount++;
mKeyLongPressCode = keyCode;
mKeyLongPressEvent = event;
return false;
}
@Override
public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
mKeyMultipleCount++;
mKeyMultipleCode = keyCode;
mKeyMultipleNumber = count;
mKeyMultipleEvent = event;
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
mKeyUpCount++;
mKeyUpCode = keyCode;
mKeyUpEvent = event;
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mTouchEventCount++;
mTouchEvent = event;
return false;
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
mTrackballEventCount++;
mTrackballEvent = event;
return false;
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
mGenricMotionEventCount++;
mGenricMotionEvent = event;
return false;
}
@Override
public void onTimeShiftPause() {
mTimeShiftPauseCount++;
}
@Override
public void onTimeShiftResume() {
mTimeShiftResumeCount++;
}
@Override
public void onTimeShiftSeekTo(long timeMs) {
mTimeShiftSeekToCount++;
mTimeShiftSeekTo = timeMs;
}
@Override
public void onTimeShiftSetPlaybackParams(PlaybackParams param) {
mTimeShiftSetPlaybackParamsCount++;
mTimeShiftSetPlaybackParams = param;
}
@Override
public void onTimeShiftPlay(Uri recordedProgramUri) {
mTimeShiftPlayCount++;
mRecordedProgramUri = recordedProgramUri;
}
@Override
public long onTimeShiftGetCurrentPosition() {
return ++mTimeShiftGetCurrentPositionCount;
}
@Override
public long onTimeShiftGetStartPosition() {
return ++mTimeShiftGetStartPositionCount;
}
@Override
public void onOverlayViewSizeChanged(int width, int height) {
mOverlayViewSizeChangedCount++;
mOverlayViewSizeChangedWidth = width;
mOverlayViewSizeChangedHeight = height;
}
}
public static class CountingRecordingSession extends RecordingSession {
public volatile int mTuneCount;
public volatile int mTuneWithBundleCount;
public volatile int mReleaseCount;
public volatile int mStartRecordingCount;
public volatile int mStartRecordingWithBundleCount;
public volatile int mStopRecordingCount;
public volatile int mAppPrivateCommandCount;
public volatile Uri mTunedChannelUri;
public volatile Bundle mTuneWithBundleData;
public volatile Uri mProgramHint;
public volatile Bundle mStartRecordingWithBundleData;
public volatile String mAppPrivateCommandAction;
public volatile Bundle mAppPrivateCommandData;
CountingRecordingSession(Context context) {
super(context);
}
public void resetCounts() {
mTuneCount = 0;
mTuneWithBundleCount = 0;
mReleaseCount = 0;
mStartRecordingCount = 0;
mStartRecordingWithBundleCount = 0;
mStopRecordingCount = 0;
mAppPrivateCommandCount = 0;
}
public void resetPassedValues() {
mTunedChannelUri = null;
mTuneWithBundleData = null;
mProgramHint = null;
mStartRecordingWithBundleData = null;
mAppPrivateCommandAction = null;
mAppPrivateCommandData = null;
}
@Override
public void onTune(Uri channelUri) {
mTuneCount++;
mTunedChannelUri = channelUri;
}
@Override
public void onTune(Uri channelUri, Bundle data) {
mTuneWithBundleCount++;
mTuneWithBundleData = data;
// Also calls {@link #onTune(Uri)} since it will never be called if the
// implementation overrides {@link #onTune(Uri, Bundle)}.
onTune(channelUri);
}
@Override
public void onRelease() {
mReleaseCount++;
}
@Override
public void onStartRecording(Uri programHint) {
mStartRecordingCount++;
mProgramHint = programHint;
}
@Override
public void onStartRecording(Uri programHint, Bundle data) {
mStartRecordingWithBundleCount++;
mProgramHint = programHint;
mStartRecordingWithBundleData = data;
// Also calls {@link #onStartRecording(Uri)} since it will never be called if the
// implementation overrides {@link #onStartRecording(Uri, Bundle)}.
onStartRecording(programHint);
}
@Override
public void onStopRecording() {
mStopRecordingCount++;
}
@Override
public void onAppPrivateCommand(String action, Bundle data) {
mAppPrivateCommandCount++;
mAppPrivateCommandAction = action;
mAppPrivateCommandData = data;
}
}
}
private static class StubRecordingCallback extends TvRecordingClient.RecordingCallback {
private int mTunedCount;
private int mRecordingStoppedCount;
private int mErrorCount;
private int mConnectionFailedCount;
private int mDisconnectedCount;
private Uri mTunedChannelUri;
private Uri mRecordedProgramUri;
private Integer mError;
@Override
public void onTuned(Uri channelUri) {
mTunedCount++;
mTunedChannelUri = channelUri;
}
@Override
public void onRecordingStopped(Uri recordedProgramUri) {
mRecordingStoppedCount++;
mRecordedProgramUri = recordedProgramUri;
}
@Override
public void onError(int error) {
mErrorCount++;
mError = error;
}
@Override
public void onConnectionFailed(String inputId) {
mConnectionFailedCount++;
}
@Override
public void onDisconnected(String inputId) {
mDisconnectedCount++;
}
public void resetCounts() {
mTunedCount = 0;
mRecordingStoppedCount = 0;
mErrorCount = 0;
mConnectionFailedCount = 0;
mDisconnectedCount = 0;
}
public void resetPassedValues() {
mTunedChannelUri = null;
mRecordedProgramUri = null;
mError = null;
}
}
}