Fix crash when no Bluetooth feature in Settings

- Move PreferenceGroup init method out of isAvailable() condition,
  then PreferenceGroup will not be null.
- Update getAvailabilityStatus(), since the controller now may have usb
  and dock.

Bug: 110712414
Test: make -j42 RunSettingsRoboTests
Change-Id: I4d85a42c26fb20d319e7321177b271933be3fdb0
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index 2128b2f..a790ba9 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -109,6 +109,10 @@
      * Force to update the list of bluetooth devices
      */
     public void forceUpdate() {
+        if (mLocalManager == null) {
+            Log.e(TAG, "forceUpdate() Bluetooth is not supported on this device");
+            return;
+        }
         if (BluetoothAdapter.getDefaultAdapter().isEnabled()) {
             final Collection<CachedBluetoothDevice> cachedDevices =
                     mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
@@ -119,6 +123,10 @@
     }
 
     public void removeAllDevicesFromPreference() {
+        if (mLocalManager == null) {
+            Log.e(TAG, "removeAllDevicesFromPreference() BT is not supported on this device");
+            return;
+        }
         final Collection<CachedBluetoothDevice> cachedDevices =
                 mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
         for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
diff --git a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
index be82284..cac46b0 100644
--- a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
@@ -83,9 +83,11 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
+
+        mPreferenceGroup = screen.findPreference(KEY);
+        mPreferenceGroup.setVisible(false);
+
         if (isAvailable()) {
-            mPreferenceGroup = screen.findPreference(KEY);
-            mPreferenceGroup.setVisible(false);
             updateTitle();
             mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
             mBluetoothDeviceUpdater.forceUpdate();
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
index aa83e67..957737a 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
@@ -73,10 +73,11 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
-        if (isAvailable()) {
-            mPreferenceGroup = screen.findPreference(KEY);
-            mPreferenceGroup.setVisible(false);
 
+        mPreferenceGroup = screen.findPreference(KEY);
+        mPreferenceGroup.setVisible(false);
+
+        if (isAvailable()) {
             final Context context = screen.getContext();
             mBluetoothDeviceUpdater.setPrefContext(context);
             mBluetoothDeviceUpdater.forceUpdate();
@@ -88,7 +89,11 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+        final PackageManager packageManager = mContext.getPackageManager();
+        return (packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                || packageManager.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY)
+                || packageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
+                || mConnectedDockUpdater != null)
                 ? AVAILABLE_UNSEARCHABLE
                 : UNSUPPORTED_ON_DEVICE;
     }
diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
index 45f128d..5b23d69 100644
--- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
@@ -52,7 +52,8 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+        return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                || mSavedDockUpdater != null)
                 ? AVAILABLE
                 : CONDITIONALLY_UNAVAILABLE;
     }
diff --git a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
index e1dc750..062fa2d 100644
--- a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
@@ -73,10 +73,10 @@
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
-        if (isAvailable()) {
-            mPreferenceGroup = screen.findPreference(KEY);
-            mPreferenceGroup.setVisible(false);
+        mPreferenceGroup = screen.findPreference(KEY);
+        mPreferenceGroup.setVisible(false);
 
+        if (isAvailable()) {
             final Context context = screen.getContext();
             mBluetoothDeviceUpdater.setPrefContext(context);
             mBluetoothDeviceUpdater.forceUpdate();
@@ -87,7 +87,8 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+        return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                || mSavedDockUpdater != null)
                 ? AVAILABLE
                 : UNSUPPORTED_ON_DEVICE;
     }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
index 23754a0..946d639 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
@@ -88,6 +88,7 @@
         mPreferenceGroup = spy(new PreferenceScreen(mContext, null));
         when(mPreferenceGroup.getPreferenceManager()).thenReturn(mPreferenceManager);
         doReturn(mContext).when(mDashboardFragment).getContext();
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
 
         mConnectedDeviceGroupController = new ConnectedDeviceGroupController(mContext);
         mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
@@ -153,8 +154,12 @@
     }
 
     @Test
-    public void getAvailabilityStatus_noBluetoothFeature_returnUnSupported() {
+    public void getAvailabilityStatus_noBluetoothUsbDockFeature_returnUnSupported() {
         mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
+        mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
+                mConnectedUsbDeviceUpdater, null);
 
         assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
                 UNSUPPORTED_ON_DEVICE);
@@ -163,8 +168,37 @@
     @Test
     public void getAvailabilityStatus_BluetoothFeature_returnSupported() {
         mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
+        mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
+                mConnectedUsbDeviceUpdater, null);
 
         assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
                 AVAILABLE_UNSEARCHABLE);
     }
+
+    @Test
+    public void getAvailabilityStatus_haveUsbFeature_returnSupported() {
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, true);
+        mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
+                mConnectedUsbDeviceUpdater, null);
+
+        assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
+                AVAILABLE_UNSEARCHABLE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_haveDockFeature_returnSupported() {
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
+        mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
+        mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
+                mConnectedUsbDeviceUpdater, mConnectedDockUpdater);
+
+        assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
+                AVAILABLE_UNSEARCHABLE);
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
index 48f734d..90cbea9 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
@@ -95,19 +95,32 @@
     }
 
     @Test
-    public void getAvailabilityStatus_noBluetoothFeature_returnUnSupported() {
+    public void getAvailabilityStatus_noBluetoothDockFeature_returnUnSupported() {
         doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+        mPreConnectedDeviceController.setSavedDockUpdater(null);
+
         assertThat(mPreConnectedDeviceController.getAvailabilityStatus()).isEqualTo(
                 CONDITIONALLY_UNAVAILABLE);
     }
+
     @Test
     public void getAvailabilityStatus_hasBluetoothFeature_returnSupported() {
         doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+        mPreConnectedDeviceController.setSavedDockUpdater(null);
+
         assertThat(mPreConnectedDeviceController.getAvailabilityStatus()).isEqualTo(
                 AVAILABLE);
     }
 
     @Test
+    public void getAvailabilityStatus_haveDockFeature_returnSupported() {
+        doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+        assertThat(mPreConnectedDeviceController.getAvailabilityStatus()).isEqualTo(
+            AVAILABLE);
+    }
+
+    @Test
     public void onDeviceAdded_addDevicePreference_displayIt() {
         mPreConnectedDeviceController.onDeviceAdded(new Preference(mContext));
 
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java
index c386719..6c6cf47 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -81,6 +82,7 @@
         verify(mBluetoothDeviceUpdater).registerCallback();
         verify(mSavedDockUpdater).registerCallback();
     }
+
     @Test
     public void testUnregister() {
         // unregister the callback in onStop()
@@ -88,16 +90,30 @@
         verify(mBluetoothDeviceUpdater).unregisterCallback();
         verify(mSavedDockUpdater).unregisterCallback();
     }
+
     @Test
-    public void testGetAvailabilityStatus_noBluetoothFeature_returnUnSupported() {
+    public void testGetAvailabilityStatus_noBluetoothDockFeature_returnUnSupported() {
         doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+        mSavedDeviceGroupController.setSavedDockUpdater(null);
+
         assertThat(mSavedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
                 UNSUPPORTED_ON_DEVICE);
     }
+
     @Test
     public void testGetAvailabilityStatus_BluetoothFeature_returnSupported() {
         doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+        mSavedDeviceGroupController.setSavedDockUpdater(null);
+
         assertThat(mSavedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
                 AVAILABLE);
     }
+
+    @Test
+    public void getAvailabilityStatus_haveDockFeature_returnSupported() {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(false);
+
+        assertThat(mSavedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
+            AVAILABLE);
+    }
 }