Merge "TIF: add TvInputManager methods to notify apps" into oc-dev am: 307bec8572
am: 081db81a99

Change-Id: I267fe4c5723ffa66c0555bba862b1618aebae5fd
diff --git a/api/system-current.txt b/api/system-current.txt
index 9e9c7e5..e2ffea0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -163,6 +163,7 @@
     field public static final java.lang.String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
     field public static final java.lang.String NFC = "android.permission.NFC";
     field public static final java.lang.String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
+    field public static final java.lang.String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
     field public static final java.lang.String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
     field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
     field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
@@ -26975,6 +26976,9 @@
     method public boolean isParentalControlsEnabled();
     method public boolean isRatingBlocked(android.media.tv.TvContentRating);
     method public boolean isSingleSessionActive();
+    method public void notifyPreviewProgramAddedToWatchNext(java.lang.String, long, long);
+    method public void notifyPreviewProgramBrowsableDisabled(java.lang.String, long);
+    method public void notifyWatchNextProgramBrowsableDisabled(java.lang.String, long);
     method public void registerCallback(android.media.tv.TvInputManager.TvInputCallback, android.os.Handler);
     method public void releaseTvInputHardware(int, android.media.tv.TvInputManager.Hardware);
     method public void removeBlockedRating(android.media.tv.TvContentRating);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 524001f..538fff3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -533,6 +533,9 @@
 
     <protected-broadcast android:name="android.content.pm.action.SESSION_COMMITTED" />
     <protected-broadcast android:name="android.os.action.USER_RESTRICTIONS_CHANGED" />
+    <protected-broadcast android:name="android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT" />
+    <protected-broadcast android:name="android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED" />
+    <protected-broadcast android:name="android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
@@ -2535,6 +2538,13 @@
     <permission android:name="android.permission.MODIFY_PARENTAL_CONTROLS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to notify TV inputs by sending broadcasts.
+         <p>Protection level: signature|privileged
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.NOTIFY_TV_INPUTS"
+         android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by a {@link android.media.routing.MediaRouteService}
          to ensure that only the system can interact with it.
          @hide -->
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 0d51b5b..af4a5be 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -17,6 +17,7 @@
 package android.media.tv;
 
 import android.content.ComponentName;
+import android.content.Intent;
 import android.graphics.Rect;
 import android.media.PlaybackParams;
 import android.media.tv.DvbDeviceInfo;
@@ -105,4 +106,7 @@
     // For DVB device binding
     List<DvbDeviceInfo> getDvbDeviceList();
     ParcelFileDescriptor openDvbDevice(in DvbDeviceInfo info, int device);
+
+    // For preview programs
+    void sendTvInputNotifyIntent(in Intent intent, int userId);
 }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 09b2050..276a0dc 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.content.Intent;
 import android.graphics.Rect;
 import android.media.PlaybackParams;
 import android.net.Uri;
@@ -1394,6 +1395,64 @@
     }
 
     /**
+     * Notifies the TV input of the given preview program that the program's browsable state is
+     * disabled.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
+    public void notifyPreviewProgramBrowsableDisabled(String packageName, long programId) {
+        Intent intent = new Intent();
+        intent.setAction(TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED);
+        intent.putExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, programId);
+        intent.setPackage(packageName);
+        try {
+            mService.sendTvInputNotifyIntent(intent, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the TV input of the given watch next program that the program's browsable state is
+     * disabled.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
+    public void notifyWatchNextProgramBrowsableDisabled(String packageName, long programId) {
+        Intent intent = new Intent();
+        intent.setAction(TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED);
+        intent.putExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, programId);
+        intent.setPackage(packageName);
+        try {
+            mService.sendTvInputNotifyIntent(intent, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the TV input of the given preview program that the program is added to watch next.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
+    public void notifyPreviewProgramAddedToWatchNext(String packageName, long previewProgramId,
+            long watchNextProgramId) {
+        Intent intent = new Intent();
+        intent.setAction(TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT);
+        intent.putExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, previewProgramId);
+        intent.putExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, watchNextProgramId);
+        intent.setPackage(packageName);
+        try {
+            mService.sendTvInputNotifyIntent(intent, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates a {@link Session} for a given TV input.
      *
      * <p>The number of sessions that can be created at the same time is limited by the capability
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index be91f48..a519acc 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -942,6 +942,50 @@
         }
 
         @Override
+        public void sendTvInputNotifyIntent(Intent intent, int userId) {
+            if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("The caller: " + getCallingPackageName()
+                        + " doesn't have permission: "
+                        + android.Manifest.permission.NOTIFY_TV_INPUTS);
+            }
+            if (TextUtils.isEmpty(intent.getPackage())) {
+                throw new IllegalArgumentException("Must specify package name to notify.");
+            }
+            switch (intent.getAction()) {
+                case TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
+                    if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
+                        throw new IllegalArgumentException("Invalid preview program ID.");
+                    }
+                    break;
+                case TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
+                    if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
+                        throw new IllegalArgumentException("Invalid watch next program ID.");
+                    }
+                    break;
+                case TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
+                    if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
+                        throw new IllegalArgumentException("Invalid preview program ID.");
+                    }
+                    if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
+                        throw new IllegalArgumentException("Invalid watch next program ID.");
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid TV input notifying action: "
+                            + intent.getAction());
+            }
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "sendTvInputNotifyIntent");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void registerCallback(final ITvInputManagerCallback callback, int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "registerCallback");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f74512a..e47da55 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1437,7 +1437,8 @@
                 traceEnd();
             }
 
-            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV)) {
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV)
+                    || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
                 traceBeginAndSlog("StartTvInputManager");
                 mSystemServiceManager.startService(TvInputManagerService.class);
                 traceEnd();