Exemple #1
0
    def download_pogo(self, architecture: APKArch) -> NoReturn:
        """ Download the package com.nianticlabs.pokemongo

        Determine if theres a newer version of com.nianticlabs.pokemongo.  If a new version exists, validate it is
        supported by MAD.  If the release is supported download and save to the storage interface

        Args:
            architecture (APKArch): Architecture of the package to download
        """
        latest_version = self.find_latest_pogo(architecture)
        if latest_version is None:
            logger.warning(
                'Unable to find latest data for PoGo.  Try again later')
        elif supported_pogo_version(architecture, latest_version):
            current_version = self.storage.get_current_version(
                APKType.pogo, architecture)
            if type(current_version) is not str:
                current_version = None
            if current_version is None or is_newer_version(
                    latest_version, current_version):
                where = {
                    'usage': APKType.pogo.value,
                    'arch': architecture.value
                }
                try:
                    update_data = {'download_status': 1}
                    self.dbc.autoexec_update('mad_apk_autosearch',
                                             update_data,
                                             where_keyvals=where)
                    retries: int = 0
                    successful: bool = False
                    while retries < MAX_RETRIES and not successful:
                        self.gpconn = GPlayConnector(architecture)
                        latest_data = self.get_latest(APKType.pogo,
                                                      architecture)
                        downloaded_file = self.gpconn.download(
                            APKPackage.pogo.value,
                            version_code=latest_data['url'])
                        if downloaded_file and downloaded_file.getbuffer(
                        ).nbytes > 0:
                            PackageImporter(APKType.pogo,
                                            architecture,
                                            self.storage,
                                            downloaded_file,
                                            'application/zip',
                                            version=latest_version)
                            successful = True
                        else:
                            logger.info("Issue downloading apk")
                            retries += 1
                            if retries < MAX_RETRIES:
                                logger.warning(
                                    'Unable to successfully download the APK')
                except:  # noqa: E722
                    raise
                finally:
                    update_data['download_status'] = 0
                    self.dbc.autoexec_update('mad_apk_autosearch',
                                             update_data,
                                             where_keyvals=where)
Exemple #2
0
    def find_latest_pogo(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package com.nianticlabs.pokemongo has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        latest = None
        logger.info('Searching for a new version of PoGo [{}]', architecture.name)
        self.gpconn = GPlayConnector(architecture)
        try:
            download_url = None
            latest = self.gpconn.get_latest_version(APKPackage.pogo.value)
            current_version = self.storage.get_current_version(APKType.pogo, architecture)
            if type(current_version) is not str:
                current_version = None
            if current_version is None or is_newer_version(latest, current_version):
                if supported_pogo_version(architecture, latest):
                    logger.info('Newer version found on the Play Store: {}', latest)
                    download_url = True
            else:
                logger.info('No newer version found')
            self.set_last_searched(APKType.pogo, architecture, version=latest, url=download_url)
        except Exception as err:
            logger.opt(exception=True).critical(err)
        return latest
Exemple #3
0
    def find_latest_pogo(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package com.nianticlabs.pokemongo has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        latest = None
        version_code: int = None
        version_str: str = None
        logger.info('Searching for a new version of PoGo [{}]', architecture.name)
        try:
            latest_pogo_version = self.get_latest_version()
            if latest_pogo_version[architecture] is None:
                return latest
            latest_supported = latest_pogo_version[architecture]
            current_version_string = self.storage.get_current_version(APKType.pogo, architecture)
            current_version_code = None
            if type(current_version_string) is not str:
                current_version_string = None
            if current_version_string:
                latest_supported_dict: dict = {architecture: current_version_string}
                current_version_code = self.get_version_code(latest_supported=latest_supported_dict, arch=architecture)
                version_str = current_version_string
                version_code = current_version_code
            # do some sanity checking until this is fixed properly
            tmp_latest = self.get_latest(APKType.pogo, architecture)
            if (current_version_string is None
                    or is_newer_version(latest_supported["version"], current_version_string)
                    or (tmp_latest and tmp_latest['url'] is not None)):
                # Validate its available via google
                gpconn = GPlayConnector(architecture)
                (store_version_code, store_version_string) = gpconn.get_latest_version(APKPackage.pogo.value)
                if not supported_pogo_version(architecture, store_version_string):
                    logger.info(f"Store version {store_version_string} is not supported by MAD. Unable to get a newer "
                                "supported version.")
                    if current_version_code:
                        version_code = current_version_code
                        version_str = current_version_string
                    else:
                        version_code = latest_supported["versionCode"]
                        version_str = latest_supported["version"]
                elif current_version_code and store_version_code <= current_version_code:
                    logger.info(f"Latest store version is {store_version_string} while {current_version_string} is "
                                "already installed. Unable to find a newer version")
                    version_code = current_version_code
                    version_str = current_version_string
                else:
                    logger.info('Newer version found: {}', store_version_string)
                    version_code = store_version_code
                    version_str = store_version_string
            else:
                logger.info('No newer version found')
            self.set_last_searched(APKType.pogo, architecture, version=version_str, url=version_code)
        except Exception as err:
            logger.opt(exception=True).critical(err)
        return {
            "version_code": version_code,
            "version": version_str
        }
Exemple #4
0
    def find_latest_pogo(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package com.nianticlabs.pokemongo has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        latest = None
        version_code: int = None
        version_str: str = None
        logger.info('Searching for a new version of PoGo [{}]',
                    architecture.name)
        try:
            latest_pogo_version = self.get_latest_version()
            if latest_pogo_version[architecture] is None:
                return latest
            latest_supported = latest_pogo_version[architecture]
            current_version = self.storage.get_current_version(
                APKType.pogo, architecture)
            if type(current_version) is not str:
                current_version = None
            # do some sanity checking until this is fixed properly
            tmp_latest = self.get_latest(APKType.pogo, architecture)
            if current_version is None or is_newer_version(
                    latest_supported["version"],
                    current_version) or (tmp_latest
                                         and tmp_latest['url'] is not None):
                # Validate its available via google
                gpconn = GPlayConnector(architecture)
                (store_vc,
                 store_vs) = gpconn.get_latest_version(APKPackage.pogo.value)
                if store_vc < latest_supported["versionCode"]:
                    logger.info(
                        f"Latest supported is {store_vc} while installed is {latest_supported['versionCode']}. "
                        "Unable to find a newer version")
                    return None
                elif store_vc > latest_supported["versionCode"]:
                    logger.info(
                        "Version in store is newer than supported version. Using an older version"
                    )
                    version_code = latest_supported["versionCode"]
                    version_str = latest_supported["version"]
                elif current_version and store_vs == current_version:
                    logger.info("Latest version [{}] is already installed",
                                store_vc)
                    version_code = store_vc
                    version_str = store_vs
                else:
                    logger.info('Newer version found: {}', store_vs)
                    version_code = store_vc
                    version_str = store_vs
            else:
                logger.info('No newer version found')
            self.set_last_searched(APKType.pogo,
                                   architecture,
                                   version=version_str,
                                   url=version_code)
        except Exception as err:
            logger.opt(exception=True).critical(err)
        return {"version_code": version_code, "version": version_str}
Exemple #5
0
class APKWizard(object):
    """ The wizard will allow for simplified APK management for the required packages

    Args:
        dbc: Database wrapper
        storage: Abstract storage element for interacting with storage medium

    Attributes:
        dbc: Database wrapper
        gpconn: Object for interacting with google play
        storage: Abstract storage element for interacting with storage medium
    """
    gpconn: GPlayConnector
    storage: AbstractAPKStorage

    def __init__(self, dbc, storage: AbstractAPKStorage):
        self.storage: AbstractAPKStorage = storage
        self.dbc = dbc
        self.gpconn = None

    def apk_all_actions(self) -> NoReturn:
        "Search and download all required packages"
        self.apk_all_download()

    def apk_all_download(self) -> NoReturn:
        "Download all packages in a non-blocking fashion"
        Thread(target=self.apk_nonblocking_download).start()

    def apk_all_search(self) -> NoReturn:
        "Search for updates for any required package"
        self.find_latest_pogo(APKArch.armeabi_v7a)
        self.find_latest_pogo(APKArch.arm64_v8a)
        self.find_latest_rgc(APKArch.noarch)
        self.find_latest_pd(APKArch.noarch)

    def apk_download(self, package: APKType,
                     architecture: APKArch) -> NoReturn:
        """Download a specific package

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download
        """
        if package == APKType.pogo:
            self.download_pogo(architecture)
        elif package == APKType.rgc:
            self.download_rgc(architecture)
        elif package == APKType.pd:
            self.download_pd(architecture)

    def apk_nonblocking_download(self) -> NoReturn:
        "Download all packages"
        self.download_pogo(APKArch.armeabi_v7a)
        self.download_pogo(APKArch.arm64_v8a)
        self.download_rgc(APKArch.noarch)
        self.download_pd(APKArch.noarch)

    def apk_search(self, package: APKType, architecture: APKArch) -> NoReturn:
        """ Search for a specific package

        Args:
            package (APKType): Package to search
            architecture (APKArch): Architecture of the package to search
        """
        if package == APKType.pogo:
            self.find_latest_pogo(architecture)
        elif package == APKType.rgc:
            self.find_latest_rgc(architecture)
        elif package == APKType.pd:
            self.find_latest_pd(architecture)

    def download_pogo(self, architecture: APKArch) -> NoReturn:
        """ Download the package com.nianticlabs.pokemongo

        Determine if theres a newer version of com.nianticlabs.pokemongo.  If a new version exists, validate it is
        supported by MAD.  If the release is supported download and save to the storage interface

        Args:
            architecture (APKArch): Architecture of the package to download
        """
        latest_version = self.find_latest_pogo(architecture)
        if latest_version is None:
            logger.warning(
                'Unable to find latest data for PoGo.  Try again later')
        elif supported_pogo_version(architecture, latest_version):
            current_version = self.storage.get_current_version(
                APKType.pogo, architecture)
            if type(current_version) is not str:
                current_version = None
            if current_version is None or is_newer_version(
                    latest_version, current_version):
                where = {
                    'usage': APKType.pogo.value,
                    'arch': architecture.value
                }
                try:
                    update_data = {'download_status': 1}
                    self.dbc.autoexec_update('mad_apk_autosearch',
                                             update_data,
                                             where_keyvals=where)
                    retries: int = 0
                    successful: bool = False
                    while retries < MAX_RETRIES and not successful:
                        self.gpconn = GPlayConnector(architecture)
                        latest_data = self.get_latest(APKType.pogo,
                                                      architecture)
                        downloaded_file = self.gpconn.download(
                            APKPackage.pogo.value,
                            version_code=latest_data['url'])
                        if downloaded_file and downloaded_file.getbuffer(
                        ).nbytes > 0:
                            PackageImporter(APKType.pogo,
                                            architecture,
                                            self.storage,
                                            downloaded_file,
                                            'application/zip',
                                            version=latest_version)
                            successful = True
                        else:
                            logger.info("Issue downloading apk")
                            retries += 1
                            if retries < MAX_RETRIES:
                                logger.warning(
                                    'Unable to successfully download the APK')
                except:  # noqa: E722
                    raise
                finally:
                    update_data['download_status'] = 0
                    self.dbc.autoexec_update('mad_apk_autosearch',
                                             update_data,
                                             where_keyvals=where)

    def download_pd(self, architecture: APKArch) -> NoReturn:
        """ Download the package com.mad.pogodroid

        Args:
            architecture (APKArch): Architecture of the package to download
        """
        logger.info("Downloading latest PogoDroid")
        self.__download_simple(APKType.pd, architecture)

    def __download_simple(self, package: APKType,
                          architecture: APKArch) -> NoReturn:
        """ Downloads the package via requests

        Determine if there is a newer version of the package.  If there is a newer version, download and save to the
        storage interface

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download
        """
        latest_data = self.get_latest(package, architecture)
        current_version = self.storage.get_current_version(
            package, architecture)
        if type(current_version) is not str:
            current_version = None
        if not latest_data or latest_data['url'] is None:
            self.apk_search(package, architecture)
            latest_data = self.get_latest(package, architecture)
        if not latest_data:
            logger.warning('Unable to find latest data')
        elif current_version and 'size' in current_version and current_version.size == int(
                latest_data['version']):
            logger.info('Latest version already installed')
        else:
            update_data = {'download_status': 1}
            where = {'usage': package.value, 'arch': architecture.value}
            self.dbc.autoexec_update('mad_apk_autosearch',
                                     update_data,
                                     where_keyvals=where)
            try:
                retries: int = 0
                successful: bool = False
                while retries < MAX_RETRIES and not successful:
                    response = requests.get(latest_data['url'],
                                            verify=False,
                                            headers=APK_HEADERS)
                    downloaded_file = io.BytesIO(response.content)
                    if downloaded_file and downloaded_file.getbuffer(
                    ).nbytes > 0:
                        PackageImporter(
                            package, architecture, self.storage,
                            downloaded_file,
                            'application/vnd.android.package-archive')
                        successful = True
                    else:
                        logger.info("Issue downloading apk")
                        retries += 1
                        if retries < MAX_RETRIES:
                            logger.warning(
                                'Unable to successfully download the APK')
            except:  # noqa: E722
                logger.warning('Unable to download the file @ {}',
                               latest_data['url'])
            finally:
                update_data['download_status'] = 0
                self.dbc.autoexec_update('mad_apk_autosearch',
                                         update_data,
                                         where_keyvals=where)

    def download_rgc(self, architecture: APKArch) -> NoReturn:
        """ Download the package de.grennith.rgc.remotegpscontroller

        Args:
            architecture (APKArch): Architecture of the package to download
        """
        logger.info("Downloading latest RGC")
        self.__download_simple(APKType.rgc, architecture)

    def __find_latest_head(self, package, architecture, url) -> NoReturn:
        """ Determine if there is a newer version by checking the size of the package from the HEAD response

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download
            url (str): URL to perform the HEAD against
        """
        (curr_info, status) = lookup_package_info(self.storage, package)
        installed_size = None
        if curr_info:
            installed_size = curr_info.get('size', None)
        head = requests.head(url,
                             verify=False,
                             headers=APK_HEADERS,
                             allow_redirects=True)
        mirror_size = int(head.headers['Content-Length'])
        if not curr_info or (installed_size and installed_size != mirror_size):
            logger.info('Newer version found on the mirror of size {}',
                        mirror_size)
        else:
            logger.info('No newer version found')
        self.set_last_searched(package,
                               architecture,
                               version=mirror_size,
                               url=url)

    def find_latest_pd(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package com.mad.pogodroid has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        logger.info('Searching for a new version of PD [{}]',
                    architecture.name)
        self.__find_latest_head(APKType.pd, architecture,
                                global_variables.URL_PD_APK)

    def find_latest_pogo(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package com.nianticlabs.pokemongo has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        latest = None
        logger.info('Searching for a new version of PoGo [{}]',
                    architecture.name)
        try:
            version_code = None
            latest_pogo_version = self.get_latest_version()
            if latest_pogo_version[architecture] is None:
                return latest
            latest = latest_pogo_version[architecture]
            current_version = self.storage.get_current_version(
                APKType.pogo, architecture)
            if type(current_version) is not str:
                current_version = None
            # do some sanity checking until this is fixed properly
            tmp_latest = self.get_latest(APKType.pogo, architecture)
            if current_version is None or is_newer_version(
                    latest,
                    current_version) or (tmp_latest
                                         and tmp_latest['url'] is not None
                                         and int(tmp_latest['url']) == 1):
                logger.info('Newer version found: {}', latest)
                version_code: int = self.get_version_code(
                    latest_pogo_version, architecture)
            else:
                logger.info('No newer version found')
            self.set_last_searched(APKType.pogo,
                                   architecture,
                                   version=latest,
                                   url=version_code)
        except Exception as err:
            logger.opt(exception=True).critical(err)
        return latest

    def find_latest_rgc(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package de.grennith.rgc.remotegpscontroller has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        logger.info('Searching for a new version of RGC [{}]',
                    architecture.name)
        self.__find_latest_head(APKType.rgc, architecture,
                                global_variables.URL_RGC_APK)

    def get_latest(self, package: APKType, architecture: APKArch) -> dict:
        """ Determine the latest found version for a given package / architecture

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download

        Returns (dict):
            Returns the latest version found for the package
        """
        sql = "SELECT `version`, `url` FROM `mad_apk_autosearch` WHERE `usage` = %s AND `arch` = %s"
        return self.dbc.autofetch_row(sql,
                                      args=(package.value, architecture.value))

    def get_latest_version(self) -> Dict[APKArch, str]:
        versions: Dict[APKArch, str] = {
            APKArch.armeabi_v7a: None,
            APKArch.arm64_v8a: None
        }
        gh_resp = requests.get(global_variables.ADDRESSES_GITHUB)
        for ver_identifier in gh_resp.json().keys():
            version, arch = ver_identifier.split('_')
            named_arch = APKArch.armeabi_v7a if arch == '32' else APKArch.arm64_v8a
            if versions[named_arch] is None:
                versions[named_arch] = version
            elif is_newer_version(version, versions[named_arch]):
                versions[named_arch] = version
        return versions

    def get_version_code(self, latest_supported: Dict[APKArch, str],
                         arch: APKArch) -> Optional[int]:
        pogo_vc_sess = requests.session()
        pogo_vc_sess.headers.update(
            {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"})
        # attempt to pull the information locally
        try:
            with open('configs/version_codes.json', 'r') as fh:
                data = json.load(fh)
        except (FileNotFoundError, json.decoder.JSONDecodeError):
            pass
        else:
            version_code = self.parse_version_code(latest_supported, arch,
                                                   data)
            if version_code is not None:
                return version_code
        # match not found locally.  Try GitHub
        try:
            data = pogo_vc_sess.get(
                global_variables.VERSIONCODES_GITHUB).json()
        except Exception:
            pass
        else:
            version_code = self.parse_version_code(latest_supported, arch,
                                                   data)
            if version_code is not None:
                return version_code
        return None

    def parse_version_code(self, latest_supported: Dict[APKArch, str],
                           arch: APKArch, data: Dict) -> Optional[int]:
        named_arch = '32' if arch == APKArch.armeabi_v7a else '64'
        latest_version = f"{latest_supported[arch]}_{named_arch}"
        if data:
            try:
                return data[latest_version]
            except KeyError:
                pass
        return None

    def set_last_searched(self,
                          package: APKType,
                          architecture: APKArch,
                          version: str = None,
                          url: str = None) -> NoReturn:
        """ Updates the last search information for the package / architecture

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download
            version (str): latest version found
            url (str): URL for the download
        """
        data = {
            'usage': package.value,
            'arch': architecture.value,
            'last_checked': 'NOW()'
        }
        if version:
            data['version'] = version
        if url:
            data['url'] = url
        self.dbc.autoexec_insert('mad_apk_autosearch',
                                 data,
                                 literals=['last_checked'],
                                 optype='ON DUPLICATE')
Exemple #6
0
class APKWizard(object):
    """ The wizard will allow for simplified APK management for the required packages

    Args:
        dbc: Database wrapper
        storage: Abstract storage element for interacting with storage medium

    Attributes:
        dbc: Database wrapper
        gpconn: Object for interacting with google play
        storage: Abstract storage element for interacting with storage medium
    """
    gpconn: GPlayConnector
    storage: AbstractAPKStorage

    def __init__(self, dbc, storage: AbstractAPKStorage):
        self.storage: AbstractAPKStorage = storage
        self.dbc = dbc
        self.gpconn = None

    def apk_all_actions(self) -> NoReturn:
        "Search and download all required packages"
        self.apk_all_search()
        self.apk_all_download()

    def apk_all_download(self) -> NoReturn:
        "Download all packages in a non-blocking fashion"
        Thread(target=self.apk_nonblocking_download).start()

    def apk_all_search(self) -> NoReturn:
        "Search for updates for any required package"
        self.find_latest_pogo(APKArch.armeabi_v7a)
        self.find_latest_pogo(APKArch.arm64_v8a)
        self.find_latest_rgc(APKArch.noarch)
        self.find_latest_pd(APKArch.noarch)

    def apk_download(self, package: APKType, architecture: APKArch) -> NoReturn:
        """Download a specific package

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download
        """
        if package == APKType.pogo:
            self.download_pogo(architecture)
        elif package == APKType.rgc:
            self.download_rgc(architecture)
        elif package == APKType.pd:
            self.download_pd(architecture)

    def apk_nonblocking_download(self) -> NoReturn:
        "Download all packages"
        self.download_pogo(APKArch.armeabi_v7a)
        self.download_pogo(APKArch.arm64_v8a)
        self.download_rgc(APKArch.noarch)
        self.download_pd(APKArch.noarch)

    def apk_search(self, package: APKType, architecture: APKArch) -> NoReturn:
        """ Search for a specific package

        Args:
            package (APKType): Package to search
            architecture (APKArch): Architecture of the package to search
        """
        if package == APKType.pogo:
            self.find_latest_pogo(architecture)
        elif package == APKType.rgc:
            self.find_latest_rgc(architecture)
        elif package == APKType.pd:
            self.find_latest_pd(architecture)

    def download_pogo(self, architecture: APKArch) -> NoReturn:
        """ Download the package com.nianticlabs.pokemongo

        Determine if theres a newer version of com.nianticlabs.pokemongo.  If a new version exists, validate it is
        supported by MAD.  If the release is supported download and save to the storage interface

        Args:
            architecture (APKArch): Architecture of the package to download
        """
        latest_version = self.find_latest_pogo(architecture)
        if latest_version is None:
            logger.warning('Unable to find latest data for PoGo.  Try again later')
        elif supported_pogo_version(architecture, latest_version):
            current_version = self.storage.get_current_version(APKType.pogo, architecture)
            if type(current_version) is not str:
                current_version = None
            if current_version is None or is_newer_version(latest_version, current_version):
                where = {
                    'usage': APKType.pogo.value,
                    'arch': architecture.value
                }
                try:
                    update_data = {
                        'download_status': 1
                    }
                    self.dbc.autoexec_update('mad_apk_autosearch', update_data, where_keyvals=where)
                    retries: int = 0
                    successful: bool = False
                    while retries < MAX_RETRIES and not successful:
                        self.gpconn = GPlayConnector(architecture)
                        downloaded_file = self.gpconn.download(APKPackage.pogo.value)
                        if downloaded_file and downloaded_file.getbuffer().nbytes > 0:
                            PackageImporter(APKType.pogo, architecture, self.storage, downloaded_file,
                                            'application/zip', version=latest_version)
                            successful = True
                        else:
                            logger.info("Issue downloading apk")
                            retries += 1
                            if retries < MAX_RETRIES:
                                logger.warning('Unable to successfully download the APK')
                except:  # noqa: E722
                    raise
                finally:
                    update_data['download_status'] = 0
                    self.dbc.autoexec_update('mad_apk_autosearch', update_data, where_keyvals=where)

    def download_pd(self, architecture: APKArch) -> NoReturn:
        """ Download the package com.mad.pogodroid

        Args:
            architecture (APKArch): Architecture of the package to download
        """
        logger.info("Downloading latest PogoDroid")
        self.__download_simple(APKType.pd, architecture)

    def __download_simple(self, package: APKType, architecture: APKArch) -> NoReturn:
        """ Downloads the package via requests

        Determine if there is a newer version of the package.  If there is a newer version, download and save to the
        storage interface

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download
        """
        latest_data = self.get_latest(package, architecture)
        current_version = self.storage.get_current_version(package, architecture)
        if type(current_version) is not str:
            current_version = None
        if not latest_data or latest_data['url'] is None:
            self.apk_search(package, architecture)
            latest_data = self.get_latest(package, architecture)
        if not latest_data:
            logger.warning('Unable to find latest data')
        elif current_version and 'size' in current_version and current_version.size == int(latest_data['version']):
            logger.info('Latest version already installed')
        else:
            update_data = {
                'download_status': 1
            }
            where = {
                'usage': package.value,
                'arch': architecture.value
            }
            self.dbc.autoexec_update('mad_apk_autosearch', update_data, where_keyvals=where)
            try:
                retries: int = 0
                successful: bool = False
                while retries < MAX_RETRIES and not successful:
                    response = requests.get(latest_data['url'], verify=False, headers=APK_HEADERS)
                    downloaded_file = io.BytesIO(response.content)
                    if downloaded_file and downloaded_file.getbuffer().nbytes > 0:
                        PackageImporter(package, architecture, self.storage, downloaded_file,
                                        'application/vnd.android.package-archive')
                        successful = True
                    else:
                        logger.info("Issue downloading apk")
                        retries += 1
                        if retries < MAX_RETRIES:
                            logger.warning('Unable to successfully download the APK')
            except:  # noqa: E722
                logger.warning('Unable to download the file @ {}', latest_data['url'])
            finally:
                update_data['download_status'] = 0
                self.dbc.autoexec_update('mad_apk_autosearch', update_data, where_keyvals=where)

    def download_rgc(self, architecture: APKArch) -> NoReturn:
        """ Download the package de.grennith.rgc.remotegpscontroller

        Args:
            architecture (APKArch): Architecture of the package to download
        """
        logger.info("Downloading latest RGC")
        self.__download_simple(APKType.rgc, architecture)

    def __find_latest_head(self, package, architecture, url) -> NoReturn:
        """ Determine if there is a newer version by checking the size of the package from the HEAD response

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download
            url (str): URL to perform the HEAD against
        """
        (curr_info, status) = lookup_package_info(self.storage, package)
        installed_size = None
        if curr_info:
            installed_size = curr_info.get('size', None)
        head = requests.head(url, verify=False, headers=APK_HEADERS, allow_redirects=True)
        mirror_size = int(head.headers['Content-Length'])
        if not curr_info or (installed_size and installed_size != mirror_size):
            logger.info('Newer version found on the mirror of size {}', mirror_size)
        else:
            logger.info('No newer version found')
        self.set_last_searched(package, architecture, version=mirror_size, url=url)

    def find_latest_pd(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package com.mad.pogodroid has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        logger.info('Searching for a new version of PD [{}]', architecture.name)
        self.__find_latest_head(APKType.pd, architecture, global_variables.URL_PD_APK)

    def find_latest_pogo(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package com.nianticlabs.pokemongo has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        latest = None
        logger.info('Searching for a new version of PoGo [{}]', architecture.name)
        self.gpconn = GPlayConnector(architecture)
        try:
            download_url = None
            latest = self.gpconn.get_latest_version(APKPackage.pogo.value)
            current_version = self.storage.get_current_version(APKType.pogo, architecture)
            if type(current_version) is not str:
                current_version = None
            if current_version is None or is_newer_version(latest, current_version):
                if supported_pogo_version(architecture, latest):
                    logger.info('Newer version found on the Play Store: {}', latest)
                    download_url = True
            else:
                logger.info('No newer version found')
            self.set_last_searched(APKType.pogo, architecture, version=latest, url=download_url)
        except Exception as err:
            logger.opt(exception=True).critical(err)
        return latest

    def find_latest_rgc(self, architecture: APKArch) -> Optional[str]:
        """ Determine if the package de.grennith.rgc.remotegpscontroller has an update

        Args:
            architecture (APKArch): Architecture of the package to check
        """
        logger.info('Searching for a new version of RGC [{}]', architecture.name)
        self.__find_latest_head(APKType.rgc, architecture, global_variables.URL_RGC_APK)

    def get_latest(self, package: APKType, architecture: APKArch) -> dict:
        """ Determine the latest found version for a given package / architecture

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download

        Returns (dict):
            Returns the latest version found for the package
        """
        sql = "SELECT `version`, `url` FROM `mad_apk_autosearch` WHERE `usage` = %s AND `arch` = %s"
        return self.dbc.autofetch_row(sql, args=(package.value, architecture.value))

    def set_last_searched(self, package: APKType, architecture: APKArch, version: str = None,
                          url: str = None) -> NoReturn:
        """ Updates the last search information for the package / architecture

        Args:
            package (APKType): Package to download
            architecture (APKArch): Architecture of the package to download
            version (str): latest version found
            url (str): URL for the download
        """
        data = {
            'usage': package.value,
            'arch': architecture.value,
            'last_checked': 'NOW()'
        }
        if version:
            data['version'] = version
        if url:
            data['url'] = url
        self.dbc.autoexec_insert('mad_apk_autosearch', data, literals=['last_checked'], optype='ON DUPLICATE')