summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Shubang Lu <shubang@google.com> 2021-11-24 11:08:50 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-11-24 11:08:50 +0000
commit04e056e16af84e1afa2131dd5b6227ee88858d5d (patch)
tree84dd34a9d6685cc4ace1d6ea05c491f9b1a1b680
parent08d2e4f5df87a14c2d65200c1a8d1a34852af56c (diff)
parent44142891097e49b1feed15bb514b82e33b34590b (diff)
Merge "TIAF: handle Input Channel"
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppClient.aidl4
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppService.aidl4
-rw-r--r--media/java/android/media/tv/interactive/TvIAppManager.java266
-rw-r--r--media/java/android/media/tv/interactive/TvIAppService.java144
-rw-r--r--services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java30
5 files changed, 420 insertions, 28 deletions
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 0dd64b83df5f..dabea304591b 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -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);
} \ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
index 2f165f0de7e5..1dee9cc4ed28 100644
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl
@@ -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);
} \ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index 093e1be6ca5e..7479b2bd9f20 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -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;
@@ -428,6 +449,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.
*/
public void release() {
@@ -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 {
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index 78b8173e0af7..25dec62a2a2e 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -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;
@@ -182,6 +192,60 @@ public abstract class TvIAppService extends Service {
}
/**
+ * 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;
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index dfb07523d830..d0c6d137f5ef 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -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();
}
}