TIAF: handle Input Channel
Bug: 206454833 Test: mmm Change-Id: Ib2371cf6d4f4219d4bcad1b2f01b2370e1c83891
This commit is contained in:
@@ -16,13 +16,15 @@
|
||||
|
||||
package android.media.tv.interactive;
|
||||
|
||||
import android.view.InputChannel;
|
||||
|
||||
/**
|
||||
* Interface a client of the ITvIAppManager implements, to identify itself and receive information
|
||||
* about changes to the state of each TV interactive application service.
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITvIAppClient {
|
||||
void onSessionCreated(in String iAppServiceId, IBinder token, int seq);
|
||||
void onSessionCreated(in String iAppServiceId, IBinder token, in InputChannel channel, int seq);
|
||||
void onSessionReleased(int seq);
|
||||
void onLayoutSurface(int left, int top, int right, int bottom, int seq);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package android.media.tv.interactive;
|
||||
|
||||
import android.media.tv.interactive.ITvIAppServiceCallback;
|
||||
import android.media.tv.interactive.ITvIAppSessionCallback;
|
||||
import android.view.InputChannel;
|
||||
|
||||
/**
|
||||
* Top-level interface to a TV IApp component (implemented in a Service). It's used for
|
||||
@@ -27,5 +28,6 @@ import android.media.tv.interactive.ITvIAppSessionCallback;
|
||||
oneway interface ITvIAppService {
|
||||
void registerCallback(in ITvIAppServiceCallback callback);
|
||||
void unregisterCallback(in ITvIAppServiceCallback callback);
|
||||
void createSession(in ITvIAppSessionCallback callback, in String iAppServiceId, int type);
|
||||
void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
|
||||
in String iAppServiceId, int type);
|
||||
}
|
||||
@@ -23,9 +23,15 @@ import android.content.Context;
|
||||
import android.media.tv.TvInputManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Pools;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InputEvent;
|
||||
import android.view.InputEventSender;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
@@ -67,8 +73,8 @@ public final class TvIAppManager {
|
||||
mUserId = userId;
|
||||
mClient = new ITvIAppClient.Stub() {
|
||||
@Override
|
||||
public void onSessionCreated(String iAppServiceId, IBinder token, int seq) {
|
||||
// TODO: use InputChannel for input events
|
||||
public void onSessionCreated(String iAppServiceId, IBinder token, InputChannel channel,
|
||||
int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
if (record == null) {
|
||||
@@ -77,7 +83,7 @@ public final class TvIAppManager {
|
||||
}
|
||||
Session session = null;
|
||||
if (token != null) {
|
||||
session = new Session(token, mService, mUserId, seq,
|
||||
session = new Session(token, channel, mService, mUserId, seq,
|
||||
mSessionCallbackRecordMap);
|
||||
} else {
|
||||
mSessionCallbackRecordMap.delete(seq);
|
||||
@@ -351,18 +357,33 @@ public final class TvIAppManager {
|
||||
* @hide
|
||||
*/
|
||||
public static final class Session {
|
||||
static final int DISPATCH_IN_PROGRESS = -1;
|
||||
static final int DISPATCH_NOT_HANDLED = 0;
|
||||
static final int DISPATCH_HANDLED = 1;
|
||||
|
||||
private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
|
||||
|
||||
private final ITvIAppManager mService;
|
||||
private final int mUserId;
|
||||
private final int mSeq;
|
||||
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
|
||||
|
||||
private IBinder mToken;
|
||||
// For scheduling input event handling on the main thread. This also serves as a lock to
|
||||
// protect pending input events and the input channel.
|
||||
private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
|
||||
|
||||
private TvInputManager.Session mInputSession;
|
||||
private final Pools.Pool<PendingEvent> mPendingEventPool = new Pools.SimplePool<>(20);
|
||||
private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
|
||||
|
||||
private Session(IBinder token, ITvIAppManager service, int userId, int seq,
|
||||
SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
|
||||
private IBinder mToken;
|
||||
private TvInputEventSender mSender;
|
||||
private InputChannel mInputChannel;
|
||||
|
||||
private Session(IBinder token, InputChannel channel, ITvIAppManager service, int userId,
|
||||
int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
|
||||
mToken = token;
|
||||
mInputChannel = channel;
|
||||
mService = service;
|
||||
mUserId = userId;
|
||||
mSeq = seq;
|
||||
@@ -427,6 +448,43 @@ public final class TvIAppManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an input event to this session.
|
||||
*
|
||||
* @param event An {@link InputEvent} to dispatch. Cannot be {@code null}.
|
||||
* @param token A token used to identify the input event later in the callback.
|
||||
* @param callback A callback used to receive the dispatch result. Cannot be {@code null}.
|
||||
* @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be
|
||||
* {@code null}.
|
||||
* @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
|
||||
* {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
|
||||
* {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
|
||||
* be invoked later.
|
||||
* @hide
|
||||
*/
|
||||
public int dispatchInputEvent(@NonNull InputEvent event, Object token,
|
||||
@NonNull FinishedInputEventCallback callback, @NonNull Handler handler) {
|
||||
Preconditions.checkNotNull(event);
|
||||
Preconditions.checkNotNull(callback);
|
||||
Preconditions.checkNotNull(handler);
|
||||
synchronized (mHandler) {
|
||||
if (mInputChannel == null) {
|
||||
return DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
// Already running on the main thread so we can send the event immediately.
|
||||
return sendInputEventOnMainLooperLocked(p);
|
||||
}
|
||||
|
||||
// Post the event to the main thread.
|
||||
Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessage(msg);
|
||||
return DISPATCH_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases this session.
|
||||
*/
|
||||
@@ -444,12 +502,208 @@ public final class TvIAppManager {
|
||||
releaseInternal();
|
||||
}
|
||||
|
||||
private void flushPendingEventsLocked() {
|
||||
mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
|
||||
|
||||
final int count = mPendingEvents.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
int seq = mPendingEvents.keyAt(i);
|
||||
Message msg = mHandler.obtainMessage(
|
||||
InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
|
||||
msg.setAsynchronous(true);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseInternal() {
|
||||
mToken = null;
|
||||
synchronized (mHandler) {
|
||||
if (mInputChannel != null) {
|
||||
if (mSender != null) {
|
||||
flushPendingEventsLocked();
|
||||
mSender.dispose();
|
||||
mSender = null;
|
||||
}
|
||||
mInputChannel.dispose();
|
||||
mInputChannel = null;
|
||||
}
|
||||
}
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
mSessionCallbackRecordMap.delete(mSeq);
|
||||
}
|
||||
}
|
||||
|
||||
private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
|
||||
FinishedInputEventCallback callback, Handler handler) {
|
||||
PendingEvent p = mPendingEventPool.acquire();
|
||||
if (p == null) {
|
||||
p = new PendingEvent();
|
||||
}
|
||||
p.mEvent = event;
|
||||
p.mEventToken = token;
|
||||
p.mCallback = callback;
|
||||
p.mEventHandler = handler;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Assumes the event has already been removed from the queue.
|
||||
void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
|
||||
p.mHandled = handled;
|
||||
if (p.mEventHandler.getLooper().isCurrentThread()) {
|
||||
// Already running on the callback handler thread so we can send the callback
|
||||
// immediately.
|
||||
p.run();
|
||||
} else {
|
||||
// Post the event to the callback handler thread.
|
||||
// In this case, the callback will be responsible for recycling the event.
|
||||
Message msg = Message.obtain(p.mEventHandler, p);
|
||||
msg.setAsynchronous(true);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
// Must be called on the main looper
|
||||
private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
|
||||
synchronized (mHandler) {
|
||||
int result = sendInputEventOnMainLooperLocked(p);
|
||||
if (result == DISPATCH_IN_PROGRESS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
invokeFinishedInputEventCallback(p, false);
|
||||
}
|
||||
|
||||
private int sendInputEventOnMainLooperLocked(PendingEvent p) {
|
||||
if (mInputChannel != null) {
|
||||
if (mSender == null) {
|
||||
mSender = new TvInputEventSender(mInputChannel, mHandler.getLooper());
|
||||
}
|
||||
|
||||
final InputEvent event = p.mEvent;
|
||||
final int seq = event.getSequenceNumber();
|
||||
if (mSender.sendInputEvent(seq, event)) {
|
||||
mPendingEvents.put(seq, p);
|
||||
Message msg = mHandler.obtainMessage(
|
||||
InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
|
||||
return DISPATCH_IN_PROGRESS;
|
||||
}
|
||||
|
||||
Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
|
||||
+ event);
|
||||
}
|
||||
return DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
void finishedInputEvent(int seq, boolean handled, boolean timeout) {
|
||||
final PendingEvent p;
|
||||
synchronized (mHandler) {
|
||||
int index = mPendingEvents.indexOfKey(seq);
|
||||
if (index < 0) {
|
||||
return; // spurious, event already finished or timed out
|
||||
}
|
||||
|
||||
p = mPendingEvents.valueAt(index);
|
||||
mPendingEvents.removeAt(index);
|
||||
|
||||
if (timeout) {
|
||||
Log.w(TAG, "Timeout waiting for session to handle input event after "
|
||||
+ INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
|
||||
} else {
|
||||
mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
|
||||
}
|
||||
}
|
||||
|
||||
invokeFinishedInputEventCallback(p, handled);
|
||||
}
|
||||
|
||||
private void recyclePendingEventLocked(PendingEvent p) {
|
||||
p.recycle();
|
||||
mPendingEventPool.release(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback that is invoked when an input event that was dispatched to this session has been
|
||||
* finished.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface FinishedInputEventCallback {
|
||||
/**
|
||||
* Called when the dispatched input event is finished.
|
||||
*
|
||||
* @param token A token passed to {@link #dispatchInputEvent}.
|
||||
* @param handled {@code true} if the dispatched input event was handled properly.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
void onFinishedInputEvent(Object token, boolean handled);
|
||||
}
|
||||
|
||||
private final class InputEventHandler extends Handler {
|
||||
public static final int MSG_SEND_INPUT_EVENT = 1;
|
||||
public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
|
||||
public static final int MSG_FLUSH_INPUT_EVENT = 3;
|
||||
|
||||
InputEventHandler(Looper looper) {
|
||||
super(looper, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SEND_INPUT_EVENT: {
|
||||
sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
|
||||
return;
|
||||
}
|
||||
case MSG_TIMEOUT_INPUT_EVENT: {
|
||||
finishedInputEvent(msg.arg1, false, true);
|
||||
return;
|
||||
}
|
||||
case MSG_FLUSH_INPUT_EVENT: {
|
||||
finishedInputEvent(msg.arg1, false, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class TvInputEventSender extends InputEventSender {
|
||||
TvInputEventSender(InputChannel inputChannel, Looper looper) {
|
||||
super(inputChannel, looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputEventFinished(int seq, boolean handled) {
|
||||
finishedInputEvent(seq, handled, false);
|
||||
}
|
||||
}
|
||||
|
||||
private final class PendingEvent implements Runnable {
|
||||
public InputEvent mEvent;
|
||||
public Object mEventToken;
|
||||
public FinishedInputEventCallback mCallback;
|
||||
public Handler mEventHandler;
|
||||
public boolean mHandled;
|
||||
|
||||
public void recycle() {
|
||||
mEvent = null;
|
||||
mEventToken = null;
|
||||
mCallback = null;
|
||||
mEventHandler = null;
|
||||
mHandled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mCallback.onFinishedInputEvent(mEventToken, mHandled);
|
||||
|
||||
synchronized (mEventHandler) {
|
||||
recyclePendingEventLocked(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SessionCallbackRecord {
|
||||
|
||||
@@ -25,11 +25,17 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InputDevice;
|
||||
import android.view.InputEvent;
|
||||
import android.view.InputEventReceiver;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.os.SomeArgs;
|
||||
@@ -85,14 +91,16 @@ public abstract class TvIAppService extends Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSession(ITvIAppSessionCallback cb, String iAppServiceId, int type) {
|
||||
public void createSession(InputChannel channel, ITvIAppSessionCallback cb,
|
||||
String iAppServiceId, int type) {
|
||||
if (cb == null) {
|
||||
return;
|
||||
}
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = cb;
|
||||
args.arg2 = iAppServiceId;
|
||||
args.arg3 = type;
|
||||
args.arg1 = channel;
|
||||
args.arg2 = cb;
|
||||
args.arg3 = iAppServiceId;
|
||||
args.arg4 = type;
|
||||
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args)
|
||||
.sendToTarget();
|
||||
}
|
||||
@@ -122,6 +130,8 @@ public abstract class TvIAppService extends Service {
|
||||
* @hide
|
||||
*/
|
||||
public abstract static class Session implements KeyEvent.Callback {
|
||||
private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
|
||||
|
||||
private final Object mLock = new Object();
|
||||
// @GuardedBy("mLock")
|
||||
private ITvIAppSessionCallback mSessionCallback;
|
||||
@@ -181,6 +191,60 @@ public abstract class TvIAppService extends Service {
|
||||
public void onRelease() {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: JavaDoc of APIs related to input events.
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public boolean onTrackballEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
|
||||
* is relative to the overlay view that sits on top of this surface.
|
||||
@@ -226,6 +290,39 @@ public abstract class TvIAppService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes care of dispatching incoming input events and tells whether the event was handled.
|
||||
*/
|
||||
int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
|
||||
if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
|
||||
if (event instanceof KeyEvent) {
|
||||
KeyEvent keyEvent = (KeyEvent) event;
|
||||
if (keyEvent.dispatch(this, mDispatcherState, this)) {
|
||||
return TvIAppManager.Session.DISPATCH_HANDLED;
|
||||
}
|
||||
|
||||
// TODO: special handlings of navigation keys and media keys
|
||||
} else if (event instanceof MotionEvent) {
|
||||
MotionEvent motionEvent = (MotionEvent) event;
|
||||
final int source = motionEvent.getSource();
|
||||
if (motionEvent.isTouchEvent()) {
|
||||
if (onTouchEvent(motionEvent)) {
|
||||
return TvIAppManager.Session.DISPATCH_HANDLED;
|
||||
}
|
||||
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
|
||||
if (onTrackballEvent(motionEvent)) {
|
||||
return TvIAppManager.Session.DISPATCH_HANDLED;
|
||||
}
|
||||
} else {
|
||||
if (onGenericMotionEvent(motionEvent)) {
|
||||
return TvIAppManager.Session.DISPATCH_HANDLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: handle overlay view
|
||||
return TvIAppManager.Session.DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
private void initialize(ITvIAppSessionCallback callback) {
|
||||
synchronized (mLock) {
|
||||
mSessionCallback = callback;
|
||||
@@ -281,10 +378,17 @@ public abstract class TvIAppService extends Service {
|
||||
* @hide
|
||||
*/
|
||||
public static class ITvIAppSessionWrapper extends ITvIAppSession.Stub {
|
||||
// TODO: put ITvIAppSessionWrapper in a separate Java file
|
||||
private final Session mSessionImpl;
|
||||
private InputChannel mChannel;
|
||||
private TvIAppEventReceiver mReceiver;
|
||||
|
||||
public ITvIAppSessionWrapper(Session mSessionImpl) {
|
||||
public ITvIAppSessionWrapper(Context context, Session mSessionImpl, InputChannel channel) {
|
||||
this.mSessionImpl = mSessionImpl;
|
||||
mChannel = channel;
|
||||
if (channel != null) {
|
||||
mReceiver = new TvIAppEventReceiver(channel, context.getMainLooper());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -306,6 +410,26 @@ public abstract class TvIAppService extends Service {
|
||||
public void dispatchSurfaceChanged(int format, int width, int height) {
|
||||
mSessionImpl.dispatchSurfaceChanged(format, width, height);
|
||||
}
|
||||
|
||||
private final class TvIAppEventReceiver extends InputEventReceiver {
|
||||
TvIAppEventReceiver(InputChannel inputChannel, Looper looper) {
|
||||
super(inputChannel, looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputEvent(InputEvent event) {
|
||||
if (mSessionImpl == null) {
|
||||
// The session has been finished.
|
||||
finishInputEvent(event, false);
|
||||
return;
|
||||
}
|
||||
|
||||
int handled = mSessionImpl.dispatchInputEvent(event, this);
|
||||
if (handled != TvIAppManager.Session.DISPATCH_IN_PROGRESS) {
|
||||
finishInputEvent(event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
@@ -318,9 +442,10 @@ public abstract class TvIAppService extends Service {
|
||||
switch (msg.what) {
|
||||
case DO_CREATE_SESSION: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg1;
|
||||
String iAppServiceId = (String) args.arg2;
|
||||
int type = (int) args.arg3;
|
||||
InputChannel channel = (InputChannel) args.arg1;
|
||||
ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg2;
|
||||
String iAppServiceId = (String) args.arg3;
|
||||
int type = (int) args.arg4;
|
||||
args.recycle();
|
||||
Session sessionImpl = onCreateSession(iAppServiceId, type);
|
||||
if (sessionImpl == null) {
|
||||
@@ -332,7 +457,8 @@ public abstract class TvIAppService extends Service {
|
||||
}
|
||||
return;
|
||||
}
|
||||
ITvIAppSession stub = new ITvIAppSessionWrapper(sessionImpl);
|
||||
ITvIAppSession stub = new ITvIAppSessionWrapper(
|
||||
TvIAppService.this, sessionImpl, channel);
|
||||
|
||||
SomeArgs someArgs = SomeArgs.obtain();
|
||||
someArgs.arg1 = sessionImpl;
|
||||
|
||||
@@ -47,6 +47,7 @@ import android.os.UserManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InputChannel;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -611,14 +612,14 @@ public class TvIAppManagerService extends SystemService {
|
||||
if (userId != mCurrentUserId && !mRunningProfiles.contains(userId)) {
|
||||
// Only current user and its running profiles can create sessions.
|
||||
// Let the client get onConnectionFailed callback for this case.
|
||||
sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
|
||||
sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
|
||||
return;
|
||||
}
|
||||
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
|
||||
TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
|
||||
if (iAppState == null) {
|
||||
Slogf.w(TAG, "Failed to find state for iAppServiceId=" + iAppServiceId);
|
||||
sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
|
||||
sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
|
||||
return;
|
||||
}
|
||||
ServiceState serviceState =
|
||||
@@ -631,7 +632,7 @@ public class TvIAppManagerService extends SystemService {
|
||||
}
|
||||
// Send a null token immediately while reconnecting.
|
||||
if (serviceState.mReconnecting) {
|
||||
sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
|
||||
sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -780,9 +781,9 @@ public class TvIAppManagerService extends SystemService {
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void sendSessionTokenToClientLocked(ITvIAppClient client, String iAppServiceId,
|
||||
IBinder sessionToken, int seq) {
|
||||
IBinder sessionToken, InputChannel channel, int seq) {
|
||||
try {
|
||||
client.onSessionCreated(iAppServiceId, sessionToken, seq);
|
||||
client.onSessionCreated(iAppServiceId, sessionToken, channel, seq);
|
||||
} catch (RemoteException e) {
|
||||
Slogf.e(TAG, "error in onSessionCreated", e);
|
||||
}
|
||||
@@ -797,20 +798,23 @@ public class TvIAppManagerService extends SystemService {
|
||||
Slogf.d(TAG, "createSessionInternalLocked(iAppServiceId="
|
||||
+ sessionState.mIAppServiceId + ")");
|
||||
}
|
||||
InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
|
||||
|
||||
// Set up a callback to send the session token.
|
||||
ITvIAppSessionCallback callback = new SessionCallback(sessionState);
|
||||
ITvIAppSessionCallback callback = new SessionCallback(sessionState, channels);
|
||||
|
||||
boolean created = true;
|
||||
// Create a session. When failed, send a null token immediately.
|
||||
try {
|
||||
service.createSession(callback, sessionState.mIAppServiceId, sessionState.mType);
|
||||
service.createSession(
|
||||
channels[1], callback, sessionState.mIAppServiceId, sessionState.mType);
|
||||
} catch (RemoteException e) {
|
||||
Slogf.e(TAG, "error in createSession", e);
|
||||
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mIAppServiceId, null,
|
||||
sessionState.mSeq);
|
||||
null, sessionState.mSeq);
|
||||
created = false;
|
||||
}
|
||||
channels[1].dispose();
|
||||
return created;
|
||||
}
|
||||
|
||||
@@ -883,7 +887,7 @@ public class TvIAppManagerService extends SystemService {
|
||||
for (SessionState sessionState : sessionsToAbort) {
|
||||
removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
|
||||
sendSessionTokenToClientLocked(sessionState.mClient,
|
||||
sessionState.mIAppServiceId, null, sessionState.mSeq);
|
||||
sessionState.mIAppServiceId, null, null, sessionState.mSeq);
|
||||
}
|
||||
updateServiceConnectionLocked(serviceState.mComponent, userId);
|
||||
}
|
||||
@@ -1136,9 +1140,11 @@ public class TvIAppManagerService extends SystemService {
|
||||
|
||||
private final class SessionCallback extends ITvIAppSessionCallback.Stub {
|
||||
private final SessionState mSessionState;
|
||||
private final InputChannel[] mInputChannels;
|
||||
|
||||
SessionCallback(SessionState sessionState) {
|
||||
SessionCallback(SessionState sessionState, InputChannel[] channels) {
|
||||
mSessionState = sessionState;
|
||||
mInputChannels = channels;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1154,12 +1160,14 @@ public class TvIAppManagerService extends SystemService {
|
||||
mSessionState.mClient,
|
||||
mSessionState.mIAppServiceId,
|
||||
mSessionState.mSessionToken,
|
||||
mInputChannels[0],
|
||||
mSessionState.mSeq);
|
||||
} else {
|
||||
removeSessionStateLocked(mSessionState.mSessionToken, mSessionState.mUserId);
|
||||
sendSessionTokenToClientLocked(mSessionState.mClient,
|
||||
mSessionState.mIAppServiceId, null, mSessionState.mSeq);
|
||||
mSessionState.mIAppServiceId, null, null, mSessionState.mSeq);
|
||||
}
|
||||
mInputChannels[0].dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user