Add a BIND_DREAM_SERVICE signature-level permission.

Starting in api 21, will be required to bind to a
declared dream or doze service.

Also added to aapt dump badging output.

Bug:15862654
Change-Id: Ifa0a594eeecf21e6146176c7810a847e1d08fe3b
diff --git a/api/current.txt b/api/current.txt
index 8add79c..f5b5107 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21,6 +21,7 @@
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
     field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
+    field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 9da3a51..6bb3458 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -124,6 +124,19 @@
  *     }
  * }
  * </pre>
+ *
+ * <p>When targeting api level 21 and above, you must declare the service in your manifest file
+ * with the {@link android.Manifest.permission#BIND_DREAM_SERVICE} permission. For example:</p>
+ * <pre>
+ * &lt;service
+ *     android:name=".MyDream"
+ *     android:exported="true"
+ *     android:icon="@drawable/my_icon"
+ *     android:label="@string/my_dream_label"
+ *     android:permission="android.permission.BIND_DREAM_SERVICE" >
+ *  ...
+ * &lt;/service>
+ * </pre>
  */
 public class DreamService extends Service implements Window.Callback {
     private final String TAG = DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ca897d5..7db478d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2719,6 +2719,13 @@
         android:description="@string/permdesc_bindConditionProviderService"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by an {@link android.service.dreams.DreamService},
+         to ensure that only the system can bind to it. -->
+    <permission android:name="android.permission.BIND_DREAM_SERVICE"
+        android:label="@string/permlab_bindDreamService"
+        android:description="@string/permdesc_bindDreamService"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to call into a carrier setup flow. It is up to the
          carrier setup application to enforce that this permission is required
          @hide This is not a third-party API (intended for OEMs and system apps). -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index db02279..a3262ed 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2122,6 +2122,11 @@
     <string name="permdesc_bindConditionProviderService">Allows the holder to bind to the top-level interface of a condition provider service. Should never be needed for normal apps.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindDreamService">bind to a dream service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindDreamService">Allows the holder to bind to the top-level interface of a dream service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_invokeCarrierSetup">invoke the carrier-provided configuration app</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_invokeCarrierSetup">Allows the holder to invoke the carrier-provided configuration app. Should never be needed for normal apps.</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0df6c74..e12549a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -267,6 +267,7 @@
             android:name=".DessertCaseDream"
             android:exported="true"
             android:label="@string/dessert_case"
+            android:permission="android.permission.BIND_DREAM_SERVICE"
             android:enabled="false"
             android:process=":sweetsweetdesserts"
             >
@@ -305,6 +306,7 @@
         <!-- I dream of notifications -->
         <service
             android:name=".doze.DozeService"
-            android:exported="true" />
+            android:exported="true"
+            android:permission="android.permission.BIND_DREAM_SERVICE" />
     </application>
 </manifest>
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index ed4ccfc..a35e2ba 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.dreams;
 
+import static android.Manifest.permission.BIND_DREAM_SERVICE;
+
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
@@ -29,6 +31,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -193,7 +196,7 @@
 
     private void startDreamInternal(boolean doze) {
         final int userId = ActivityManager.getCurrentUser();
-        final ComponentName dream = doze ? getDozeComponent() : chooseDreamForUser(userId);
+        final ComponentName dream = chooseDreamForUser(doze, userId);
         if (dream != null) {
             synchronized (mLock) {
                 startDreamLocked(dream, false /*isTest*/, doze, userId);
@@ -245,11 +248,31 @@
         }
     }
 
-    private ComponentName chooseDreamForUser(int userId) {
+    private ComponentName chooseDreamForUser(boolean doze, int userId) {
+        if (doze) {
+            ComponentName dozeComponent = getDozeComponent();
+            return validateDream(dozeComponent) ? dozeComponent : null;
+        }
         ComponentName[] dreams = getDreamComponentsForUser(userId);
         return dreams != null && dreams.length != 0 ? dreams[0] : null;
     }
 
+    private boolean validateDream(ComponentName component) {
+        if (component == null) return false;
+        final ServiceInfo serviceInfo = getServiceInfo(component);
+        if (serviceInfo == null) {
+            Slog.w(TAG, "Dream " + component + " does not exist");
+            return false;
+        } else if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.L
+                && !BIND_DREAM_SERVICE.equals(serviceInfo.permission)) {
+            Slog.w(TAG, "Dream " + component
+                    + " is not available because its manifest is missing the " + BIND_DREAM_SERVICE
+                    + " permission on the dream service declaration.");
+            return false;
+        }
+        return true;
+    }
+
     private ComponentName[] getDreamComponentsForUser(int userId) {
         String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                 Settings.Secure.SCREENSAVER_COMPONENTS,
@@ -260,10 +283,8 @@
         List<ComponentName> validComponents = new ArrayList<ComponentName>();
         if (components != null) {
             for (ComponentName component : components) {
-                if (serviceExists(component)) {
+                if (validateDream(component)) {
                     validComponents.add(component);
-                } else {
-                    Slog.w(TAG, "Dream " + component + " does not exist");
                 }
             }
         }
@@ -307,11 +328,11 @@
         return TextUtils.isEmpty(name) ? null : ComponentName.unflattenFromString(name);
     }
 
-    private boolean serviceExists(ComponentName name) {
+    private ServiceInfo getServiceInfo(ComponentName name) {
         try {
-            return name != null && mContext.getPackageManager().getServiceInfo(name, 0) != null;
+            return name != null ? mContext.getPackageManager().getServiceInfo(name, 0) : null;
         } catch (NameNotFoundException e) {
-            return false;
+            return null;
         }
     }
 
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 816033e..5fefab6 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -819,6 +819,7 @@
             bool hasCameraSecureActivity = false;
             bool hasLauncher = false;
             bool hasNotificationListenerService = false;
+            bool hasDreamService = false;
 
             bool actMainActivity = false;
             bool actWidgetReceivers = false;
@@ -831,6 +832,7 @@
             bool actOffHostApduService = false;
             bool actDocumentsProvider = false;
             bool actNotificationListenerService = false;
+            bool actDreamService = false;
             bool actCamera = false;
             bool actCameraSecure = false;
             bool catLauncher = false;
@@ -846,6 +848,7 @@
             bool hasBindNfcServicePermission = false;
             bool hasRequiredSafAttributes = false;
             bool hasBindNotificationListenerServicePermission = false;
+            bool hasBindDreamServicePermission = false;
 
             // These two implement the implicit permissions that are granted
             // to pre-1.6 applications.
@@ -1007,6 +1010,7 @@
                                 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
                                 hasNotificationListenerService |= actNotificationListenerService &&
                                         hasBindNotificationListenerServicePermission;
+                                hasDreamService |= actDreamService && hasBindDreamServicePermission;
                                 hasOtherServices |= (!actImeService && !actWallpaperService &&
                                         !actAccessibilityService && !actPrintService &&
                                         !actHostApduService && !actOffHostApduService &&
@@ -1389,6 +1393,7 @@
                     hasBindNfcServicePermission = false;
                     hasRequiredSafAttributes = false;
                     hasBindNotificationListenerServicePermission = false;
+                    hasBindDreamServicePermission = false;
                     if (withinApplication) {
                         if(tag == "activity") {
                             withinActivity = true;
@@ -1486,6 +1491,8 @@
                                     hasBindNfcServicePermission = true;
                                 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
                                     hasBindNotificationListenerServicePermission = true;
+                                } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
+                                    hasBindDreamServicePermission = true;
                                 }
                             } else {
                                 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
@@ -1569,6 +1576,7 @@
                         actOffHostApduService = false;
                         actDocumentsProvider = false;
                         actNotificationListenerService = false;
+                        actDreamService = false;
                         actCamera = false;
                         actCameraSecure = false;
                         catLauncher = false;
@@ -1654,6 +1662,8 @@
                                 actOffHostApduService = true;
                             } else if (action == "android.service.notification.NotificationListenerService") {
                                 actNotificationListenerService = true;
+                            } else if (action == "android.service.dreams.DreamService") {
+                                actDreamService = true;
                             }
                         } else if (withinProvider) {
                             if (action == "android.content.action.DOCUMENTS_PROVIDER") {
@@ -1889,6 +1899,9 @@
             if (hasNotificationListenerService) {
                 printComponentPresence("notification-listener");
             }
+            if (hasDreamService) {
+                printComponentPresence("dream");
+            }
             if (hasCameraActivity) {
                 printComponentPresence("camera");
             }