Merge "TIF: introduce TvInputPassthroughWrapperService class to support 2-way pairing use-case." into lmp-dev

This commit is contained in:
Dongwon Kang
2014-07-19 04:44:25 +00:00
committed by Android (Google) Code Review
4 changed files with 279 additions and 5 deletions

View File

@@ -16721,6 +16721,22 @@ package android.media.tv {
method public void onInputStateChanged(java.lang.String, int);
}
public abstract class TvInputPassthroughWrapperService extends android.media.tv.TvInputService {
ctor public TvInputPassthroughWrapperService();
method public abstract java.lang.String getPassthroughInputId(java.lang.String);
method public abstract android.media.tv.TvInputPassthroughWrapperService.PassthroughWrapperSession onCreatePassthroughWrapperSession();
method public final android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
}
public abstract class TvInputPassthroughWrapperService.PassthroughWrapperSession extends android.media.tv.TvInputService.Session {
ctor public TvInputPassthroughWrapperService.PassthroughWrapperSession();
method public abstract void onPassthroughSessionCreationFailed();
method public abstract void onPassthroughSessionReleased();
method public abstract void onPassthroughVideoAvailable();
method public abstract void onPassthroughVideoUnavailable(int);
method public final boolean onSetSurface(android.view.Surface);
}
public abstract class TvInputService extends android.app.Service {
ctor public TvInputService();
method public final android.os.IBinder onBind(android.content.Intent);

View File

@@ -43,7 +43,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
private static final int DO_RELEASE = 1;
private static final int DO_SET_SURFACE = 2;
private static final int DO_DISPATCH_SURFACE_CHANGED = 3;
private static final int DO_SET_VOLUME = 4;
private static final int DO_SET_STREAM_VOLUME = 4;
private static final int DO_TUNE = 5;
private static final int DO_SET_CAPTION_ENABLED = 6;
private static final int DO_SELECT_TRACK = 7;
@@ -99,8 +99,8 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
args.recycle();
return;
}
case DO_SET_VOLUME: {
mTvInputSessionImpl.setVolume((Float) msg.obj);
case DO_SET_STREAM_VOLUME: {
mTvInputSessionImpl.setStreamVolume((Float) msg.obj);
return;
}
case DO_TUNE: {
@@ -162,7 +162,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
@Override
public final void setVolume(float volume) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_VOLUME, volume));
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_STREAM_VOLUME, volume));
}
@Override

View File

@@ -0,0 +1,258 @@
/*
* 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;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Surface;
/**
* TvInputPassthroughWrapperService represents a TV input which controls an external device
* connected to a pass-through TV input (e.g. HDMI 1).
* <p>
* This service wraps around a pass-through TV input and delegates the {@link Surface} to the
* connected TV input so that the application can show the pass-through TV input while
* TvInputPassthroughWrapperService controls the underlying external device via a separate
* connection. In the setup activity, the TV input should get the pass-through TV input ID, around
* which this service will wrap. The service implementation should pass the ID via
* {@link TvInputPassthroughWrapperService#getPassthroughInputId(String)}. In addition,
* it needs to implement {@link TvInputPassthroughWrapperService#onCreatePassthroughWrapperSession}
* to handle requests from the application.
* </p>
*/
public abstract class TvInputPassthroughWrapperService extends TvInputService {
private static final String TAG = "TvInputPassthroughWrapperService";
// STOPSHIP: Turn debugging off.
private static final boolean DEBUG = true;
private TvInputManager mTvInputManager;
private Handler mHandler;
@Override
public void onCreate() {
if (DEBUG) Log.d(TAG, "onCreate()");
super.onCreate();
mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
mHandler = new Handler();
}
@Override
public final Session onCreateSession(String inputId) {
if (DEBUG) Log.d(TAG, "onCreateSession()");
// Checks the pass-through TV input is properly setup.
String passthroughInputId = getPassthroughInputId(inputId);
if (passthroughInputId == null) {
Log.w(TAG, "The passthrough TV input for input id(" + inputId + ") is not setup yet.");
return null;
}
// Checks if input id from the derived class is really pass-through type.
TvInputInfo info = mTvInputManager.getTvInputInfo(passthroughInputId);
if (info == null || !info.isPassthroughInputType()) {
Log.w(TAG, "Invalid TV input id from derived class: " + passthroughInputId);
return null;
}
// Creates a PassthroughWrapperSession.
PassthroughWrapperSession session = onCreatePassthroughWrapperSession();
if (session == null) {
return null;
}
// Connects to the pass-through input the external device is connected to.
if (!session.connect(passthroughInputId)) {
throw new IllegalStateException("WrapperSession cannot be reused.");
}
return session;
}
/**
* Returns an implementation of {@link PassthroughWrapperSession}.
* <p>
* May return {@code null} if {@link TvInputPassthroughWrapperService} fails to create a
* session.
* </p>
*/
public abstract PassthroughWrapperSession onCreatePassthroughWrapperSession();
/**
* Returns the TV input id the external device is connected to.
* <p>
* {@link TvInputPassthroughWrapperService} is expected to identify the pass-though TV
* input the external device is connected to in the setup phase of this TV input.
* May return {@code null} if the pass-though TV input is not identified yet.
* </p>
* @param inputId The ID of the TV input which controls the external device.
*/
public abstract String getPassthroughInputId(String inputId);
/**
* Base session class for derived classes to handle the request from the application. This
* creates additional session to the pass-through TV input internally and delegates the
* {@link Surface} given from the application.
*/
public abstract class PassthroughWrapperSession extends Session {
private static final float VOLUME_ON = 1.0f;
private static final float VOLUME_OFF = 0f;
private TvInputManager.Session mSession;
private Surface mSurface;
private Float mVolume;
private boolean mReleased;
private int mSurfaceFormat;
private int mSurfaceWidth;
private int mSurfaceHeight;
private boolean mSurfaceChanged;
private boolean mConnectionRequested;
private final TvInputManager.SessionCallback mSessionCallback =
new TvInputManager.SessionCallback() {
@Override
public void onSessionCreated(TvInputManager.Session session) {
if (session == null) {
Log.w(TAG, "Failed to create session.");
onPassthroughSessionCreationFailed();
return;
}
if (mReleased) {
session.release();
return;
}
if (mSurface != null) {
session.setSurface(mSurface);
mSurface = null;
}
if (mVolume != null) {
session.setStreamVolume(mVolume);
mVolume = null;
}
if (mSurfaceChanged) {
session.dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
mSurfaceChanged = false;
}
mSession = session;
}
@Override
public void onSessionReleased(TvInputManager.Session session) {
mReleased = true;
mSession = null;
onPassthroughSessionReleased();
}
@Override
public void onVideoAvailable(TvInputManager.Session session) {
if (mSession == session) {
onPassthroughVideoAvailable();
}
}
@Override
public void onVideoUnavailable(TvInputManager.Session session, int reason) {
if (mSession == session) {
onPassthroughVideoUnavailable(reason);
}
}
@Override
public void onSessionEvent(TvInputManager.Session session, String eventType,
Bundle eventArgs) {
if (mSession == session) {
dispatchSessionEvent(eventType, eventArgs);
}
}
};
/**
* Called when failed to create a session for pass-through TV input.
*/
public abstract void onPassthroughSessionCreationFailed();
/**
* Called when the pass-through TV input session is released. This typically happens when
* the process hosting the pass-through TV input has crashed or been killed.
*/
public abstract void onPassthroughSessionReleased();
/**
* Called when the underlying pass-through TV input session calls
* {@link #dispatchVideoAvailable()}.
*/
public abstract void onPassthroughVideoAvailable();
/**
* Called when the underlying pass-through TV input session calls
* {@link #dispatchVideoUnavailable(int)}.
*
* @param reason The reason why the pass-through TV input stopped the playback.
*/
public abstract void onPassthroughVideoUnavailable(int reason);
@Override
public final boolean onSetSurface(Surface surface) {
if (DEBUG) Log.d(TAG, "onSetSurface(" + surface + ")");
if (mSession == null) {
mSurface = surface;
} else {
mSession.setSurface(surface);
}
return true;
}
private boolean connect(String inputId) {
if (mConnectionRequested) {
return false;
}
mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
mConnectionRequested = true;
return true;
}
@Override
void release() {
super.release();
mReleased = true;
if (mSession != null) {
mSession.release();
}
}
@Override
void dispatchSurfaceChanged(int format, int width, int height) {
super.dispatchSurfaceChanged(format, width, height);
if (mSession == null) {
mSurfaceFormat = format;
mSurfaceWidth = width;
mSurfaceHeight = height;
mSurfaceChanged = true;
} else {
mSession.dispatchSurfaceChanged(format, width, height);
}
}
@Override
void setStreamVolume(float volume) {
super.setStreamVolume(volume);
// Here, we let the pass-through TV input know only whether volume is on or off and
// make the fine control done in the derived class to prevent that the volume is
// controlled in the both side.
float volumeForPassthriughInput = (volume > 0.0f) ? VOLUME_ON : VOLUME_OFF;
if (mSession == null) {
mVolume = Float.valueOf(volumeForPassthriughInput);
} else {
mSession.setStreamVolume(volumeForPassthriughInput);
}
}
}
}

View File

@@ -681,7 +681,7 @@ public abstract class TvInputService extends Service {
/**
* Calls {@link #onSetStreamVolume}.
*/
void setVolume(float volume) {
void setStreamVolume(float volume) {
onSetStreamVolume(volume);
}