diff --git a/devlib/utils/android.py b/devlib/utils/android.py
index f107667..f02f2a7 100755
--- a/devlib/utils/android.py
+++ b/devlib/utils/android.py
@@ -89,14 +89,22 @@ INTENT_FLAGS = {
 }
 
 
-# Initialized in functions near the botton of the file
-android_home = None
-platform_tools = None
+# These were not set or not set to anything meaningful, so just keep them for
+# backward compat but they are not lazily detected.
 adb = None
-aapt = None
-aapt_version = None
 fastboot = None
 
+# Lazy init of some globals
+def __getattr__(attr):
+    env = _AndroidEnvironment()
+
+    glob = globals()
+    glob.update(env.paths)
+    try:
+        return glob[attr]
+    except KeyError:
+        raise AttributeError(f"Module '{__name__}' has no attribute '{attr}'")
+
 
 class AndroidProperties(object):
 
@@ -162,7 +170,6 @@ class ApkInfo(object):
 
     # pylint: disable=too-many-branches
     def parse(self, apk_path):
-        _check_env()
         output = self._run([aapt, 'dump', 'badging', apk_path])
         for line in output.split('\n'):
             if line.startswith('application-label:'):
@@ -237,8 +244,8 @@ class ApkInfo(object):
                     package.append(i)
 
             for elem in package:
-                self._methods.extend([(meth.attrib['name'], klass.attrib['name']) 
-                                      for klass in elem.iter('class') 
+                self._methods.extend([(meth.attrib['name'], klass.attrib['name'])
+                                      for klass in elem.iter('class')
                                       for meth in klass.iter('method')])
         return self._methods
 
@@ -482,7 +489,6 @@ class AdbConnection(ConnectionBase):
 
 
 def fastboot_command(command, timeout=None, device=None):
-    _check_env()
     target = '-s {}'.format(quote(device)) if device else ''
     full_command = 'fastboot {} {}'.format(target, command)
     logger.debug(full_command)
@@ -532,7 +538,6 @@ def adb_get_device(timeout=None, adb_server=None, adb_port=None):
 
 
 def adb_connect(device, timeout=None, attempts=MAX_ATTEMPTS, adb_server=None, adb_port=None):
-    _check_env()
     tries = 0
     output = None
     while tries <= attempts:
@@ -560,7 +565,6 @@ def adb_connect(device, timeout=None, attempts=MAX_ATTEMPTS, adb_server=None, ad
 
 
 def adb_disconnect(device, adb_server=None, adb_port=None):
-    _check_env()
     if not device:
         return
     if ":" in device and device in adb_list_devices(adb_server, adb_port):
@@ -573,7 +577,6 @@ def adb_disconnect(device, adb_server=None, adb_port=None):
 
 
 def _ping(device, adb_server=None, adb_port=None):
-    _check_env()
     adb_cmd = get_adb_command(device, 'shell', adb_server, adb_port)
     command = "{} {}".format(adb_cmd, quote('ls /data/local/tmp > /dev/null'))
     logger.debug(command)
@@ -587,7 +590,6 @@ def _ping(device, adb_server=None, adb_port=None):
 # pylint: disable=too-many-locals
 def adb_shell(device, command, timeout=None, check_exit_code=False,
               as_root=False, adb_server=None, adb_port=None, su_cmd='su -c {}'):  # NOQA
-    _check_env()
 
     # On older combinations of ADB/Android versions, the adb host command always
     # exits with 0 if it was able to run the command on the target, even if the
@@ -655,9 +657,8 @@ def adb_background_shell(conn, command,
     adb_server = conn.adb_server
     adb_port = conn.adb_port
     busybox = conn.busybox
-
-    _check_env()
     orig_command = command
+
     stdout, stderr, command = redirect_streams(stdout, stderr, command)
     if as_root:
         command = f'{busybox} printf "%s" {quote(command)} | su'
@@ -742,7 +743,6 @@ def _get_adb_parts(command, device=None, adb_server=None, adb_port=None, quote_a
 
 
 def get_adb_command(device, command, adb_server=None, adb_port=None):
-    _check_env()
     parts, env = _get_adb_parts((command,), device, adb_server, adb_port, quote_adb=True)
     env = [quote(f'{name}={val}') for name, val in sorted(env.items())]
     parts = [*env, *parts]
@@ -786,120 +786,133 @@ def grant_app_permissions(target, package):
 
 # Messy environment initialisation stuff...
 
-class _AndroidEnvironment(object):
-
+class _AndroidEnvironment:
     def __init__(self):
-        self.android_home = None
-        self.platform_tools = None
-        self.build_tools = None
-        self.adb = None
-        self.aapt = None
-        self.aapt_version = None
-        self.fastboot = None
-
-
-def _initialize_with_android_home(env):
-    logger.debug('Using ANDROID_HOME from the environment.')
-    env.android_home = android_home
-    env.platform_tools = os.path.join(android_home, 'platform-tools')
-    os.environ['PATH'] = env.platform_tools + os.pathsep + os.environ['PATH']
-    _init_common(env)
-    return env
-
-
-def _initialize_without_android_home(env):
-    adb_full_path = which('adb')
-    if adb_full_path:
-        env.adb = 'adb'
-    else:
-        raise HostError('ANDROID_HOME is not set and adb is not in PATH. '
-                        'Have you installed Android SDK?')
-    logger.debug('Discovering ANDROID_HOME from adb path.')
-    env.platform_tools = os.path.dirname(adb_full_path)
-    env.android_home = os.path.dirname(env.platform_tools)
-    _init_common(env)
-    return env
-
-def _init_common(env):
-    _discover_build_tools(env)
-    _discover_aapt(env)
-
-def _discover_build_tools(env):
-    logger.debug('ANDROID_HOME: {}'.format(env.android_home))
-    build_tools_directory = os.path.join(env.android_home, 'build-tools')
-    if os.path.isdir(build_tools_directory):
-        env.build_tools = build_tools_directory
-
-def _check_supported_aapt2(binary):
-    # At time of writing the version argument of aapt2 is not helpful as
-    # the output is only a placeholder that does not distinguish between versions
-    # with and without support for badging. Unfortunately aapt has been
-    # deprecated and fails to parse some valid apks so we will try to favour
-    # aapt2 if possible else will fall back to aapt.
-    # Try to execute the badging command and check if we get an expected error
-    # message as opposed to an unknown command error to determine if we have a
-    # suitable version.
-    cmd = '{} dump badging'.format(binary)
-    result = subprocess.run(cmd.encode('utf-8'), shell=True, stderr=subprocess.PIPE)
-    supported = bool(AAPT_BADGING_OUTPUT.search(result.stderr.decode('utf-8')))
-    msg = 'Found a {} aapt2 binary at: {}'
-    logger.debug(msg.format('supported' if supported else 'unsupported', binary))
-    return supported
-
-def _discover_aapt(env):
-    if env.build_tools:
-        aapt_path = ''
-        aapt2_path = ''
-        versions = os.listdir(env.build_tools)
-        for version in reversed(sorted(versions)):
-            if not os.path.isfile(aapt2_path):
-                aapt2_path = os.path.join(env.build_tools, version, 'aapt2')
-            if not os.path.isfile(aapt_path):
-                aapt_path = os.path.join(env.build_tools, version, 'aapt')
-                aapt_version = 1
-            # Use latest available version for aapt/appt2 but ensure at least one is valid.
-            if os.path.isfile(aapt2_path) or os.path.isfile(aapt_path):
-                break
-
-        # Use aapt2 only if present and we have a suitable version
-        if aapt2_path and _check_supported_aapt2(aapt2_path):
-            aapt_path = aapt2_path
-            aapt_version = 2
-
-        # Use the aapt version discoverted from build tools.
-        if aapt_path:
-            logger.debug('Using {} for version {}'.format(aapt_path, version))
-            env.aapt = aapt_path
-            env.aapt_version = aapt_version
-            return
-
-    # Try detecting aapt2 and aapt from PATH
-    if not env.aapt:
-            aapt2_path = which('aapt2')
-            if _check_supported_aapt2(aapt2_path):
-                env.aapt = aapt2_path
-                env.aapt_version = 2
-            else:
-                env.aapt = which('aapt')
-                env.aapt_version = 1
-
-    if not env.aapt:
-        raise HostError('aapt/aapt2 not found. Please make sure it is avaliable in PATH'
-                        ' or at least one Android platform is installed')
-
-def _check_env():
-    global android_home, platform_tools, adb, aapt, aapt_version  # pylint: disable=W0603
-    if not android_home:
         android_home = os.getenv('ANDROID_HOME')
         if android_home:
-            _env = _initialize_with_android_home(_AndroidEnvironment())
+            paths = self._from_android_home(android_home)
         else:
-            _env = _initialize_without_android_home(_AndroidEnvironment())
-        android_home = _env.android_home
-        platform_tools = _env.platform_tools
-        adb = _env.adb
-        aapt = _env.aapt
-        aapt_version = _env.aapt_version
+            paths = self._from_adb()
+
+        self.paths = paths
+
+    @classmethod
+    def _from_android_home(cls, android_home):
+        if android_home:
+            logger.debug('Using ANDROID_HOME from the environment.')
+            platform_tools = os.path.join(android_home, 'platform-tools')
+
+            # TODO: that is very fishy
+            os.environ['PATH'] = platform_tools + os.pathsep + os.environ['PATH']
+
+            return {
+                'android_home': android_home,
+                'platform_tools': platform_tools,
+                **cls._init_common(
+                    android_home=android_home,
+                )
+            }
+            return paths
+
+    @classmethod
+    def _from_adb(cls):
+        adb_path = which('adb')
+        if adb_path:
+            logger.debug('Discovering ANDROID_HOME from adb path.')
+            platform_tools = os.path.dirname(adb_path)
+            android_home = os.path.dirname(platform_tools)
+
+            return {
+                'android_home': android_home,
+                'platform_tools': platform_tools,
+                **cls._init_common(android_home)
+            }
+        else:
+            raise HostError('ANDROID_HOME is not set and adb is not in PATH. '
+                            'Have you installed Android SDK?')
+
+    @classmethod
+    def _init_common(cls, android_home):
+        logger.debug(f'ANDROID_HOME: {android_home}')
+        build_tools = cls._discover_build_tools(android_home)
+        return {
+            'build_tools': build_tools,
+            **cls._discover_aapt(build_tools)
+        }
+
+    @staticmethod
+    def _discover_build_tools(android_home):
+        build_tools = os.path.join(android_home, 'build-tools')
+        if os.path.isdir(build_tools):
+            return build_tools
+        else:
+            return None
+
+    @staticmethod
+    def _check_supported_aapt2(binary):
+        # At time of writing the version argument of aapt2 is not helpful as
+        # the output is only a placeholder that does not distinguish between versions
+        # with and without support for badging. Unfortunately aapt has been
+        # deprecated and fails to parse some valid apks so we will try to favour
+        # aapt2 if possible else will fall back to aapt.
+        # Try to execute the badging command and check if we get an expected error
+        # message as opposed to an unknown command error to determine if we have a
+        # suitable version.
+        result = subprocess.run([str(binary), 'dump', 'badging'], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, universal_newlines=True)
+        supported = bool(AAPT_BADGING_OUTPUT.search(result.stderr))
+        msg = 'Found a {} aapt2 binary at: {}'
+        logger.debug(msg.format('supported' if supported else 'unsupported', binary))
+        return supported
+
+    @classmethod
+    def _discover_aapt(cls, build_tools):
+        if build_tools:
+
+            def find_aapt2(version):
+                path = os.path.join(build_tools, version, 'aapt2')
+                if os.path.isfile(path) and cls._check_supported_aapt2(path):
+                    return (2, path)
+                else:
+                    return (None, None)
+
+            def find_aapt(version):
+                path = os.path.join(build_tools, version, 'aapt')
+                if os.path.isfile(path):
+                    return (1, path)
+                else:
+                    return (None, None)
+
+            versions = os.listdir(build_tools)
+            found = (
+                (version, finder(version))
+                for version in reversed(sorted(versions))
+                for finder in (find_aapt2, find_aapt)
+            )
+
+            for version, (aapt_version, aapt_path) in found:
+                if aapt_path:
+                    logger.debug(f'Using {aapt_path} for version {version}')
+                    return dict(
+                        aapt=aapt_path,
+                        aapt_version=aapt_version,
+                    )
+
+        # Try detecting aapt2 and aapt from PATH
+        aapt2_path = which('aapt2')
+        aapt_path = which('aapt')
+        if aapt2_path and cls._check_supported_aapt2(aapt2_path):
+            return dict(
+                aapt=aapt2_path,
+                aapt_version=2,
+            )
+        elif aapt_path:
+            return dict(
+                aapt=aapt_path,
+                aapt_version=1,
+            )
+        else:
+            raise HostError('aapt/aapt2 not found. Please make sure it is avaliable in PATH or at least one Android platform is installed')
+
 
 class LogcatMonitor(object):
     """