1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""A temp file that automatically gets pushed and deleted from a device.""" 6 7# pylint: disable=W0622 8 9import logging 10import posixpath 11import random 12import threading 13 14from devil import base_error 15from devil.android import device_errors 16from devil.utils import cmd_helper 17 18logger = logging.getLogger(__name__) 19 20 21def _GenerateName(prefix, suffix, dir): 22 random_hex = hex(random.randint(0, 2 ** 52))[2:] 23 return posixpath.join(dir, '%s-%s%s' % (prefix, random_hex, suffix)) 24 25 26class DeviceTempFile(object): 27 """A named temporary file on a device. 28 29 Behaves like tempfile.NamedTemporaryFile. 30 """ 31 32 def __init__(self, adb, suffix='', prefix='temp_file', dir='/data/local/tmp'): 33 """Find an unused temporary file path on the device. 34 35 When this object is closed, the file will be deleted on the device. 36 37 Args: 38 adb: An instance of AdbWrapper 39 suffix: The suffix of the name of the temporary file. 40 prefix: The prefix of the name of the temporary file. 41 dir: The directory on the device in which the temporary file should be 42 placed. 43 Raises: 44 ValueError if any of suffix, prefix, or dir are None. 45 """ 46 if None in (dir, prefix, suffix): 47 m = 'Provided None path component. (dir: %s, prefix: %s, suffix: %s)' % ( 48 dir, prefix, suffix) 49 raise ValueError(m) 50 51 self._adb = adb 52 # Python's random module use 52-bit numbers according to its docs. 53 self.name = _GenerateName(prefix, suffix, dir) 54 self.name_quoted = cmd_helper.SingleQuote(self.name) 55 56 def close(self): 57 """Deletes the temporary file from the device.""" 58 # ignore exception if the file is already gone. 59 def delete_temporary_file(): 60 try: 61 self._adb.Shell('rm -f %s' % self.name_quoted, expect_status=None) 62 except base_error.BaseError as e: 63 # We don't really care, and stack traces clog up the log. 64 # Log a warning and move on. 65 logger.warning('Failed to delete temporary file %s: %s', 66 self.name, str(e)) 67 68 # It shouldn't matter when the temp file gets deleted, so do so 69 # asynchronously. 70 threading.Thread( 71 target=delete_temporary_file, 72 name='delete_temporary_file(%s)' % self._adb.GetDeviceSerial()).start() 73 74 def __enter__(self): 75 return self 76 77 def __exit__(self, type, value, traceback): 78 self.close() 79 80 81class NamedDeviceTemporaryDirectory(object): 82 """A named temporary directory on a device.""" 83 84 def __init__(self, adb, suffix='', prefix='tmp', dir='/data/local/tmp'): 85 """Find an unused temporary directory path on the device. The directory is 86 not created until it is used with a 'with' statement. 87 88 When this object is closed, the directory will be deleted on the device. 89 90 Args: 91 adb: An instance of AdbWrapper 92 suffix: The suffix of the name of the temporary directory. 93 prefix: The prefix of the name of the temporary directory. 94 dir: The directory on the device where to place the temporary directory. 95 Raises: 96 ValueError if any of suffix, prefix, or dir are None. 97 """ 98 self._adb = adb 99 self.name = _GenerateName(prefix, suffix, dir) 100 self.name_quoted = cmd_helper.SingleQuote(self.name) 101 102 def close(self): 103 """Deletes the temporary directory from the device.""" 104 def delete_temporary_dir(): 105 try: 106 self._adb.Shell('rm -rf %s' % self.name, expect_status=None) 107 except device_errors.AdbCommandFailedError: 108 pass 109 110 threading.Thread( 111 target=delete_temporary_dir, 112 name='delete_temporary_dir(%s)' % self._adb.GetDeviceSerial()).start() 113 114 def __enter__(self): 115 self._adb.Shell('mkdir -p %s' % self.name) 116 return self 117 118 def __exit__(self, exc_type, exc_val, exc_tb): 119 self.close() 120