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