Add system feature for Companion Device Manager

Bug: 30932767
Test: Add just the feature gating first, ensure Context#getSystemSetrvice now
returns null.
Add feature in the configuration files, ensure companion device feature works again.
Change-Id: I4ad2d52f7877eae0e951f43ddbb23881d5de8d8b
diff --git a/api/current.txt b/api/current.txt
index d837786..e945ebf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10589,6 +10589,7 @@
     field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
     field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
     field public static final java.lang.String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
+    field public static final java.lang.String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
diff --git a/api/system-current.txt b/api/system-current.txt
index b95f27a8..ee9805c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -11248,6 +11248,7 @@
     field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
     field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
     field public static final java.lang.String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
+    field public static final java.lang.String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
diff --git a/api/test-current.txt b/api/test-current.txt
index 51ff912..b6059bf 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10625,6 +10625,7 @@
     field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
     field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
     field public static final java.lang.String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
+    field public static final java.lang.String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 6fa32b4..fd84aa6 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -22,10 +22,13 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.IntentSender;
+import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.util.Log;
 
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -40,6 +43,9 @@
  */
 public final class CompanionDeviceManager {
 
+    private static final boolean DEBUG = false; //TODO
+    private static final String LOG_TAG = "CompanionDeviceManager";
+
     /**
      * A device, returned in the activity result of the {@link IntentSender} received in
      * {@link Callback#onDeviceFound}
@@ -81,7 +87,7 @@
 
     /** @hide */
     public CompanionDeviceManager(
-            @NonNull ICompanionDeviceManager service, @NonNull Context context) {
+            @Nullable ICompanionDeviceManager service, @NonNull Context context) {
         mService = service;
         mContext = context;
     }
@@ -120,6 +126,10 @@
             @NonNull AssociationRequest<?> request,
             @NonNull Callback callback,
             @Nullable Handler handler) {
+        if (!checkFeaturePresent()) {
+            return;
+        }
+
         final Handler finalHandler = handler != null
                 ? handler
                 : new Handler(Looper.getMainLooper());
@@ -153,6 +163,9 @@
      */
     @NonNull
     public List<String> getAssociations() {
+        if (!checkFeaturePresent()) {
+            return Collections.emptyList();
+        }
         try {
             return mService.getAssociations(mContext.getPackageName());
         } catch (RemoteException e) {
@@ -172,6 +185,9 @@
      * @param deviceMacAddress the MAC address of device to disassociate from this app
      */
     public void disassociate(@NonNull String deviceMacAddress) {
+        if (!checkFeaturePresent()) {
+            return;
+        }
         try {
             mService.disassociate(deviceMacAddress, mContext.getPackageName());
         } catch (RemoteException e) {
@@ -181,14 +197,28 @@
 
     /** @hide */
     public void requestNotificationAccess() {
+        if (!checkFeaturePresent()) {
+            return;
+        }
         //TODO implement
         throw new UnsupportedOperationException("Not yet implemented");
     }
 
     /** @hide */
     public boolean haveNotificationAccess() {
+        if (!checkFeaturePresent()) {
+            return false;
+        }
         //TODO implement
         throw new UnsupportedOperationException("Not yet implemented");
     }
 
+    private boolean checkFeaturePresent() {
+        boolean featurePresent = mService == null;
+        if (!featurePresent && DEBUG) {
+            Log.d(LOG_TAG, "Feature " + PackageManager.FEATURE_COMPANION_DEVICE_SETUP
+                    + " not available");
+        }
+        return featurePresent;
+    }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3a875bc..08d723d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2220,6 +2220,15 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports {@link android.companion.CompanionDeviceManager#associate associating}
+     * with devices via {@link android.companion.CompanionDeviceManager}.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_COMPANION_DEVICE_SETUP
+            = "android.software.companion_device_setup";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device can perform backup and restore operations on installed applications.
      */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1a0aff7..8b5e24f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1376,9 +1376,11 @@
                 traceEnd();
             }
 
-            traceBeginAndSlog("StartCompanionDeviceManager");
-            mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS);
-            traceEnd();
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
+                traceBeginAndSlog("StartCompanionDeviceManager");
+                mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS);
+                traceEnd();
+            }
 
             traceBeginAndSlog("StartRestrictionManager");
             mSystemServiceManager.startService(RestrictionsManagerService.class);