Check for uses-feature in Companion APIs

Fixes: 37629514
Test: Invoke API from app without uses-feature
Ensure an exception with an appropriate message is thrown
Invoke API from app with uses-feature
Ensure no exception

Change-Id: I53665732264ea2de2b4c8c251b1f00bf3a256dad
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 2c8e4e0..73886a7 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -169,6 +169,13 @@
     }
 
     /**
+     * Length of the given array or 0 if it's null.
+     */
+    public static int size(@Nullable Object[] array) {
+        return array == null ? 0 : array.length;
+    }
+
+    /**
      * Checks that value is present as at least one of the elements of the array.
      * @param array the array to check in
      * @param value the value to check for
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 41b70a1..9bb0f86 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -36,6 +36,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.net.NetworkPolicyManager;
@@ -203,13 +204,15 @@
             checkNotNull(request, "Request cannot be null");
             checkNotNull(callback, "Callback cannot be null");
             checkCallerIsSystemOr(callingPackage);
+            int userId = getCallingUserId();
+            checkUsesFeature(callingPackage, userId);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                //TODO bindServiceAsUser
-                getContext().bindService(
+                getContext().bindServiceAsUser(
                         new Intent().setComponent(SERVICE_TO_BIND_TO),
                         createServiceConnection(request, callback, callingPackage),
-                        Context.BIND_AUTO_CREATE);
+                        Context.BIND_AUTO_CREATE,
+                        UserHandle.of(userId));
             } finally {
                 Binder.restoreCallingIdentity(callingIdentity);
             }
@@ -219,6 +222,7 @@
         public List<String> getAssociations(String callingPackage, int userId)
                 throws RemoteException {
             checkCallerIsSystemOr(callingPackage, userId);
+            checkUsesFeature(callingPackage, getCallingUserId());
             return CollectionUtils.map(
                     readAllAssociations(userId, callingPackage),
                     a -> a.deviceAddress);
@@ -230,6 +234,7 @@
                 throws RemoteException {
             checkNotNull(deviceMacAddress);
             checkCallerIsSystemOr(callingPackage);
+            checkUsesFeature(callingPackage, getCallingUserId());
             updateAssociations(associations -> CollectionUtils.remove(associations,
                     new Association(getCallingUserId(), deviceMacAddress, callingPackage)));
         }
@@ -282,12 +287,25 @@
 
         private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
             checkCallerIsSystemOr(callingPackage);
-            checkState(!ArrayUtils.isEmpty(readAllAssociations(getCallingUserId(), callingPackage)),
+            int userId = getCallingUserId();
+            checkState(!ArrayUtils.isEmpty(readAllAssociations(userId, callingPackage)),
                     "App must have an association before calling this API");
+            checkUsesFeature(callingPackage, userId);
+        }
+
+        private void checkUsesFeature(String pkg, int userId) {
+            FeatureInfo[] reqFeatures = getPackageInfo(pkg, userId).reqFeatures;
+            String requiredFeature = PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
+            int numFeatures = ArrayUtils.size(reqFeatures);
+            for (int i = 0; i < numFeatures; i++) {
+                if (requiredFeature.equals(reqFeatures[i].name)) return;
+            }
+            throw new IllegalStateException("Must declare uses-feature "
+                    + requiredFeature
+                    + " in manifest to use this API");
         }
     }
 
-
     private int getCallingUserId() {
         return UserHandle.getUserId(Binder.getCallingUid());
     }
@@ -398,7 +416,9 @@
         return Binder.withCleanCallingIdentity(() -> {
             try {
                 return getContext().getPackageManager().getPackageInfoAsUser(
-                        packageName, PackageManager.GET_PERMISSIONS, userId);
+                        packageName,
+                        PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS,
+                        userId);
             } catch (PackageManager.NameNotFoundException e) {
                 Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + packageName, e);
                 return null;