Hide mobile broadband devices supported by mist.

BUG=chromium:251180
TEST=Tested the following:
1. Build and run unit tests.
2. Run platform_CrosDisksDBus and platform_CrosDisksFilesystem tests.
3. Manually verify that CrOS File Manager:
   - Mount a regular USB drive
   - Do not mount a USB drive exposed by a 3G dongle

Change-Id: Ia14b11034001cb90cb7d533c0982304b58611e7d
Reviewed-on: https://gerrit.chromium.org/gerrit/58992
Reviewed-by: Darin Petkov <petkov@chromium.org>
Commit-Queue: Ben Chan <benchan@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
diff --git a/udev-device.cc b/udev-device.cc
index e1302d2..e3d705f 100644
--- a/udev-device.cc
+++ b/udev-device.cc
@@ -44,10 +44,12 @@
 const char kPropertyDeviceType[] = "DEVTYPE";
 const char kPropertyDeviceTypeUSBDevice[] = "usb_device";
 const char kPropertyFilesystemUsage[] = "ID_FS_USAGE";
+const char kPropertyMistSupportedDevice[] = "MIST_SUPPORTED_DEVICE";
 const char kPropertyModel[] = "ID_MODEL";
 const char kPropertyPartitionSize[] = "UDISKS_PARTITION_SIZE";
 const char kPropertyPresentationHide[] = "UDISKS_PRESENTATION_HIDE";
 const char kPropertyRotationRate[] = "ID_ATA_ROTATION_RATE_RPM";
+const char kSubsystemUsb[] = "usb";
 const char kVirtualDevicePathPrefix[] = "/sys/devices/virtual/";
 const char kUSBDeviceInfoFile[] = "/opt/google/cros-disks/usb-device-info";
 const char kUSBIdentifierDatabase[] = "/usr/share/misc/usb.ids";
@@ -74,11 +76,13 @@
   udev_device_unref(dev_);
 }
 
+// static
 string UdevDevice::EnsureUTF8String(const string& str) {
   return IsStringUTF8(str) ? str : "";
 }
 
-bool UdevDevice::IsValueBooleanTrue(const char *value) const {
+// static
+bool UdevDevice::IsValueBooleanTrue(const char *value) {
   return value && strcmp(value, "1") == 0;
 }
 
@@ -250,6 +254,20 @@
   return is_media_available;
 }
 
+bool UdevDevice::IsMobileBroadbandDevice() const {
+  // Check if a parent device, which belongs to the "usb" subsystem and has a
+  // device type "usb_device", has a property "MIST_SUPPORTED_DEVICE=1". If so,
+  // it is a mobile broadband device supported by mist.
+  struct udev_device* parent = udev_device_get_parent_with_subsystem_devtype(
+      dev_, kSubsystemUsb, kPropertyDeviceTypeUSBDevice);
+  if (!parent)
+    return false;
+
+  const char* value =
+      udev_device_get_property_value(parent, kPropertyMistSupportedDevice);
+  return IsValueBooleanTrue(value);
+}
+
 bool UdevDevice::IsAutoMountable() const {
   // TODO(benchan): Find a reliable way to detect if a device is a removable
   // storage as the removable attribute in sysfs does not always tell the truth.
@@ -268,6 +286,11 @@
     return true;
   }
 
+  // Hide a mobile broadband device, which may initially expose itself as a USB
+  // mass storage device and later be switched to a modem by mist.
+  if (IsMobileBroadbandDevice())
+    return true;
+
   // Hide a device that is neither marked as a partition nor a filesystem,
   // unless it has no valid partitions (e.g. the device is unformatted or
   // corrupted). An unformatted or corrupted device is visible in the file
diff --git a/udev-device.h b/udev-device.h
index e9f3839..64122f2 100644
--- a/udev-device.h
+++ b/udev-device.h
@@ -72,6 +72,9 @@
   // Checks if any media is available in the device.
   bool IsMediaAvailable() const;
 
+  // Checks if the device is a mobile broadband device.
+  bool IsMobileBroadbandDevice() const;
+
   // Checks if the device is on the boot device.
   bool IsOnBootDevice() const;
 
@@ -103,7 +106,7 @@
   bool IsOnMMCDevice() const;
 
   // Checks if a string contains a "1" (as Boolean true).
-  bool IsValueBooleanTrue(const char *value) const;
+  static bool IsValueBooleanTrue(const char *value);
 
   mutable struct udev_device *dev_;
 
diff --git a/udev-device_unittest.cc b/udev-device_unittest.cc
index 1c57df8..3ee69ea 100644
--- a/udev-device_unittest.cc
+++ b/udev-device_unittest.cc
@@ -115,14 +115,11 @@
 }
 
 TEST_F(UdevDeviceTest, IsValueBooleanTrue) {
-  if (mounted_device_) {
-    UdevDevice device(mounted_device_);
-    EXPECT_FALSE(device.IsValueBooleanTrue(NULL));
-    EXPECT_FALSE(device.IsValueBooleanTrue(""));
-    EXPECT_FALSE(device.IsValueBooleanTrue("0"));
-    EXPECT_FALSE(device.IsValueBooleanTrue("test"));
-    EXPECT_TRUE(device.IsValueBooleanTrue("1"));
-  }
+  EXPECT_FALSE(UdevDevice::IsValueBooleanTrue(NULL));
+  EXPECT_FALSE(UdevDevice::IsValueBooleanTrue(""));
+  EXPECT_FALSE(UdevDevice::IsValueBooleanTrue("0"));
+  EXPECT_FALSE(UdevDevice::IsValueBooleanTrue("test"));
+  EXPECT_TRUE(UdevDevice::IsValueBooleanTrue("1"));
 }
 
 TEST_F(UdevDeviceTest, IsAttributeTrueForNonexistentAttribute) {
@@ -247,6 +244,17 @@
   }
 }
 
+TEST_F(UdevDeviceTest, IsMobileBroadbandDevice) {
+  if (boot_device_) {
+    UdevDevice device(boot_device_);
+    EXPECT_FALSE(device.IsMobileBroadbandDevice());
+  }
+  if (loop_device_) {
+    UdevDevice device(loop_device_);
+    EXPECT_FALSE(device.IsMobileBroadbandDevice());
+  }
+}
+
 TEST_F(UdevDeviceTest, IsVirtual) {
   if (loop_device_) {
     UdevDevice device(loop_device_);