Cache `AndroidDevice#model` to reduce fastboot calls (#910)

fastboot calls are expensive and can increase USB flakiness when issued in large amount.
So we should reduce fastboot calls whenever possible
diff --git a/mobly/controllers/android_device.py b/mobly/controllers/android_device.py
index 981af27..20c5762 100644
--- a/mobly/controllers/android_device.py
+++ b/mobly/controllers/android_device.py
@@ -14,6 +14,7 @@
 
 import contextlib
 import enum
+import functools
 import logging
 import os
 import re
@@ -845,7 +846,7 @@
   def is_rootable(self):
     return not self.is_bootloader and self.build_info['debuggable'] == '1'
 
-  @property
+  @functools.cached_property
   def model(self):
     """The Android code name for the device."""
     # If device is in bootloader mode, get mode name from fastboot.
diff --git a/tests/mobly/controllers/android_device_test.py b/tests/mobly/controllers/android_device_test.py
index fc939db..81c2965 100755
--- a/tests/mobly/controllers/android_device_test.py
+++ b/tests/mobly/controllers/android_device_test.py
@@ -495,6 +495,30 @@
 
   @mock.patch(
       'mobly.controllers.android_device_lib.adb.AdbProxy',
+      return_value=mock_android_device.MockAdbProxy('1'),
+  )
+  @mock.patch(
+      'mobly.controllers.android_device.list_fastboot_devices',
+      return_value=mock.MagicMock(),
+  )
+  def test_AndroidDevice_model_property_is_cached(
+      self, mock_list_fastboot_devices, _
+  ):
+    """Accessing `model` a second time shouldn't invoke all the fastboot/adb
+    calls again.
+    """
+    mock_serial = 1
+    # mock returns '2' so the device is not considered in bootloader mode.
+    mock_list_fastboot_devices.return_value = ['2']
+    ad = android_device.AndroidDevice(serial=mock_serial)
+    ad.model
+    mock_count_first = mock_list_fastboot_devices.call_count
+    ad.model  # access `model` again.
+    mock_count_second = mock_list_fastboot_devices.call_count
+    self.assertEqual(mock_count_first, mock_count_second)
+
+  @mock.patch(
+      'mobly.controllers.android_device_lib.adb.AdbProxy',
       return_value=mock_android_device.MockAdbProxy(
           '1',
           mock_properties={