Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b35a72a authored by Sungsoo Lim's avatar Sungsoo Lim
Browse files

Handle the crash of TIS.

- Implement DeathRecipient for SessionState.
- Implement onServiceDisconnected.
- Add callback methods for notifying the death of Session.

Bug: 14073482
Change-Id: Ifdc54266a878224844a221f0b52415d0bc378e39
parent 51391666
Loading
Loading
Loading
Loading
+6 −4
Original line number Original line Diff line number Diff line
@@ -29179,7 +29179,7 @@ package android.tv {
  }
  }
  public final class TvInputManager {
  public final class TvInputManager {
    method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCreateCallback, android.os.Handler);
    method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCallback, android.os.Handler);
    method public boolean getAvailability(java.lang.String);
    method public boolean getAvailability(java.lang.String);
    method public java.util.List<android.tv.TvInputInfo> getTvInputList();
    method public java.util.List<android.tv.TvInputInfo> getTvInputList();
    method public void registerListener(java.lang.String, android.tv.TvInputManager.TvInputListener, android.os.Handler);
    method public void registerListener(java.lang.String, android.tv.TvInputManager.TvInputListener, android.os.Handler);
@@ -29192,8 +29192,10 @@ package android.tv {
    method public void tune(android.net.Uri);
    method public void tune(android.net.Uri);
  }
  }
  public static abstract interface TvInputManager.SessionCreateCallback {
  public static abstract class TvInputManager.SessionCallback {
    method public abstract void onSessionCreated(android.tv.TvInputManager.Session);
    ctor public TvInputManager.SessionCallback();
    method public void onSessionCreated(android.tv.TvInputManager.Session);
    method public void onSessionReleased(android.tv.TvInputManager.Session);
  }
  }
  public static abstract class TvInputManager.TvInputListener {
  public static abstract class TvInputManager.TvInputListener {
@@ -29230,7 +29232,7 @@ package android.tv {
    ctor public TvView(android.content.Context);
    ctor public TvView(android.content.Context);
    ctor public TvView(android.content.Context, android.util.AttributeSet);
    ctor public TvView(android.content.Context, android.util.AttributeSet);
    ctor public TvView(android.content.Context, android.util.AttributeSet, int);
    ctor public TvView(android.content.Context, android.util.AttributeSet, int);
    method public void bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCreateCallback);
    method public void bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCallback);
    method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
    method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
    method public boolean onUnhandledInputEvent(android.view.InputEvent);
    method public boolean onUnhandledInputEvent(android.view.InputEvent);
    method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener);
    method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener);
+1 −0
Original line number Original line Diff line number Diff line
@@ -28,4 +28,5 @@ import android.view.InputChannel;
oneway interface ITvInputClient {
oneway interface ITvInputClient {
    void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
    void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
    void onAvailabilityChanged(in String inputId, boolean isAvailable);
    void onAvailabilityChanged(in String inputId, boolean isAvailable);
    void onSessionReleased(int seq);
}
}
+79 −32
Original line number Original line Diff line number Diff line
@@ -52,12 +52,12 @@ public final class TvInputManager {
    private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
    private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
            new HashMap<String, List<TvInputListenerRecord>>();
            new HashMap<String, List<TvInputListenerRecord>>();


    // A mapping from the sequence number of a session to its SessionCreateCallbackRecord.
    // A mapping from the sequence number of a session to its SessionCallbackRecord.
    private final SparseArray<SessionCreateCallbackRecord> mSessionCreateCallbackRecordMap =
    private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
            new SparseArray<SessionCreateCallbackRecord>();
            new SparseArray<SessionCallbackRecord>();


    // A sequence number for the next session to be created. Should be protected by a lock
    // A sequence number for the next session to be created. Should be protected by a lock
    // {@code mSessionCreateCallbackRecordMap}.
    // {@code mSessionCallbackRecordMap}.
    private int mNextSeq;
    private int mNextSeq;


    private final ITvInputClient mClient;
    private final ITvInputClient mClient;
@@ -67,31 +67,52 @@ public final class TvInputManager {
    /**
    /**
     * Interface used to receive the created session.
     * Interface used to receive the created session.
     */
     */
    public interface SessionCreateCallback {
    public abstract static class SessionCallback {
        /**
        /**
         * This is called after {@link TvInputManager#createSession} has been processed.
         * This is called after {@link TvInputManager#createSession} has been processed.
         *
         *
         * @param session A {@link TvInputManager.Session} instance created. This can be
         * @param session A {@link TvInputManager.Session} instance created. This can be
         *            {@code null} if the creation request failed.
         *            {@code null} if the creation request failed.
         */
         */
        void onSessionCreated(Session session);
        public void onSessionCreated(Session session) {
        }
        }


    private static final class SessionCreateCallbackRecord {
        /**
        private final SessionCreateCallback mSessionCreateCallback;
         * This is called when {@link TvInputManager.Session} is released.
         * This typically happens when the process hosting the session has crashed or been killed.
         *
         * @param session A {@link TvInputManager.Session} instance released.
         */
        public void onSessionReleased(Session session) {
        }
    }

    private static final class SessionCallbackRecord {
        private final SessionCallback mSessionCallback;
        private final Handler mHandler;
        private final Handler mHandler;
        private Session mSession;


        public SessionCreateCallbackRecord(SessionCreateCallback sessionCreateCallback,
        public SessionCallbackRecord(SessionCallback sessionCallback,
                Handler handler) {
                Handler handler) {
            mSessionCreateCallback = sessionCreateCallback;
            mSessionCallback = sessionCallback;
            mHandler = handler;
            mHandler = handler;
        }
        }


        public void postSessionCreated(final Session session) {
        public void postSessionCreated(final Session session) {
            mSession = session;
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mSessionCallback.onSessionCreated(session);
                }
            });
        }

        public void postSessionReleased() {
            mHandler.post(new Runnable() {
            mHandler.post(new Runnable() {
                @Override
                @Override
                public void run() {
                public void run() {
                    mSessionCreateCallback.onSessionCreated(session);
                    mSessionCallback.onSessionReleased(mSession);
                }
                }
            });
            });
        }
        }
@@ -145,21 +166,35 @@ public final class TvInputManager {
            @Override
            @Override
            public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
            public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
                    int seq) {
                    int seq) {
                synchronized (mSessionCreateCallbackRecordMap) {
                synchronized (mSessionCallbackRecordMap) {
                    SessionCreateCallbackRecord record = mSessionCreateCallbackRecordMap.get(seq);
                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                    mSessionCreateCallbackRecordMap.delete(seq);
                    if (record == null) {
                    if (record == null) {
                        Log.e(TAG, "Callback not found for " + token);
                        Log.e(TAG, "Callback not found for " + token);
                        return;
                        return;
                    }
                    }
                    Session session = null;
                    Session session = null;
                    if (token != null) {
                    if (token != null) {
                        session = new Session(token, channel, mService, mUserId);
                        session = new Session(token, channel, mService, mUserId, seq,
                                mSessionCallbackRecordMap);
                    }
                    }
                    record.postSessionCreated(session);
                    record.postSessionCreated(session);
                }
                }
            }
            }


            @Override
            public void onSessionReleased(int seq) {
                synchronized (mSessionCallbackRecordMap) {
                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                    mSessionCallbackRecordMap.delete(seq);
                    if (record == null) {
                        Log.e(TAG, "Callback not found for seq:" + seq);
                        return;
                    }
                    record.mSession.releaseInternal();
                    record.postSessionReleased();
                }
            }

            @Override
            @Override
            public void onAvailabilityChanged(String inputId, boolean isAvailable) {
            public void onAvailabilityChanged(String inputId, boolean isAvailable) {
                synchronized (mTvInputListenerRecordsMap) {
                synchronized (mTvInputListenerRecordsMap) {
@@ -298,7 +333,7 @@ public final class TvInputManager {
     * @param handler a {@link Handler} that the session creation will be delivered to.
     * @param handler a {@link Handler} that the session creation will be delivered to.
     * @throws IllegalArgumentException if any of the arguments is {@code null}.
     * @throws IllegalArgumentException if any of the arguments is {@code null}.
     */
     */
    public void createSession(String inputId, final SessionCreateCallback callback,
    public void createSession(String inputId, final SessionCallback callback,
            Handler handler) {
            Handler handler) {
        if (inputId == null) {
        if (inputId == null) {
            throw new IllegalArgumentException("id cannot be null");
            throw new IllegalArgumentException("id cannot be null");
@@ -309,10 +344,10 @@ public final class TvInputManager {
        if (handler == null) {
        if (handler == null) {
            throw new IllegalArgumentException("handler cannot be null");
            throw new IllegalArgumentException("handler cannot be null");
        }
        }
        SessionCreateCallbackRecord record = new SessionCreateCallbackRecord(callback, handler);
        SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
        synchronized (mSessionCreateCallbackRecordMap) {
        synchronized (mSessionCallbackRecordMap) {
            int seq = mNextSeq++;
            int seq = mNextSeq++;
            mSessionCreateCallbackRecordMap.put(seq, record);
            mSessionCallbackRecordMap.put(seq, record);
            try {
            try {
                mService.createSession(mClient, inputId, seq, mUserId);
                mService.createSession(mClient, inputId, seq, mUserId);
            } catch (RemoteException e) {
            } catch (RemoteException e) {
@@ -331,6 +366,7 @@ public final class TvInputManager {


        private final ITvInputManager mService;
        private final ITvInputManager mService;
        private final int mUserId;
        private final int mUserId;
        private final int mSeq;


        // For scheduling input event handling on the main thread. This also serves as a lock to
        // For scheduling input event handling on the main thread. This also serves as a lock to
        // protect pending input events and the input channel.
        // protect pending input events and the input channel.
@@ -338,17 +374,21 @@ public final class TvInputManager {


        private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
        private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
        private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
        private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
        private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;


        private IBinder mToken;
        private IBinder mToken;
        private TvInputEventSender mSender;
        private TvInputEventSender mSender;
        private InputChannel mChannel;
        private InputChannel mChannel;


        /** @hide */
        /** @hide */
        private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId) {
        private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
                int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
            mToken = token;
            mToken = token;
            mChannel = channel;
            mChannel = channel;
            mService = service;
            mService = service;
            mUserId = userId;
            mUserId = userId;
            mSeq = seq;
            mSessionCallbackRecordMap = sessionCallbackRecordMap;
        }
        }


        /**
        /**
@@ -362,22 +402,11 @@ public final class TvInputManager {
            }
            }
            try {
            try {
                mService.releaseSession(mToken, mUserId);
                mService.releaseSession(mToken, mUserId);
                mToken = null;
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                throw new RuntimeException(e);
                throw new RuntimeException(e);
            }
            }


            synchronized (mHandler) {
            releaseInternal();
                if (mChannel != null) {
                    if (mSender != null) {
                        flushPendingEventsLocked();
                        mSender.dispose();
                        mSender = null;
                    }
                    mChannel.dispose();
                    mChannel = null;
                }
            }
        }
        }


        /**
        /**
@@ -669,6 +698,24 @@ public final class TvInputManager {
            mPendingEventPool.release(p);
            mPendingEventPool.release(p);
        }
        }


        private void releaseInternal() {
            mToken = null;
            synchronized (mHandler) {
                if (mChannel != null) {
                    if (mSender != null) {
                        flushPendingEventsLocked();
                        mSender.dispose();
                        mSender = null;
                    }
                    mChannel.dispose();
                    mChannel = null;
                }
            }
            synchronized (mSessionCallbackRecordMap) {
                mSessionCallbackRecordMap.remove(mSeq);
            }
        }

        private final class InputEventHandler extends Handler {
        private final class InputEventHandler extends Handler {
            public static final int MSG_SEND_INPUT_EVENT = 1;
            public static final int MSG_SEND_INPUT_EVENT = 1;
            public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
            public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
+19 −11
Original line number Original line Diff line number Diff line
@@ -22,7 +22,7 @@ import android.os.Handler;
import android.text.TextUtils;
import android.text.TextUtils;
import android.tv.TvInputManager.Session;
import android.tv.TvInputManager.Session;
import android.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.tv.TvInputManager.SessionCreateCallback;
import android.tv.TvInputManager.SessionCallback;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Log;
import android.view.InputEvent;
import android.view.InputEvent;
@@ -46,7 +46,7 @@ public class TvView extends SurfaceView {
    private boolean mOverlayViewCreated;
    private boolean mOverlayViewCreated;
    private Rect mOverlayViewFrame;
    private Rect mOverlayViewFrame;
    private final TvInputManager mTvInputManager;
    private final TvInputManager mTvInputManager;
    private SessionCreateCallback mSessionCreateCallback;
    private SessionCallback mSessionCallback;
    private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
    private OnUnhandledInputEventListener mOnUnhandledInputEventListener;


    private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
    private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@@ -108,7 +108,7 @@ public class TvView extends SurfaceView {
    }
    }


    /**
    /**
     * Binds a TV input to this view. {@link SessionCreateCallback#onSessionCreated} will be
     * Binds a TV input to this view. {@link SessionCallback#onSessionCreated} will be
     * called to send the result of this binding with {@link TvInputManager.Session}.
     * called to send the result of this binding with {@link TvInputManager.Session}.
     * If a TV input is already bound, the input will be unbound from this view and its session
     * If a TV input is already bound, the input will be unbound from this view and its session
     * will be released.
     * will be released.
@@ -118,7 +118,7 @@ public class TvView extends SurfaceView {
     *        {@link TvInputManager.Session}
     *        {@link TvInputManager.Session}
     * @throws IllegalArgumentException if any of the arguments is {@code null}.
     * @throws IllegalArgumentException if any of the arguments is {@code null}.
     */
     */
    public void bindTvInput(String inputId, SessionCreateCallback callback) {
    public void bindTvInput(String inputId, SessionCallback callback) {
        if (TextUtils.isEmpty(inputId)) {
        if (TextUtils.isEmpty(inputId)) {
            throw new IllegalArgumentException("inputId cannot be null or an empty string");
            throw new IllegalArgumentException("inputId cannot be null or an empty string");
        }
        }
@@ -130,11 +130,11 @@ public class TvView extends SurfaceView {
        }
        }
        // When bindTvInput is called multiple times before the callback is called,
        // When bindTvInput is called multiple times before the callback is called,
        // only the callback of the last bindTvInput call will be actually called back.
        // only the callback of the last bindTvInput call will be actually called back.
        // The previous callbacks will be ignored. For the logic, mSessionCreateCallback
        // The previous callbacks will be ignored. For the logic, mSessionCallback
        // is newly assigned for every bindTvInput call and compared with
        // is newly assigned for every bindTvInput call and compared with
        // MySessionCreateCallback.this.
        // MySessionCreateCallback.this.
        mSessionCreateCallback = new MySessionCreateCallback(callback);
        mSessionCallback = new MySessionCallback(callback);
        mTvInputManager.createSession(inputId, mSessionCreateCallback, mHandler);
        mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
    }
    }


    /**
    /**
@@ -336,16 +336,16 @@ public class TvView extends SurfaceView {
        boolean onUnhandledInputEvent(InputEvent event);
        boolean onUnhandledInputEvent(InputEvent event);
    }
    }


    private class MySessionCreateCallback implements SessionCreateCallback {
    private class MySessionCallback extends SessionCallback {
        final SessionCreateCallback mExternalCallback;
        final SessionCallback mExternalCallback;


        MySessionCreateCallback(SessionCreateCallback externalCallback) {
        MySessionCallback(SessionCallback externalCallback) {
            mExternalCallback = externalCallback;
            mExternalCallback = externalCallback;
        }
        }


        @Override
        @Override
        public void onSessionCreated(Session session) {
        public void onSessionCreated(Session session) {
            if (this != mSessionCreateCallback) {
            if (this != mSessionCallback) {
                // This callback is obsolete.
                // This callback is obsolete.
                session.release();
                session.release();
                return;
                return;
@@ -364,5 +364,13 @@ public class TvView extends SurfaceView {
                mExternalCallback.onSessionCreated(session);
                mExternalCallback.onSessionCreated(session);
            }
            }
        }
        }

        @Override
        public void onSessionReleased(Session session) {
            mSession = null;
            if (mExternalCallback != null) {
                mExternalCallback.onSessionReleased(session);
            }
        }
    }
    }
}
}
+125 −37

File changed.

Preview size limit exceeded, changes collapsed.