diff options
author | Dongwon Kang <dwkang@google.com> | 2014-10-27 17:55:27 +0900 |
---|---|---|
committer | Dongwon Kang <dwkang@google.com> | 2014-10-30 13:37:03 +0900 |
commit | ce34c6d308629c214ab9b7963755eb60cac03c9d (patch) | |
tree | 6cee3d4d06757d33b84591bdf073220cfa46dbb8 /media | |
parent | c1f26faa2db2062ed42e422b038442fdb0aa3c68 (diff) | |
download | frameworks_base-ce34c6d308629c214ab9b7963755eb60cac03c9d.zip frameworks_base-ce34c6d308629c214ab9b7963755eb60cac03c9d.tar.gz frameworks_base-ce34c6d308629c214ab9b7963755eb60cac03c9d.tar.bz2 |
TIF: handle inputs holding the overlay view even after the session release
Bug: 17336221
Change-Id: I7fb7890039bc3731b62410caa9802db5870ec2e2
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/tv/ITvInputSessionWrapper.java | 1 | ||||
-rw-r--r-- | media/java/android/media/tv/TvInputService.java | 89 |
2 files changed, 74 insertions, 16 deletions
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index b8cdc4b..1ac80c1 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -166,6 +166,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand @Override public void release() { + mTvInputSessionImpl.scheduleOverlayViewCleanup(); mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE)); } diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 2c97db0..0ca5810 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -25,10 +25,12 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.hdmi.HdmiDeviceInfo; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.text.TextUtils; @@ -44,6 +46,7 @@ import android.view.Surface; import android.view.View; import android.view.WindowManager; import android.view.accessibility.CaptioningManager; +import android.widget.FrameLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; @@ -242,12 +245,16 @@ public abstract class TvInputService extends Service { * Base class for derived classes to implement to provide a TV input session. */ public abstract static class Session implements KeyEvent.Callback { + private static final int DETACH_OVERLAY_VIEW_TIMEOUT = 5000; private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); private final WindowManager mWindowManager; final Handler mHandler; private WindowManager.LayoutParams mWindowParams; private Surface mSurface; + private Context mContext; + private FrameLayout mOverlayViewContainer; private View mOverlayView; + private OverlayViewCleanUpTask mOverlayViewCleanUpTask; private boolean mOverlayViewEnabled; private IBinder mWindowToken; private Rect mOverlayFrame; @@ -264,6 +271,7 @@ public abstract class TvInputService extends Service { * @param context The context of the application */ public Session(Context context) { + mContext = context; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mHandler = new Handler(context.getMainLooper()); } @@ -853,7 +861,6 @@ public abstract class TvInputService extends Service { * session. */ void release() { - removeOverlayView(true); onRelease(); if (mSurface != null) { mSurface.release(); @@ -863,6 +870,9 @@ public abstract class TvInputService extends Service { mSessionCallback = null; mPendingActions.clear(); } + // Removes the overlay view lastly so that any hanging on the main thread can be handled + // in {@link #scheduleOverlayViewCleanup}. + removeOverlayView(true); } /** @@ -947,9 +957,8 @@ public abstract class TvInputService extends Service { * @param frame A position of the overlay view. */ void createOverlayView(IBinder windowToken, Rect frame) { - if (mOverlayView != null) { - mWindowManager.removeView(mOverlayView); - mOverlayView = null; + if (mOverlayViewContainer != null) { + removeOverlayView(false); } if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")"); mWindowToken = windowToken; @@ -962,6 +971,15 @@ public abstract class TvInputService extends Service { if (mOverlayView == null) { return; } + if (mOverlayViewCleanUpTask != null) { + mOverlayViewCleanUpTask.cancel(true); + mOverlayViewCleanUpTask = null; + } + // Creates a container view to check hanging on the overlay view detaching. + // Adding/removing the overlay view to/from the container make the view attach/detach + // logic run on the main thread. + mOverlayViewContainer = new FrameLayout(mContext); + mOverlayViewContainer.addView(mOverlayView); // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create // an overlay window above the media window but below the application window. int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; @@ -978,7 +996,7 @@ public abstract class TvInputService extends Service { WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; mWindowParams.gravity = Gravity.START | Gravity.TOP; mWindowParams.token = windowToken; - mWindowManager.addView(mOverlayView, mWindowParams); + mWindowManager.addView(mOverlayViewContainer, mWindowParams); } /** @@ -995,33 +1013,51 @@ public abstract class TvInputService extends Service { onOverlayViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); } mOverlayFrame = frame; - if (!mOverlayViewEnabled || mOverlayView == null) { + if (!mOverlayViewEnabled || mOverlayViewContainer == null) { return; } mWindowParams.x = frame.left; mWindowParams.y = frame.top; mWindowParams.width = frame.right - frame.left; mWindowParams.height = frame.bottom - frame.top; - mWindowManager.updateViewLayout(mOverlayView, mWindowParams); + mWindowManager.updateViewLayout(mOverlayViewContainer, mWindowParams); } /** * Removes the current overlay view. */ void removeOverlayView(boolean clearWindowToken) { - if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayView + ")"); + if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayViewContainer + ")"); if (clearWindowToken) { mWindowToken = null; mOverlayFrame = null; } - if (mOverlayView != null) { - mWindowManager.removeView(mOverlayView); + if (mOverlayViewContainer != null) { + // Removes the overlay view from the view hierarchy in advance so that it can be + // cleaned up in the {@link OverlayViewCleanUpTask} if the remove process is + // hanging. + mOverlayViewContainer.removeView(mOverlayView); mOverlayView = null; + mWindowManager.removeView(mOverlayViewContainer); + mOverlayViewContainer = null; mWindowParams = null; } } /** + * Schedules a task which checks whether the overlay view is detached and kills the process + * if it is not. Note that this method is expected to be called in a non-main thread. + */ + void scheduleOverlayViewCleanup() { + View overlayViewParent = mOverlayViewContainer; + if (overlayViewParent != null) { + mOverlayViewCleanUpTask = new OverlayViewCleanUpTask(); + mOverlayViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, + overlayViewParent); + } + } + + /** * Takes care of dispatching incoming input events and tells whether the event was handled. */ int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { @@ -1050,22 +1086,22 @@ public abstract class TvInputService extends Service { } } } - if (mOverlayView == null || !mOverlayView.isAttachedToWindow()) { + if (mOverlayViewContainer == null || !mOverlayViewContainer.isAttachedToWindow()) { return TvInputManager.Session.DISPATCH_NOT_HANDLED; } - if (!mOverlayView.hasWindowFocus()) { - mOverlayView.getViewRootImpl().windowFocusChanged(true, true); + if (!mOverlayViewContainer.hasWindowFocus()) { + mOverlayViewContainer.getViewRootImpl().windowFocusChanged(true, true); } - if (isNavigationKey && mOverlayView.hasFocusable()) { + if (isNavigationKey && mOverlayViewContainer.hasFocusable()) { // If mOverlayView has focusable views, navigation key events should be always // handled. If not, it can make the application UI navigation messed up. // For example, in the case that the left-most view is focused, a left key event // will not be handled in ViewRootImpl. Then, the left key event will be handled in // the application during the UI navigation of the TV input. - mOverlayView.getViewRootImpl().dispatchInputEvent(event); + mOverlayViewContainer.getViewRootImpl().dispatchInputEvent(event); return TvInputManager.Session.DISPATCH_HANDLED; } else { - mOverlayView.getViewRootImpl().dispatchInputEvent(event, receiver); + mOverlayViewContainer.getViewRootImpl().dispatchInputEvent(event, receiver); return TvInputManager.Session.DISPATCH_IN_PROGRESS; } } @@ -1095,6 +1131,27 @@ public abstract class TvInputService extends Service { } } } + + private final class OverlayViewCleanUpTask extends AsyncTask<View, Void, Void> { + @Override + protected Void doInBackground(View... views) { + View overlayViewParent = views[0]; + try { + Thread.sleep(DETACH_OVERLAY_VIEW_TIMEOUT); + } catch (InterruptedException e) { + return null; + } + if (isCancelled()) { + return null; + } + if (overlayViewParent.isAttachedToWindow()) { + Log.e(TAG, "Time out on releasing overlay view. Killing " + + overlayViewParent.getContext().getPackageName()); + Process.killProcess(Process.myPid()); + } + return null; + } + } } /** |