def _exec_cmd(self, cmd, no_except=False, timeout=DEFAULT_ADB_TIMEOUT): """Executes adb commands in a new shell. This is specific to executing adb binary because stderr is not a good indicator of cmd execution status. Args: cmd: string, the adb command to execute. no_except: bool, controls whether exception can be thrown. timeout: float, timeout in seconds. If the command times out, the exit code is not 0. Returns: The output of the adb command run if the exit code is 0 and if exceptions are allowed. Otherwise, returns a dictionary containing stdout, stderr, and exit code. Raises: AdbError if the adb command exit code is not 0 and exceptions are allowed. """ out, err, ret = cmd_utils.ExecuteOneShellCommand(cmd, timeout) logging.debug("cmd: %s, stdout: %s, stderr: %s, ret: %s", cmd, out, err, ret) if no_except: return { const.STDOUT: out, const.STDERR: err, const.EXIT_CODE: ret, } else: if ret == 0: return out else: raise AdbError(cmd=cmd, stdout=out, stderr=err, ret_code=ret)
def CallVtableDumper(self, lib_path): """Calls vndk-vtable-dumper and returns its output. Args: lib_path: The host-side path to the library. Returns: A string which is the output of vndk-vtable-dumper Raises: VtableError if the command fails. """ bin_dir = os.path.join(self._dumper_path, "bin") if not utils.is_on_windows(): bin_path = os.path.join(bin_dir, self.VNDK_VTABLE_DUMPER) cmd = "%s %s -mangled" % (bin_path, lib_path) else: bin_path = os.path.join(bin_dir, self.VNDK_VTABLE_DUMPER_EXE) dll_dir = os.path.join(self._dumper_path, "lib") cmd = 'set "PATH=%s;%%PATH%%" && %s %s -mangled' % ( dll_dir, bin_path, lib_path) logging.debug("Execute command: %s" % cmd) stdout, stderr, exit_code = cmd_utils.ExecuteOneShellCommand(cmd) logging.debug("stdout: " + stdout) logging.debug("stderr: " + stderr) if exit_code: raise VtableError("command: %s\n" "exit code: %s\n" "stdout: %s\n" "stderr: %s" % (cmd, exit_code, stdout, stderr)) return stdout
def GetGsutilPath(): """Returns the gsutil file path if found; None otherwise.""" sh_stdout, sh_stderr, ret_code = cmd_utils.ExecuteOneShellCommand( "which gsutil") if ret_code == 0: return sh_stdout.strip() else: logging.fatal("`gsutil` doesn't exist on the host; " "please install Google Cloud SDK before retrying.") return None
def Run(self, arg_line): """Runs a shell command.""" args = self.arg_parser.ParseLine(arg_line) cmd_list = self.ReplaceVars(args.command) stdout, stderr, retcode = cmd_utils.ExecuteOneShellCommand( " ".join(cmd_list)) if stdout: logging.info(stdout) if stderr: logging.error(stderr) if retcode != 0: return False
def GetGcloudAuth(self): """Connects to a service account with access to the gcloud bucket.""" gcloud_path = GcsUtils.GetGcloudPath() gcloud_key = getattr(self, keys.ConfigKeys.IKEY_SERVICE_JSON_PATH) if gcloud_path is not None: auth_cmd = "%s auth activate-service-account --key-file %s" % ( gcloud_path, gcloud_key) _, stderr, ret_code = cmd_utils.ExecuteOneShellCommand(auth_cmd) if ret_code == 0: logging.info(stderr) else: logging.error(stderr)
def IsGcsFile(gsutil_path, gs_path): """Checks whether a given path is for a GCS file. Args: gsutil_path: string, the path of a gsutil binary. gs_path: string, the GCS file path (e.g., gs://<bucket>/<file>. Returns: True if gs_path is a file, False otherwise. """ check_command = "%s stat %s" % (gsutil_path, gs_path) _, _, ret_code = cmd_utils.ExecuteOneShellCommand(check_command) return ret_code == 0
def __init__(self): super(BuildProviderGCS, self).__init__() if _GCLOUD_AUTH_ENV_KEY in os.environ: gcloud_path = BuildProviderGCS.GetGcloudPath() if gcloud_path is not None: auth_cmd = "%s auth activate-service-account --key-file=%s" % ( gcloud_path, os.environ[_GCLOUD_AUTH_ENV_KEY]) _, stderr, ret_code = cmd_utils.ExecuteOneShellCommand( auth_cmd) if ret_code == 0: logging.info(stderr) else: logging.error(stderr)
def RunTestCase(self, test_case): """Run a syzkaller test case. Args: test_case: SyzkallerTestCase object, the test case to run. """ test_command = test_case.GetRunCommand() stdout, stderr, err_code = cmd_utils.ExecuteOneShellCommand( test_command, timeout=18000) if err_code: logging.error(stderr) else: logging.info(stdout) self.ReportTestCase(test_case)
def GetResultFromGCS(self, gcs_result_path, local_results_dir): """Downloads a vts-tf result zip archive from GCS. And unzip the file to "android-vts/results/" path so the vts-tf will parse the result correctly. Args: gcs_result_path: string, path to GCS file. local_results_dir: string, abs path to the result directory of currently running vts-tf. Returns: A string which is the name of unzipped result directory. None if the download has failed or the downloaded zip file is not a correct result archive. """ gsutil_path = build_provider_gcs.BuildProviderGCS.GetGsutilPath() if not gsutil_path: logging.error("Please check gsutil is installed and on your PATH") return None if (not gcs_result_path.startswith("gs://") or not build_provider_gcs.BuildProviderGCS.IsGcsFile( gsutil_path, gcs_result_path)): logging.error("%s is not correct GCS url.", gcs_result_path) return None if not gcs_result_path.endswith(".zip"): logging.error("%s is not a correct result archive file.", gcs_result_path) return None if not os.path.exists(local_results_dir): os.mkdir(local_results_dir) copy_command = "%s cp %s %s" % (gsutil_path, gcs_result_path, local_results_dir) stdout, stderr, err_code = cmd_utils.ExecuteOneShellCommand( copy_command) if err_code != 0: logging.error("Error in copy file from %s (code %s).", err_code) return None result_zip = os.path.join(local_results_dir, gcs_result_path.split("/")[-1]) with zipfile.ZipFile(result_zip, mode="r") as zip_ref: if self.IsResultZipFile(zip_ref): unzipped_result_dir = zip_ref.namelist()[0].rstrip("/") zip_ref.extractall(local_results_dir) return unzipped_result_dir else: logging.error("Not a correct vts-tf result archive file.") return None
def ExecCustomFlasherCmd(self, name, arg_str): """Passes joined shell command line to ExecuteOneShellCommand(). Args: name: stirng, command to pass to custom flasher binary. arg_str: string, contains additional argument(s). Returns: contents of stdout if command line returned without error; stderr otherwise. """ out, err, error_code = cmd_utils.ExecuteOneShellCommand(' '.join( (self.customflasher_str, name, arg_str))) if error_code: return err return out
def Run(self, arg_line): """Upload args.src file to args.dest Google Cloud Storage.""" args = self.arg_parser.ParseLine(arg_line) gsutil_path = build_provider_gcs.BuildProviderGCS.GetGsutilPath() if not gsutil_path: print("Please check gsutil is installed and on your PATH") return False if args.src.startswith("latest-"): src_name = args.src[7:] if src_name in self.console.device_image_info: src_path = self.console.device_image_info[src_name] else: print( "Unable to find {} in device_image_info".format(src_name)) return False else: try: src_paths = self.FormatString(args.src) except KeyError as e: print("Unknown or uninitialized variable in src: %s" % e) return False src_path_list = src_paths.split(" ") for path in src_path_list: src_path = path.strip() if not os.path.isfile(src_path): print("Cannot find a file: {}".format(src_path)) return False try: dest_path = self.FormatString(args.dest) except KeyError as e: print("Unknown or uninitialized variable in dest: %s" % e) return False if not dest_path.startswith("gs://"): print("{} is not correct GCS url.".format(dest_path)) return False """ TODO(jongmok) : Before upload, login status, authorization, and dest check are required. """ copy_command = "{} cp {} {}".format(gsutil_path, src_paths, dest_path) _, stderr, err_code = cmd_utils.ExecuteOneShellCommand(copy_command) if err_code: print stderr
def Run(self, arg_line): """Runs an adb command.""" args = self.arg_parser.ParseLine(arg_line) cmd_list = ["adb"] if args.serial: if "," in args.serial: logging.error("Only one serial can be specified") return False cmd_list.append("-s %s" % args.serial) cmd_list.extend(self.ReplaceVars(args.command)) stdout, stderr, retcode = cmd_utils.ExecuteOneShellCommand( " ".join(cmd_list)) if stdout: logging.info(stdout) if stderr: logging.error(stderr) if retcode != 0: return False
def Run(self, arg_line): """Runs a fastboot command.""" args = self.arg_parser.ParseLine(arg_line) cmd_list = ["fastboot"] if args.serial: if "," in args.serial: logging.error("Only one serial can be specified") return False cmd_list.append("-s %s" % args.serial) cmd_list.extend(self.ReplaceVars(args.command)) cmd = " ".join(cmd_list) for _ in range(args.retry + 1): stdout, stderr, retcode = cmd_utils.ExecuteOneShellCommand(cmd) if stdout: logging.info(stdout) if stderr: logging.error(stderr) if retcode == 0: return logging.warn("Retrying... (%s)", cmd) return False
def Fetch(self, path): """Fetches Android device artifact file(s) from GCS. Args: path: string, the path of a directory which keeps artifacts. Returns: a dict containing the device image info. a dict containing the test suite package info. a dict containing the info about custom tool files. """ if not path.startswith("gs://"): path = "gs://" + re.sub("^/*", "", path) path = re.sub("/*$", "", path) # make sure gsutil is available. Instead of a Python library, # gsutil binary is used that is to avoid packaging GCS PIP package # as part of VTS HC (Host Controller). gsutil_path = BuildProviderGCS.GetGsutilPath() if gsutil_path: temp_dir_path = self.CreateNewTmpDir() # IsGcsFile returns False if path is directory or doesn't exist. # cp command returns non-zero if path doesn't exist. if not BuildProviderGCS.IsGcsFile(gsutil_path, path): dest_path = temp_dir_path copy_command = "%s cp -r %s/* %s" % (gsutil_path, path, temp_dir_path) else: dest_path = os.path.join(temp_dir_path, os.path.basename(path)) copy_command = "%s cp %s %s" % (gsutil_path, path, temp_dir_path) _, _, ret_code = cmd_utils.ExecuteOneShellCommand(copy_command) if ret_code == 0: self.SetFetchedFile(dest_path, temp_dir_path) else: logging.error("Error in copy file from GCS (code %s)." % ret_code) return (self.GetDeviceImage(), self.GetTestSuitePackage(), self.GetAdditionalFile())
def Fetch(self, path, full_device_images=False, set_suite_as=None): """Fetches Android device artifact file(s) from GCS. Args: path: string, the path of a directory which keeps artifacts. set_suite_as: string, the test suite name to use for the given artifact. Used when the file name does not follow the standard "android-*ts.zip" file name pattern. Returns: a dict containing the device image info. a dict containing the test suite package info. a dict containing the info about custom tool files. """ if not path.startswith("gs://"): path = "gs://" + re.sub("^/*", "", path) path = re.sub("/*$", "", path) # make sure gsutil is available. Instead of a Python library, # gsutil binary is used that is to avoid packaging GCS PIP package # as part of VTS HC (Host Controller). gsutil_path = BuildProviderGCS.GetGsutilPath() if gsutil_path: temp_dir_path = self.CreateNewTmpDir() # IsGcsFile returns False if path is directory or doesn't exist. # cp command returns non-zero if path doesn't exist. if not BuildProviderGCS.IsGcsFile(gsutil_path, path): dest_path = temp_dir_path if "latest.zip" in path: gsutil_ls_path = re.sub("latest.zip", "*.zip", path) ls_command = "%s ls %s" % (gsutil_path, gsutil_ls_path) std_out, _, ret_code = cmd_utils.ExecuteOneShellCommand( ls_command) if ret_code == 0 and std_out: lines_gsutil_ls = std_out.split("\n") lines_gsutil_ls.sort() path = lines_gsutil_ls[-1] copy_command = "%s cp %s %s" % (gsutil_path, path, temp_dir_path) else: logging.error( "There is no file(s) that matches the URL %s.", path) return (self.GetDeviceImage(), self.GetTestSuitePackage(), self.GetAdditionalFile()) else: copy_command = "%s cp -r %s/* %s" % (gsutil_path, path, temp_dir_path) else: dest_path = os.path.join(temp_dir_path, os.path.basename(path)) copy_command = "%s cp %s %s" % (gsutil_path, path, temp_dir_path) _, _, ret_code = cmd_utils.ExecuteOneShellCommand(copy_command) if ret_code == 0: self.SetFetchedFile(dest_path, temp_dir_path, full_device_images, set_suite_as) else: logging.error("Error in copy file from GCS (code %s).", ret_code) return (self.GetDeviceImage(), self.GetTestSuitePackage(), self.GetAdditionalFile())
def RepackageArtifacts(self, device_images, repackage_form): """Repackage artifacts into a given format. Once repackaged, device_images becomes {"img": "path_to_repackaged_image"} Args: device_images: dict, where the key is partition name and value is image file path. repackage_form: string, format to repackage. Returns: True if succesful; False otherwise. """ if not device_images: logging.warn("Repackage skipped because no device image is given.") return False if repackage_form == "tar.md5": tmp_file_name = next(tempfile._get_candidate_names()) + ".tar" tmp_dir_path = os.path.dirname( device_images[device_images.keys()[0]]) for img in device_images: if os.path.dirname(device_images[img]) != tmp_dir_path: os.rename(device_images[img], os.path.join(tmp_dir_path, img)) device_images[img] = os.path.join(tmp_dir_path, img) current_dir = os.getcwd() os.chdir(tmp_dir_path) if sys.platform == "linux2": tar_cmd = "tar -cf %s %s" % (tmp_file_name, ' '.join( (device_images.keys()))) else: logging.error("Unsupported OS for the given repackage form.") return False logging.info(tar_cmd) std_out, std_err, err_code = cmd_utils.ExecuteOneShellCommand( tar_cmd) if err_code: logging.error(std_err) return False hash_md5 = hashlib.md5() try: with open(tmp_file_name, "rb") as file: data_chunk = 0 chunk_size = resource.getpagesize() while data_chunk != b'': data_chunk = file.read(chunk_size) hash_md5.update(data_chunk) hash_ret = hash_md5.hexdigest() with open(tmp_file_name, "a") as file: file.write("%s %s" % (hash_ret, tmp_file_name)) except IOError as e: logging.error(e.strerror) return False device_images.clear() device_images["img"] = os.path.join(tmp_dir_path, tmp_file_name) os.chdir(current_dir) else: logging.error( "Please specify correct repackage form: --repackage=%s" % repackage_form) return False return True
def UpdateDevice(self, server_type, host, lease): """Updates the device state of all devices on a given host. Args: server_type: string, the type of a test secheduling server. host: HostController object lease: boolean, True to lease and execute jobs. """ if server_type == "vti": devices = [] stdout, stderr, returncode = cmd_utils.ExecuteOneShellCommand( "adb devices") lines = stdout.split("\n")[1:] for line in lines: if len(line.strip()): device = {} device["serial"] = line.split()[0] serial = device["serial"] if (self.console.device_status[serial] != common._DEVICE_STATUS_DICT["use"]): stdout, _, retcode = cmd_utils.ExecuteOneShellCommand( "adb -s %s shell getprop ro.product.board" % device["serial"]) if retcode == 0: device["product"] = stdout.strip() else: device["product"] = "error" self.console.device_status[ serial] = common._DEVICE_STATUS_DICT["online"] device["status"] = self.console.device_status[serial] devices.append(device) stdout, stderr, returncode = cmd_utils.ExecuteOneShellCommand( "fastboot devices") lines = stdout.split("\n") for line in lines: if len(line.strip()): device = {} device["serial"] = line.split()[0] serial = device["serial"] if (self.console.device_status[serial] != common._DEVICE_STATUS_DICT["use"]): _, stderr, retcode = cmd_utils.ExecuteOneShellCommand( "fastboot -s %s getvar product" % device["serial"]) if retcode == 0: res = stderr.splitlines()[0].rstrip() if ":" in res: device["product"] = res.split(":")[1].strip() else: device["product"] = "error" else: device["product"] = "error" self.console.device_status[ serial] = common._DEVICE_STATUS_DICT["fastboot"] device["status"] = self.console.device_status[serial] devices.append(device) self.console._vti_endpoint_client.UploadDeviceInfo( host.hostname, devices) if lease: self.console._job_in_queue.put("lease") elif server_type == "tfc": devices = host.ListDevices() for device in devices: device.Extend(['sim_state', 'sim_operator', 'mac_address']) snapshots = self.console._tfc_client.CreateDeviceSnapshot( host._cluster_ids[0], host.hostname, devices) self.console._tfc_client.SubmitHostEvents([snapshots]) else: print "Error: unknown server_type %s for UpdateDevice" % server_type
def StartBatch(values, output_path): """Start batch process to fetch images, change spl version, and archive Args: values : a dict containing batch process information output_path : a string, output path """ if not os.path.exists(output_path): os.makedirs(output_path) logging.info("\noutput path: {}\n".format(output_path)) if "build_id" in values: build_id = values["build_id"] else: build_id = "latest" for target in values["targets"]: if values["build_provider"] == "ab": build_provider = build_provider_ab.BuildProviderAB() logging.info("Start downloading target:{} ...".format(target)) device_images, _, _ = build_provider.Fetch( branch=values["branch"], target="{}-{}".format(target, values["build_variant"]), artifact_name="{}-img-{}.zip".format(target, "{build_id}"), build_id=build_id) else: build_provider = build_provider_pab.BuildProviderPAB() device_images, _, _ = build_provider.GetArtifact( account_id=common._DEFAULT_ACCOUNT_ID, branch=values["branch"], target="{}-{}".format(target, values["build_variant"]), artifact_name="{}-img-{}.zip".format(target, "{build_id}"), build_id=build_id, method="GET") if "system.img" in device_images: logging.info("Downloading completed. system.img path: {}".format( device_images["system.img"])) else: logging.error("Could not download system image.") continue outputs = [] version_month = values["startMonth"] while True: for day in values["days"]: version = "{:04d}-{:02d}-{:02d}".format( version_month.year, version_month.month, day) output = os.path.join( output_path, "system-{}-{}.img".format(target, version)) logging.info("Change SPL to {} ...".format(version)) stdout, _, err_code = cmd_utils.ExecuteOneShellCommand( "{} {} {} {}".format( os.path.join(os.getcwd(), "..", "bin", "change_security_patch_ver.sh"), device_images["system.img"], output, version)) if not err_code: outputs.append(output) else: logging.error(stdout) version_month = version_month.replace( year=version_month.year + (version_month.month - 1) / 12, month=(version_month.month + 1) % 12) if version_month > values["endMonth"]: break if values["archive"] == "zip": zip_path = os.path.join(output_path, "system-{}.zip".format(target)) logging.info("archive to {} ...".format(zip_path)) with zipfile.ZipFile(zip_path, mode='w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as target: # writing each file one by one for file in outputs: target.write(file, os.path.basename(file)) os.remove(file) logging.info("archive completed.") # Delete build provider so temp directory will be removed. del build_provider return 0
def UpdateDevice(self, server_type, host, lease, suppress_lock_warning=True): """Updates the device state of all devices on a given host. Args: server_type: string, the type of a test secheduling server. host: HostController object lease: boolean, True to lease and execute jobs. """ if server_type == "vti": devices = [] stdout, stderr, returncode = cmd_utils.ExecuteOneShellCommand( "adb devices") lines_adb = stdout.split("\n") stdout, stderr, returncode = cmd_utils.ExecuteOneShellCommand( "fastboot devices") lines_fastboot = stdout.split("\n") for line in lines_adb: if (len(line.strip()) and not (line.startswith("* ") or line.startswith("List "))): device = {} device["serial"] = line.split()[0] serial = device["serial"] if self.console.file_lock.LockDevice( serial, suppress_lock_warning) == False: self.console.device_status[ serial] = common._DEVICE_STATUS_DICT["use"] if not suppress_lock_warning: logging.info("Device %s already locked." % serial) continue stdout, _, retcode = cmd_utils.ExecuteOneShellCommand( "adb -s %s reboot bootloader" % device["serial"]) if retcode == 0: lines_fastboot.append(line) self.console.file_lock.UnlockDevice(serial) for line in lines_fastboot: if len(line.strip()): device = {} device["serial"] = line.split()[0] serial = device["serial"] if self.console.file_lock.LockDevice( serial, suppress_lock_warning) == False: self.console.device_status[ serial] = common._DEVICE_STATUS_DICT["use"] if not suppress_lock_warning: logging.info("Device %s already locked." % serial) continue _, stderr, retcode = cmd_utils.ExecuteOneShellCommand( "fastboot -s %s getvar product" % device["serial"]) if retcode == 0: res = stderr.splitlines()[0].rstrip() if ":" in res: device["product"] = res.split(":")[1].strip() elif "waiting for %s" % serial in res: res = stderr.splitlines()[1].rstrip() device["product"] = res.split(":")[1].strip() else: device["product"] = "error" else: device["product"] = "error" self.console.device_status[ serial] = common._DEVICE_STATUS_DICT["fastboot"] device["status"] = self.console.device_status[serial] devices.append(device) self.console.file_lock.UnlockDevice(serial) self.console._vti_endpoint_client.UploadDeviceInfo( host.hostname, devices) if lease: self.console._job_in_queue.put("lease") if self.console.vtslab_version: self.console._vti_endpoint_client.UploadHostVersion( host.hostname, self.console.vtslab_version) elif server_type == "tfc": devices = host.ListDevices() for device in devices: device.Extend(['sim_state', 'sim_operator', 'mac_address']) snapshots = self.console._tfc_client.CreateDeviceSnapshot( host._cluster_ids[0], host.hostname, devices) self.console._tfc_client.SubmitHostEvents([snapshots]) else: logging.error("Error: unknown server_type %s for UpdateDevice", server_type)
def Run(self, arg_line): """Changes security patch level on a selected GSI file.""" args = self.arg_parser.ParseLine(arg_line) if args.gsi: if os.path.isfile(args.gsi): gsi_path = args.gsi else: logging.error("Cannot find system image in given path") return elif "system.img" in self.console.device_image_info: gsi_path = self.console.device_image_info["system.img"] else: logging.error("Cannot find system image.") return False if args.version: try: version_date = datetime.datetime.strptime( args.version, "%Y-%m-%d") version = "{:04d}-{:02d}-{:02d}".format( version_date.year, version_date.month, version_date.day) except ValueError as e: logging.error("version ID should be YYYY-mm-dd format.") return elif args.version_from_path: dest_path = None if os.path.isabs(args.version_from_path) and os.path.exists( args.version_from_path): img_path = args.version_from_path elif args.version_from_path in self.console.device_image_info: img_path = self.console.device_image_info[ args.version_from_path] elif (args.version_from_path == "boot.img" and "full-zipfile" in self.console.device_image_info): tempdir_base = os.path.join(os.getcwd(), "tmp") if not os.path.exists(tempdir_base): os.mkdir(tempdir_base) dest_path = tempfile.mkdtemp(dir=tempdir_base) with zipfile.ZipFile( self.console.device_image_info["full-zipfile"], 'r') as zip_ref: zip_ref.extractall(dest_path) img_path = os.path.join(dest_path, "boot.img") if not os.path.exists(img_path): logging.error("No %s file in device img .zip.", args.version_from_path) return else: logging.error("Cannot find %s file.", args.version_from_path) return False version_dict = img_utils.GetSPLVersionFromBootImg(img_path) if dest_path: shutil.rmtree(dest_path) if "year" in version_dict and "month" in version_dict: version = "{:04d}-{:02d}-{:02d}".format( version_dict["year"], version_dict["month"], common._SPL_DEFAULT_DAY) else: logging.error("Failed to fetch SPL version from %s file.", img_path) return False else: logging.error("version ID or path of .img file must be given.") return False output_path = os.path.join(os.path.dirname(os.path.abspath(gsi_path)), "system-{}.img".format(version)) command = "{} {} {} {}".format( os.path.join(os.getcwd(), "..", "bin", "change_security_patch_ver.sh"), gsi_path, output_path, version) if args.vendor_version: command = command + " -v " + args.vendor_version if self.console.password.value: command = "echo {} | sudo -S {}".format( self.console.password.value, command) stdout, stderr, err_code = cmd_utils.ExecuteOneShellCommand(command) if err_code is 0: if not args.gsi: logging.info( "system.img path is updated to : {}".format(output_path)) self.console.device_image_info["system.img"] = output_path else: logging.error("gsispl error: {} {}".format(stdout, stderr)) return False