def _install(self,
                path_or_url: Union[str, Path],
                nolaunch: bool = False,
                uninstall: bool = False,
                silent: bool = False,
                callback: typing.Callable[[str], None] = None,
                flags: list = ["-r", "-t"],
                has_apkutils: bool = True):
        """
        Install APK to device

        Args:
            path_or_url: local path or http url
            nolaunch: do not launch app after install
            uninstall: uninstall app before install
            silent: disable log message print
            callback: only two event now: <"BEFORE_INSTALL" | "FINALLY">
            flags (list): default ["-r", "-t"]
            has_apkutils: whether apkutils is installed

        Raises:
            AdbInstallError, BrokenPipeError
        """
        def dprint(msg):
            if not silent:
                print(msg)
                
        if isinstance(path_or_url, str) and re.match(r"^https?://", path_or_url):
            tmpfile = tempfile.NamedTemporaryFile(suffix=".apk")
            self.download_apk(path_or_url, Path(tmpfile.name))
            tmpfile.flush()
            tmpfile.seek(0)
            src_path = Path(tmpfile.name)
            dprint(f"download apk to {src_path}")
        else:
            src_path = Path(path_or_url)
        if not src_path.is_file():
            raise FileNotFoundError(f"File or URL not found: {path_or_url}")
        
        package_name = None
        main_activity = None
        
        if has_apkutils:
            import apkutils
            with apkutils.APK.from_file(str(src_path)) as apk:
                activities = apk.get_main_activities()
                main_activity = activities[0] if activities else None
                package_name = apk.get_package_name()
                if main_activity and main_activity.find(".") == -1:
                    main_activity = "." + main_activity
            
            dprint(f"APK packageName: {package_name}")
            dprint(f"APK mainActivity: {main_activity}")

        device_dst = f"/data/local/tmp/{package_name or 'unknown'}.apk"
        dprint(f'push apk to device: {device_dst}')
        self._push_apk(src_path, device_dst, show_progress=not silent)

        info = self.sync.stat(device_dst)
        apk_size = src_path.stat().st_size
        if not info.size == apk_size:
            AdbInstallError(f'pushed apk size not matched, expect {apk_size} got {info.size}')
        
        if uninstall and package_name:
            dprint(f"uninstall app: {package_name}")
            self.uninstall(package_name)

        dprint("install to android system ...")
        try:
            start = time.time()
            if callback:
                callback("BEFORE_INSTALL")

            self.install_remote(device_dst, clean=True, flags=flags)
            time_used = time.time() - start
            dprint(f"successfully installed, time used {time_used:.1f} seconds")
            if not nolaunch and package_name and main_activity:
                dprint("launch app: %s/%s" % (package_name, main_activity))
                self.app_start(package_name, main_activity)

        except AdbInstallError as e:
            if package_name and e.reason in [
                "INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE",
                "INSTALL_FAILED_UPDATE_INCOMPATIBLE",
                "INSTALL_FAILED_VERSION_DOWNGRADE"
            ]:
                dprint("uninstall %s because %s" % (package_name, e.reason))
                self.uninstall(package_name)
                self.install_remote(device_dst, clean=True, flags=flags)
                dprint(f"successfully installed, time used {time.time() - start} seconds")
                if not nolaunch and main_activity:
                    dprint(f"Launch app: {package_name}/{main_activity}")
                    self.app_start(package_name, main_activity)
            else:
                # print to console
                print(
                    "Failure " + e.reason + "\n" +
                    "Remote apk is not removed. Manually install command:\n\t"
                    + "adb shell pm install -r -t " + device_dst)
                raise
        finally:
            if callback:
                callback("FINALLY")

        Install APK to device

        Args:
            path_or_url: local path or http url
            nolaunch: do not launch app after install
            uninstall: uninstall app before install
            silent: disable log message print
            callback: only two event now: <"BEFORE_INSTALL" | "FINALLY">
            flags (list): default ["-r", "-t"]
            has_apkutils: whether apkutils is installed

        Raises:
            AdbInstallError, BrokenPipeError
        
adbutils/install.py