Merge "TIF: Add content ratings for Australia" into lmp-dev
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 37c7553..7a023d6 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -39,4 +39,5 @@
     void onVideoUnavailable(int reason, int seq);
     void onContentAllowed(int seq);
     void onContentBlocked(in String rating, int seq);
+    void onLayoutSurface(int left, int top, int right, int bottom, int seq);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 8a918e1..063d10d 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -36,4 +36,5 @@
     void onVideoUnavailable(int reason);
     void onContentAllowed();
     void onContentBlocked(in String rating);
+    void onLayoutSurface(int left, int top, int right, int bottom);
 }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index fdf0d9c..d556369 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -235,6 +235,21 @@
         }
 
         /**
+         * This is called when {@link TvInputService.Session#layoutSurface} is called to
+         * change the layout of surface.
+         *
+         * @param session A {@link TvInputManager.Session} associated with this callback
+         * @param l Left position.
+         * @param t Top position.
+         * @param r Right position.
+         * @param b Bottom position.
+         * @hide
+         */
+        @SystemApi
+        public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
+        }
+
+        /**
          * This is called when a custom event has been sent from this session.
          *
          * @param session A {@link TvInputManager.Session} associated with this callback
@@ -364,6 +379,16 @@
             });
         }
 
+        public void postLayoutSurface(final int left, final int top, final int right,
+                final int bottom) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onLayoutSurface(mSession, left, top, right, bottom);
+                }
+            });
+        }
+
         public void postSessionEvent(final String eventType, final Bundle eventArgs) {
             mHandler.post(new Runnable() {
                 @Override
@@ -574,6 +599,18 @@
             }
 
             @Override
+            public void onLayoutSurface(int left, int top, int right, int bottom, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postLayoutSurface(left, top, right, bottom);
+                }
+            }
+
+            @Override
             public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 642993a..d6d2d48 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -42,6 +42,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.Surface;
+import android.view.SurfaceView;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.accessibility.CaptioningManager;
@@ -91,7 +92,7 @@
      * Handler instance to handle request from TV Input Manager Service. Should be run in the main
      * looper to be synchronously run with {@code Session.mHandler}.
      */
-    private final Handler mServiceHandler = new ServiceHandler(getMainLooper());
+    private Handler mServiceHandler = new ServiceHandler();
     private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
             new RemoteCallbackList<ITvInputServiceCallback>();
 
@@ -504,6 +505,35 @@
         }
 
         /**
+         * Assigns a position of the {@link Surface} passed by {@link #onSetSurface}. The position
+         * is relative to an overlay view. {@see #onOverlayViewSizeChanged}.
+         *
+         * @param left Left position in pixels, relative to the overlay view.
+         * @param top Top position in pixels, relative to the overlay view.
+         * @param right Right position in pixels, relative to the overlay view.
+         * @param bottm Bottom position in pixels, relative to the overlay view.
+         * @hide
+         */
+        @SystemApi
+        public void layoutSurface(final int left, final int top, final int right, final int bottm) {
+            if (left > right || top > bottm) {
+                throw new IllegalArgumentException("Invalid parameter");
+            }
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top + ", r="
+                                + right + ", b=" + bottm + ",)");
+                        mSessionCallback.onLayoutSurface(left, top, right, bottm);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in layoutSurface");
+                    }
+                }
+            });
+        }
+
+        /**
          * Called when the session is released.
          */
         public abstract void onRelease();
@@ -556,6 +586,20 @@
         }
 
         /**
+         * Called when a size of an overlay view is changed by an application. Even when the overlay
+         * view is disabled by {@link #setOverlayViewEnabled}, this is called. The size is same as
+         * the size of {@link Surface} in general. Once {@link #layoutSurface} is called, the sizes
+         * of {@link Surface} and the overlay view can be different.
+         *
+         * @param width The width of the overlay view.
+         * @param height The height of the overlay view.
+         * @hide
+         */
+        @SystemApi
+        public void onOverlayViewSizeChanged(int width, int height) {
+        }
+
+        /**
          * Sets the relative stream volume of the current TV input session to handle the change of
          * audio focus by setting.
          *
@@ -873,6 +917,7 @@
             if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")");
             mWindowToken = windowToken;
             mOverlayFrame = frame;
+            onOverlayViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
             if (!mOverlayViewEnabled) {
                 return;
             }
@@ -887,7 +932,7 @@
             // the application that owns the window token can decide whether to consume or
             // dispatch the input events.
             int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
             mWindowParams = new WindowManager.LayoutParams(
                     frame.right - frame.left, frame.bottom - frame.top,
@@ -906,6 +951,12 @@
          */
         void relayoutOverlayView(Rect frame) {
             if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")");
+            if (mOverlayFrame == null || mOverlayFrame.width() != frame.width()
+                    || mOverlayFrame.height() != frame.height()) {
+                // Note: relayoutOverlayView is called whenever TvView's layout is changed
+                // regardless of setOverlayViewEnabled.
+                onOverlayViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
+            }
             mOverlayFrame = frame;
             if (!mOverlayViewEnabled || mOverlayView == null) {
                 return;
@@ -1116,10 +1167,6 @@
         private static final int DO_ADD_HDMI_CEC_TV_INPUT = 5;
         private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 6;
 
-        public ServiceHandler(Looper looper) {
-            super(looper);
-        }
-
         private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
             int n = mCallbacks.beginBroadcast();
             for (int i = 0; i < n; ++i) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 82761ec..66daf87 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -41,7 +41,18 @@
 import java.util.List;
 
 /**
- * View playing TV
+ * Displays TV contents. The TvView class provides a high level interface for applications to show
+ * TV programs from various TV sources that implement {@link TvInputService}. (Note that the list of
+ * TV inputs available on the system can be obtained by calling
+ * {@link TvInputManager#getTvInputList() TvInputManager.getTvInputList()}.)
+ * <p>
+ * Once the application supplies the URI for a specific TV channel to {@link #tune(String, Uri)}
+ * method, it takes care of underlying service binding (and unbinding if the current TvView is
+ * already bound to a service) and automatically allocates/deallocates resources needed. In addition
+ * to a few essential methods to control how the contents are presented, it also provides a way to
+ * dispatch input events to the connected TvInputService in order to enable custom key actions for
+ * the TV input.
+ * </p>
  */
 public class TvView extends ViewGroup {
     private static final String TAG = "TvView";
@@ -91,6 +102,11 @@
     private final AttributeSet mAttrs;
     private final int mDefStyleAttr;
     private int mWindowZOrder;
+    private boolean mUseRequestedSurfaceLayout;
+    private int mSurfaceViewLeft;
+    private int mSurfaceViewRight;
+    private int mSurfaceViewTop;
+    private int mSurfaceViewBottom;
 
     private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
         @Override
@@ -535,7 +551,16 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        mSurfaceView.layout(0, 0, right - left, bottom - top);
+        if (DEBUG) {
+            Log.d(TAG, "onLayout (left=" + left + ", top=" + top + ", right=" + right
+                    + ", bottom=" + bottom + ",)");
+        }
+        if (mUseRequestedSurfaceLayout) {
+            mSurfaceView.layout(mSurfaceViewLeft, mSurfaceViewTop, mSurfaceViewRight,
+                    mSurfaceViewBottom);
+        } else {
+            mSurfaceView.layout(0, 0, right - left, bottom - top);
+        }
     }
 
     @Override
@@ -583,6 +608,7 @@
     private void release() {
         setSessionSurface(null);
         removeSessionOverlayView();
+        mUseRequestedSurfaceLayout = false;
         mSession.release();
         mSession = null;
         mSessionCallback = null;
@@ -919,6 +945,20 @@
         }
 
         @Override
+        public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
+            if (DEBUG) {
+                Log.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top + ", right="
+                        + right + ", bottom=" + bottom + ",)");
+            }
+            mSurfaceViewLeft = left;
+            mSurfaceViewTop = top;
+            mSurfaceViewRight = right;
+            mSurfaceViewBottom = bottom;
+            mUseRequestedSurfaceLayout = true;
+            requestLayout();
+        }
+
+        @Override
         public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
             if (this != mSessionCallback) {
                 return;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 18446ae..594e9d0 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -52,7 +52,6 @@
 import android.media.tv.TvContract;
 import android.media.tv.TvInputHardwareInfo;
 import android.media.tv.TvInputInfo;
-import android.media.tv.TvInputManager;
 import android.media.tv.TvInputService;
 import android.media.tv.TvStreamConfig;
 import android.media.tv.TvTrackInfo;
@@ -636,6 +635,25 @@
             }
 
             @Override
+            public void onLayoutSurface(int left, int top, int right, int bottom) {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
+                                + ", right=" + right + ", bottom=" + bottom + ",)");
+                    }
+                    if (sessionState.mSession == null || sessionState.mClient == null) {
+                        return;
+                    }
+                    try {
+                        sessionState.mClient.onLayoutSurface(left, top, right, bottom,
+                                sessionState.mSeq);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in onLayoutSurface");
+                    }
+                }
+            }
+
+            @Override
             public void onSessionEvent(String eventType, Bundle eventArgs) {
                 synchronized (mLock) {
                     if (DEBUG) {