[
  {
    "path": ".flake8",
    "content": "[flake8]\nignore = E501,W504\n"
  },
  {
    "path": ".gitattributes",
    "content": "Makefile eol=lf\n*.sh eol=lf\n*.ipynb eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n### Description\n\n<!--What is the bug and how to reproduce it-->\n\n### Log\n\n<!--A log from when the issue occurred-->\n\nTo enable debug logging in Python:\n\n```python\nimport logging\n\nlogging.basicConfig(level=logging.DEBUG)\n```\n\nTo enable debug logging in Home Assistant:\n\n#### Approach 1: configuration.yaml\n\n```yaml\nlogger:\n  default: warning  # or whatever\n    logs:\n      adb_shell: debug\n```\n\n#### Approach 2: `logger.set_level` service\n\n```yaml\nadb_shell: debug\n```\n"
  },
  {
    "path": ".github/workflows/python-package.yml",
    "content": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions\n\nname: Python package\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  ENV_GITHUB_ACTIONS: 'ENV_GITHUB_ACTIONS'\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v2\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        make venv\n    - name: Linting checks with pylint, flake8, and (soon) black\n      run: |\n        make lint-flake8 lint-pylint\n    - name: Test with pytest\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        COVERALLS_SERVICE_NAME: github\n      run: |\n        make coverage && coveralls\n    - name: Upload wheel as a workflow artifact\n      uses: actions/upload-artifact@v4\n      with:\n        name: wheel\n        path: dist/*.whl\n"
  },
  {
    "path": ".gitignore",
    "content": "# Python files\n*.idea\n*.pyc\n**/__pycache__/\nadb_shell.egg-info\n\n# Build files\nbuild/\ndist/\n\n# Documentation\ndocs/build/\ndocs/html/\n\n# Coverage\n.coverage\nhtmlcov/\n"
  },
  {
    "path": ".pylintrc",
    "content": "[MASTER]\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code.\nextension-pkg-whitelist=\n\n# Add files or directories to the blacklist. They should be base names, not\n# paths.\nignore=CVS\n\n# Add files or directories matching the regex patterns to the blacklist. The\n# regex matches against base names, not paths.\nignore-patterns=\n\n# Python code to execute, usually for sys.path manipulation such as\n# pygtk.require().\n#init-hook=\n\n# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the\n# number of processors available to use.\njobs=1\n\n# Control the amount of potential inferred values when inferring a single\n# object. This can help the performance when dealing with large functions or\n# complex, nested conditions.\nlimit-inference-results=100\n\n# List of plugins (as comma separated values of python modules names) to load,\n# usually to register additional checkers.\nload-plugins=\n\n# Pickle collected data for later comparisons.\npersistent=yes\n\n# Specify a configuration file.\n#rcfile=\n\n# When enabled, pylint would attempt to guess common misconfiguration and emit\n# user-friendly hints instead of false-positive error messages.\nsuggestion-mode=yes\n\n# Allow loading of arbitrary C extensions. Extensions are imported into the\n# active Python interpreter and may run arbitrary code.\nunsafe-load-any-extension=no\n\n\n[MESSAGES CONTROL]\n\n# Only show warnings with the listed confidence levels. Leave empty to show\n# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.\nconfidence=\n\n# Disable the message, report, category or checker with the given id(s). You\n# can either give multiple identifiers separated by comma (,) or put this\n# option multiple times (only on the command line, not in the configuration\n# file where it should appear only once). You can also use \"--disable=all\" to\n# disable everything first and then reenable specific checks. For example, if\n# you want to run only the similarities checker, you can use \"--disable=all\n# --enable=similarities\". If you want to run only the classes checker, but have\n# no Warning level messages displayed, use \"--disable=all --enable=classes\n# --disable=W\".\ndisable=consider-using-f-string,\n        duplicate-code,\n        invalid-name,\n        line-too-long,\n        raise-missing-from,\n        super-with-arguments,\n        too-many-arguments,\n        too-many-branches,\n        too-many-instance-attributes,\n        too-many-lines,\n        too-many-locals,\n        too-many-nested-blocks,\n        too-many-positional-arguments,\n        too-many-public-methods,\n        too-many-return-statements,\n        too-many-statements,\n        useless-object-inheritance,\n        unspecified-encoding\n\n# Enable the message, report, category or checker with the given id(s). You can\n# either give multiple identifier separated by comma (,) or put this option\n# multiple time (only on the command line, not in the configuration file where\n# it should appear only once). See also the \"--disable\" option for examples.\nenable=c-extension-no-member\n\n\n[REPORTS]\n\n# Python expression which should return a note less than 10 (10 is the highest\n# note). You have access to the variables errors warning, statement which\n# respectively contain the number of errors / warnings messages and the total\n# number of statements analyzed. This is used by the global evaluation report\n# (RP0004).\nevaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)\n\n# Template used to display messages. This is a python new-style format string\n# used to format the message information. See doc for all details.\n#msg-template=\n\n# Set the output format. Available formats are text, parseable, colorized, json\n# and msvs (visual studio). You can also give a reporter class, e.g.\n# mypackage.mymodule.MyReporterClass.\noutput-format=text\n\n# Tells whether to display a full report or only the messages.\nreports=no\n\n# Activate the evaluation score.\nscore=yes\n\n\n[REFACTORING]\n\n# Maximum number of nested blocks for function / method body\nmax-nested-blocks=5\n\n# Complete name of functions that never returns. When checking for\n# inconsistent-return-statements if a never returning function is called then\n# it will be considered as an explicit return statement and no message will be\n# printed.\nnever-returning-functions=sys.exit\n\n\n[LOGGING]\n\n# Format style used to check logging format string. `old` means using %\n# formatting, while `new` is for `{}` formatting.\nlogging-format-style=old\n\n# Logging modules to check that the string format arguments are in logging\n# function parameter format.\nlogging-modules=logging\n\n\n[FORMAT]\n\n# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.\nexpected-line-ending-format=\n\n# Regexp for a line that is allowed to be longer than the limit.\nignore-long-lines=^\\s*(# )?<?https?://\\S+>?$\n\n# Number of spaces of indent required inside a hanging or continued line.\nindent-after-paren=4\n\n# String used as indentation unit. This is usually \"    \" (4 spaces) or \"\\t\" (1\n# tab).\nindent-string='    '\n\n# Maximum number of characters on a single line.\nmax-line-length=100\n\n# Maximum number of lines in a module.\nmax-module-lines=1000\n\n# Allow the body of a class to be on the same line as the declaration if body\n# contains single statement.\nsingle-line-class-stmt=no\n\n# Allow the body of an if to be on the same line as the test if there is no\n# else.\nsingle-line-if-stmt=no\n\n\n[MISCELLANEOUS]\n\n# List of note tags to take in consideration, separated by a comma.\nnotes=FIXME,\n      XXX,\n      TODO\n\n\n[BASIC]\n\n# Naming style matching correct argument names.\nargument-naming-style=snake_case\n\n# Regular expression matching correct argument names. Overrides argument-\n# naming-style.\n#argument-rgx=\n\n# Naming style matching correct attribute names.\nattr-naming-style=snake_case\n\n# Regular expression matching correct attribute names. Overrides attr-naming-\n# style.\n#attr-rgx=\n\n# Bad variable names which should always be refused, separated by a comma.\nbad-names=foo,\n          bar,\n          baz,\n          toto,\n          tutu,\n          tata\n\n# Naming style matching correct class attribute names.\nclass-attribute-naming-style=any\n\n# Regular expression matching correct class attribute names. Overrides class-\n# attribute-naming-style.\n#class-attribute-rgx=\n\n# Naming style matching correct class names.\nclass-naming-style=PascalCase\n\n# Regular expression matching correct class names. Overrides class-naming-\n# style.\n#class-rgx=\n\n# Naming style matching correct constant names.\nconst-naming-style=UPPER_CASE\n\n# Regular expression matching correct constant names. Overrides const-naming-\n# style.\n#const-rgx=\n\n# Minimum line length for functions/classes that require docstrings, shorter\n# ones are exempt.\ndocstring-min-length=-1\n\n# Naming style matching correct function names.\nfunction-naming-style=snake_case\n\n# Regular expression matching correct function names. Overrides function-\n# naming-style.\n#function-rgx=\n\n# Good variable names which should always be accepted, separated by a comma.\ngood-names=i,\n           j,\n           k,\n           ex,\n           Run,\n           _\n\n# Include a hint for the correct naming format with invalid-name.\ninclude-naming-hint=no\n\n# Naming style matching correct inline iteration names.\ninlinevar-naming-style=any\n\n# Regular expression matching correct inline iteration names. Overrides\n# inlinevar-naming-style.\n#inlinevar-rgx=\n\n# Naming style matching correct method names.\nmethod-naming-style=snake_case\n\n# Regular expression matching correct method names. Overrides method-naming-\n# style.\n#method-rgx=\n\n# Naming style matching correct module names.\nmodule-naming-style=snake_case\n\n# Regular expression matching correct module names. Overrides module-naming-\n# style.\n#module-rgx=\n\n# Colon-delimited sets of names that determine each other's naming style when\n# the name regexes allow several styles.\nname-group=\n\n# Regular expression which should only match function or class names that do\n# not require a docstring.\nno-docstring-rgx=^_\n\n# List of decorators that produce properties, such as abc.abstractproperty. Add\n# to this list to register other decorators that produce valid properties.\n# These decorators are taken in consideration only for invalid-name.\nproperty-classes=abc.abstractproperty\n\n# Naming style matching correct variable names.\nvariable-naming-style=snake_case\n\n# Regular expression matching correct variable names. Overrides variable-\n# naming-style.\n#variable-rgx=\n\n\n[TYPECHECK]\n\n# List of decorators that produce context managers, such as\n# contextlib.contextmanager. Add to this list to register other decorators that\n# produce valid context managers.\ncontextmanager-decorators=contextlib.contextmanager\n\n# List of members which are set dynamically and missed by pylint inference\n# system, and so shouldn't trigger E1101 when accessed. Python regular\n# expressions are accepted.\ngenerated-members=\n\n# Tells whether missing members accessed in mixin class should be ignored. A\n# mixin class is detected if its name ends with \"mixin\" (case insensitive).\nignore-mixin-members=yes\n\n# Tells whether to warn about missing members when the owner of the attribute\n# is inferred to be None.\nignore-none=yes\n\n# This flag controls whether pylint should warn about no-member and similar\n# checks whenever an opaque object is returned when inferring. The inference\n# can return multiple potential results while evaluating a Python object, but\n# some branches might not be evaluated, which results in partial inference. In\n# that case, it might be useful to still emit no-member and other checks for\n# the rest of the inferred objects.\nignore-on-opaque-inference=yes\n\n# List of class names for which member attributes should not be checked (useful\n# for classes with dynamically set attributes). This supports the use of\n# qualified names.\nignored-classes=optparse.Values,thread._local,_thread._local,_socketobject\n\n# List of module names for which member attributes should not be checked\n# (useful for modules/projects where namespaces are manipulated during runtime\n# and thus existing member attributes cannot be deduced by static analysis. It\n# supports qualified module names, as well as Unix pattern matching.\nignored-modules=\n\n# Show a hint with possible names when a member name was not found. The aspect\n# of finding the hint is based on edit distance.\nmissing-member-hint=yes\n\n# The minimum edit distance a name should have in order to be considered a\n# similar match for a missing member name.\nmissing-member-hint-distance=1\n\n# The total number of similar names that should be taken in consideration when\n# showing a hint for a missing member.\nmissing-member-max-choices=1\n\n\n[STRING]\n\n# This flag controls whether the implicit-str-concat-in-sequence should\n# generate a warning on implicit string concatenation in sequences defined over\n# several lines.\ncheck-str-concat-over-line-jumps=no\n\n\n[SPELLING]\n\n# Limits count of emitted suggestions for spelling mistakes.\nmax-spelling-suggestions=4\n\n# Spelling dictionary name. Available dictionaries: none. To make it working\n# install python-enchant package..\nspelling-dict=\n\n# List of comma separated words that should not be checked.\nspelling-ignore-words=\n\n# A path to a file that contains private dictionary; one word per line.\nspelling-private-dict-file=\n\n# Tells whether to store unknown words to indicated private dictionary in\n# --spelling-private-dict-file option instead of raising a message.\nspelling-store-unknown-words=no\n\n\n[SIMILARITIES]\n\n# Ignore comments when computing similarities.\nignore-comments=yes\n\n# Ignore docstrings when computing similarities.\nignore-docstrings=yes\n\n# Ignore imports when computing similarities.\nignore-imports=no\n\n# Minimum lines number of a similarity.\nmin-similarity-lines=4\n\n\n[VARIABLES]\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid defining new builtins when possible.\nadditional-builtins=\n\n# Tells whether unused global variables should be treated as a violation.\nallow-global-unused-variables=yes\n\n# List of strings which can identify a callback function by name. A callback\n# name must start or end with one of those strings.\ncallbacks=cb_,\n          _cb\n\n# A regular expression matching the name of dummy variables (i.e. expected to\n# not be used).\ndummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_\n\n# Argument names that match this expression will be ignored. Default to name\n# with leading underscore.\nignored-argument-names=_.*|^ignored_|^unused_\n\n# Tells whether we should check for unused import in __init__ files.\ninit-import=no\n\n# List of qualified module names which can have objects that can redefine\n# builtins.\nredefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io\n\n\n[DESIGN]\n\n# Maximum number of arguments for function / method.\nmax-args=5\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=7\n\n# Maximum number of boolean expressions in an if statement.\nmax-bool-expr=5\n\n# Maximum number of branch for function / method body.\nmax-branches=12\n\n# Maximum number of locals for function / method body.\nmax-locals=15\n\n# Maximum number of parents for a class (see R0901).\nmax-parents=7\n\n# Maximum number of public methods for a class (see R0904).\nmax-public-methods=20\n\n# Maximum number of return / yield for function / method body.\nmax-returns=6\n\n# Maximum number of statements in function / method body.\nmax-statements=50\n\n# Minimum number of public methods for a class (see R0903).\nmin-public-methods=2\n\n\n[IMPORTS]\n\n# Allow wildcard imports from modules that define __all__.\nallow-wildcard-with-all=no\n\n# Analyse import fallback blocks. This can be used to support both Python 2 and\n# 3 compatible code, which means that the block might have code that exists\n# only in one or another interpreter, leading to false positives when analysed.\nanalyse-fallback-blocks=no\n\n# Deprecated modules which should not be used, separated by a comma.\ndeprecated-modules=optparse,tkinter.tix\n\n# Create a graph of external dependencies in the given file (report RP0402 must\n# not be disabled).\next-import-graph=\n\n# Create a graph of every (i.e. internal and external) dependencies in the\n# given file (report RP0402 must not be disabled).\nimport-graph=\n\n# Create a graph of internal dependencies in the given file (report RP0402 must\n# not be disabled).\nint-import-graph=\n\n# Force import order to recognize a module as part of the standard\n# compatibility libraries.\nknown-standard-library=\n\n# Force import order to recognize a module as part of a third party library.\nknown-third-party=enchant\n\n\n[CLASSES]\n\n# List of method names used to declare (i.e. assign) instance attributes.\ndefining-attr-methods=__init__,\n                      __new__,\n                      setUp\n\n# List of member names, which should be excluded from the protected access\n# warning.\nexclude-protected=_asdict,\n                  _fields,\n                  _replace,\n                  _source,\n                  _make\n\n# List of valid names for the first argument in a class method.\nvalid-classmethod-first-arg=cls\n\n# List of valid names for the first argument in a metaclass class method.\nvalid-metaclass-classmethod-first-arg=cls\n\n\n[EXCEPTIONS]\n\n# Exceptions that will emit a warning when being caught. Defaults to\n# \"BaseException, Exception\".\novergeneral-exceptions=builtins.Exception\n"
  },
  {
    "path": ".readthedocs.yml",
    "content": "# .readthedocs.yml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n\n# Required\nversion: 2\n\n# Set the OS\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.11\"\n\n# Build documentation in the docs/ directory with Sphinx\nsphinx:\n  configuration: docs/source/conf.py\n\n# Optionally build your docs in additional formats such as PDF and ePub\nformats: all\n\n# Optionally set the version of Python and requirements required to build your docs\npython:\n  install:\n    - requirements: docs/requirements.txt\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\npython:\n  - \"2.7\"\n  - \"3.5\"\n  - \"3.6\"\n  - \"3.7\"\n  - \"3.8\"\n  - \"3.9\"\naddons:\n  apt:\n    packages:\n      - swig\n      - libusb-1.0-0-dev\ninstall:\n  - pip install .\n  - pip install flake8 pylint coveralls cryptography libusb1>=1.0.16 pycryptodome\n  - python --version 2>&1 | grep -q \"Python 2\" && pip install mock || true\n  - if python --version 2>&1 | grep -q \"Python 3.7\" || python --version 2>&1 | grep -q \"Python 3.8\" || python --version 2>&1 | grep -q \"Python 3.9\"; then pip install aiofiles; fi\nscript:\n  - if python --version 2>&1 | grep -q \"Python 2\" || python --version 2>&1 | grep -q \"Python 3.5\" || python --version 2>&1 | grep -q \"Python 3.6\"; then flake8 adb_shell/ --exclude=\"adb_shell/adb_device_async.py,adb_shell/transport/base_transport_async.py,adb_shell/transport/tcp_transport_async.py\" && pylint --ignore=\"adb_device_async.py,base_transport_async.py,tcp_transport_async.py\" adb_shell/; fi\n  - if python --version 2>&1 | grep -q \"Python 3.7\" || python --version 2>&1 | grep -q \"Python 3.8\" || python --version 2>&1 | grep -q \"Python 3.9\"; then flake8 adb_shell/ && pylint adb_shell/; fi\n  - if python --version 2>&1 | grep -q \"Python 2\" || python --version 2>&1 | grep -q \"Python 3.5\" || python --version 2>&1 | grep -q \"Python 3.6\"; then for synctest in $(cd tests && ls test*.py | grep -v async); do python -m unittest discover -s tests/ -t . -p \"$synctest\" || exit 1; done; fi\n  - if python --version 2>&1 | grep -q \"Python 3.7\" || python --version 2>&1 | grep -q \"Python 3.8\" || python --version 2>&1 | grep -q \"Python 3.9\"; then coverage run --source adb_shell -m unittest discover -s tests/ -t . && coverage report -m; fi\nafter_success:\n  - if python --version 2>&1 | grep -q \"Python 3.7\" || python --version 2>&1 | grep -q \"Python 3.8\" || python --version 2>&1 | grep -q \"Python 3.9\"; then coveralls; fi\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include LICENSE\n"
  },
  {
    "path": "Makefile",
    "content": "#-------------------- ONLY MODIFY CODE IN THIS SECTION --------------------#\nPACKAGE_DIR := adb_shell\nTEST_DIR := tests\nDOCS_DIR := docs\n\n# Change to false if you don't want to use pytest\nUSE_PYTEST := true\n\n# Change this to false if you don't want to run linting checks on the tests\nLINT_TEST_DIR := false\n#-------------------- DO NOT MODIFY CODE BELOW!!!!!!!! --------------------#\n\nexport PATH := $(abspath venv)/bin:${PATH}\n\n# Binaries to run\nBLACK := $(abspath venv)/bin/black\nCOVERAGE := $(abspath venv)/bin/coverage\nFLAKE8 := $(abspath venv)/bin/flake8\nPIP := $(abspath venv)/bin/pip3\nPYLINT := $(abspath venv)/bin/pylint\nPYTEST := $(abspath venv)/bin/pytest\nPYTHON := $(abspath venv)/bin/python\nSPHINX_APIDOC := $(abspath venv)/bin/sphinx-apidoc\nTWINE := $(abspath venv)/bin/twine\n\n# Whether to include \"*_async.py\" files\nINCLUDE_ASYNC = $(shell $(PYTHON) --version | grep -q \"Python 3.[7891]\" && echo \"true\" || echo \"false\")\n\n# Async vs. Sync files\nPACKAGE_ASYNC_FILES = $(shell ls -m $(PACKAGE_DIR)/*_async.py 2>/dev/null)\nTEST_ASYNC_FILES = $(shell ls -m $(TEST_DIR)/*_async.py 2>/dev/null)\nTEST_SYNC_FILES = $(shell cd $(TEST_DIR) && ls test*.py | grep -v async)\n\n# Target prerequisites that may or may not exist\nVENV_REQUIREMENTS_TXT := $(wildcard venv_requirements.txt)\nSETUP_PY := $(wildcard setup.py)\n\n\n# A prerequisite for forcing targets to run\nFORCE:\n\n# Help!\nhelp:  ## Show this help menu\n\t@echo \"\\n\\033[1mUsage:\\033[0m\"; \\\n\tawk -F ':|##' '/^[^\\t].+?:.*?##/ { printf \"\\033[36m  make %-20s\\033[0m %s\\n\", $$1, $$NF }' $(MAKEFILE_LIST) | grep -v \"make venv/\\.\" | sort\n\t@echo \"\"\n\t@echo \"NOTES:\"\n\t@echo \"- The 'venv/.bin' target may fail because newer Python versions include the 'venv' package.  Follow the instructions to create the virtual environment manually.\"\nifneq (\"$(wildcard scripts/pre-commit.sh)\", \"\")\n\t@echo \"- To install the git pre-commit hook:\\n\\n    scripts/pre-commit.sh\\n\"\nendif\n\t@echo \"- You may need to activate the virtual environment prior to running any Make commands:\\n\\n    source venv/bin/activate\\n\"\n\n\n# Virtual environment targets\n.PHONY: clean-venv\nclean-venv:  ## Remove the virtual environment\n\trm -rf venv\n\nvenv: venv/.bin venv/.requirements venv/.setup .git/hooks/pre-commit  ## Create the virtual environment and install all necessary packages\n\nvenv/.bin:  ## Create the virtual environment\n\tif [ -z \"$$ENV_GITHUB_ACTIONS\" ]; then \\\n\t  echo -e \"If this target fails, you can perform this action manually via:\\n\\n    make clean-venv && python3 -m venv venv && source venv/bin/activate && pip install -U setuptools && echo -e '*.*\\\\\\n**/' > venv/.gitignore && touch venv/.bin\\n\\n\"; \\\n\t  apt list -a --installed python3-venv 2>&1 | grep -q installed || sudo apt update && sudo apt install python3-venv; \\\n\t  python3 -m venv venv; \\\n\t  $(PIP) install -U setuptools; \\\n\telse \\\n\t  mkdir -p venv/bin; \\\n\t  ln -s $$(which pip) $(PIP); \\\n\t  ln -s $$(which python) $(PYTHON); \\\n\tfi\n\tmkdir -p venv/bin\n\techo '*.*\\n**/' > venv/.gitignore\n\ttouch venv/.bin\n\nvenv/.requirements: venv/.bin $(VENV_REQUIREMENTS_TXT)  ## Install the requirements from 'venv_requirements.txt' in the virtual environment\nifneq (\"$(wildcard venv_requirements.txt)\", \"\")\n\t$(PIP) install -U -r venv_requirements.txt\n\tif ! [ -z \"$$ENV_GITHUB_ACTIONS\" ]; then \\\n\t  ln -s $$(which black) $(BLACK); \\\n\t  ln -s $$(which coverage) $(COVERAGE); \\\n\t  ln -s $$(which flake8) $(FLAKE8); \\\n\t  ln -s $$(which pylint) $(PYLINT); \\\n\t  ln -s $$(which pytest) $(PYTEST); \\\n\t  ln -s $$(which sphinx-apidoc) $(SPHINX_APIDOC); \\\n\t  ln -s $$(which twine) $(TWINE); \\\n\tfi\nendif\n\ttouch venv/.requirements\n\n# Install the package in the virtual environment\nvenv/.setup: venv/.bin $(SETUP_PY)\nifneq (\"$(wildcard setup.py)\", \"\")\n\t$(PIP) install .\nendif\n\ttouch venv/.setup\n\n.PHONY: uninstall\nuninstall:\n\trm -f venv/.setup\n\n.PHONY: install\ninstall: uninstall venv/.setup  ## Install the package in the virtual environment\n\n# Create the pre-commit hook\n.git/hooks/pre-commit:\n\t./scripts/pre-commit.sh MAKE_PRECOMMIT_HOOK\n\n.PHONY: pre-commit\npre-commit: .git/hooks/pre-commit  ## Create the pre-commit hook\n\n# Linting and code analysis\n.PHONY: black\nblack: venv  ## Format the code using black\n\t$(BLACK) --safe --line-length 120 --target-version py35 $(PACKAGE_DIR)\n\t$(BLACK) --safe --line-length 120 --target-version py35 $(TEST_DIR)\nifneq (\"$(wildcard setup.py)\", \"\")\n\t$(BLACK) --safe --line-length 120 --target-version py35 setup.py\nendif\n\n.PHONY: lint-black\nlint-black: venv  ## Check that the code is formatted using black\n\t$(BLACK) --check --line-length 120 --safe --target-version py35 $(PACKAGE_DIR)\n\t$(BLACK) --check --line-length 120 --safe --target-version py35 $(TEST_DIR)\nifneq (\"$(wildcard setup.py)\", \"\")\n\t$(BLACK) --check --line-length 120 --safe --target-version py35 setup.py\nendif\n\n.PHONY: lint-flake8\nlint-flake8: venv  ## Check the code using flake8\nifeq ($(INCLUDE_ASYNC), true)\n\t$(FLAKE8) $(PACKAGE_DIR)\nifeq ($(LINT_TEST_DIR), true)\n\t$(FLAKE8) $(TEST_DIR)\nendif\nelse\n\t$(FLAKE8) $(PACKAGE_DIR) --exclude=\"$(PACKAGE_ASYNC_FILES)\"\nifeq ($(LINT_TEST_DIR), true)\n\t$(FLAKE8) $(TEST_DIR) --exclude=\"$(TEST_ASYNC_FILES)\"\nendif\nendif\nifneq (\"$(wildcard setup.py)\", \"\")\n\t$(FLAKE8) setup.py\nendif\n\n.PHONY: lint-pylint\nlint-pylint: venv  ## Check the code using pylint\nifeq ($(INCLUDE_ASYNC), true)\n\t$(PYLINT) $(PACKAGE_DIR)\nifeq ($(LINT_TEST_DIR), true)\n\t$(PYLINT) $(TEST_DIR)\nendif\nelse\n\t$(PYLINT) $(PACKAGE_DIR) --ignore=\"$(PACKAGE_ASYNC_FILES)\"\nifeq ($(LINT_TEST_DIR), true)\n\t$(PYLINT) $(TEST_DIR) --ignore=\"$(TEST_ASYNC_FILES)\"\nendif\nendif\nifneq (\"$(wildcard setup.py)\", \"\")\n\t$(PYLINT) setup.py\nendif\n\n.PHONY: lint\nlint: lint-black lint-flake8 lint-pylint  ## Run all linting checks on the code\n\n\n# Testing and coverage.\n.PHONY: test\ntest: venv  ## Run the unit tests\nifeq ($(INCLUDE_ASYNC), true)\nifeq ($(USE_PYTEST), true)\n\t$(PYTEST) $(TEST_DIR)\nelse\n\t$(PYTHON) -m unittest discover -s $(TEST_DIR)/ -t .\nendif\nelse\nifeq ($(USE_PYTEST), true)\n\t$(PYTEST) $(TEST_DIR) --ignore-glob=\"*async.py\"\nelse\n\tfor synctest in $(TEST_SYNC_FILES); do echo \"\\033[1;32m$(TEST_DIR)/$$synctest\\033[0m\" && $(PYTHON) -m unittest \"$(TEST_DIR)/$$synctest\"; done\nendif\nendif\n\n.PHONY: coverage\ncoverage: venv  ## Run the unit tests and produce coverage info\nifeq ($(INCLUDE_ASYNC), true)\nifeq ($(USE_PYTEST), true)\n\t$(COVERAGE) run --source $(PACKAGE_DIR) -m pytest $(TEST_DIR)/ && $(COVERAGE) report -m\nelse\n\t$(COVERAGE) run --source $(PACKAGE_DIR) -m unittest discover -s $(TEST_DIR) -t . && $(COVERAGE) report -m\nendif\nelse\nifeq ($(USE_PYTEST), true)\n\t$(COVERAGE) run --source $(PACKAGE_DIR) -m pytest $(TEST_DIR)/ --ignore-glob=\"*async.py\" && $(COVERAGE) report -m\nelse\n\tfor synctest in $(TEST_SYNC_FILES); do echo \"\\033[1;32m$(TEST_DIR)/$$synctest\\033[0m\" && $(COVERAGE) run --source $(PACKAGE_DIR) -m unittest \"$(TEST_DIR)/$$synctest\"; done\n\t$(COVERAGE) report -m\nendif\nendif\n\n.PHONY: htmlcov\nhtmlcov: coverage  ## Produce a coverage report\n\t$(COVERAGE) html\n\n\n# Documentation\n.PHONY: docs\ndocs: venv  ## Build the documentation\n\trm -rf $(DOCS_DIR)/build\n\t@cd $(DOCS_DIR) && $(SPHINX_APIDOC) -f -e -o source/ $(CURDIR)/$(PACKAGE_DIR)/\n\t@cd $(DOCS_DIR) && make html && make html\n\n\n.PHONY: release\nrelease:  ## Make a release and upload it to pypi\n\trm -rf dist\n\tscripts/git_tag.sh\n\t$(PYTHON) setup.py sdist bdist_wheel\n\t$(TWINE) upload dist/*\n\n\n.PHONY: all\nall: lint htmlcov  ## Run all linting checks and unit tests and produce a coverage report\n"
  },
  {
    "path": "README.rst",
    "content": "adb\\_shell\n==========\n\n.. image:: https://travis-ci.com/JeffLIrion/adb_shell.svg?branch=master\n   :target: https://travis-ci.com/JeffLIrion/adb_shell\n\n.. image:: https://coveralls.io/repos/github/JeffLIrion/adb_shell/badge.svg?branch=master\n   :target: https://coveralls.io/github/JeffLIrion/adb_shell?branch=master\n\n.. image:: https://pepy.tech/badge/adb-shell\n   :target: https://pepy.tech/project/adb-shell\n\n\nDocumentation for this package can be found at https://adb-shell.readthedocs.io/.\n\nPrebuilt wheel can be downloaded from `nightly.link <https://nightly.link/JeffLIrion/adb_shell/workflows/python-package/master/wheel.zip>`_.\n\nThis Python package implements ADB shell and FileSync functionality.  It originated from `python-adb <https://github.com/google/python-adb>`_.\n\nInstallation\n------------\n\n.. code-block::\n\n   pip install adb-shell\n\n\nAsync\n*****\n\nTo utilize the async version of this code, you must install into a Python 3.7+ environment via:\n\n.. code-block::\n\n   pip install adb-shell[async]\n\n\nUSB Support (Experimental)\n**************************\n\nTo connect to a device via USB, install this package via:\n\n.. code-block::\n\n   pip install adb-shell[usb]\n\n\nExample Usage\n-------------\n\n(Based on `androidtv/adb_manager.py <https://github.com/JeffLIrion/python-androidtv/blob/133063c8d6793a88259af405d6a69ceb301a0ca0/androidtv/adb_manager.py#L67>`_)\n\n.. code-block:: python\n\n   from adb_shell.adb_device import AdbDeviceTcp, AdbDeviceUsb\n   from adb_shell.auth.sign_pythonrsa import PythonRSASigner\n\n   # Load the public and private keys\n   adbkey = 'path/to/adbkey'\n   with open(adbkey) as f:\n       priv = f.read()\n   with open(adbkey + '.pub') as f:\n        pub = f.read()\n   signer = PythonRSASigner(pub, priv)\n\n   # Connect\n   device1 = AdbDeviceTcp('192.168.0.222', 5555, default_transport_timeout_s=9.)\n   device1.connect(rsa_keys=[signer], auth_timeout_s=0.1)\n\n   # Connect via USB (package must be installed via `pip install adb-shell[usb])`\n   device2 = AdbDeviceUsb()\n   device2.connect(rsa_keys=[signer], auth_timeout_s=0.1)\n\n   # Send a shell command\n   response1 = device1.shell('echo TEST1')\n   response2 = device2.shell('echo TEST2')\n\n\nGenerate ADB Key Files\n**********************\n\nIf you need to generate a key, you can do so as follows.\n\n.. code-block:: python\n\n  from adb_shell.auth.keygen import keygen\n\n  keygen('path/to/adbkey')\n"
  },
  {
    "path": "adb_shell/__init__.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.\n\n\"\"\"ADB shell functionality.\n\n\"\"\"\n\n\n__version__ = \"0.4.4\"\n"
  },
  {
    "path": "adb_shell/adb_device.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It incorporates work\n# covered by the following license notice:\n#\n#\n#   Copyright 2014 Google Inc. All rights reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\n\"\"\"Implement the :class:`AdbDevice` class, which can connect to a device and run ADB shell commands.\n\n.. rubric:: Contents\n\n* :class:`_AdbIOManager`\n\n    * :meth:`_AdbIOManager._read_bytes_from_device`\n    * :meth:`_AdbIOManager._read_expected_packet_from_device`\n    * :meth:`_AdbIOManager._read_packet_from_device`\n    * :meth:`_AdbIOManager._send`\n    * :meth:`_AdbIOManager.close`\n    * :meth:`_AdbIOManager.connect`\n    * :meth:`_AdbIOManager.read`\n    * :meth:`_AdbIOManager.send`\n\n* :func:`_open_bytesio`\n\n* :class:`AdbDevice`\n\n    * :meth:`AdbDevice._clse`\n    * :meth:`AdbDevice._filesync_flush`\n    * :meth:`AdbDevice._filesync_read`\n    * :meth:`AdbDevice._filesync_read_buffered`\n    * :meth:`AdbDevice._filesync_read_until`\n    * :meth:`AdbDevice._filesync_send`\n    * :meth:`AdbDevice._okay`\n    * :meth:`AdbDevice._open`\n    * :meth:`AdbDevice._pull`\n    * :meth:`AdbDevice._push`\n    * :meth:`AdbDevice._read_until`\n    * :meth:`AdbDevice._read_until_close`\n    * :meth:`AdbDevice._service`\n    * :meth:`AdbDevice._streaming_command`\n    * :meth:`AdbDevice._streaming_service`\n    * :attr:`AdbDevice.available`\n    * :meth:`AdbDevice.close`\n    * :meth:`AdbDevice.connect`\n    * :meth:`AdbDevice.list`\n    * :attr:`AdbDevice.max_chunk_size`\n    * :meth:`AdbDevice.pull`\n    * :meth:`AdbDevice.push`\n    * :meth:`AdbDevice.root`\n    * :meth:`AdbDevice.shell`\n    * :meth:`AdbDevice.stat`\n    * :meth:`AdbDevice.streaming_shell`\n\n* :class:`AdbDeviceTcp`\n* :class:`AdbDeviceUsb`\n\n\"\"\"\n\n\nfrom contextlib import contextmanager\nfrom io import BytesIO\nimport logging\nimport os\nimport struct\nimport sys\nfrom threading import Lock\nimport time\n\nfrom . import constants\nfrom . import exceptions\nfrom .adb_message import AdbMessage, checksum, int_to_cmd, unpack\nfrom .transport.base_transport import BaseTransport\nfrom .transport.tcp_transport import TcpTransport\nfrom .hidden_helpers import DeviceFile, _AdbPacketStore, _AdbTransactionInfo, _FileSyncTransactionInfo, get_banner, get_files_to_push\n\ntry:\n    from .transport.usb_transport import UsbTransport\nexcept (ImportError, OSError):\n    UsbTransport = None\n\n\n_LOGGER = logging.getLogger(__name__)\n\n_DECODE_ERRORS = \"backslashreplace\" if sys.version_info[0] > 2 else \"replace\"\n\n\n@contextmanager\ndef _open_bytesio(stream, *args, **kwargs):  # pylint: disable=unused-argument\n    \"\"\"A context manager for a BytesIO object that does nothing.\n\n    Parameters\n    ----------\n    stream : BytesIO\n        The BytesIO stream\n    args : list\n        Unused positional arguments\n    kwargs : dict\n        Unused keyword arguments\n\n    Yields\n    ------\n    stream : BytesIO\n        The `stream` input parameter\n\n    \"\"\"\n    yield stream\n\n\nclass _AdbIOManager(object):\n    \"\"\"A class for handling all ADB I/O.\n\n    Notes\n    -----\n    When the ``self._store_lock`` and ``self._transport_lock`` locks are held at the same time, it must always be the\n    case that the ``self._transport_lock`` is acquired first.  This ensures that  there is no potential for deadlock.\n\n    Parameters\n    ----------\n    transport : BaseTransport\n        A transport for communicating with the device; must be an instance of a subclass of :class:`~adb_shell.transport.base_transport.BaseTransport`\n\n    Attributes\n    ----------\n    _packet_store : _AdbPacketStore\n        A store for holding packets that correspond to different ADB streams\n    _store_lock : Lock\n        A lock for protecting ``self._packet_store`` (this lock is never held for long)\n    _transport : BaseTransport\n        A transport for communicating with the device; must be an instance of a subclass of :class:`~adb_shell.transport.base_transport.BaseTransport`\n    _transport_lock : Lock\n        A lock for protecting ``self._transport``\n\n    \"\"\"\n\n    def __init__(self, transport):\n        self._packet_store = _AdbPacketStore()\n        self._transport = transport\n\n        self._store_lock = Lock()\n        self._transport_lock = Lock()\n\n    def close(self):\n        \"\"\"Close the connection via the provided transport's ``close()`` method and clear the packet store.\n\n        \"\"\"\n        with self._transport_lock:\n            self._transport.close()\n\n            with self._store_lock:\n                self._packet_store.clear_all()\n\n    def connect(self, banner, rsa_keys, auth_timeout_s, auth_callback, adb_info):\n        \"\"\"Establish an ADB connection to the device.\n\n        1. Use the transport to establish a connection\n        2. Send a ``b'CNXN'`` message\n        3. Read the response from the device\n        4. If ``cmd`` is not ``b'AUTH'``, then authentication is not necesary and so we are done\n        5. If no ``rsa_keys`` are provided, raise an exception\n        6. Loop through our keys, signing the last ``banner2`` that we received\n\n            1. If the last ``arg0`` was not :const:`adb_shell.constants.AUTH_TOKEN`, raise an exception\n            2. Sign the last ``banner2`` and send it in an ``b'AUTH'`` message\n            3. Read the response from the device\n            4. If ``cmd`` is ``b'CNXN'``, we are done\n\n        7. None of the keys worked, so send ``rsa_keys[0]``'s public key; if the response does not time out, we must have connected successfully\n\n\n        Parameters\n        ----------\n        banner : bytearray, bytes\n            The hostname of the machine where the Python interpreter is currently running (:attr:`adb_shell.adb_device.AdbDevice._banner`)\n        rsa_keys : list, None\n            A list of signers of type :class:`~adb_shell.auth.sign_cryptography.CryptographySigner`,\n            :class:`~adb_shell.auth.sign_pycryptodome.PycryptodomeAuthSigner`, or :class:`~adb_shell.auth.sign_pythonrsa.PythonRSASigner`\n        auth_timeout_s : float, None\n            The time in seconds to wait for a ``b'CNXN'`` authentication response\n        auth_callback : function, None\n            Function callback invoked when the connection needs to be accepted on the device\n        adb_info : _AdbTransactionInfo\n            Info and settings for this connection attempt\n\n        Returns\n        -------\n        bool\n            Whether the connection was established\n        maxdata : int\n            Maximum amount of data in an ADB packet\n\n        Raises\n        ------\n        adb_shell.exceptions.DeviceAuthError\n            Device authentication required, no keys available\n        adb_shell.exceptions.InvalidResponseError\n            Invalid auth response from the device\n\n        \"\"\"\n        with self._transport_lock:\n            # 0. Close the connection and clear the store\n            self._transport.close()\n\n            with self._store_lock:\n                # We can release this lock because packets are only added to the store when the transport lock is held\n                self._packet_store.clear_all()\n\n            # 1. Use the transport to establish a connection\n            self._transport.connect(adb_info.transport_timeout_s)\n\n            # 2. Send a ``b'CNXN'`` message\n            msg = AdbMessage(constants.CNXN, constants.VERSION, constants.MAX_ADB_DATA, b'host::%s\\0' % banner)\n            self._send(msg, adb_info)\n\n            # 3. Read the response from the device\n            cmd, arg0, maxdata, banner2 = self._read_expected_packet_from_device([constants.AUTH, constants.CNXN], adb_info)\n\n            # 4. If ``cmd`` is not ``b'AUTH'``, then authentication is not necesary and so we are done\n            if cmd != constants.AUTH:\n                return True, maxdata\n\n            # 5. If no ``rsa_keys`` are provided, raise an exception\n            if not rsa_keys:\n                self._transport.close()\n                raise exceptions.DeviceAuthError('Device authentication required, no keys available.')\n\n            # 6. Loop through our keys, signing the last ``banner2`` that we received\n            for rsa_key in rsa_keys:\n                # 6.1. If the last ``arg0`` was not :const:`adb_shell.constants.AUTH_TOKEN`, raise an exception\n                if arg0 != constants.AUTH_TOKEN:\n                    self._transport.close()\n                    raise exceptions.InvalidResponseError('Unknown AUTH response: %s %s %s' % (arg0, maxdata, banner2))\n\n                # 6.2. Sign the last ``banner2`` and send it in an ``b'AUTH'`` message\n                signed_token = rsa_key.Sign(banner2)\n                msg = AdbMessage(constants.AUTH, constants.AUTH_SIGNATURE, 0, signed_token)\n                self._send(msg, adb_info)\n\n                # 6.3. Read the response from the device\n                cmd, arg0, maxdata, banner2 = self._read_expected_packet_from_device([constants.CNXN, constants.AUTH], adb_info)\n\n                # 6.4. If ``cmd`` is ``b'CNXN'``, we are done\n                if cmd == constants.CNXN:\n                    return True, maxdata\n\n            # 7. None of the keys worked, so send ``rsa_keys[0]``'s public key; if the response does not time out, we must have connected successfully\n            pubkey = rsa_keys[0].GetPublicKey()\n            if not isinstance(pubkey, (bytes, bytearray)):\n                pubkey = bytearray(pubkey, 'utf-8')\n\n            if auth_callback is not None:\n                auth_callback(self)\n\n            msg = AdbMessage(constants.AUTH, constants.AUTH_RSAPUBLICKEY, 0, pubkey + b'\\0')\n            self._send(msg, adb_info)\n\n            adb_info.transport_timeout_s = auth_timeout_s\n            _, _, maxdata, _ = self._read_expected_packet_from_device([constants.CNXN], adb_info)\n            return True, maxdata\n\n    def read(self, expected_cmds, adb_info, allow_zeros=False):\n        \"\"\"Read packets from the device until we get an expected packet type.\n\n        1. See if the expected packet is in the packet store\n        2. While the time limit has not been exceeded:\n\n            1. See if the expected packet is in the packet store\n            2. Read a packet from the device.  If it matches what we are looking for, we are done.  If it corresponds to a different stream, add it to the store.\n\n        3. Raise a timeout exception\n\n\n        Parameters\n        ----------\n        expected_cmds : list[bytes]\n            We will read packets until we encounter one whose \"command\" field is in ``expected_cmds``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        allow_zeros : bool\n            Whether to allow the received ``arg0`` and ``arg1`` values to match with 0, in addition to ``adb_info.remote_id`` and ``adb_info.local_id``, respectively\n\n        Returns\n        -------\n        cmd : bytes\n            The received command, which is in :const:`adb_shell.constants.WIRE_TO_ID` and must be in ``expected_cmds``\n        arg0 : int\n            TODO\n        arg1 : int\n            TODO\n        data : bytes\n            The data that was read\n\n        Raises\n        ------\n        adb_shell.exceptions.AdbTimeoutError\n            Never got one of the expected responses\n\n        \"\"\"\n        # First, try reading from the store. This way, you won't be waiting for the transport if it isn't needed\n        with self._store_lock:\n            # Recall that `arg0` from the device corresponds to `adb_info.remote_id` and `arg1` from the device corresponds to `adb_info.local_id`\n            arg0_arg1 = self._packet_store.find(adb_info.remote_id, adb_info.local_id) if not allow_zeros else self._packet_store.find_allow_zeros(adb_info.remote_id, adb_info.local_id)\n            while arg0_arg1:\n                cmd, arg0, arg1, data = self._packet_store.get(arg0_arg1[0], arg0_arg1[1])\n                if cmd in expected_cmds:\n                    return cmd, arg0, arg1, data\n\n                arg0_arg1 = self._packet_store.find(adb_info.remote_id, adb_info.local_id) if not allow_zeros else self._packet_store.find_allow_zeros(adb_info.remote_id, adb_info.local_id)\n\n        # Start the timer\n        start = time.time()\n\n        while True:\n            with self._transport_lock:\n                # Try reading from the store (again) in case a packet got added while waiting to acquire the transport lock\n                with self._store_lock:\n                    # Recall that `arg0` from the device corresponds to `adb_info.remote_id` and `arg1` from the device corresponds to `adb_info.local_id`\n                    arg0_arg1 = self._packet_store.find(adb_info.remote_id, adb_info.local_id) if not allow_zeros else self._packet_store.find_allow_zeros(adb_info.remote_id, adb_info.local_id)\n                    while arg0_arg1:\n                        cmd, arg0, arg1, data = self._packet_store.get(arg0_arg1[0], arg0_arg1[1])\n                        if cmd in expected_cmds:\n                            return cmd, arg0, arg1, data\n\n                        arg0_arg1 = self._packet_store.find(adb_info.remote_id, adb_info.local_id) if not allow_zeros else self._packet_store.find_allow_zeros(adb_info.remote_id, adb_info.local_id)\n\n                # Read from the device\n                cmd, arg0, arg1, data = self._read_packet_from_device(adb_info)\n\n                if not adb_info.args_match(arg0, arg1, allow_zeros):\n                    # The packet is not a match -> put it in the store\n                    with self._store_lock:\n                        self._packet_store.put(arg0, arg1, cmd, data)\n\n                else:\n                    # The packet is a match for this `(adb_info.local_id, adb_info.remote_id)` pair\n                    if cmd == constants.CLSE:\n                        # Clear the entry in the store\n                        with self._store_lock:\n                            self._packet_store.clear(arg0, arg1)\n\n                    # If `cmd` is a match, then we are done\n                    if cmd in expected_cmds:\n                        return cmd, arg0, arg1, data\n\n            # Check if time is up\n            if time.time() - start > adb_info.read_timeout_s:\n                break\n\n        # Timeout\n        raise exceptions.AdbTimeoutError(\"Never got one of the expected responses: {} (transport_timeout_s = {}, read_timeout_s = {})\".format(expected_cmds, adb_info.transport_timeout_s, adb_info.read_timeout_s))\n\n    def send(self, msg, adb_info):\n        \"\"\"Send a message to the device.\n\n        Parameters\n        ----------\n        msg : AdbMessage\n            The data that will be sent\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        with self._transport_lock:\n            self._send(msg, adb_info)\n\n    def _read_expected_packet_from_device(self, expected_cmds, adb_info):\n        \"\"\"Read packets from the device until we get an expected packet type.\n\n        Parameters\n        ----------\n        expected_cmds : list[bytes]\n            We will read packets until we encounter one whose \"command\" field is in ``expected_cmds``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Returns\n        -------\n        cmd : bytes\n            The received command, which is in :const:`adb_shell.constants.WIRE_TO_ID` and must be in ``expected_cmds``\n        arg0 : int\n            TODO\n        arg1 : int\n            TODO\n        data : bytes\n            The data that was read\n\n        Raises\n        ------\n        adb_shell.exceptions.AdbTimeoutError\n            Never got one of the expected responses\n\n        \"\"\"\n        start = time.time()\n\n        while True:\n            cmd, arg0, arg1, data = self._read_packet_from_device(adb_info)\n\n            if cmd in expected_cmds:\n                return cmd, arg0, arg1, data\n\n            if time.time() - start > adb_info.read_timeout_s:\n                # Timeout\n                raise exceptions.AdbTimeoutError(\"Never got one of the expected responses: {} (transport_timeout_s = {}, read_timeout_s = {})\".format(expected_cmds, adb_info.transport_timeout_s, adb_info.read_timeout_s))\n\n    def _read_bytes_from_device(self, length, adb_info):\n        \"\"\"Read ``length`` bytes from the device.\n\n        Parameters\n        ----------\n        length : int\n            We will read packets until we get this length of data\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Returns\n        -------\n        bytes\n            The data that was read\n\n        Raises\n        ------\n        adb_shell.exceptions.AdbTimeoutError\n            Did not read ``length`` bytes in time\n\n        \"\"\"\n        start = time.time()\n        data = bytearray()\n\n        while length > 0:\n            temp = self._transport.bulk_read(length, adb_info.transport_timeout_s)\n            if temp:\n                # Only log if `temp` is not empty\n                _LOGGER.debug(\"bulk_read(%d): %.1000r\", length, temp)\n\n            data += temp\n            length -= len(temp)\n\n            if length == 0:\n                break\n\n            if time.time() - start > adb_info.read_timeout_s:\n                # Timeout\n                raise exceptions.AdbTimeoutError(\"Timeout: read {} of {} bytes (transport_timeout_s = {}, read_timeout_s = {})\".format(len(data), len(data) + length, adb_info.transport_timeout_s, adb_info.read_timeout_s))\n\n        return bytes(data)\n\n    def _read_packet_from_device(self, adb_info):\n        \"\"\"Read a complete ADB packet (header + data) from the device.\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Returns\n        -------\n        cmd : bytes\n            The received command, which is in :const:`adb_shell.constants.WIRE_TO_ID` and must be in ``expected_cmds``\n        arg0 : int\n            TODO\n        arg1 : int\n            TODO\n        bytes\n            The data that was read\n\n        Raises\n        ------\n        adb_shell.exceptions.InvalidCommandError\n            Unknown command\n        adb_shell.exceptions.InvalidChecksumError\n            Received checksum does not match the expected checksum\n\n       \"\"\"\n        msg = self._read_bytes_from_device(constants.MESSAGE_SIZE, adb_info)\n        cmd, arg0, arg1, data_length, data_checksum = unpack(msg)\n        command = constants.WIRE_TO_ID.get(cmd)\n\n        if not command:\n            raise exceptions.InvalidCommandError(\"Unknown command: %d = '%s' (arg0 = %d, arg1 = %d, msg = '%s')\" % (cmd, int_to_cmd(cmd), arg0, arg1, msg))\n\n        if data_length == 0:\n            return command, arg0, arg1, b\"\"\n\n        data = self._read_bytes_from_device(data_length, adb_info)\n        actual_checksum = checksum(data)\n        if actual_checksum != data_checksum:\n            raise exceptions.InvalidChecksumError(\"Received checksum {} != {}\".format(actual_checksum, data_checksum))\n\n        return command, arg0, arg1, data\n\n    def _send(self, msg, adb_info):\n        \"\"\"Send a message to the device.\n\n        1. Send the message header (:meth:`adb_shell.adb_message.AdbMessage.pack <AdbMessage.pack>`)\n        2. Send the message data\n\n\n        Parameters\n        ----------\n        msg : AdbMessage\n            The data that will be sent\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        packed = msg.pack()\n        _LOGGER.debug(\"bulk_write(%d): %r\", len(packed), packed)\n        self._transport.bulk_write(packed, adb_info.transport_timeout_s)\n\n        if msg.data:\n            _LOGGER.debug(\"bulk_write(%d): %r\", len(msg.data), msg.data)\n            self._transport.bulk_write(msg.data, adb_info.transport_timeout_s)\n\n\nclass AdbDevice(object):\n    \"\"\"A class with methods for connecting to a device and executing ADB commands.\n\n    Parameters\n    ----------\n    transport : BaseTransport\n        A user-provided transport for communicating with the device; must be an instance of a subclass of :class:`~adb_shell.transport.base_transport.BaseTransport`\n    default_transport_timeout_s : float, None\n        Default timeout in seconds for transport packets, or ``None``\n    banner : str, bytes, None\n        The hostname of the machine where the Python interpreter is currently running; if\n        it is not provided, it will be determined via ``socket.gethostname()``\n\n    Raises\n    ------\n    adb_shell.exceptions.InvalidTransportError\n        The passed ``transport`` is not an instance of a subclass of :class:`~adb_shell.transport.base_transport.BaseTransport`\n\n    Attributes\n    ----------\n    _available : bool\n        Whether an ADB connection to the device has been established\n    _banner : bytearray, bytes\n        The hostname of the machine where the Python interpreter is currently running\n    _default_transport_timeout_s : float, None\n        Default timeout in seconds for transport packets, or ``None``\n    _io_manager : _AdbIOManager\n        Used for handling all ADB I/O\n    _local_id : int\n        The local ID that is used for ADB transactions; the value is incremented each time and is always in the range ``[1, 2^32)``\n    _local_id_lock : Lock\n        A lock for protecting ``_local_id``; this is never held for long\n    _maxdata: int\n        Maximum amount of data in an ADB packet\n\n    \"\"\"\n\n    def __init__(self, transport, default_transport_timeout_s=None, banner=None):\n        if banner and not isinstance(banner, (bytes, bytearray)):\n            self._banner = bytearray(banner, 'utf-8')\n        else:\n            self._banner = banner\n\n        if not isinstance(transport, BaseTransport):\n            raise exceptions.InvalidTransportError(\"`transport` must be an instance of a subclass of `BaseTransport`\")\n\n        self._io_manager = _AdbIOManager(transport)\n\n        self._available = False\n        self._default_transport_timeout_s = default_transport_timeout_s\n        self._local_id = 0\n        self._local_id_lock = Lock()\n        self._maxdata = constants.MAX_PUSH_DATA\n\n    # ======================================================================= #\n    #                                                                         #\n    #                       Properties & simple methods                       #\n    #                                                                         #\n    # ======================================================================= #\n    @property\n    def available(self):\n        \"\"\"Whether or not an ADB connection to the device has been established.\n\n        Returns\n        -------\n        bool\n            ``self._available``\n\n        \"\"\"\n        return self._available\n\n    @property\n    def max_chunk_size(self):\n        \"\"\"Maximum chunk size for filesync operations\n\n        Returns\n        -------\n        int\n            Minimum value based on :const:`adb_shell.constants.MAX_CHUNK_SIZE` and ``_max_data / 2``, fallback to legacy :const:`adb_shell.constants.MAX_PUSH_DATA`\n\n        \"\"\"\n        return min(constants.MAX_CHUNK_SIZE, self._maxdata // 2) or constants.MAX_PUSH_DATA\n\n    def _get_transport_timeout_s(self, transport_timeout_s):\n        \"\"\"Use the provided ``transport_timeout_s`` if it is not ``None``; otherwise, use ``self._default_transport_timeout_s``\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            The potential transport timeout\n\n        Returns\n        -------\n        float\n            ``transport_timeout_s`` if it is not ``None``; otherwise, ``self._default_transport_timeout_s``\n\n        \"\"\"\n        return transport_timeout_s if transport_timeout_s is not None else self._default_transport_timeout_s\n\n    # ======================================================================= #\n    #                                                                         #\n    #                             Close & Connect                             #\n    #                                                                         #\n    # ======================================================================= #\n    def close(self):\n        \"\"\"Close the connection via the provided transport's ``close()`` method.\n\n        \"\"\"\n        self._available = False\n        self._io_manager.close()\n\n    def connect(self, rsa_keys=None, transport_timeout_s=None, auth_timeout_s=constants.DEFAULT_AUTH_TIMEOUT_S, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, auth_callback=None):\n        \"\"\"Establish an ADB connection to the device.\n\n        See :meth:`_AdbIOManager.connect`.\n\n        Parameters\n        ----------\n        rsa_keys : list, None\n            A list of signers of type :class:`~adb_shell.auth.sign_cryptography.CryptographySigner`,\n            :class:`~adb_shell.auth.sign_pycryptodome.PycryptodomeAuthSigner`, or :class:`~adb_shell.auth.sign_pythonrsa.PythonRSASigner`\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        auth_timeout_s : float, None\n            The time in seconds to wait for a ``b'CNXN'`` authentication response\n        read_timeout_s : float\n            The total time in seconds to wait for expected commands in :meth:`_AdbIOManager._read_expected_packet_from_device`\n        auth_callback : function, None\n            Function callback invoked when the connection needs to be accepted on the device\n\n        Returns\n        -------\n        bool\n            Whether the connection was established (:attr:`AdbDevice.available`)\n\n        \"\"\"\n        # Get `self._banner` if it was not provided in the constructor\n        if not self._banner:\n            self._banner = get_banner()\n\n        # Instantiate the `_AdbTransactionInfo`\n        adb_info = _AdbTransactionInfo(None, None, self._get_transport_timeout_s(transport_timeout_s), read_timeout_s, None)\n\n        # Mark the device as unavailable\n        self._available = False\n\n        # Use the IO manager to connect\n        self._available, self._maxdata = self._io_manager.connect(self._banner, rsa_keys, auth_timeout_s, auth_callback, adb_info)\n\n        return self._available\n\n    # ======================================================================= #\n    #                                                                         #\n    #                                 Services                                #\n    #                                                                         #\n    # ======================================================================= #\n    def _service(self, service, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True):\n        \"\"\"Send an ADB command to the device.\n\n        Parameters\n        ----------\n        service : bytes\n            The ADB service to talk to (e.g., ``b'shell'``)\n        command : bytes\n            The command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Returns\n        -------\n        bytes, str\n            The output of the ADB command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        if decode:\n            return b''.join(self._streaming_command(service, command, transport_timeout_s, read_timeout_s, timeout_s)).decode('utf8', _DECODE_ERRORS)\n        return b''.join(self._streaming_command(service, command, transport_timeout_s, read_timeout_s, timeout_s))\n\n    def _streaming_service(self, service, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, decode=True):\n        \"\"\"Send an ADB command to the device, yielding each line of output.\n\n        Parameters\n        ----------\n        service : bytes\n            The ADB service to talk to (e.g., ``b'shell'``)\n        command : bytes\n            The command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Yields\n        -------\n        bytes, str\n            The line-by-line output of the ADB command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        stream = self._streaming_command(service, command, transport_timeout_s, read_timeout_s, None)\n        if decode:\n            yield from (stream_line.decode('utf8', _DECODE_ERRORS) for stream_line in stream)\n        else:\n            yield from stream\n\n    def exec_out(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True):\n        \"\"\"Send an ADB ``exec-out`` command to the device.\n\n        https://www.linux-magazine.com/Issues/2017/195/Ask-Klaus\n\n        Parameters\n        ----------\n        command : str\n            The exec-out command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Returns\n        -------\n        bytes, str\n            The output of the ADB exec-out command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        return self._service(b'exec', command.encode('utf8'), transport_timeout_s, read_timeout_s, timeout_s, decode)\n\n    def reboot(self, fastboot=False, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None):\n        \"\"\"Reboot the device.\n\n        Parameters\n        ----------\n        fastboot : bool\n            Whether to reboot the device into fastboot\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        self._open(b'reboot:bootloader' if fastboot else b'reboot:', transport_timeout_s, read_timeout_s, timeout_s)\n\n    def root(self, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None):\n        \"\"\"Gain root access.\n\n        The device must be rooted in order for this to work.\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        self._service(b'root', b'', transport_timeout_s, read_timeout_s, timeout_s, False)\n\n    def shell(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True):\n        \"\"\"Send an ADB shell command to the device.\n\n        Parameters\n        ----------\n        command : str\n            The shell command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Returns\n        -------\n        bytes, str\n            The output of the ADB shell command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        return self._service(b'shell', command.encode('utf8'), transport_timeout_s, read_timeout_s, timeout_s, decode)\n\n    def streaming_shell(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, decode=True):\n        \"\"\"Send an ADB shell command to the device, yielding each line of output.\n\n        Parameters\n        ----------\n        command : str\n            The shell command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Yields\n        -------\n        bytes, str\n            The line-by-line output of the ADB shell command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        yield from self._streaming_service(b'shell', command.encode('utf8'), transport_timeout_s, read_timeout_s, decode)\n\n    # ======================================================================= #\n    #                                                                         #\n    #                                 FileSync                                #\n    #                                                                         #\n    # ======================================================================= #\n    def list(self, device_path, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S):\n        \"\"\"Return a directory listing of the given path.\n\n        Parameters\n        ----------\n        device_path : str\n            Directory to list.\n        transport_timeout_s : float, None\n            Expected timeout for any part of the pull.\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n\n        Returns\n        -------\n        files : list[DeviceFile]\n            Filename, mode, size, and mtime info for the files in the directory\n\n        \"\"\"\n        if not device_path:\n            raise exceptions.DevicePathInvalidError(\"Cannot list an empty device path\")\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        adb_info = self._open(b\"sync:\", transport_timeout_s, read_timeout_s, None)\n        filesync_info = _FileSyncTransactionInfo(constants.FILESYNC_LIST_FORMAT, maxdata=self._maxdata)\n\n        self._filesync_send(constants.LIST, adb_info, filesync_info, data=device_path)\n        files = []\n\n        for cmd_id, header, filename in self._filesync_read_until([constants.DENT], [constants.DONE], adb_info, filesync_info):\n            if cmd_id == constants.DONE:\n                break\n\n            mode, size, mtime = header\n            files.append(DeviceFile(filename, mode, size, mtime))\n\n        self._clse(adb_info)\n\n        return files\n\n    def pull(self, device_path, local_path, progress_callback=None, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S):\n        \"\"\"Pull a file from the device.\n\n        Parameters\n        ----------\n        device_path : str\n            The file on the device that will be pulled\n        local_path : str, BytesIO\n            The path or BytesIO stream where the file will be downloaded\n        progress_callback : function, None\n            Callback method that accepts ``device_path``, ``bytes_written``, and ``total_bytes``\n        transport_timeout_s : float, None\n            Expected timeout for any part of the pull.\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n\n        \"\"\"\n        if not device_path:\n            raise exceptions.DevicePathInvalidError(\"Cannot pull from an empty device path\")\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        opener = _open_bytesio if isinstance(local_path, BytesIO) else open\n        with opener(local_path, 'wb') as stream:\n            adb_info = self._open(b'sync:', transport_timeout_s, read_timeout_s, None)\n            filesync_info = _FileSyncTransactionInfo(constants.FILESYNC_PULL_FORMAT, maxdata=self._maxdata)\n\n            try:\n                self._pull(device_path, stream, progress_callback, adb_info, filesync_info)\n            finally:\n                self._clse(adb_info)\n\n    def _pull(self, device_path, stream, progress_callback, adb_info, filesync_info):\n        \"\"\"Pull a file from the device into the file-like ``local_path``.\n\n        Parameters\n        ----------\n        device_path : str\n            The file on the device that will be pulled\n        stream : _io.BytesIO\n            File-like object for writing to\n        progress_callback : function, None\n            Callback method that accepts ``device_path``, ``bytes_written``, and ``total_bytes``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        \"\"\"\n        if progress_callback:\n            total_bytes = self.stat(device_path)[1]\n\n        self._filesync_send(constants.RECV, adb_info, filesync_info, data=device_path)\n        for cmd_id, _, data in self._filesync_read_until([constants.DATA], [constants.DONE], adb_info, filesync_info):\n            if cmd_id == constants.DONE:\n                break\n\n            stream.write(data)\n            if progress_callback:\n                try:\n                    progress_callback(device_path, len(data), total_bytes)\n                except:  # noqa pylint: disable=bare-except\n                    pass\n\n    def push(self, local_path, device_path, st_mode=constants.DEFAULT_PUSH_MODE, mtime=0, progress_callback=None, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S):\n        \"\"\"Push a file or directory to the device.\n\n        Parameters\n        ----------\n        local_path : str, BytesIO\n            A filename, directory, or BytesIO stream to push to the device\n        device_path : str\n            Destination on the device to write to.\n        st_mode : int\n            Stat mode for ``local_path``\n        mtime : int\n            Modification time to set on the file.\n        progress_callback : function, None\n            Callback method that accepts ``device_path``, ``bytes_written``, and ``total_bytes``\n        transport_timeout_s : float, None\n            Expected timeout for any part of the push.\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n\n        \"\"\"\n        if not device_path:\n            raise exceptions.DevicePathInvalidError(\"Cannot push to an empty device path\")\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        local_path_is_dir, local_paths, device_paths = get_files_to_push(local_path, device_path)\n\n        if local_path_is_dir:\n            self.shell(\"mkdir \" + device_path, transport_timeout_s, read_timeout_s)\n\n        for _local_path, _device_path in zip(local_paths, device_paths):\n            opener = _open_bytesio if isinstance(local_path, BytesIO) else open\n            with opener(_local_path, 'rb') as stream:\n                adb_info = self._open(b'sync:', transport_timeout_s, read_timeout_s, None)\n                filesync_info = _FileSyncTransactionInfo(constants.FILESYNC_PUSH_FORMAT, maxdata=self._maxdata)\n\n                self._push(stream, _device_path, st_mode, mtime, progress_callback, adb_info, filesync_info)\n\n            self._clse(adb_info)\n\n    def _push(self, stream, device_path, st_mode, mtime, progress_callback, adb_info, filesync_info):\n        \"\"\"Push a file-like object to the device.\n\n        Parameters\n        ----------\n        stream : _io.BytesIO\n            File-like object for reading from\n        device_path : str\n            Destination on the device to write to\n        st_mode : int\n            Stat mode for the file\n        mtime : int\n            Modification time\n        progress_callback : function, None\n            Callback method that accepts ``device_path``, ``bytes_written``, and ``total_bytes``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Raises\n        ------\n        PushFailedError\n            Raised on push failure.\n\n        \"\"\"\n        fileinfo = ('{},{}'.format(device_path, int(st_mode))).encode('utf-8')\n\n        self._filesync_send(constants.SEND, adb_info, filesync_info, data=fileinfo)\n\n        if progress_callback:\n            total_bytes = os.fstat(stream.fileno()).st_size\n\n        while True:\n            data = stream.read(self.max_chunk_size)\n            if data:\n                self._filesync_send(constants.DATA, adb_info, filesync_info, data=data)\n\n                if progress_callback:\n                    try:\n                        progress_callback(device_path, len(data), total_bytes)\n                    except:  # noqa pylint: disable=bare-except\n                        pass\n            else:\n                break\n\n        if mtime == 0:\n            mtime = int(time.time())\n\n        # DONE doesn't send data, but it hides the last bit of data in the size field.\n        self._filesync_send(constants.DONE, adb_info, filesync_info, size=mtime)\n        for cmd_id, _, data in self._filesync_read_until([], [constants.OKAY, constants.FAIL], adb_info, filesync_info):\n            if cmd_id == constants.OKAY:\n                return\n\n            raise exceptions.PushFailedError(data)\n\n    def stat(self, device_path, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S):\n        \"\"\"Get a file's ``stat()`` information.\n\n        Parameters\n        ----------\n        device_path : str\n            The file on the device for which we will get information.\n        transport_timeout_s : float, None\n            Expected timeout for any part of the pull.\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n\n        Returns\n        -------\n        mode : int\n            The octal permissions for the file\n        size : int\n            The size of the file\n        mtime : int\n            The last modified time for the file\n\n        \"\"\"\n        if not device_path:\n            raise exceptions.DevicePathInvalidError(\"Cannot stat an empty device path\")\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDevice.connect()`?)\")\n\n        adb_info = self._open(b'sync:', transport_timeout_s, read_timeout_s, None)\n        filesync_info = _FileSyncTransactionInfo(constants.FILESYNC_STAT_FORMAT, maxdata=self._maxdata)\n\n        self._filesync_send(constants.STAT, adb_info, filesync_info, data=device_path)\n        _, (mode, size, mtime), _ = self._filesync_read([constants.STAT], adb_info, filesync_info)\n        self._clse(adb_info)\n\n        return mode, size, mtime\n\n    # ======================================================================= #\n    #                                                                         #\n    #                       Hidden Methods: send packets                      #\n    #                                                                         #\n    # ======================================================================= #\n    def _clse(self, adb_info):\n        \"\"\"Send a ``b'CLSE'`` message and then read a ``b'CLSE'`` message.\n\n        .. warning::\n\n           This is not to be confused with the :meth:`AdbDevice.close` method!\n\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        msg = AdbMessage(constants.CLSE, adb_info.local_id, adb_info.remote_id)\n        self._io_manager.send(msg, adb_info)\n        self._read_until([constants.CLSE], adb_info)\n\n    def _okay(self, adb_info):\n        \"\"\"Send an ``b'OKAY'`` mesage.\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        msg = AdbMessage(constants.OKAY, adb_info.local_id, adb_info.remote_id)\n        self._io_manager.send(msg, adb_info)\n\n    # ======================================================================= #\n    #                                                                         #\n    #                              Hidden Methods                             #\n    #                                                                         #\n    # ======================================================================= #\n    def _open(self, destination, transport_timeout_s, read_timeout_s, timeout_s):\n        \"\"\"Opens a new connection to the device via an ``b'OPEN'`` message.\n\n        1. :meth:`~_AdbIOManager.send` an ``b'OPEN'`` command to the device that specifies the ``local_id``\n        2. :meth:`~_AdbIOManager.read` the response from the device and fill in the ``adb_info.remote_id`` attribute\n\n\n        Parameters\n        ----------\n        destination : bytes\n            ``b'SERVICE:COMMAND'``\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n\n        Returns\n        -------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        with self._local_id_lock:\n            self._local_id += 1\n            if self._local_id == 2**32:\n                self._local_id = 1\n\n            adb_info = _AdbTransactionInfo(self._local_id, None, self._get_transport_timeout_s(transport_timeout_s), read_timeout_s, timeout_s)\n\n        msg = AdbMessage(constants.OPEN, adb_info.local_id, 0, destination + b'\\0')\n        self._io_manager.send(msg, adb_info)\n        _, adb_info.remote_id, _, _ = self._io_manager.read([constants.OKAY], adb_info)\n\n        return adb_info\n\n    def _read_until(self, expected_cmds, adb_info):\n        \"\"\"Read a packet, acknowledging any write packets.\n\n        1. Read data via :meth:`_AdbIOManager.read`\n        2. If a ``b'WRTE'`` packet is received, send an ``b'OKAY'`` packet via :meth:`AdbDevice._okay`\n        3. Return the ``cmd`` and ``data`` that were read by :meth:`_AdbIOManager.read`\n\n\n        Parameters\n        ----------\n        expected_cmds : list[bytes]\n            :meth:`_AdbIOManager.read` will look for a packet whose command is in ``expected_cmds``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Returns\n        -------\n        cmd : bytes\n            The command that was received by :meth:`_AdbIOManager.read`, which is in :const:`adb_shell.constants.WIRE_TO_ID` and must be in ``expected_cmds``\n        data : bytes\n            The data that was received by :meth:`_AdbIOManager.read`\n\n        \"\"\"\n        cmd, _, _, data = self._io_manager.read(expected_cmds, adb_info, allow_zeros=True)\n\n        # Acknowledge write packets\n        if cmd == constants.WRTE:\n            self._okay(adb_info)\n\n        return cmd, data\n\n    def _read_until_close(self, adb_info):\n        \"\"\"Yield packets until a ``b'CLSE'`` packet is received.\n\n        1. Read the ``cmd`` and ``data`` fields from a ``b'CLSE'`` or ``b'WRTE'`` packet via :meth:`AdbDevice._read_until`\n        2. If ``cmd`` is ``b'CLSE'``, then send a ``b'CLSE'`` message and stop\n        3. Yield ``data`` and repeat\n\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Yields\n        ------\n        data : bytes\n            The data that was read by :meth:`AdbDevice._read_until`\n\n        \"\"\"\n        start = time.time()\n\n        while True:\n            cmd, data = self._read_until([constants.CLSE, constants.WRTE], adb_info)\n\n            if cmd == constants.CLSE:\n                msg = AdbMessage(constants.CLSE, adb_info.local_id, adb_info.remote_id)\n                self._io_manager.send(msg, adb_info)\n                break\n\n            yield data\n\n            # Make sure the ADB command has not timed out\n            if adb_info.timeout_s is not None and time.time() - start > adb_info.timeout_s:\n                raise exceptions.AdbTimeoutError(\"The command did not complete within {} seconds\".format(adb_info.timeout_s))\n\n    def _streaming_command(self, service, command, transport_timeout_s, read_timeout_s, timeout_s):\n        \"\"\"One complete set of packets for a single command.\n\n        1. :meth:`~AdbDevice._open` a new connection to the device, where the ``destination`` parameter is ``service:command``\n        2. Read the response data via :meth:`AdbDevice._read_until_close`\n\n\n        .. note::\n\n           All the data is held in memory, and thus large responses will be slow and can fill up memory.\n\n\n        Parameters\n        ----------\n        service : bytes\n            The ADB service (e.g., ``b'shell'``, as used by :meth:`AdbDevice.shell`)\n        command : bytes\n            The service command\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`\n            and :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n\n        Yields\n        ------\n        bytes\n            The responses from the service.\n\n        \"\"\"\n        adb_info = self._open(b'%s:%s' % (service, command), transport_timeout_s, read_timeout_s, timeout_s)\n\n        yield from self._read_until_close(adb_info)\n\n    # ======================================================================= #\n    #                                                                         #\n    #                         FileSync Hidden Methods                         #\n    #                                                                         #\n    # ======================================================================= #\n    def _filesync_flush(self, adb_info, filesync_info):\n        \"\"\"Write the data in the buffer up to ``filesync_info.send_idx``, then set ``filesync_info.send_idx`` to 0.\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        \"\"\"\n        # Send the buffer\n        msg = AdbMessage(constants.WRTE, adb_info.local_id, adb_info.remote_id, filesync_info.send_buffer[:filesync_info.send_idx])\n        self._io_manager.send(msg, adb_info)\n\n        # Expect an 'OKAY' in response\n        self._read_until([constants.OKAY], adb_info)\n\n        # Reset the send index\n        filesync_info.send_idx = 0\n\n    def _filesync_read(self, expected_ids, adb_info, filesync_info):\n        \"\"\"Read ADB messages and return FileSync packets.\n\n        Parameters\n        ----------\n        expected_ids : tuple[bytes]\n            If the received header ID is not in ``expected_ids``, an exception will be raised\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        Returns\n        -------\n        command_id : bytes\n            The received header ID\n        tuple\n            The contents of the header\n        data : bytearray, None\n            The received data, or ``None`` if the command ID is :const:`adb_shell.constants.STAT`\n\n        Raises\n        ------\n        adb_shell.exceptions.AdbCommandFailureException\n            Command failed\n        adb_shell.exceptions.InvalidResponseError\n            Received response was not in ``expected_ids``\n\n        \"\"\"\n        if filesync_info.send_idx:\n            self._filesync_flush(adb_info, filesync_info)\n\n        # Read one filesync packet off the recv buffer.\n        header_data = self._filesync_read_buffered(filesync_info.recv_message_size, adb_info, filesync_info)\n        header = struct.unpack(filesync_info.recv_message_format, header_data)\n\n        # Header is (ID, ...).\n        command_id = constants.FILESYNC_WIRE_TO_ID[header[0]]\n\n        # Whether there is data to read\n        read_data = command_id != constants.STAT\n\n        if read_data:\n            # Header is (ID, ..., size) --> read the data\n            data = self._filesync_read_buffered(header[-1], adb_info, filesync_info)\n        else:\n            # No data to be read\n            data = bytearray()\n\n        if command_id not in expected_ids:\n            if command_id == constants.FAIL:\n                reason = data.decode('utf-8', errors=_DECODE_ERRORS)\n\n                raise exceptions.AdbCommandFailureException('Command failed: {}'.format(reason))\n\n            raise exceptions.InvalidResponseError('Expected one of %s, got %s' % (expected_ids, command_id))\n\n        if not read_data:\n            return command_id, header[1:], None\n\n        return command_id, header[1:-1], data\n\n    def _filesync_read_buffered(self, size, adb_info, filesync_info):\n        \"\"\"Read ``size`` bytes of data from ``self.recv_buffer``.\n\n        Parameters\n        ----------\n        size : int\n            The amount of data to read\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        Returns\n        -------\n        result : bytearray\n            The read data\n\n        \"\"\"\n        # Ensure recv buffer has enough data.\n        while len(filesync_info.recv_buffer) < size:\n            _, data = self._read_until([constants.WRTE], adb_info)\n            filesync_info.recv_buffer += data\n\n        result = filesync_info.recv_buffer[:size]\n        filesync_info.recv_buffer = filesync_info.recv_buffer[size:]\n        return result\n\n    def _filesync_read_until(self, expected_ids, finish_ids, adb_info, filesync_info):\n        \"\"\"Useful wrapper around :meth:`AdbDevice._filesync_read`.\n\n        Parameters\n        ----------\n        expected_ids : tuple[bytes]\n            If the received header ID is not in ``expected_ids``, an exception will be raised\n        finish_ids : tuple[bytes]\n            We will read until we find a header ID that is in ``finish_ids``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        Yields\n        ------\n        cmd_id : bytes\n            The received header ID\n        header : tuple\n            TODO\n        data : bytearray\n            The received data\n\n        \"\"\"\n        while True:\n            cmd_id, header, data = self._filesync_read(expected_ids + finish_ids, adb_info, filesync_info)\n            yield cmd_id, header, data\n\n            # These lines are not reachable because whenever this method is called and `cmd_id` is in `finish_ids`, the code\n            # either breaks (`list` and `_pull`), returns (`_push`), or raises an exception (`_push`)\n            if cmd_id in finish_ids:  # pragma: no cover\n                break\n\n    def _filesync_send(self, command_id, adb_info, filesync_info, data=b'', size=None):\n        \"\"\"Send/buffer FileSync packets.\n\n        Packets are buffered and only flushed when this connection is read from. All\n        messages have a response from the device, so this will always get flushed.\n\n        Parameters\n        ----------\n        command_id : bytes\n            Command to send.\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n        data : str, bytes\n            Optional data to send, must set data or size.\n        size : int, None\n            Optionally override size from len(data).\n\n        \"\"\"\n        if not isinstance(data, bytes):\n            data = data.encode('utf8')\n        if size is None:\n            size = len(data)\n\n        if not filesync_info.can_add_to_send_buffer(len(data)):\n            self._filesync_flush(adb_info, filesync_info)\n\n        buf = struct.pack(b'<2I', constants.FILESYNC_ID_TO_WIRE[command_id], size) + data\n        filesync_info.send_buffer[filesync_info.send_idx:filesync_info.send_idx + len(buf)] = buf\n        filesync_info.send_idx += len(buf)\n\n\nclass AdbDeviceTcp(AdbDevice):\n    \"\"\"A class with methods for connecting to a device via TCP and executing ADB commands.\n\n    Parameters\n    ----------\n    host : str\n        The address of the device; may be an IP address or a host name\n    port : int\n        The device port to which we are connecting (default is 5555)\n    default_transport_timeout_s : float, None\n        Default timeout in seconds for TCP packets, or ``None``\n    banner : str, bytes, None\n        The hostname of the machine where the Python interpreter is currently running; if\n        it is not provided, it will be determined via ``socket.gethostname()``\n\n    Attributes\n    ----------\n    _available : bool\n        Whether an ADB connection to the device has been established\n    _banner : bytearray, bytes\n        The hostname of the machine where the Python interpreter is currently running\n    _default_transport_timeout_s : float, None\n        Default timeout in seconds for TCP packets, or ``None``\n    _local_id : int\n        The local ID that is used for ADB transactions; the value is incremented each time and is always in the range ``[1, 2^32)``\n    _maxdata : int\n        Maximum amount of data in an ADB packet\n    _transport : TcpTransport\n        The transport that is used to connect to the device\n\n    \"\"\"\n\n    def __init__(self, host, port=5555, default_transport_timeout_s=None, banner=None):\n        transport = TcpTransport(host, port)\n        super(AdbDeviceTcp, self).__init__(transport, default_transport_timeout_s, banner)\n\n\nclass AdbDeviceUsb(AdbDevice):\n    \"\"\"A class with methods for connecting to a device via USB and executing ADB commands.\n\n    Parameters\n    ----------\n    serial : str, None\n        The USB device serial ID\n    port_path : TODO, None\n        TODO\n    default_transport_timeout_s : float, None\n        Default timeout in seconds for USB packets, or ``None``\n    banner : str, bytes, None\n        The hostname of the machine where the Python interpreter is currently running; if\n        it is not provided, it will be determined via ``socket.gethostname()``\n\n    Raises\n    ------\n    adb_shell.exceptions.InvalidTransportError\n        Raised if package was not installed with the \"usb\" extras option (``pip install adb-shell[usb]``)\n\n    Attributes\n    ----------\n    _available : bool\n        Whether an ADB connection to the device has been established\n    _banner : bytearray, bytes\n        The hostname of the machine where the Python interpreter is currently running\n    _default_transport_timeout_s : float, None\n        Default timeout in seconds for USB packets, or ``None``\n    _local_id : int\n        The local ID that is used for ADB transactions; the value is incremented each time and is always in the range ``[1, 2^32)``\n    _maxdata : int\n        Maximum amount of data in an ADB packet\n    _transport : UsbTransport\n        The transport that is used to connect to the device\n\n    \"\"\"\n\n    def __init__(self, serial=None, port_path=None, default_transport_timeout_s=None, banner=None):\n        if UsbTransport is None:\n            raise exceptions.InvalidTransportError(\"To enable USB support you must install this package via `pip install adb-shell[usb]`\")\n\n        transport = UsbTransport.find_adb(serial, port_path, default_transport_timeout_s)\n        super(AdbDeviceUsb, self).__init__(transport, default_transport_timeout_s, banner)\n"
  },
  {
    "path": "adb_shell/adb_device_async.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It incorporates work\n# covered by the following license notice:\n#\n#\n#   Copyright 2014 Google Inc. All rights reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\n\"\"\"Implement the :class:`AdbDeviceAsync` class, which can connect to a device and run ADB shell commands.\n\n* :class:`_AdbIOManagerAsync`\n\n    * :meth:`_AdbIOManagerAsync._read_bytes_from_device`\n    * :meth:`_AdbIOManagerAsync._read_expected_packet_from_device`\n    * :meth:`_AdbIOManagerAsync._read_packet_from_device`\n    * :meth:`_AdbIOManagerAsync._send`\n    * :meth:`_AdbIOManagerAsync.close`\n    * :meth:`_AdbIOManagerAsync.connect`\n    * :meth:`_AdbIOManagerAsync.read`\n    * :meth:`_AdbIOManagerAsync.send`\n\n* :class:`_AsyncBytesIO`\n\n    * :meth:`_AsyncBytesIO.read`\n    * :meth:`_AsyncBytesIO.write`\n\n* :func:`_open_bytesio`\n\n* :class:`AdbDeviceAsync`\n\n    * :meth:`AdbDeviceAsync._clse`\n    * :meth:`AdbDeviceAsync._filesync_flush`\n    * :meth:`AdbDeviceAsync._filesync_read`\n    * :meth:`AdbDeviceAsync._filesync_read_buffered`\n    * :meth:`AdbDeviceAsync._filesync_read_until`\n    * :meth:`AdbDeviceAsync._filesync_send`\n    * :meth:`AdbDeviceAsync._okay`\n    * :meth:`AdbDeviceAsync._open`\n    * :meth:`AdbDeviceAsync._pull`\n    * :meth:`AdbDeviceAsync._push`\n    * :meth:`AdbDeviceAsync._read_until`\n    * :meth:`AdbDeviceAsync._read_until_close`\n    * :meth:`AdbDeviceAsync._service`\n    * :meth:`AdbDeviceAsync._streaming_command`\n    * :meth:`AdbDeviceAsync._streaming_service`\n    * :attr:`AdbDeviceAsync.available`\n    * :meth:`AdbDeviceAsync.close`\n    * :meth:`AdbDeviceAsync.connect`\n    * :meth:`AdbDeviceAsync.list`\n    * :attr:`AdbDeviceAsync.max_chunk_size`\n    * :meth:`AdbDeviceAsync.pull`\n    * :meth:`AdbDeviceAsync.push`\n    * :meth:`AdbDeviceAsync.root`\n    * :meth:`AdbDeviceAsync.shell`\n    * :meth:`AdbDeviceAsync.stat`\n    * :meth:`AdbDeviceAsync.streaming_shell`\n\n* :class:`AdbDeviceTcpAsync`\n\n\"\"\"\n\n\nfrom contextlib import asynccontextmanager\nfrom io import BytesIO\nfrom asyncio import Lock, get_running_loop\nimport logging\nimport os\nimport struct\nimport time\n\nimport aiofiles\n\nfrom . import constants\nfrom . import exceptions\nfrom .adb_message import AdbMessage, checksum, int_to_cmd, unpack\nfrom .transport.base_transport_async import BaseTransportAsync\nfrom .transport.tcp_transport_async import TcpTransportAsync\nfrom .hidden_helpers import DeviceFile, _AdbPacketStore, _AdbTransactionInfo, _FileSyncTransactionInfo, get_banner, get_files_to_push\n\n\n_LOGGER = logging.getLogger(__name__)\n\n\nclass _AsyncBytesIO:\n    \"\"\"An async wrapper for `BytesIO`.\n\n    Parameters\n    ----------\n    bytesio : BytesIO\n        The BytesIO object that is wrapped\n\n    \"\"\"\n\n    def __init__(self, bytesio):\n        self._bytesio = bytesio\n\n    async def read(self, size=-1):\n        \"\"\"Read data.\n\n        Parameters\n        ----------\n        size : int\n            The size of the data to be read\n\n        Returns\n        -------\n        bytes\n            The data that was read\n\n        \"\"\"\n        return self._bytesio.read(size)\n\n    async def write(self, data):\n        \"\"\"Write data.\n\n        Parameters\n        ----------\n        data : bytes\n            The data to be written\n\n        \"\"\"\n        self._bytesio.write(data)\n\n\n@asynccontextmanager\nasync def _open_bytesio(stream, *args, **kwargs):  # pylint: disable=unused-argument\n    \"\"\"An async context manager for a BytesIO object that does nothing.\n\n    Parameters\n    ----------\n    stream : BytesIO\n        The BytesIO stream\n    args : list\n        Unused positional arguments\n    kwargs : dict\n        Unused keyword arguments\n\n    Yields\n    ------\n    _AsyncBytesIO\n        The wrapped `stream` input parameter\n\n    \"\"\"\n    yield _AsyncBytesIO(stream)\n\n\nclass _AdbIOManagerAsync(object):\n    \"\"\"A class for handling all ADB I/O.\n\n    Notes\n    -----\n    When the ``self._store_lock`` and ``self._transport_lock`` locks are held at the same time, it must always be the\n    case that the ``self._transport_lock`` is acquired first.  This ensures that there is no potential for deadlock.\n\n    Parameters\n    ----------\n    transport : BaseTransportAsync\n        A transport for communicating with the device; must be an instance of a subclass of :class:`~adb_shell.transport.base_transport_async.BaseTransportAsync`\n\n    Attributes\n    ----------\n    _packet_store : _AdbPacketStore\n        A store for holding packets that correspond to different ADB streams\n    _store_lock : Lock\n        A lock for protecting ``self._packet_store`` (this lock is never held for long)\n    _transport : BaseTransportAsync\n        A transport for communicating with the device; must be an instance of a subclass of :class:`~adb_shell.transport.base_transport_async.BaseTransportAsync`\n    _transport_lock : Lock\n        A lock for protecting ``self._transport``\n\n    \"\"\"\n\n    def __init__(self, transport):\n        self._packet_store = _AdbPacketStore()\n        self._transport = transport\n\n        self._store_lock = Lock()\n        self._transport_lock = Lock()\n\n    async def close(self):\n        \"\"\"Close the connection via the provided transport's ``close()`` method and clear the packet store.\n\n        \"\"\"\n        async with self._transport_lock:\n            await self._transport.close()\n\n            async with self._store_lock:\n                self._packet_store.clear_all()\n\n    async def connect(self, banner, rsa_keys, auth_timeout_s, auth_callback, adb_info):\n        \"\"\"Establish an ADB connection to the device.\n\n        1. Use the transport to establish a connection\n        2. Send a ``b'CNXN'`` message\n        3. Read the response from the device\n        4. If ``cmd`` is not ``b'AUTH'``, then authentication is not necesary and so we are done\n        5. If no ``rsa_keys`` are provided, raise an exception\n        6. Loop through our keys, signing the last ``banner2`` that we received\n\n            1. If the last ``arg0`` was not :const:`adb_shell.constants.AUTH_TOKEN`, raise an exception\n            2. Sign the last ``banner2`` and send it in an ``b'AUTH'`` message\n            3. Read the response from the device\n            4. If ``cmd`` is ``b'CNXN'``, we are done\n\n        7. None of the keys worked, so send ``rsa_keys[0]``'s public key; if the response does not time out, we must have connected successfully\n\n\n        Parameters\n        ----------\n        banner : bytearray, bytes\n            The hostname of the machine where the Python interpreter is currently running (:attr:`adb_shell.adb_device_async.AdbDeviceAsync._banner`)\n        rsa_keys : list, None\n            A list of signers of type :class:`~adb_shell.auth.sign_cryptography.CryptographySigner`,\n            :class:`~adb_shell.auth.sign_pycryptodome.PycryptodomeAuthSigner`, or :class:`~adb_shell.auth.sign_pythonrsa.PythonRSASigner`\n        auth_timeout_s : float, None\n            The time in seconds to wait for a ``b'CNXN'`` authentication response\n        auth_callback : function, None\n            Function callback invoked when the connection needs to be accepted on the device\n        adb_info : _AdbTransactionInfo\n            Info and settings for this connection attempt\n\n        Returns\n        -------\n        bool\n            Whether the connection was established\n        maxdata : int\n            Maximum amount of data in an ADB packet\n\n        Raises\n        ------\n        adb_shell.exceptions.DeviceAuthError\n            Device authentication required, no keys available\n        adb_shell.exceptions.InvalidResponseError\n            Invalid auth response from the device\n\n        \"\"\"\n        async with self._transport_lock:\n            # 0. Close the connection and clear the store\n            await self._transport.close()\n\n            async with self._store_lock:\n                # We can release this lock because packets are only added to the store when the transport lock is held\n                self._packet_store.clear_all()\n\n            # 1. Use the transport to establish a connection\n            await self._transport.connect(adb_info.transport_timeout_s)\n\n            # 2. Send a ``b'CNXN'`` message\n            msg = AdbMessage(constants.CNXN, constants.VERSION, constants.MAX_ADB_DATA, b'host::%s\\0' % banner)\n            await self._send(msg, adb_info)\n\n            # 3. Read the response from the device\n            cmd, arg0, maxdata, banner2 = await self._read_expected_packet_from_device([constants.AUTH, constants.CNXN], adb_info)\n\n            # 4. If ``cmd`` is not ``b'AUTH'``, then authentication is not necesary and so we are done\n            if cmd != constants.AUTH:\n                return True, maxdata\n\n            # 5. If no ``rsa_keys`` are provided, raise an exception\n            if not rsa_keys:\n                await self._transport.close()\n                raise exceptions.DeviceAuthError('Device authentication required, no keys available.')\n\n            # 6. Loop through our keys, signing the last ``banner2`` that we received\n            for rsa_key in rsa_keys:\n                # 6.1. If the last ``arg0`` was not :const:`adb_shell.constants.AUTH_TOKEN`, raise an exception\n                if arg0 != constants.AUTH_TOKEN:\n                    await self._transport.close()\n                    raise exceptions.InvalidResponseError('Unknown AUTH response: %s %s %s' % (arg0, maxdata, banner2))\n\n                # 6.2. Sign the last ``banner2`` and send it in an ``b'AUTH'`` message\n                signed_token = rsa_key.Sign(banner2)\n                msg = AdbMessage(constants.AUTH, constants.AUTH_SIGNATURE, 0, signed_token)\n                await self._send(msg, adb_info)\n\n                # 6.3. Read the response from the device\n                cmd, arg0, maxdata, banner2 = await self._read_expected_packet_from_device([constants.CNXN, constants.AUTH], adb_info)\n\n                # 6.4. If ``cmd`` is ``b'CNXN'``, we are done\n                if cmd == constants.CNXN:\n                    return True, maxdata\n\n            # 7. None of the keys worked, so send ``rsa_keys[0]``'s public key; if the response does not time out, we must have connected successfully\n            pubkey = rsa_keys[0].GetPublicKey()\n            if not isinstance(pubkey, (bytes, bytearray)):\n                pubkey = bytearray(pubkey, 'utf-8')\n\n            if auth_callback is not None:\n                auth_callback(self)\n\n            msg = AdbMessage(constants.AUTH, constants.AUTH_RSAPUBLICKEY, 0, pubkey + b'\\0')\n            await self._send(msg, adb_info)\n\n            adb_info.transport_timeout_s = auth_timeout_s\n            _, _, maxdata, _ = await self._read_expected_packet_from_device([constants.CNXN], adb_info)\n            return True, maxdata\n\n    async def read(self, expected_cmds, adb_info, allow_zeros=False):\n        \"\"\"Read packets from the device until we get an expected packet type.\n\n        1. See if the expected packet is in the packet store\n        2. While the time limit has not been exceeded:\n\n            1. See if the expected packet is in the packet store\n            2. Read a packet from the device.  If it matches what we are looking for, we are done.  If it corresponds to a different stream, add it to the store.\n\n        3. Raise a timeout exception\n\n\n        Parameters\n        ----------\n        expected_cmds : list[bytes]\n            We will read packets until we encounter one whose \"command\" field is in ``expected_cmds``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        allow_zeros : bool\n            Whether to allow the received ``arg0`` and ``arg1`` values to match with 0, in addition to ``adb_info.remote_id`` and ``adb_info.local_id``, respectively\n\n        Returns\n        -------\n        cmd : bytes\n            The received command, which is in :const:`adb_shell.constants.WIRE_TO_ID` and must be in ``expected_cmds``\n        arg0 : int\n            TODO\n        arg1 : int\n            TODO\n        data : bytes\n            The data that was read\n\n        Raises\n        ------\n        adb_shell.exceptions.AdbTimeoutError\n            Never got one of the expected responses\n\n        \"\"\"\n        # First, try reading from the store. This way, you won't be waiting for the transport if it isn't needed\n        async with self._store_lock:\n            # Recall that `arg0` from the device corresponds to `adb_info.remote_id` and `arg1` from the device corresponds to `adb_info.local_id`\n            arg0_arg1 = self._packet_store.find(adb_info.remote_id, adb_info.local_id) if not allow_zeros else self._packet_store.find_allow_zeros(adb_info.remote_id, adb_info.local_id)\n            while arg0_arg1:\n                cmd, arg0, arg1, data = self._packet_store.get(arg0_arg1[0], arg0_arg1[1])\n                if cmd in expected_cmds:\n                    return cmd, arg0, arg1, data\n\n                arg0_arg1 = self._packet_store.find(adb_info.remote_id, adb_info.local_id) if not allow_zeros else self._packet_store.find_allow_zeros(adb_info.remote_id, adb_info.local_id)\n\n        # Start the timer\n        start = time.time()\n\n        while True:\n            async with self._transport_lock:\n                # Try reading from the store (again) in case a packet got added while waiting to acquire the transport lock\n                async with self._store_lock:\n                    # Recall that `arg0` from the device corresponds to `adb_info.remote_id` and `arg1` from the device corresponds to `adb_info.local_id`\n                    arg0_arg1 = self._packet_store.find(adb_info.remote_id, adb_info.local_id) if not allow_zeros else self._packet_store.find_allow_zeros(adb_info.remote_id, adb_info.local_id)\n                    while arg0_arg1:\n                        cmd, arg0, arg1, data = self._packet_store.get(arg0_arg1[0], arg0_arg1[1])\n                        if cmd in expected_cmds:\n                            return cmd, arg0, arg1, data\n\n                        arg0_arg1 = self._packet_store.find(adb_info.remote_id, adb_info.local_id) if not allow_zeros else self._packet_store.find_allow_zeros(adb_info.remote_id, adb_info.local_id)\n\n                # Read from the device\n                cmd, arg0, arg1, data = await self._read_packet_from_device(adb_info)\n\n                if not adb_info.args_match(arg0, arg1, allow_zeros):\n                    # The packet is not a match -> put it in the store\n                    async with self._store_lock:\n                        self._packet_store.put(arg0, arg1, cmd, data)\n\n                else:\n                    # The packet is a match for this `(adb_info.local_id, adb_info.remote_id)` pair\n                    if cmd == constants.CLSE:\n                        # Clear the entry in the store\n                        async with self._store_lock:\n                            self._packet_store.clear(arg0, arg1)\n\n                    # If `cmd` is a match, then we are done\n                    if cmd in expected_cmds:\n                        return cmd, arg0, arg1, data\n\n            # Check if time is up\n            if time.time() - start > adb_info.read_timeout_s:\n                break\n\n        # Timeout\n        raise exceptions.AdbTimeoutError(\"Never got one of the expected responses: {} (transport_timeout_s = {}, read_timeout_s = {})\".format(expected_cmds, adb_info.transport_timeout_s, adb_info.read_timeout_s))\n\n    async def send(self, msg, adb_info):\n        \"\"\"Send a message to the device.\n\n        Parameters\n        ----------\n        msg : AdbMessage\n            The data that will be sent\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        async with self._transport_lock:\n            await self._send(msg, adb_info)\n\n    async def _read_expected_packet_from_device(self, expected_cmds, adb_info):\n        \"\"\"Read packets from the device until we get an expected packet type.\n\n        Parameters\n        ----------\n        expected_cmds : list[bytes]\n            We will read packets until we encounter one whose \"command\" field is in ``expected_cmds``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Returns\n        -------\n        cmd : bytes\n            The received command, which is in :const:`adb_shell.constants.WIRE_TO_ID` and must be in ``expected_cmds``\n        arg0 : int\n            TODO\n        arg1 : int\n            TODO\n        data : bytes\n            The data that was read\n\n        Raises\n        ------\n        adb_shell.exceptions.AdbTimeoutError\n            Never got one of the expected responses\n\n        \"\"\"\n        start = time.time()\n\n        while True:\n            cmd, arg0, arg1, data = await self._read_packet_from_device(adb_info)\n\n            if cmd in expected_cmds:\n                return cmd, arg0, arg1, data\n\n            if time.time() - start > adb_info.read_timeout_s:\n                # Timeout\n                raise exceptions.AdbTimeoutError(\"Never got one of the expected responses: {} (transport_timeout_s = {}, read_timeout_s = {})\".format(expected_cmds, adb_info.transport_timeout_s, adb_info.read_timeout_s))\n\n    async def _read_bytes_from_device(self, length, adb_info):\n        \"\"\"Read ``length`` bytes from the device.\n\n        Parameters\n        ----------\n        length : int\n            We will read packets until we get this length of data\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Returns\n        -------\n        bytes\n            The data that was read\n\n        Raises\n        ------\n        adb_shell.exceptions.AdbTimeoutError\n            Did not read ``length`` bytes in time\n\n        \"\"\"\n        start = time.time()\n        data = bytearray()\n\n        while length > 0:\n            temp = await self._transport.bulk_read(length, adb_info.transport_timeout_s)\n            if temp:\n                # Only log if `temp` is not empty\n                _LOGGER.debug(\"bulk_read(%d): %.1000r\", length, temp)\n\n            data += temp\n            length -= len(temp)\n\n            if length == 0:\n                break\n\n            if time.time() - start > adb_info.read_timeout_s:\n                # Timeout\n                raise exceptions.AdbTimeoutError(\"Timeout: read {} of {} bytes (transport_timeout_s = {}, read_timeout_s = {})\".format(len(data), len(data) + length, adb_info.transport_timeout_s, adb_info.read_timeout_s))\n\n        return bytes(data)\n\n    async def _read_packet_from_device(self, adb_info):\n        \"\"\"Read a complete ADB packet (header + data) from the device.\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Returns\n        -------\n        cmd : bytes\n            The received command, which is in :const:`adb_shell.constants.WIRE_TO_ID` and must be in ``expected_cmds``\n        arg0 : int\n            TODO\n        arg1 : int\n            TODO\n        bytes\n            The data that was read\n\n        Raises\n        ------\n        adb_shell.exceptions.InvalidCommandError\n            Unknown command\n        adb_shell.exceptions.InvalidChecksumError\n            Received checksum does not match the expected checksum\n\n       \"\"\"\n        msg = await self._read_bytes_from_device(constants.MESSAGE_SIZE, adb_info)\n        cmd, arg0, arg1, data_length, data_checksum = unpack(msg)\n        command = constants.WIRE_TO_ID.get(cmd)\n\n        if not command:\n            raise exceptions.InvalidCommandError(\"Unknown command: %d = '%s' (arg0 = %d, arg1 = %d, msg = '%s')\" % (cmd, int_to_cmd(cmd), arg0, arg1, msg))\n\n        if data_length == 0:\n            return command, arg0, arg1, b\"\"\n\n        data = await self._read_bytes_from_device(data_length, adb_info)\n        actual_checksum = checksum(data)\n        if actual_checksum != data_checksum:\n            raise exceptions.InvalidChecksumError(\"Received checksum {} != {}\".format(actual_checksum, data_checksum))\n\n        return command, arg0, arg1, data\n\n    async def _send(self, msg, adb_info):\n        \"\"\"Send a message to the device.\n\n        1. Send the message header (:meth:`adb_shell.adb_message.AdbMessage.pack <AdbMessage.pack>`)\n        2. Send the message data\n\n\n        Parameters\n        ----------\n        msg : AdbMessage\n            The data that will be sent\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        packed = msg.pack()\n        _LOGGER.debug(\"bulk_write(%d): %r\", len(packed), packed)\n        await self._transport.bulk_write(packed, adb_info.transport_timeout_s)\n\n        if msg.data:\n            _LOGGER.debug(\"bulk_write(%d): %r\", len(msg.data), msg.data)\n            await self._transport.bulk_write(msg.data, adb_info.transport_timeout_s)\n\n\nclass AdbDeviceAsync(object):\n    \"\"\"A class with methods for connecting to a device and executing ADB commands.\n\n    Parameters\n    ----------\n    transport : BaseTransportAsync\n        A user-provided transport for communicating with the device; must be an instance of a subclass of :class:`~adb_shell.transport.base_transport_async.BaseTransportAsync`\n    default_transport_timeout_s : float, None\n        Default timeout in seconds for transport packets, or ``None``\n    banner : str, bytes, None\n        The hostname of the machine where the Python interpreter is currently running; if\n        it is not provided, it will be determined via ``socket.gethostname()``\n\n    Raises\n    ------\n    adb_shell.exceptions.InvalidTransportError\n        The passed ``transport`` is not an instance of a subclass of :class:`~adb_shell.transport.base_transport_async.BaseTransportAsync`\n\n    Attributes\n    ----------\n    _available : bool\n        Whether an ADB connection to the device has been established\n    _banner : bytearray, bytes\n        The hostname of the machine where the Python interpreter is currently running\n    _default_transport_timeout_s : float, None\n        Default timeout in seconds for transport packets, or ``None``\n    _io_manager : _AdbIOManagerAsync\n        Used for handling all ADB I/O\n    _local_id : int\n        The local ID that is used for ADB transactions; the value is incremented each time and is always in the range ``[1, 2^32)``\n    _local_id_lock : Lock\n        A lock for protecting ``_local_id``; this is never held for long\n    _maxdata: int\n        Maximum amount of data in an ADB packet\n    _transport : BaseTransportAsync\n        The transport that is used to connect to the device; must be a subclass of :class:`~adb_shell.transport.base_transport_async.BaseTransportAsync`\n\n    \"\"\"\n\n    def __init__(self, transport, default_transport_timeout_s=None, banner=None):\n        if banner and not isinstance(banner, (bytes, bytearray)):\n            self._banner = bytearray(banner, 'utf-8')\n        else:\n            self._banner = banner\n\n        if not isinstance(transport, BaseTransportAsync):\n            raise exceptions.InvalidTransportError(\"`transport` must be an instance of a subclass of `BaseTransportAsync`\")\n\n        self._io_manager = _AdbIOManagerAsync(transport)\n\n        self._available = False\n        self._default_transport_timeout_s = default_transport_timeout_s\n        self._local_id = 0\n        self._local_id_lock = Lock()\n        self._maxdata = constants.MAX_PUSH_DATA\n\n    # ======================================================================= #\n    #                                                                         #\n    #                       Properties & simple methods                       #\n    #                                                                         #\n    # ======================================================================= #\n    @property\n    def available(self):\n        \"\"\"Whether or not an ADB connection to the device has been established.\n\n        Returns\n        -------\n        bool\n            ``self._available``\n\n        \"\"\"\n        return self._available\n\n    @property\n    def max_chunk_size(self):\n        \"\"\"Maximum chunk size for filesync operations\n\n        Returns\n        -------\n        int\n            Minimum value based on :const:`adb_shell.constants.MAX_CHUNK_SIZE` and ``_max_data / 2``, fallback to legacy :const:`adb_shell.constants.MAX_PUSH_DATA`\n\n        \"\"\"\n        return min(constants.MAX_CHUNK_SIZE, self._maxdata // 2) or constants.MAX_PUSH_DATA\n\n    def _get_transport_timeout_s(self, transport_timeout_s):\n        \"\"\"Use the provided ``transport_timeout_s`` if it is not ``None``; otherwise, use ``self._default_transport_timeout_s``\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            The potential transport timeout\n\n        Returns\n        -------\n        float\n            ``transport_timeout_s`` if it is not ``None``; otherwise, ``self._default_transport_timeout_s``\n\n        \"\"\"\n        return transport_timeout_s if transport_timeout_s is not None else self._default_transport_timeout_s\n\n    # ======================================================================= #\n    #                                                                         #\n    #                             Close & Connect                             #\n    #                                                                         #\n    # ======================================================================= #\n    async def close(self):\n        \"\"\"Close the connection via the provided transport's ``close()`` method.\n\n        \"\"\"\n        self._available = False\n        await self._io_manager.close()\n\n    async def connect(self, rsa_keys=None, transport_timeout_s=None, auth_timeout_s=constants.DEFAULT_AUTH_TIMEOUT_S, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, auth_callback=None):\n        \"\"\"Establish an ADB connection to the device.\n\n        See :meth:`_AdbIOManagerAsync.connect`.\n\n        Parameters\n        ----------\n        rsa_keys : list, None\n            A list of signers of type :class:`~adb_shell.auth.sign_cryptography.CryptographySigner`,\n            :class:`~adb_shell.auth.sign_pycryptodome.PycryptodomeAuthSigner`, or :class:`~adb_shell.auth.sign_pythonrsa.PythonRSASigner`\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        auth_timeout_s : float, None\n            The time in seconds to wait for a ``b'CNXN'`` authentication response\n        read_timeout_s : float\n            The total time in seconds to wait for expected commands in :meth:`_AdbIOManagerAsync._read_expected_packet_from_device`\n        auth_callback : function, None\n            Function callback invoked when the connection needs to be accepted on the device\n\n        Returns\n        -------\n        bool\n            Whether the connection was established (:attr:`AdbDeviceAsync.available`)\n\n        \"\"\"\n        # Get `self._banner` if it was not provided in the constructor\n        if not self._banner:\n            self._banner = await get_running_loop().run_in_executor(None, get_banner)\n\n        # Instantiate the `_AdbTransactionInfo`\n        adb_info = _AdbTransactionInfo(None, None, self._get_transport_timeout_s(transport_timeout_s), read_timeout_s, None)\n\n        # Mark the device as unavailable\n        self._available = False\n\n        # Use the IO manager to connect\n        self._available, self._maxdata = await self._io_manager.connect(self._banner, rsa_keys, auth_timeout_s, auth_callback, adb_info)\n\n        return self._available\n\n    # ======================================================================= #\n    #                                                                         #\n    #                                 Services                                #\n    #                                                                         #\n    # ======================================================================= #\n    async def _service(self, service, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True):\n        \"\"\"Send an ADB command to the device.\n\n        Parameters\n        ----------\n        service : bytes\n            The ADB service to talk to (e.g., ``b'shell'``)\n        command : bytes\n            The command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Returns\n        -------\n        bytes, str\n            The output of the ADB command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        if decode:\n            return b''.join([x async for x in self._streaming_command(service, command, transport_timeout_s, read_timeout_s, timeout_s)]).decode('utf8', 'backslashreplace')\n        return b''.join([x async for x in self._streaming_command(service, command, transport_timeout_s, read_timeout_s, timeout_s)])\n\n    async def _streaming_service(self, service, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, decode=True):\n        \"\"\"Send an ADB command to the device, yielding each line of output.\n\n        Parameters\n        ----------\n        service : bytes\n            The ADB service to talk to (e.g., ``b'shell'``)\n        command : bytes\n            The command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Yields\n        -------\n        bytes, str\n            The line-by-line output of the ADB command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        stream = self._streaming_command(service, command, transport_timeout_s, read_timeout_s, None)\n        if decode:\n            async for line in (stream_line.decode('utf8', 'backslashreplace') async for stream_line in stream):\n                yield line\n        else:\n            async for line in stream:\n                yield line\n\n    async def exec_out(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True):\n        \"\"\"Send an ADB ``exec-out`` command to the device.\n\n        https://www.linux-magazine.com/Issues/2017/195/Ask-Klaus\n\n        Parameters\n        ----------\n        command : str\n            The exec-out command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Returns\n        -------\n        bytes, str\n            The output of the ADB exec-out command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        return await self._service(b'exec', command.encode('utf8'), transport_timeout_s, read_timeout_s, timeout_s, decode)\n\n    async def reboot(self, fastboot=False, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None):\n        \"\"\"Reboot the device.\n\n        Parameters\n        ----------\n        fastboot : bool\n            Whether to reboot the device into fastboot\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManager.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        await self._open(b'reboot:bootloader' if fastboot else b'reboot:', transport_timeout_s, read_timeout_s, timeout_s)\n\n    async def root(self, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None):\n        \"\"\"Gain root access.\n\n        The device must be rooted in order for this to work.\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        await self._service(b'root', b'', transport_timeout_s, read_timeout_s, timeout_s, False)\n\n    async def shell(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True):\n        \"\"\"Send an ADB shell command to the device.\n\n        Parameters\n        ----------\n        command : str\n            The shell command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Returns\n        -------\n        bytes, str\n            The output of the ADB shell command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        return await self._service(b'shell', command.encode('utf8'), transport_timeout_s, read_timeout_s, timeout_s, decode)\n\n    async def streaming_shell(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, decode=True):\n        \"\"\"Send an ADB shell command to the device, yielding each line of output.\n\n        Parameters\n        ----------\n        command : str\n            The shell command that will be sent\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n        decode : bool\n            Whether to decode the output to utf8 before returning\n\n        Yields\n        -------\n        bytes, str\n            The line-by-line output of the ADB shell command as a string if ``decode`` is True, otherwise as bytes.\n\n        \"\"\"\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        async for line in self._streaming_service(b'shell', command.encode('utf8'), transport_timeout_s, read_timeout_s, decode):\n            yield line\n\n    # ======================================================================= #\n    #                                                                         #\n    #                                 FileSync                                #\n    #                                                                         #\n    # ======================================================================= #\n    async def list(self, device_path, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S):\n        \"\"\"Return a directory listing of the given path.\n\n        Parameters\n        ----------\n        device_path : str\n            Directory to list.\n        transport_timeout_s : float, None\n            Expected timeout for any part of the pull.\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n\n        Returns\n        -------\n        files : list[DeviceFile]\n            Filename, mode, size, and mtime info for the files in the directory\n\n        \"\"\"\n        if not device_path:\n            raise exceptions.DevicePathInvalidError(\"Cannot list an empty device path\")\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        adb_info = await self._open(b'sync:', transport_timeout_s, read_timeout_s, None)\n        filesync_info = _FileSyncTransactionInfo(constants.FILESYNC_LIST_FORMAT, maxdata=self._maxdata)\n\n        await self._filesync_send(constants.LIST, adb_info, filesync_info, data=device_path)\n        files = []\n\n        async for cmd_id, header, filename in self._filesync_read_until([constants.DENT], [constants.DONE], adb_info, filesync_info):\n            if cmd_id == constants.DONE:\n                break\n\n            mode, size, mtime = header\n            files.append(DeviceFile(filename, mode, size, mtime))\n\n        await self._clse(adb_info)\n\n        return files\n\n    async def pull(self, device_path, local_path, progress_callback=None, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S):\n        \"\"\"Pull a file from the device.\n\n        Parameters\n        ----------\n        device_path : str\n            The file on the device that will be pulled\n        local_path : str, BytesIO\n            The path or BytesIO stream where the file will be downloaded\n        progress_callback : function, None\n            Callback method that accepts ``device_path``, ``bytes_written``, and ``total_bytes``\n        transport_timeout_s : float, None\n            Expected timeout for any part of the pull.\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n\n        \"\"\"\n        if not device_path:\n            raise exceptions.DevicePathInvalidError(\"Cannot pull from an empty device path\")\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        opener = _open_bytesio if isinstance(local_path, BytesIO) else aiofiles.open\n        async with opener(local_path, 'wb') as stream:\n            adb_info = await self._open(b'sync:', transport_timeout_s, read_timeout_s, None)\n            filesync_info = _FileSyncTransactionInfo(constants.FILESYNC_PULL_FORMAT, maxdata=self._maxdata)\n\n            try:\n                await self._pull(device_path, stream, progress_callback, adb_info, filesync_info)\n            finally:\n                await self._clse(adb_info)\n\n    async def _pull(self, device_path, stream, progress_callback, adb_info, filesync_info):\n        \"\"\"Pull a file from the device into the file-like ``local_path``.\n\n        Parameters\n        ----------\n        device_path : str\n            The file on the device that will be pulled\n        stream : AsyncBufferedIOBase, _AsyncBytesIO\n            File-like object for writing to\n        progress_callback : function, None\n            Callback method that accepts ``device_path``, ``bytes_written``, and ``total_bytes``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        \"\"\"\n        if progress_callback:\n            total_bytes = (await self.stat(device_path))[1]\n\n        await self._filesync_send(constants.RECV, adb_info, filesync_info, data=device_path)\n        async for cmd_id, _, data in self._filesync_read_until([constants.DATA], [constants.DONE], adb_info, filesync_info):\n            if cmd_id == constants.DONE:\n                break\n\n            await stream.write(data)\n            if progress_callback:\n                try:\n                    await progress_callback(device_path, len(data), total_bytes)\n                except:  # noqa pylint: disable=bare-except\n                    pass\n\n    async def push(self, local_path, device_path, st_mode=constants.DEFAULT_PUSH_MODE, mtime=0, progress_callback=None, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S):\n        \"\"\"Push a file or directory to the device.\n\n        Parameters\n        ----------\n        local_path : str, BytesIO\n            A filename, directory, or BytesIO stream to push to the device\n        device_path : str\n            Destination on the device to write to\n        st_mode : int\n            Stat mode for ``local_path``\n        mtime : int\n            Modification time to set on the file\n        progress_callback : function, None\n            Callback method that accepts ``device_path``, ``bytes_written``, and ``total_bytes``\n        transport_timeout_s : float, None\n            Expected timeout for any part of the push\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n\n        \"\"\"\n        if not device_path:\n            raise exceptions.DevicePathInvalidError(\"Cannot push to an empty device path\")\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        local_path_is_dir, local_paths, device_paths = await get_running_loop().run_in_executor(None, get_files_to_push, local_path, device_path)\n\n        if local_path_is_dir:\n            await self.shell(\"mkdir \" + device_path, transport_timeout_s, read_timeout_s)\n\n        for _local_path, _device_path in zip(local_paths, device_paths):\n            opener = _open_bytesio if isinstance(local_path, BytesIO) else aiofiles.open\n            async with opener(_local_path, 'rb') as stream:\n                adb_info = await self._open(b'sync:', transport_timeout_s, read_timeout_s, None)\n                filesync_info = _FileSyncTransactionInfo(constants.FILESYNC_PUSH_FORMAT, maxdata=self._maxdata)\n\n                await self._push(stream, _device_path, st_mode, mtime, progress_callback, adb_info, filesync_info)\n\n            await self._clse(adb_info)\n\n    async def _push(self, stream, device_path, st_mode, mtime, progress_callback, adb_info, filesync_info):\n        \"\"\"Push a file-like object to the device.\n\n        Parameters\n        ----------\n        stream : AsyncBufferedReader, _AsyncBytesIO\n            File-like object for reading from\n        device_path : str\n            Destination on the device to write to\n        st_mode : int\n            Stat mode for the file\n        mtime : int\n            Modification time\n        progress_callback : function, None\n            Callback method that accepts ``device_path``, ``bytes_written``, and ``total_bytes``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Raises\n        ------\n        PushFailedError\n            Raised on push failure.\n\n        \"\"\"\n        fileinfo = ('{},{}'.format(device_path, int(st_mode))).encode('utf-8')\n\n        await self._filesync_send(constants.SEND, adb_info, filesync_info, data=fileinfo)\n\n        if progress_callback:\n            total_bytes = (await get_running_loop().run_in_executor(None, os.fstat, stream.fileno())).st_size\n\n        while True:\n            data = await stream.read(self.max_chunk_size)\n            if data:\n                await self._filesync_send(constants.DATA, adb_info, filesync_info, data=data)\n\n                if progress_callback:\n                    try:\n                        await progress_callback(device_path, len(data), total_bytes)\n                    except:  # noqa pylint: disable=bare-except\n                        pass\n            else:\n                break\n\n        if mtime == 0:\n            mtime = int(time.time())\n\n        # DONE doesn't send data, but it hides the last bit of data in the size field.\n        await self._filesync_send(constants.DONE, adb_info, filesync_info, size=mtime)\n        async for cmd_id, _, data in self._filesync_read_until([], [constants.OKAY, constants.FAIL], adb_info, filesync_info):\n            if cmd_id == constants.OKAY:\n                return\n\n            raise exceptions.PushFailedError(data)\n\n    async def stat(self, device_path, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S):\n        \"\"\"Get a file's ``stat()`` information.\n\n        Parameters\n        ----------\n        device_path : str\n            The file on the device for which we will get information.\n        transport_timeout_s : float, None\n            Expected timeout for any part of the pull.\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n\n        Returns\n        -------\n        mode : int\n            The octal permissions for the file\n        size : int\n            The size of the file\n        mtime : int\n            The last modified time for the file\n\n        \"\"\"\n        if not device_path:\n            raise exceptions.DevicePathInvalidError(\"Cannot stat an empty device path\")\n        if not self.available:\n            raise exceptions.AdbConnectionError(\"ADB command not sent because a connection to the device has not been established.  (Did you call `AdbDeviceAsync.connect()`?)\")\n\n        adb_info = await self._open(b'sync:', transport_timeout_s, read_timeout_s, None)\n        filesync_info = _FileSyncTransactionInfo(constants.FILESYNC_STAT_FORMAT, maxdata=self._maxdata)\n\n        await self._filesync_send(constants.STAT, adb_info, filesync_info, data=device_path)\n        _, (mode, size, mtime), _ = await self._filesync_read([constants.STAT], adb_info, filesync_info)\n        await self._clse(adb_info)\n\n        return mode, size, mtime\n\n    # ======================================================================= #\n    #                                                                         #\n    #                       Hidden Methods: send packets                      #\n    #                                                                         #\n    # ======================================================================= #\n    async def _clse(self, adb_info):\n        \"\"\"Send a ``b'CLSE'`` message and then read a ``b'CLSE'`` message.\n\n        .. warning::\n\n           This is not to be confused with the :meth:`AdbDeviceAsync.close` method!\n\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        msg = AdbMessage(constants.CLSE, adb_info.local_id, adb_info.remote_id)\n        await self._io_manager.send(msg, adb_info)\n        await self._read_until([constants.CLSE], adb_info)\n\n    async def _okay(self, adb_info):\n        \"\"\"Send an ``b'OKAY'`` mesage.\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        msg = AdbMessage(constants.OKAY, adb_info.local_id, adb_info.remote_id)\n        await self._io_manager.send(msg, adb_info)\n\n    # ======================================================================= #\n    #                                                                         #\n    #                              Hidden Methods                             #\n    #                                                                         #\n    # ======================================================================= #\n    async def _open(self, destination, transport_timeout_s, read_timeout_s, timeout_s):\n        \"\"\"Opens a new connection to the device via an ``b'OPEN'`` message.\n\n        1. :meth:`~_AdbIOManagerAsync.send` an ``b'OPEN'`` command to the device that specifies the ``local_id``\n        2. :meth:`~_AdbIOManagerAsync.read` the response from the device and fill in the ``adb_info.remote_id`` attribute\n\n\n        Parameters\n        ----------\n        destination : bytes\n            ``b'SERVICE:COMMAND'``\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n\n        Returns\n        -------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        \"\"\"\n        async with self._local_id_lock:\n            self._local_id += 1\n            if self._local_id == 2**32:\n                self._local_id = 1\n\n            adb_info = _AdbTransactionInfo(self._local_id, None, self._get_transport_timeout_s(transport_timeout_s), read_timeout_s, timeout_s)\n\n        msg = AdbMessage(constants.OPEN, adb_info.local_id, 0, destination + b'\\0')\n        await self._io_manager.send(msg, adb_info)\n        _, adb_info.remote_id, _, _ = await self._io_manager.read([constants.OKAY], adb_info)\n\n        return adb_info\n\n    async def _read_until(self, expected_cmds, adb_info):\n        \"\"\"Read a packet, acknowledging any write packets.\n\n        1. Read data via :meth:`_AdbIOManagerAsync.read`\n        2. If a ``b'WRTE'`` packet is received, send an ``b'OKAY'`` packet via :meth:`AdbDeviceAsync._okay`\n        3. Return the ``cmd`` and ``data`` that were read by :meth:`_AdbIOManagerAsync.read`\n\n\n        Parameters\n        ----------\n        expected_cmds : list[bytes]\n            :meth:`_AdbIOManagerAsync.read` will look for a packet whose command is in ``expected_cmds``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Returns\n        -------\n        cmd : bytes\n            The command that was received by :meth:`_AdbIOManagerAsync.read`, which is in :const:`adb_shell.constants.WIRE_TO_ID` and must be in ``expected_cmds``\n        data : bytes\n            The data that was received by :meth:`_AdbIOManagerAsync.read`\n\n        \"\"\"\n        cmd, _, _, data = await self._io_manager.read(expected_cmds, adb_info, allow_zeros=True)\n\n        # Acknowledge write packets\n        if cmd == constants.WRTE:\n            await self._okay(adb_info)\n\n        return cmd, data\n\n    async def _read_until_close(self, adb_info):\n        \"\"\"Yield packets until a ``b'CLSE'`` packet is received.\n\n        1. Read the ``cmd`` and ``data`` fields from a ``b'CLSE'`` or ``b'WRTE'`` packet via :meth:`AdbDeviceAsync._read_until`\n        2. If ``cmd`` is ``b'CLSE'``, then send a ``b'CLSE'`` message and stop\n        3. Yield ``data`` and repeat\n\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n\n        Yields\n        ------\n        data : bytes\n            The data that was read by :meth:`AdbDeviceAsync._read_until`\n\n        \"\"\"\n        start = time.time()\n\n        while True:\n            cmd, data = await self._read_until([constants.CLSE, constants.WRTE], adb_info)\n\n            if cmd == constants.CLSE:\n                msg = AdbMessage(constants.CLSE, adb_info.local_id, adb_info.remote_id)\n                await self._io_manager.send(msg, adb_info)\n                break\n\n            yield data\n\n            # Make sure the ADB command has not timed out\n            if adb_info.timeout_s is not None and time.time() - start > adb_info.timeout_s:\n                raise exceptions.AdbTimeoutError(\"The command did not complete within {} seconds\".format(adb_info.timeout_s))\n\n    async def _streaming_command(self, service, command, transport_timeout_s, read_timeout_s, timeout_s):\n        \"\"\"One complete set of packets for a single command.\n\n        1. :meth:`~AdbDeviceAsync._open` a new connection to the device, where the ``destination`` parameter is ``service:command``\n        2. Read the response data via :meth:`AdbDeviceAsync._read_until_close`\n\n\n        .. note::\n\n           All the data is held in memory, and thus large responses will be slow and can fill up memory.\n\n\n        Parameters\n        ----------\n        service : bytes\n            The ADB service (e.g., ``b'shell'``, as used by :meth:`AdbDeviceAsync.shell`)\n        command : bytes\n            The service command\n        transport_timeout_s : float, None\n            Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`\n            and :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n        read_timeout_s : float\n            The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`_AdbIOManagerAsync.read`\n        timeout_s : float, None\n            The total time in seconds to wait for the ADB command to finish\n\n        Yields\n        ------\n        bytes\n            The responses from the service.\n\n        \"\"\"\n        adb_info = await self._open(b'%s:%s' % (service, command), transport_timeout_s, read_timeout_s, timeout_s)\n\n        async for data in self._read_until_close(adb_info):\n            yield data\n\n    # ======================================================================= #\n    #                                                                         #\n    #                         FileSync Hidden Methods                         #\n    #                                                                         #\n    # ======================================================================= #\n    async def _filesync_flush(self, adb_info, filesync_info):\n        \"\"\"Write the data in the buffer up to ``filesync_info.send_idx``, then set ``filesync_info.send_idx`` to 0.\n\n        Parameters\n        ----------\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        \"\"\"\n        # Send the buffer\n        msg = AdbMessage(constants.WRTE, adb_info.local_id, adb_info.remote_id, filesync_info.send_buffer[:filesync_info.send_idx])\n        await self._io_manager.send(msg, adb_info)\n\n        # Expect an 'OKAY' in response\n        await self._read_until([constants.OKAY], adb_info)\n\n        # Reset the send index\n        filesync_info.send_idx = 0\n\n    async def _filesync_read(self, expected_ids, adb_info, filesync_info):\n        \"\"\"Read ADB messages and return FileSync packets.\n\n        Parameters\n        ----------\n        expected_ids : tuple[bytes]\n            If the received header ID is not in ``expected_ids``, an exception will be raised\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        Returns\n        -------\n        command_id : bytes\n            The received header ID\n        tuple\n            The contents of the header\n        data : bytearray, None\n            The received data, or ``None`` if the command ID is :const:`adb_shell.constants.STAT`\n\n        Raises\n        ------\n        adb_shell.exceptions.AdbCommandFailureException\n            Command failed\n        adb_shell.exceptions.InvalidResponseError\n            Received response was not in ``expected_ids``\n\n        \"\"\"\n        if filesync_info.send_idx:\n            await self._filesync_flush(adb_info, filesync_info)\n\n        # Read one filesync packet off the recv buffer.\n        header_data = await self._filesync_read_buffered(filesync_info.recv_message_size, adb_info, filesync_info)\n        header = struct.unpack(filesync_info.recv_message_format, header_data)\n\n        # Header is (ID, ...).\n        command_id = constants.FILESYNC_WIRE_TO_ID[header[0]]\n\n        # Whether there is data to read\n        read_data = command_id != constants.STAT\n\n        if read_data:\n            # Header is (ID, ..., size) --> read the data\n            data = await self._filesync_read_buffered(header[-1], adb_info, filesync_info)\n        else:\n            # No data to be read\n            data = bytearray()\n\n        if command_id not in expected_ids:\n            if command_id == constants.FAIL:\n                reason = data.decode('utf-8', errors='backslashreplace')\n\n                raise exceptions.AdbCommandFailureException('Command failed: {}'.format(reason))\n\n            raise exceptions.InvalidResponseError('Expected one of %s, got %s' % (expected_ids, command_id))\n\n        if not read_data:\n            return command_id, header[1:], None\n\n        return command_id, header[1:-1], data\n\n    async def _filesync_read_buffered(self, size, adb_info, filesync_info):\n        \"\"\"Read ``size`` bytes of data from ``self.recv_buffer``.\n\n        Parameters\n        ----------\n        size : int\n            The amount of data to read\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        Returns\n        -------\n        result : bytearray\n            The read data\n\n        \"\"\"\n        # Ensure recv buffer has enough data.\n        while len(filesync_info.recv_buffer) < size:\n            _, data = await self._read_until([constants.WRTE], adb_info)\n            filesync_info.recv_buffer += data\n\n        result = filesync_info.recv_buffer[:size]\n        filesync_info.recv_buffer = filesync_info.recv_buffer[size:]\n        return result\n\n    async def _filesync_read_until(self, expected_ids, finish_ids, adb_info, filesync_info):\n        \"\"\"Useful wrapper around :meth:`AdbDeviceAsync._filesync_read`.\n\n        Parameters\n        ----------\n        expected_ids : tuple[bytes]\n            If the received header ID is not in ``expected_ids``, an exception will be raised\n        finish_ids : tuple[bytes]\n            We will read until we find a header ID that is in ``finish_ids``\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n\n        Yields\n        ------\n        cmd_id : bytes\n            The received header ID\n        header : tuple\n            TODO\n        data : bytearray\n            The received data\n\n        \"\"\"\n        while True:\n            cmd_id, header, data = await self._filesync_read(expected_ids + finish_ids, adb_info, filesync_info)\n            yield cmd_id, header, data\n\n            # These lines are not reachable because whenever this method is called and `cmd_id` is in `finish_ids`, the code\n            # either breaks (`list` and `_pull`), returns (`_push`), or raises an exception (`_push`)\n            if cmd_id in finish_ids:  # pragma: no cover\n                break\n\n    async def _filesync_send(self, command_id, adb_info, filesync_info, data=b'', size=None):\n        \"\"\"Send/buffer FileSync packets.\n\n        Packets are buffered and only flushed when this connection is read from. All\n        messages have a response from the device, so this will always get flushed.\n\n        Parameters\n        ----------\n        command_id : bytes\n            Command to send.\n        adb_info : _AdbTransactionInfo\n            Info and settings for this ADB transaction\n        filesync_info : _FileSyncTransactionInfo\n            Data and storage for this FileSync transaction\n        data : str, bytes\n            Optional data to send, must set data or size.\n        size : int, None\n            Optionally override size from len(data).\n\n        \"\"\"\n        if not isinstance(data, bytes):\n            data = data.encode('utf8')\n        if size is None:\n            size = len(data)\n\n        if not filesync_info.can_add_to_send_buffer(len(data)):\n            await self._filesync_flush(adb_info, filesync_info)\n\n        buf = struct.pack(b'<2I', constants.FILESYNC_ID_TO_WIRE[command_id], size) + data\n        filesync_info.send_buffer[filesync_info.send_idx:filesync_info.send_idx + len(buf)] = buf\n        filesync_info.send_idx += len(buf)\n\n\nclass AdbDeviceTcpAsync(AdbDeviceAsync):\n    \"\"\"A class with methods for connecting to a device via TCP and executing ADB commands.\n\n    Parameters\n    ----------\n    host : str\n        The address of the device; may be an IP address or a host name\n    port : int\n        The device port to which we are connecting (default is 5555)\n    default_transport_timeout_s : float, None\n        Default timeout in seconds for TCP packets, or ``None``\n    banner : str, bytes, None\n        The hostname of the machine where the Python interpreter is currently running; if\n        it is not provided, it will be determined via ``socket.gethostname()``\n\n    Attributes\n    ----------\n    _available : bool\n        Whether an ADB connection to the device has been established\n    _banner : bytearray, bytes\n        The hostname of the machine where the Python interpreter is currently running\n    _default_transport_timeout_s : float, None\n        Default timeout in seconds for TCP packets, or ``None``\n    _local_id : int\n        The local ID that is used for ADB transactions; the value is incremented each time and is always in the range ``[1, 2^32)``\n    _maxdata : int\n        Maximum amount of data in an ADB packet\n    _transport : TcpTransportAsync\n        The transport that is used to connect to the device\n\n    \"\"\"\n\n    def __init__(self, host, port=5555, default_transport_timeout_s=None, banner=None):\n        transport = TcpTransportAsync(host, port)\n        super(AdbDeviceTcpAsync, self).__init__(transport, default_transport_timeout_s, banner)\n"
  },
  {
    "path": "adb_shell/adb_message.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It incorporates work\n# covered by the following license notice:\n#\n#\n#   Copyright 2014 Google Inc. All rights reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\n\"\"\"Functions and an :class:`AdbMessage` class for packing and unpacking ADB messages.\n\n.. rubric:: Contents\n\n* :class:`AdbMessage`\n\n    * :attr:`AdbMessage.checksum`\n    * :meth:`AdbMessage.pack`\n\n* :func:`checksum`\n* :func:`int_to_cmd`\n* :func:`unpack`\n\n\"\"\"\n\n\nimport struct\n\nfrom . import constants\n\n\ndef checksum(data):\n    \"\"\"Calculate the checksum of the provided data.\n\n    Parameters\n    ----------\n    data : bytearray, bytes, str\n        The data\n\n    Returns\n    -------\n    int\n        The checksum\n\n    \"\"\"\n    # The checksum is just a sum of all the bytes. I swear.\n    if isinstance(data, bytearray):\n        total = sum(data)\n\n    elif isinstance(data, bytes):\n        if data and isinstance(data[0], bytes):\n            # Python 2 bytes (str) index as single-character strings.\n            total = sum((ord(d) for d in data))  # pragma: no cover\n        else:\n            # Python 3 bytes index as numbers (and PY2 empty strings sum() to 0)\n            total = sum(data)\n\n    else:\n        # Unicode strings (should never see?)\n        total = sum((ord(d) for d in data))\n\n    return total & 0xFFFFFFFF\n\n\ndef int_to_cmd(n):\n    \"\"\"Convert from an integer (4 bytes) to an ADB command.\n\n    Parameters\n    ----------\n    n : int\n        The integer that will be converted to an ADB command\n\n    Returns\n    -------\n    str\n        The ADB command (e.g., ``'CNXN'``)\n\n    \"\"\"\n    return ''.join(chr((n >> (i * 8)) % 256) for i in range(4)).encode('utf-8')\n\n\ndef unpack(message):\n    \"\"\"Unpack a received ADB message.\n\n    Parameters\n    ----------\n    message : bytes\n        The received message\n\n    Returns\n    -------\n    cmd : int\n        The ADB command\n    arg0 : int\n        TODO\n    arg1 : int\n        TODO\n    data_length : int\n        The length of the message's data\n    data_checksum : int\n        The checksum of the message's data\n\n    Raises\n    ------\n    ValueError\n        Unable to unpack the ADB command.\n\n    \"\"\"\n    try:\n        cmd, arg0, arg1, data_length, data_checksum, _ = struct.unpack(constants.MESSAGE_FORMAT, message)\n    except struct.error as e:\n        raise ValueError('Unable to unpack ADB command. (length={})'.format(len(message)), constants.MESSAGE_FORMAT, message, e)\n\n    return cmd, arg0, arg1, data_length, data_checksum\n\n\nclass AdbMessage(object):\n    \"\"\"A helper class for packing ADB messages.\n\n    Parameters\n    ----------\n    command : bytes\n        A command; examples used in this package include :const:`adb_shell.constants.AUTH`, :const:`adb_shell.constants.CNXN`, :const:`adb_shell.constants.CLSE`, :const:`adb_shell.constants.OPEN`, and :const:`adb_shell.constants.OKAY`\n    arg0 : int\n        Usually the local ID, but :meth:`~adb_shell.adb_device.AdbDevice.connect` and :meth:`~adb_shell.adb_device_async.AdbDeviceAsync.connect` provide :const:`adb_shell.constants.VERSION`, :const:`adb_shell.constants.AUTH_SIGNATURE`, and :const:`adb_shell.constants.AUTH_RSAPUBLICKEY`\n    arg1 : int\n        Usually the remote ID, but :meth:`~adb_shell.adb_device.AdbDevice.connect` and :meth:`~adb_shell.adb_device_async.AdbDeviceAsync.connect` provide :const:`adb_shell.constants.MAX_ADB_DATA`\n    data : bytes\n        The data that will be sent\n\n    Attributes\n    ----------\n    arg0 : int\n        Usually the local ID, but :meth:`~adb_shell.adb_device.AdbDevice.connect` and :meth:`~adb_shell.adb_device_async.AdbDeviceAsync.connect` provide :const:`adb_shell.constants.VERSION`, :const:`adb_shell.constants.AUTH_SIGNATURE`, and :const:`adb_shell.constants.AUTH_RSAPUBLICKEY`\n    arg1 : int\n        Usually the remote ID, but :meth:`~adb_shell.adb_device.AdbDevice.connect` and :meth:`~adb_shell.adb_device_async.AdbDeviceAsync.connect` provide :const:`adb_shell.constants.MAX_ADB_DATA`\n    command : int\n        The input parameter ``command`` converted to an integer via :const:`adb_shell.constants.ID_TO_WIRE`\n    data : bytes\n        The data that will be sent\n    magic : int\n        ``self.command`` with its bits flipped; in other words, ``self.command + self.magic == 2**32 - 1``\n\n    \"\"\"\n    def __init__(self, command, arg0, arg1, data=b''):\n        self.command = constants.ID_TO_WIRE[command]\n        self.magic = self.command ^ 0xFFFFFFFF\n        self.arg0 = arg0\n        self.arg1 = arg1\n        self.data = data\n\n    def pack(self):\n        \"\"\"Returns this message in an over-the-wire format.\n\n        Returns\n        -------\n        bytes\n            The message packed into the format required by ADB\n\n        \"\"\"\n        return struct.pack(constants.MESSAGE_FORMAT, self.command, self.arg0, self.arg1, len(self.data), self.checksum, self.magic)\n\n    @property\n    def checksum(self):\n        \"\"\"Return ``checksum(self.data)``\n\n        Returns\n        -------\n        int\n            The checksum of ``self.data``\n\n        \"\"\"\n        return checksum(self.data)\n"
  },
  {
    "path": "adb_shell/auth/__init__.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.\n"
  },
  {
    "path": "adb_shell/auth/keygen.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It was originally written by\n# @joeleong, and it was obtained from: https://github.com/google/python-adb/pull/144\n\n\"\"\"This file implements encoding and decoding logic for Android's custom RSA\npublic key binary format. Public keys are stored as a sequence of\nlittle-endian 32 bit words. Note that Android only supports little-endian\nprocessors, so we don't do any byte order conversions when parsing the binary\nstruct.\n\nStructure from:\nhttps://github.com/aosp-mirror/platform_system_core/blob/c55fab4a59cfa461857c6a61d8a0f1ae4591900c/libcrypto_utils/android_pubkey.c\n\n.. code-block:: c\n\n   typedef struct RSAPublicKey {\n       // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE_WORDS\n       uint32_t modulus_size_words;\n\n       // Precomputed montgomery parameter: -1 / n[0] mod 2^32\n       uint32_t n0inv;\n\n       // RSA modulus as a little-endian array\n       uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];\n\n       // Montgomery parameter R^2 as a little-endian array of little-endian words\n       uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];\n\n       // RSA modulus: 3 or 65537\n       uint32_t exponent;\n   } RSAPublicKey;\n\n\n.. rubric:: Contents\n\n* :func:`_to_bytes`\n* :func:`decode_pubkey`\n* :func:`decode_pubkey_file`\n* :func:`encode_pubkey`\n* :func:`get_user_info`\n* :func:`keygen`\n* :func:`write_public_keyfile`\n\n\"\"\"\n\n\nimport os\nimport base64\nimport logging\nimport socket\nimport struct\nimport sys\n\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.primitives import serialization\nfrom cryptography.hazmat.primitives.asymmetric import rsa\n\n\n_LOGGER = logging.getLogger(__name__)\n\nif sys.version_info[0] == 2:  # pragma: no cover\n    FileNotFoundError = IOError  # pylint: disable=redefined-builtin\n\n\n#: Size of an RSA modulus such as an encrypted block or a signature.\nANDROID_PUBKEY_MODULUS_SIZE = 2048 // 8\n\n#: Python representation of \"struct RSAPublicKey\"\nANDROID_RSAPUBLICKEY_STRUCT = (\n    '<'                 # Little-endian\n    'L'                 # uint32_t modulus_size_words;\n    'L'                 # uint32_t n0inv;\n    '{modulus_size}s'   # uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];\n    '{modulus_size}s'   # uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];\n    'L'                 # uint32_t exponent;\n).format(modulus_size=ANDROID_PUBKEY_MODULUS_SIZE)\n\n\n#: Size of the RSA modulus in words.\nANDROID_PUBKEY_MODULUS_SIZE_WORDS = ANDROID_PUBKEY_MODULUS_SIZE // 4\n\n\ndef _to_bytes(n, length, endianess='big'):\n    \"\"\"Partial python2 compatibility with int.to_bytes\n\n    https://stackoverflow.com/a/20793663\n\n    Parameters\n    ----------\n    n : TODO\n        TODO\n    length : TODO\n        TODO\n    endianess : str, TODO\n        TODO\n\n    Returns\n    -------\n    TODO\n        TODO\n\n    \"\"\"\n    if not hasattr(n, 'to_bytes'):\n        h = '{:x}'.format(n)\n        s = ('0' * (len(h) % 2) + h).zfill(length * 2).decode('hex')\n        return s if endianess == 'big' else s[::-1]\n    return n.to_bytes(length, endianess)\n\n\ndef decode_pubkey(public_key):\n    \"\"\"Decode a public RSA key stored in Android's custom binary format.\n\n    Parameters\n    ----------\n    public_key : TODO\n        TODO\n\n    \"\"\"\n    binary_key_data = base64.b64decode(public_key)\n    modulus_size_words, n0inv, modulus_bytes, rr_bytes, exponent = struct.unpack(ANDROID_RSAPUBLICKEY_STRUCT, binary_key_data)\n    assert modulus_size_words == ANDROID_PUBKEY_MODULUS_SIZE_WORDS\n    modulus = reversed(modulus_bytes)\n    rr = reversed(rr_bytes)\n    _LOGGER.debug('modulus_size_words: %s', hex(modulus_size_words))\n    _LOGGER.debug('n0inv: %s', hex(n0inv))\n    _LOGGER.debug('modulus: %s', ':'.join((hex(m) for m in modulus)))\n    _LOGGER.debug('rr: %s', ':'.join((hex(r) for r in rr)))\n    _LOGGER.debug('exponent: %s', hex(exponent))\n\n\ndef decode_pubkey_file(public_key_path):\n    \"\"\"TODO\n\n    Parameters\n    ----------\n    public_key_path : str\n        TODO\n\n    \"\"\"\n    with open(public_key_path, 'rb') as fd:\n        decode_pubkey(fd.read())\n\n\ndef encode_pubkey(private_key_path):\n    \"\"\"Encodes a public RSA key into Android's custom binary format.\n\n    Parameters\n    ----------\n    private_key_path : str\n        TODO\n\n    Returns\n    -------\n    TODO\n        TODO\n\n    \"\"\"\n    with open(private_key_path, 'rb') as key_file:\n        key = serialization.load_pem_private_key(key_file.read(), password=None, backend=default_backend()).private_numbers().public_numbers\n\n    # Compute and store n0inv = -1 / N[0] mod 2^32.\n    # BN_set_bit(r32, 32)\n    r32 = 1 << 32\n    # BN_mod(n0inv, key->n, r32, ctx)\n    n0inv = key.n % r32\n    # BN_mod_inverse(n0inv, n0inv, r32, ctx)\n    n0inv = rsa._modinv(n0inv, r32)  # pylint: disable=protected-access\n    # BN_sub(n0inv, r32, n0inv)\n    n0inv = r32 - n0inv\n\n    # Compute and store rr = (2^(rsa_size)) ^ 2 mod N.\n    # BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8)\n    rr = 1 << (ANDROID_PUBKEY_MODULUS_SIZE * 8)\n    # BN_mod_sqr(rr, rr, key->n, ctx)\n    rr = (rr ** 2) % key.n\n\n    return struct.pack(\n        ANDROID_RSAPUBLICKEY_STRUCT,\n        ANDROID_PUBKEY_MODULUS_SIZE_WORDS,\n        n0inv,\n        _to_bytes(key.n, ANDROID_PUBKEY_MODULUS_SIZE, 'little'),\n        _to_bytes(rr, ANDROID_PUBKEY_MODULUS_SIZE, 'little'),\n        key.e\n    )\n\n\ndef get_user_info():\n    \"\"\"TODO\n\n    Returns\n    -------\n    str\n        ``' <username>@<hostname>``\n\n    \"\"\"\n    try:\n        username = os.getlogin()\n    except (FileNotFoundError, OSError):\n        username = 'unknown'\n\n    if not username:\n        username = 'unknown'\n\n    hostname = socket.gethostname()\n    if not hostname:\n        hostname = 'unknown'\n\n    return ' ' + username + '@' + hostname\n\n\ndef write_public_keyfile(private_key_path, public_key_path):\n    \"\"\"Write a public keyfile to ``public_key_path`` in Android's custom\n    RSA public key format given a path to a private keyfile.\n\n    Parameters\n    ----------\n    private_key_path : TODO\n        TODO\n    public_key_path : TODO\n        TODO\n\n    \"\"\"\n    public_key = encode_pubkey(private_key_path)\n    assert len(public_key) == struct.calcsize(ANDROID_RSAPUBLICKEY_STRUCT)\n\n    with open(public_key_path, 'wb') as public_key_file:\n        public_key_file.write(base64.b64encode(public_key))\n        public_key_file.write(get_user_info().encode())\n\n\ndef keygen(filepath):\n    \"\"\"Generate an ADB public/private key pair.\n\n    * The private key is stored in ``filepath``.\n    * The public key is stored in ``filepath + '.pub'``\n\n    (Existing files will be overwritten.)\n\n    Parameters\n    ----------\n    filepath : str\n        File path to write the private/public keypair\n\n    \"\"\"\n    private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())\n\n    with open(filepath, 'wb') as private_key_file:\n        private_key_file.write(private_key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption()))\n\n    write_public_keyfile(filepath, filepath + '.pub')\n"
  },
  {
    "path": "adb_shell/auth/sign_cryptography.py",
    "content": "# Copyright 2014 Google Inc. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"ADB authentication using the ``cryptography`` package.\n\n\n.. rubric:: Contents\n\n* :class:`CryptographySigner`\n\n    * :meth:`CryptographySigner.GetPublicKey`\n    * :meth:`CryptographySigner.Sign`\n\n\"\"\"\n\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives import serialization\nfrom cryptography.hazmat.primitives.asymmetric import padding\nfrom cryptography.hazmat.primitives.asymmetric import utils\n\n\n# pylint: disable=abstract-method\nclass CryptographySigner(object):\n    \"\"\"AuthSigner using cryptography.io.\n\n    Parameters\n    ----------\n    rsa_key_path : str\n        The path to the private key.\n\n    Attributes\n    ----------\n    public_key : str\n        The contents of the public key file\n    rsa_key : cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey\n        The loaded private key\n\n    \"\"\"\n    def __init__(self, rsa_key_path):\n        with open(rsa_key_path + '.pub', 'rb') as rsa_pub_file:\n            self.public_key = rsa_pub_file.read()\n\n        with open(rsa_key_path, 'rb') as rsa_prv_file:\n            self.rsa_key = serialization.load_pem_private_key(rsa_prv_file.read(), None, default_backend())\n\n    def Sign(self, data):\n        \"\"\"Signs given data using a private key.\n\n        Parameters\n        ----------\n        data : TODO\n            TODO\n\n        Returns\n        -------\n        TODO\n            The signed ``data``\n\n        \"\"\"\n        return self.rsa_key.sign(data, padding.PKCS1v15(), utils.Prehashed(hashes.SHA1()))\n\n    def GetPublicKey(self):\n        \"\"\"Returns the public key in PEM format without headers or newlines.\n\n        Returns\n        -------\n        self.public_key : str\n            The contents of the public key file\n\n        \"\"\"\n        return self.public_key\n"
  },
  {
    "path": "adb_shell/auth/sign_pycryptodome.py",
    "content": "# Copyright 2014 Google Inc. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"ADB authentication using ``pycryptodome``.\n\n\n.. rubric:: Contents\n\n* :class:`PycryptodomeAuthSigner`\n\n    * :meth:`PycryptodomeAuthSigner.GetPublicKey`\n    * :meth:`PycryptodomeAuthSigner.Sign`\n\n\"\"\"\n\nfrom Crypto.Hash import SHA256\nfrom Crypto.PublicKey import RSA\nfrom Crypto.Signature import pkcs1_15\n\n\nclass PycryptodomeAuthSigner(object):\n    \"\"\"AuthSigner using the pycryptodome package.\n\n    Parameters\n    ----------\n    rsa_key_path : str, None\n        The path to the private key\n\n    Attributes\n    ----------\n    public_key : str\n        The contents of the public key file\n    rsa_key : Crypto.PublicKey.RSA.RsaKey\n        The contents of theprivate key\n\n    \"\"\"\n    def __init__(self, rsa_key_path=None):\n        super(PycryptodomeAuthSigner, self).__init__()\n\n        if rsa_key_path:\n            with open(rsa_key_path + '.pub', 'rb') as rsa_pub_file:\n                self.public_key = rsa_pub_file.read()\n\n            with open(rsa_key_path, 'rb') as rsa_priv_file:\n                self.rsa_key = RSA.import_key(rsa_priv_file.read())\n\n    def Sign(self, data):\n        \"\"\"Signs given data using a private key.\n\n        Parameters\n        ----------\n        data : bytes, bytearray\n            The data to be signed\n\n        Returns\n        -------\n        bytes\n            The signed ``data``\n\n        \"\"\"\n        h = SHA256.new(data)\n        return pkcs1_15.new(self.rsa_key).sign(h)\n\n    def GetPublicKey(self):\n        \"\"\"Returns the public key in PEM format without headers or newlines.\n\n        Returns\n        -------\n        self.public_key : str\n            The contents of the public key file\n\n        \"\"\"\n        return self.public_key\n"
  },
  {
    "path": "adb_shell/auth/sign_pythonrsa.py",
    "content": "# Copyright 2014 Google Inc. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"ADB authentication using the ``rsa`` package.\n\n\n.. rubric:: Contents\n\n* :class:`_Accum`\n\n    * :meth:`_Accum.digest`\n    * :meth:`_Accum.update`\n\n* :func:`_load_rsa_private_key`\n* :class:`PythonRSASigner`\n\n    * :meth:`PythonRSASigner.FromRSAKeyPath`\n    * :meth:`PythonRSASigner.GetPublicKey`\n    * :meth:`PythonRSASigner.Sign`\n\n\"\"\"\n\n\nfrom pyasn1.codec.der import decoder\nfrom pyasn1.type import univ\nimport rsa\nfrom rsa import pkcs1\n\n\nclass _Accum(object):\n    \"\"\"A fake hashing algorithm.\n\n    The Python ``rsa`` lib hashes all messages it signs. ADB does it already, we just\n    need to slap a signature on top of already hashed message.  Introduce a \"fake\"\n    hashing algo for this.\n\n    Attributes\n    ----------\n    _buf : bytes\n        A buffer for storing data before it is signed\n\n    \"\"\"\n    def __init__(self):\n        self._buf = b''\n\n    def update(self, msg):\n        \"\"\"Update this hash object's state with the provided ``msg``.\n\n        Parameters\n        ----------\n        msg : bytes\n            The message to be appended to ``self._buf``\n\n        \"\"\"\n        self._buf += msg\n\n    def digest(self):\n        \"\"\"Return the digest value as a string of binary data.\n\n        Returns\n        -------\n        self._buf : bytes\n            ``self._buf``\n\n        \"\"\"\n        return self._buf\n\n\npkcs1.HASH_METHODS['SHA-1-PREHASHED'] = _Accum\npkcs1.HASH_ASN1['SHA-1-PREHASHED'] = pkcs1.HASH_ASN1['SHA-1']\n\n\ndef _load_rsa_private_key(pem):\n    \"\"\"PEM encoded PKCS#8 private key -> ``rsa.PrivateKey``.\n\n    ADB uses private RSA keys in pkcs#8 format. The ``rsa`` library doesn't\n    support them natively.  Do some ASN unwrapping to extract naked RSA key\n    (in der-encoded form).\n\n    See:\n\n    * https://www.ietf.org/rfc/rfc2313.txt\n    * http://superuser.com/a/606266\n\n    Parameters\n    ----------\n    pem : str\n        The private key to be loaded\n\n    Returns\n    -------\n    rsa.key.PrivateKey\n        The loaded private key\n\n    \"\"\"\n    try:\n        der = rsa.pem.load_pem(pem, 'PRIVATE KEY')\n        keyinfo, _ = decoder.decode(der)\n\n        if keyinfo[1][0] != univ.ObjectIdentifier('1.2.840.113549.1.1.1'):\n            raise ValueError('Not a DER-encoded OpenSSL private RSA key')\n\n        private_key_der = keyinfo[2].asOctets()\n\n    except IndexError:\n        raise ValueError('Not a DER-encoded OpenSSL private RSA key')\n\n    return rsa.PrivateKey.load_pkcs1(private_key_der, format='DER')\n\n\nclass PythonRSASigner(object):\n    \"\"\"Implements :class:`adb_protocol.AuthSigner` using http://stuvel.eu/rsa.\n\n    Parameters\n    ----------\n    pub : str, None\n        The contents of the public key file\n    priv : str, None\n        The contents of the private key file\n\n    Attributes\n    ----------\n    priv_key : rsa.key.PrivateKey\n        The loaded private key\n    pub_key : str, None\n        The contents of the public key file\n\n    \"\"\"\n    def __init__(self, pub=None, priv=None):\n        self.priv_key = _load_rsa_private_key(priv)\n        self.pub_key = pub\n\n    @classmethod\n    def FromRSAKeyPath(cls, rsa_key_path):\n        \"\"\"Create a :class:`PythonRSASigner` instance using the provided private key.\n\n        Parameters\n        ----------\n        rsa_key_path : str\n            The path to the private key; the public key must be ``rsa_key_path + '.pub'``.\n\n        Returns\n        -------\n        PythonRSASigner\n            A :class:`PythonRSASigner` with private key ``rsa_key_path`` and public key ``rsa_key_path + '.pub'``\n\n        \"\"\"\n        with open(rsa_key_path + '.pub') as f:\n            pub = f.read()\n        with open(rsa_key_path) as f:\n            priv = f.read()\n        return cls(pub, priv)\n\n    def Sign(self, data):\n        \"\"\"Signs given data using a private key.\n\n        Parameters\n        ----------\n        data : bytes\n            The data to be signed\n\n        Returns\n        -------\n        bytes\n            The signed ``data``\n\n        \"\"\"\n        return rsa.sign(data, self.priv_key, 'SHA-1-PREHASHED')\n\n    def GetPublicKey(self):\n        \"\"\"Returns the public key in PEM format without headers or newlines.\n\n        Returns\n        -------\n        self.pub_key : str, None\n            The contents of the public key file, or ``None`` if a public key was not provided.\n\n        \"\"\"\n        return self.pub_key\n"
  },
  {
    "path": "adb_shell/constants.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It incorporates work\n# covered by the following license notice:\n#\n#\n#   Copyright 2014 Google Inc. All rights reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\n\"\"\"Constants used throughout the code.\n\n\"\"\"\n\n\nimport stat\nimport struct\n\n\n#: From adb.h\nCLASS = 0xFF\n\n#: From adb.h\nSUBCLASS = 0x42\n\n#: From adb.h\nPROTOCOL = 0x01\n\n#: ADB protocol version.\nVERSION = 0x01000000\n\n#: Maximum amount of data in an ADB packet. According to: https://android.googlesource.com/platform/system/core/+/master/adb/adb.h\nMAX_ADB_DATA = 1024 * 1024\nMAX_LEGACY_ADB_DATA = 4 * 1024\n\n#: Maximum size of a filesync DATA packet. Default size.\nMAX_PUSH_DATA = 2 * 1024\n\n#: Maximum chunk size. According to https://android.googlesource.com/platform/system/core/+/master/adb/SYNC.TXT\nMAX_CHUNK_SIZE = 64 * 1024\n\n#: Default mode for pushed files.\nDEFAULT_PUSH_MODE = stat.S_IFREG | stat.S_IRWXU | stat.S_IRWXG\n\n#: AUTH constant for ``arg0``\nAUTH_TOKEN = 1\n\n#: AUTH constant for ``arg0``\nAUTH_SIGNATURE = 2\n\n#: AUTH constant for ``arg0``\nAUTH_RSAPUBLICKEY = 3\n\nAUTH = b'AUTH'\nCLSE = b'CLSE'\nCNXN = b'CNXN'\nFAIL = b'FAIL'\nOKAY = b'OKAY'\nOPEN = b'OPEN'\nSYNC = b'SYNC'\nWRTE = b'WRTE'\n\nDATA = b'DATA'\nDENT = b'DENT'\nDONE = b'DONE'\nLIST = b'LIST'\nQUIT = b'QUIT'\nRECV = b'RECV'\nSEND = b'SEND'\nSTAT = b'STAT'\n\n#: Commands that are recognized by :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device`\nIDS = (AUTH, CLSE, CNXN, OKAY, OPEN, SYNC, WRTE)\n\n#: A dictionary where the keys are the commands in :const:`IDS` and the values are the keys converted to integers\nID_TO_WIRE = {cmd_id: sum(c << (i * 8) for i, c in enumerate(bytearray(cmd_id))) for cmd_id in IDS}\n\n#: A dictionary where the keys are integers and the values are their corresponding commands (type = bytes) from :const:`IDS`\nWIRE_TO_ID = {wire: cmd_id for cmd_id, wire in ID_TO_WIRE.items()}\n\n#: Commands that are recognized by :meth:`adb_shell.adb_device.AdbDevice._filesync_read` and :meth:`adb_shell.adb_device_async.AdbDeviceAsync._filesync_read`\nFILESYNC_IDS = (DATA, DENT, DONE, FAIL, LIST, OKAY, QUIT, RECV, SEND, STAT)\n\n#: A dictionary where the keys are the commands in :const:`FILESYNC_IDS` and the values are the keys converted to integers\nFILESYNC_ID_TO_WIRE = {cmd_id: sum(c << (i * 8) for i, c in enumerate(bytearray(cmd_id))) for cmd_id in FILESYNC_IDS}\n\n#: A dictionary where the keys are integers and the values are their corresponding commands (type = bytes) from :const:`FILESYNC_IDS`\nFILESYNC_WIRE_TO_ID = {wire: cmd_id for cmd_id, wire in FILESYNC_ID_TO_WIRE.items()}\n\n#: An ADB message is 6 words in little-endian.\nMESSAGE_FORMAT = b'<6I'\n\n#: The format for FileSync \"list\" messages\nFILESYNC_LIST_FORMAT = b'<5I'\n\n#: The format for FileSync \"pull\" messages\nFILESYNC_PULL_FORMAT = b'<2I'\n\n#: The format for FileSync \"push\" messages\nFILESYNC_PUSH_FORMAT = b'<2I'\n\n#: The format for FileSync \"stat\" messages\nFILESYNC_STAT_FORMAT = b'<4I'\n\n#: The size of an ADB message\nMESSAGE_SIZE = struct.calcsize(MESSAGE_FORMAT)\n\n#: Default authentication timeout (in s) for :meth:`adb_shell.adb_device.AdbDevice.connect` and :meth:`adb_shell.adb_device_async.AdbDeviceAsync.connect`\nDEFAULT_AUTH_TIMEOUT_S = 10.\n\n#: Default total timeout (in s) for reading data from the device\nDEFAULT_READ_TIMEOUT_S = 10.\n"
  },
  {
    "path": "adb_shell/exceptions.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It incorporates work\n# covered by the following license notice:\n#\n#\n#   Copyright 2014 Google Inc. All rights reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\n\"\"\"ADB-related exceptions.\n\n\"\"\"\n\n\nclass AdbCommandFailureException(Exception):\n    \"\"\"A ``b'FAIL'`` packet was received.\n\n    \"\"\"\n\n\nclass AdbConnectionError(Exception):\n    \"\"\"ADB command not sent because a connection to the device has not been established.\n\n    \"\"\"\n\n\nclass AdbTimeoutError(Exception):\n    \"\"\"ADB command did not complete within the specified time.\n\n    \"\"\"\n\n\nclass DeviceAuthError(Exception):\n    \"\"\"Device authentication failed.\n\n    \"\"\"\n    def __init__(self, message, *args):\n        message %= args\n        super(DeviceAuthError, self).__init__(message, *args)\n\n\nclass InvalidChecksumError(Exception):\n    \"\"\"Checksum of data didn't match expected checksum.\n\n    \"\"\"\n\n\nclass InvalidCommandError(Exception):\n    \"\"\"Got an invalid command.\n\n    \"\"\"\n\n\nclass InvalidTransportError(Exception):\n    \"\"\"The provided transport does not implement the necessary methods: ``close``, ``connect``, ``bulk_read``, and ``bulk_write``.\n\n    \"\"\"\n\n\nclass InvalidResponseError(Exception):\n    \"\"\"Got an invalid response to our command.\n\n    \"\"\"\n\n\nclass DevicePathInvalidError(Exception):\n    \"\"\"A file command was passed an invalid path.\n\n    \"\"\"\n\n\nclass PushFailedError(Exception):\n    \"\"\"Pushing a file failed for some reason.\n\n    \"\"\"\n\n\nclass TcpTimeoutException(Exception):\n    \"\"\"TCP connection timed read/write operation exceeded the allowed time.\n\n    \"\"\"\n\n\nclass UsbDeviceNotFoundError(Exception):\n    \"\"\"TODO\n\n    \"\"\"\n\n\nclass UsbReadFailedError(Exception):\n    \"\"\"TODO\n\n    Parameters\n    ----------\n    msg : str\n        The error message\n    usb_error : libusb1.USBError\n        An exception from ``libusb1``\n\n    Attributes\n    ----------\n    usb_error : libusb1.USBError\n        An exception from ``libusb1``\n\n    \"\"\"\n    def __init__(self, msg, usb_error):\n        super(UsbReadFailedError, self).__init__(msg, usb_error)\n        self.usb_error = usb_error\n\n    def __str__(self):\n        return '%s: %s' % self.args\n\n\nclass UsbWriteFailedError(Exception):\n    \"\"\":meth:`adb_shell.transport.usb_transport.UsbTransport.bulk_write` failed.\n\n    \"\"\"\n"
  },
  {
    "path": "adb_shell/hidden_helpers.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It incorporates work\n# covered by the following license notice:\n#\n#\n#   Copyright 2014 Google Inc. All rights reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\n\"\"\"Implement helpers for the :class:`~adb_shell.adb_device.AdbDevice` and :class:`~adb_shell.adb_device_async.AdbDeviceAsync` classes.\n\n.. rubric:: Contents\n\n* :class:`_AdbPacketStore`\n\n    * :meth:`_AdbPacketStore.__contains__`\n    * :meth:`_AdbPacketStore.__len__`\n    * :meth:`_AdbPacketStore.clear`\n    * :meth:`_AdbPacketStore.clear_all`\n    * :meth:`_AdbPacketStore.find`\n    * :meth:`_AdbPacketStore.find_allow_zeros`\n    * :meth:`_AdbPacketStore.get`\n    * :meth:`_AdbPacketStore.put`\n\n* :class:`_AdbTransactionInfo`\n\n    * :meth:`_AdbTransactionInfo.args_match`\n\n* :class:`_FileSyncTransactionInfo`\n\n    * :meth:`_FileSyncTransactionInfo.can_add_to_send_buffer`\n\n* :func:`get_banner`\n* :func:`get_files_to_push`\n\n\"\"\"\n\n\nfrom collections import namedtuple\nfrom io import BytesIO\nimport os\nimport socket\nimport struct\n\ntry:\n    from asyncio import Queue\nexcept ImportError:  # pragma: no cover\n    try:\n        from queue import Queue\n    except ImportError:\n        from Queue import Queue\n\nfrom . import constants\n\n\nDeviceFile = namedtuple('DeviceFile', ['filename', 'mode', 'size', 'mtime'])\n\n\ndef get_files_to_push(local_path, device_path):\n    \"\"\"Get a list of the file(s) to push.\n\n    Parameters\n    ----------\n    local_path : str\n        A path to a local file or directory\n    device_path : str\n        A path to a file or directory on the device\n\n    Returns\n    -------\n    local_path_is_dir : bool\n        Whether or not ``local_path`` is a directory\n    local_paths : list[str]\n        A list of the file(s) to push\n    device_paths : list[str]\n        A list of destination paths on the device that corresponds to ``local_paths``\n\n    \"\"\"\n    local_path_is_dir = not isinstance(local_path, BytesIO) and os.path.isdir(local_path)\n    local_paths = [local_path] if not local_path_is_dir else os.listdir(local_path)\n    device_paths = [device_path] if not local_path_is_dir else [device_path + '/' + f for f in local_paths]\n\n    return local_path_is_dir, local_paths, device_paths\n\n\ndef get_banner():\n    \"\"\"Get the ``banner`` that will be signed in :meth:`adb_shell.adb_device.AdbDevice.connect` / :meth:`adb_shell.adb_device_async.AdbDeviceAsync.connect`.\n\n    Returns\n    -------\n    bytearray\n        The hostname, or \"unknown\" if it could not be determined\n\n    \"\"\"\n    try:\n        return bytearray(socket.gethostname(), 'utf-8')\n    except:  # noqa pylint: disable=bare-except\n        return bytearray('unknown', 'utf-8')\n\n\nclass _AdbTransactionInfo(object):  # pylint: disable=too-few-public-methods\n    \"\"\"A class for storing info and settings used during a single ADB \"transaction.\"\n\n    Note that if ``timeout_s`` is not ``None``, then:\n\n    ::\n\n       self.transport_timeout_s <= self.read_timeout_s <= self.timeout_s\n\n    If ``timeout_s`` is ``None``, the first inequality still applies.\n\n\n    Parameters\n    ----------\n    local_id : int\n        The ID for the sender (i.e., the device running this code)\n    remote_id : int\n        The ID for the recipient\n    transport_timeout_s : float, None\n        Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`,\n        :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`,\n        :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`, and\n        :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n    read_timeout_s : float\n        The total time in seconds to wait for data and packets from the device\n    timeout_s : float, None\n        The total time in seconds to wait for the ADB command to finish\n\n    Attributes\n    ----------\n    local_id : int\n        The ID for the sender (i.e., the device running this code)\n    read_timeout_s : float\n        The total time in seconds to wait for data and packets from the device\n    remote_id : int\n        The ID for the recipient\n    timeout_s : float, None\n        The total time in seconds to wait for the ADB command to finish\n    transport_timeout_s : float, None\n        Timeout in seconds for sending and receiving data, or ``None``; see :meth:`BaseTransport.bulk_read() <adb_shell.transport.base_transport.BaseTransport.bulk_read>`,\n        :meth:`BaseTransport.bulk_write() <adb_shell.transport.base_transport.BaseTransport.bulk_write>`,\n        :meth:`BaseTransportAsync.bulk_read() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_read>`, and\n        :meth:`BaseTransportAsync.bulk_write() <adb_shell.transport.base_transport_async.BaseTransportAsync.bulk_write>`\n\n    \"\"\"\n    def __init__(self, local_id, remote_id, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None):\n        self.local_id = local_id\n        self.remote_id = remote_id\n        self.timeout_s = timeout_s\n        self.read_timeout_s = read_timeout_s if self.timeout_s is None else min(read_timeout_s, self.timeout_s)\n        self.transport_timeout_s = self.read_timeout_s if transport_timeout_s is None else min(transport_timeout_s, self.read_timeout_s)\n\n    def args_match(self, arg0, arg1, allow_zeros=False):\n        \"\"\"Check if ``arg0`` and ``arg1`` match this object's ``remote_id`` and ``local_id`` attributes, respectively.\n\n        Parameters\n        ----------\n        arg0 : int\n            The ``arg0`` value from an ADB packet, which will be compared to this object's ``remote_id`` attribute\n        arg1 : int\n            The ``arg1`` value from an ADB packet, which will be compared to this object's ``local_id`` attribute\n        allow_zeros : bool\n            Whether to check if ``arg0`` and ``arg1`` match 0, in addition to this object's ``local_id`` and ``remote_id`` attributes\n\n        Returns\n        -------\n        bool\n            Whether ``arg0`` and ``arg1`` match this object's ``local_id`` and ``remote_id`` attributes\n\n        \"\"\"\n        if not allow_zeros:\n            return arg1 == self.local_id and (self.remote_id is None or arg0 == self.remote_id)\n\n        # https://github.com/JeffLIrion/adb_shell/blob/17540be9b3b84637aca9b994ae3e0b35d02b1a03/adb_shell/adb_device.py#L923-L929\n        return arg1 in (0, self.local_id) and (self.remote_id is None or arg0 in (0, self.remote_id))\n\n\nclass _FileSyncTransactionInfo(object):  # pylint: disable=too-few-public-methods\n    \"\"\"A class for storing info used during a single FileSync \"transaction.\"\n\n    Parameters\n    ----------\n    recv_message_format : bytes\n        The FileSync message format\n    maxdata: int\n        Maximum amount of data in an ADB packet\n\n    Attributes\n    ----------\n    _maxdata: int\n        Maximum amount of data in an ADB packet\n    recv_buffer : bytearray\n        A buffer for storing received data\n    recv_message_format : bytes\n        The FileSync message format\n    recv_message_size : int\n        The FileSync message size\n    send_buffer : bytearray\n        A buffer for storing data to be sent\n    send_idx : int\n        The index in ``recv_buffer`` that will be the start of the next data packet sent\n\n    \"\"\"\n    def __init__(self, recv_message_format, maxdata=constants.MAX_ADB_DATA):\n        self.send_buffer = bytearray(maxdata)\n        self.send_idx = 0\n\n        self.recv_buffer = bytearray()\n        self.recv_message_format = recv_message_format\n        self.recv_message_size = struct.calcsize(recv_message_format)\n\n        self._maxdata = maxdata\n\n    def can_add_to_send_buffer(self, data_len):\n        \"\"\"Determine whether ``data_len`` bytes of data can be added to the send buffer without exceeding :const:`constants.MAX_ADB_DATA`.\n\n        Parameters\n        ----------\n        data_len : int\n            The length of the data to be potentially added to the send buffer (not including the length of its header)\n\n        Returns\n        -------\n        bool\n            Whether ``data_len`` bytes of data can be added to the send buffer without exceeding :const:`constants.MAX_ADB_DATA`\n\n        \"\"\"\n        added_len = self.recv_message_size + data_len\n        return self.send_idx + added_len < self._maxdata\n\n\nclass _AdbPacketStore(object):\n    \"\"\"A class for storing ADB packets.\n\n    This class is used to support multiple streams.\n\n    Attributes\n    ----------\n    _dict : dict[int: dict[int: Queue]]\n        A dictionary of dictionaries of queues.  The first (outer) dictionary keys are the ``arg1`` return values from\n        the :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and\n        :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device` methods.  The second (inner)\n        dictionary keys are the ``arg0`` return values from those methods.  And the values of this inner dictionary are\n        queues of ``(cmd, data)`` tuples.\n\n    \"\"\"\n\n    def __init__(self):\n        self._dict = {}\n\n    def __contains__(self, value):\n        \"\"\"Check if there are any entries in a queue for the specified value.\n\n        Note that ``None`` is used as a wildcard.\n\n        Parameters\n        ----------\n        value : tuple[int, int]\n            An ``(arg0, arg1)`` pair; either or both values can be ``None``\n\n        Returns\n        -------\n        bool\n            Whether the ``(arg0, arg1)`` tuple has any corresponding queue entries\n\n        \"\"\"\n        return bool(self.find(value[0], value[1]))\n\n    def __len__(self):\n        \"\"\"Get the number of non-empty queues.\n\n        Returns\n        -------\n        int\n            The number of non-empty queues\n\n        \"\"\"\n        return sum(not val0.empty() for val1 in self._dict.values() for val0 in val1.values())\n\n    def clear(self, arg0, arg1):\n        \"\"\"Delete the entry for ``(arg0, arg1)``, if it exists.\n\n        Parameters\n        ----------\n        arg0 : int\n            The ``arg0`` return value from the :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device` methods\n        arg1 : int\n            The ``arg1`` return value from the :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device` methods\n\n        \"\"\"\n        if arg1 in self._dict and arg0 in self._dict[arg1]:\n            del self._dict[arg1][arg0]\n\n            if not self._dict[arg1]:\n                # `self._dict[arg1]` is an empty dictionary now, so delete it\n                del self._dict[arg1]\n\n    def clear_all(self):\n        \"\"\"Clear all the entries.\"\"\"\n        self._dict = {}\n\n    def find(self, arg0, arg1):\n        \"\"\"Find the entry corresponding to ``arg0`` and ``arg1``.\n\n        Parameters\n        ----------\n        arg0 : int, None\n            The ``arg0`` value that we are looking for; ``None`` serves as a wildcard\n        arg1 : int, None\n            The ``arg1`` value that we are looking for; ``None`` serves as a wildcard\n\n        Returns\n        -------\n        tuple[int, int], None\n            The ``(arg0, arg1)`` pair that was found in the dictionary of dictionaries, or ``None`` if no match was found\n\n        \"\"\"\n        if not self._dict:\n            return None\n\n        if arg1 is None:\n            if arg0 is None:\n                # `value = (None, None)` -> search for any non-empty queue\n                return next(((key0, key1) for key1, val1 in self._dict.items() for key0, val0 in val1.items() if not val0.empty()), None)\n\n            # Search for a non-empty queue with a key of `arg0 == value[0]`\n            return next(((arg0, key1) for key1, val1 in self._dict.items() for key0, val0 in val1.items() if key0 == arg0 and not val0.empty()), None)\n\n        if arg1 not in self._dict:\n            return None\n\n        if arg0 is None:\n            # Look for a non-empty queue in the `self._dict[value[1]]` dictionary\n            return next(((key0, arg1) for key0, val0 in self._dict[arg1].items() if not val0.empty()), None)\n\n        if arg0 in self._dict[arg1] and not self._dict[arg1][arg0].empty():\n            return (arg0, arg1)\n\n        return None\n\n    def find_allow_zeros(self, arg0, arg1):\n        \"\"\"Find the entry corresponding to (``arg0`` or 0) and (``arg1`` or 0).\n\n        Parameters\n        ----------\n        arg0 : int, None\n            The ``arg0`` value that we are looking for; ``None`` serves as a wildcard\n        arg1 : int, None\n            The ``arg1`` value that we are looking for; ``None`` serves as a wildcard\n\n        Returns\n        -------\n        tuple[int, int], None\n            The first matching ``(arg0, arg1)`` pair that was found in the dictionary of dictionaries, or ``None`` if no match was found\n\n        \"\"\"\n        for arg0_, arg1_ in ((arg0, arg1), (arg0, 0), (0, arg1), (0, 0)):\n            arg0_arg1 = self.find(arg0_, arg1_)\n            if arg0_arg1:\n                return arg0_arg1\n\n        return None\n\n    def get(self, arg0, arg1):\n        \"\"\"Get the next entry from the queue for ``arg0`` and ``arg1``.\n\n        This function assumes you have already checked that ``(arg0, arg1) in self``.\n\n        Parameters\n        ----------\n        arg0 : int, None\n            The ``arg0`` return value from the :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device` methods; ``None`` serves as a wildcard\n        arg1 : int, None\n            The ``arg1`` return value from the :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device` methods; ``None`` serves as a wildcard\n\n        Returns\n        -------\n        cmd : bytes\n            The ADB packet's command\n        arg0 : int\n            The ``arg0`` value from the returned packet\n        arg1 : int\n            The ``arg1`` value from the returned packet\n        data : bytes\n            The ADB packet's data\n\n        \"\"\"\n        if arg0 is None or arg1 is None:\n            arg0, arg1 = self.find(arg0, arg1)\n\n        # Get the data from the queue\n        cmd, data = self._dict[arg1][arg0].get_nowait()\n\n        # If this is a `CLSE` packet, then clear the entry in the store\n        if cmd == constants.CLSE:\n            self.clear(arg0, arg1)\n\n        return cmd, arg0, arg1, data\n\n    def put(self, arg0, arg1, cmd, data):\n        \"\"\"Add an entry to the queue for ``arg0`` and ``arg1``.\n\n        Note that a new dictionary entry will not be created if ``cmd == constants.CLSE``.\n\n        Parameters\n        ----------\n        arg0 : int\n            The ``arg0`` return value from the :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device` methods\n        arg1 : int\n            The ``arg1`` return value from the :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device` methods\n        cmd : bytes\n            The ADB packet's command\n        data : bytes\n            The ADB packet's data\n\n        \"\"\"\n        if arg1 in self._dict:\n            if arg0 not in self._dict[arg1]:\n                if cmd == constants.CLSE:\n                    return\n\n                # Create the `arg0` entry in the `arg1` dict\n                self._dict[arg1][arg0] = Queue()\n        else:\n            if cmd == constants.CLSE:\n                return\n\n            # Create the `arg1` entry with a new dict\n            self._dict[arg1] = {arg0: Queue()}\n\n        # Put the data into the queue\n        self._dict[arg1][arg0].put_nowait((cmd, data))\n"
  },
  {
    "path": "adb_shell/transport/__init__.py",
    "content": ""
  },
  {
    "path": "adb_shell/transport/base_transport.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.\n\n\"\"\"A base class for transports used to communicate with a device.\n\n* :class:`BaseTransport`\n\n    * :meth:`BaseTransport.bulk_read`\n    * :meth:`BaseTransport.bulk_write`\n    * :meth:`BaseTransport.close`\n    * :meth:`BaseTransport.connect`\n\n\"\"\"\n\n\ntry:\n    from abc import ABC, abstractmethod\nexcept ImportError:  # pragma: no cover\n    from abc import ABCMeta, abstractmethod\n\n    class ABC(object):  # pylint: disable=too-few-public-methods\n        \"\"\"A Python2-compatible `ABC` class.\n\n        \"\"\"\n        __metaclass__ = ABCMeta\n\n\nclass BaseTransport(ABC):\n    \"\"\"A base transport class.\n\n    \"\"\"\n\n    @abstractmethod\n    def close(self):\n        \"\"\"Close the connection.\n\n        \"\"\"\n\n    @abstractmethod\n    def connect(self, transport_timeout_s):\n        \"\"\"Create a connection to the device.\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            A connection timeout\n\n        \"\"\"\n\n    @abstractmethod\n    def bulk_read(self, numbytes, transport_timeout_s):\n        \"\"\"Read data from the device.\n\n        Parameters\n        ----------\n        numbytes : int\n            The maximum amount of data to be received\n        transport_timeout_s : float, None\n            A timeout for the read operation\n\n        Returns\n        -------\n        bytes\n            The received data\n\n        \"\"\"\n\n    @abstractmethod\n    def bulk_write(self, data, transport_timeout_s):\n        \"\"\"Send data to the device.\n\n        Parameters\n        ----------\n        data : bytes\n            The data to be sent\n        transport_timeout_s : float, None\n            A timeout for the write operation\n\n        Returns\n        -------\n        int\n            The number of bytes sent\n\n        \"\"\"\n"
  },
  {
    "path": "adb_shell/transport/base_transport_async.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.\n\n\"\"\"A base class for transports used to communicate with a device.\n\n* :class:`BaseTransportAsync`\n\n    * :meth:`BaseTransportAsync.bulk_read`\n    * :meth:`BaseTransportAsync.bulk_write`\n    * :meth:`BaseTransportAsync.close`\n    * :meth:`BaseTransportAsync.connect`\n\n\"\"\"\n\n\nfrom abc import ABC, abstractmethod\n\n\nclass BaseTransportAsync(ABC):\n    \"\"\"A base transport class.\n\n    \"\"\"\n\n    @abstractmethod\n    async def close(self):\n        \"\"\"Close the connection.\n\n        \"\"\"\n\n    @abstractmethod\n    async def connect(self, transport_timeout_s):\n        \"\"\"Create a connection to the device.\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            A connection timeout\n\n        \"\"\"\n\n    @abstractmethod\n    async def bulk_read(self, numbytes, transport_timeout_s):\n        \"\"\"Read data from the device.\n\n        Parameters\n        ----------\n        numbytes : int\n            The maximum amount of data to be received\n        transport_timeout_s : float, None\n            A timeout for the read operation\n\n        Returns\n        -------\n        bytes\n            The received data\n\n        \"\"\"\n\n    @abstractmethod\n    async def bulk_write(self, data, transport_timeout_s):\n        \"\"\"Send data to the device.\n\n        Parameters\n        ----------\n        data : bytes\n            The data to be sent\n        transport_timeout_s : float, None\n            A timeout for the write operation\n\n        Returns\n        -------\n        int\n            The number of bytes sent\n\n        \"\"\"\n"
  },
  {
    "path": "adb_shell/transport/tcp_transport.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It incorporates work\n# covered by the following license notice:\n#\n#\n#   Copyright 2014 Google Inc. All rights reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\n\"\"\"A class for creating a socket connection with the device and sending and receiving data.\n\n* :class:`TcpTransport`\n\n    * :meth:`TcpTransport.bulk_read`\n    * :meth:`TcpTransport.bulk_write`\n    * :meth:`TcpTransport.close`\n    * :meth:`TcpTransport.connect`\n\n\"\"\"\n\n\nimport select\nimport socket\n\nfrom .base_transport import BaseTransport\nfrom ..exceptions import TcpTimeoutException\n\n\nclass TcpTransport(BaseTransport):\n    \"\"\"TCP connection object.\n\n    Parameters\n    ----------\n    host : str\n        The address of the device; may be an IP address or a host name\n    port : int\n        The device port to which we are connecting (default is 5555)\n\n    Attributes\n    ----------\n    _connection : socket.socket, None\n        A socket connection to the device\n    _host : str\n        The address of the device; may be an IP address or a host name\n    _port : int\n        The device port to which we are connecting (default is 5555)\n\n    \"\"\"\n    def __init__(self, host, port=5555):\n        self._host = host\n        self._port = port\n\n        self._connection = None\n\n    def close(self):\n        \"\"\"Close the socket connection.\n\n        \"\"\"\n        if self._connection:\n            try:\n                self._connection.shutdown(socket.SHUT_RDWR)\n            except OSError:\n                pass\n\n            self._connection.close()\n            self._connection = None\n\n    def connect(self, transport_timeout_s):\n        \"\"\"Create a socket connection to the device.\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            Set the timeout on the socket instance\n\n        \"\"\"\n        self._connection = socket.create_connection((self._host, self._port), timeout=transport_timeout_s)\n        if transport_timeout_s:\n            # Put the socket in non-blocking mode\n            # https://docs.python.org/3/library/socket.html#socket.socket.settimeout\n            self._connection.setblocking(False)\n\n    def bulk_read(self, numbytes, transport_timeout_s):\n        \"\"\"Receive data from the socket.\n\n        Parameters\n        ----------\n        numbytes : int\n            The maximum amount of data to be received\n        transport_timeout_s : float, None\n            When the timeout argument is omitted, ``select.select`` blocks until at least one file descriptor is ready. A time-out value of zero specifies a poll and never blocks.\n\n        Returns\n        -------\n        bytes\n            The received data\n\n        Raises\n        ------\n        TcpTimeoutException\n            Reading timed out.\n\n        \"\"\"\n        readable, _, _ = select.select([self._connection], [], [], transport_timeout_s)\n        if readable:\n            return self._connection.recv(numbytes)\n\n        msg = 'Reading from {}:{} timed out ({} seconds)'.format(self._host, self._port, transport_timeout_s)\n        raise TcpTimeoutException(msg)\n\n    def bulk_write(self, data, transport_timeout_s):\n        \"\"\"Send data to the socket.\n\n        Parameters\n        ----------\n        data : bytes\n            The data to be sent\n        transport_timeout_s : float, None\n            When the timeout argument is omitted, ``select.select`` blocks until at least one file descriptor is ready. A time-out value of zero specifies a poll and never blocks.\n\n        Returns\n        -------\n        int\n            The number of bytes sent\n\n        Raises\n        ------\n        TcpTimeoutException\n            Sending data timed out.  No data was sent.\n\n        \"\"\"\n        _, writeable, _ = select.select([], [self._connection], [], transport_timeout_s)\n        if writeable:\n            return self._connection.send(data)\n\n        msg = 'Sending data to {}:{} timed out after {} seconds. No data was sent.'.format(self._host, self._port, transport_timeout_s)\n        raise TcpTimeoutException(msg)\n"
  },
  {
    "path": "adb_shell/transport/tcp_transport_async.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.\n\n\"\"\"A class for creating a socket connection with the device and sending and receiving data.\n\n* :class:`TcpTransportAsync`\n\n    * :meth:`TcpTransportAsync.bulk_read`\n    * :meth:`TcpTransportAsync.bulk_write`\n    * :meth:`TcpTransportAsync.close`\n    * :meth:`TcpTransportAsync.connect`\n\n\"\"\"\n\n\nimport asyncio\n\nimport async_timeout\n\nfrom .base_transport_async import BaseTransportAsync\nfrom ..exceptions import TcpTimeoutException\n\n\nclass TcpTransportAsync(BaseTransportAsync):\n    \"\"\"TCP connection object.\n\n    Parameters\n    ----------\n    host : str\n        The address of the device; may be an IP address or a host name\n    port : int\n        The device port to which we are connecting (default is 5555)\n\n    Attributes\n    ----------\n    _host : str\n        The address of the device; may be an IP address or a host name\n    _port : int\n        The device port to which we are connecting (default is 5555)\n    _reader : StreamReader, None\n        Object for reading data from the socket\n    _writer : StreamWriter, None\n        Object for writing data to the socket\n\n    \"\"\"\n    def __init__(self, host, port=5555):\n        self._host = host\n        self._port = port\n\n        self._reader = None\n        self._writer = None\n\n    async def close(self):\n        \"\"\"Close the socket connection.\n\n        \"\"\"\n        if self._writer:\n            try:\n                self._writer.close()\n                await self._writer.wait_closed()\n            except OSError:\n                pass\n\n        self._reader = None\n        self._writer = None\n\n    async def connect(self, transport_timeout_s):\n        \"\"\"Create a socket connection to the device.\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            Timeout for connecting to the socket; if it is ``None``, then it will block until the operation completes\n\n        \"\"\"\n        try:\n            async with async_timeout.timeout(transport_timeout_s):\n                self._reader, self._writer = await asyncio.open_connection(self._host, self._port)\n        except asyncio.TimeoutError as exc:\n            msg = 'Connecting to {}:{} timed out ({} seconds)'.format(self._host, self._port, transport_timeout_s)\n            raise TcpTimeoutException(msg) from exc\n\n    async def bulk_read(self, numbytes, transport_timeout_s):\n        \"\"\"Receive data from the socket.\n\n        Parameters\n        ----------\n        numbytes : int\n            The maximum amount of data to be received\n        transport_timeout_s : float, None\n            Timeout for reading data from the socket; if it is ``None``, then it will block until the read operation completes\n\n        Returns\n        -------\n        bytes\n            The received data\n\n        Raises\n        ------\n        TcpTimeoutException\n            Reading timed out.\n\n        \"\"\"\n        try:\n            async with async_timeout.timeout(transport_timeout_s):\n                return await self._reader.read(numbytes)\n        except asyncio.TimeoutError as exc:\n            msg = 'Reading from {}:{} timed out ({} seconds)'.format(self._host, self._port, transport_timeout_s)\n            raise TcpTimeoutException(msg) from exc\n\n    async def bulk_write(self, data, transport_timeout_s):\n        \"\"\"Send data to the socket.\n\n        Parameters\n        ----------\n        data : bytes\n            The data to be sent\n        transport_timeout_s : float, None\n            Timeout for writing data to the socket; if it is ``None``, then it will block until the write operation completes\n\n        Returns\n        -------\n        int\n            The number of bytes sent\n\n        Raises\n        ------\n        TcpTimeoutException\n            Sending data timed out.  No data was sent.\n\n        \"\"\"\n        try:\n            self._writer.write(data)\n            async with async_timeout.timeout(transport_timeout_s):\n                await self._writer.drain()\n                return len(data)\n        except asyncio.TimeoutError as exc:\n            msg = 'Sending data to {}:{} timed out after {} seconds. No data was sent.'.format(self._host, self._port, transport_timeout_s)\n            raise TcpTimeoutException(msg) from exc\n"
  },
  {
    "path": "adb_shell/transport/usb_transport.py",
    "content": "# Copyright (c) 2021 Jeff Irion and contributors\n#\n# This file is part of the adb-shell package.  It incorporates work\n# covered by the following license notice:\n#\n#\n#   Copyright 2014 Google Inc. All rights reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\n\"\"\"A class for creating a USB connection with the device and sending and receiving data.\n\n.. warning::\n\n   USB support is an experimental feature.\n\n\n* :func:`get_interface`\n* :func:`interface_matcher`\n* :class:`UsbTransport`\n\n    * :meth:`UsbTransport._find`\n    * :meth:`UsbTransport._find_and_open`\n    * :meth:`UsbTransport._find_devices`\n    * :meth:`UsbTransport._find_first`\n    * :meth:`UsbTransport._flush_buffers`\n    * :meth:`UsbTransport._open`\n    * :meth:`UsbTransport._port_path_matcher`\n    * :meth:`UsbTransport._serial_matcher`\n    * :meth:`UsbTransport._timeout`\n    * :meth:`UsbTransport.bulk_read`\n    * :meth:`UsbTransport.bulk_write`\n    * :meth:`UsbTransport.close`\n    * :meth:`UsbTransport.connect`\n    * :attr:`UsbTransport.port_path`\n    * :attr:`UsbTransport.serial_number`\n    * :attr:`UsbTransport.usb_info`\n\n\"\"\"\n\n\nimport logging\nimport platform\nimport re\nimport threading\nimport warnings\nimport weakref\n\nimport usb1\n\nfrom .base_transport import BaseTransport\n\nfrom .. import exceptions\n\n\n#: Default timeout\nDEFAULT_TIMEOUT_S = 10\n\nSYSFS_PORT_SPLIT_RE = re.compile(\"[,/:.-]\")\n\n_LOGGER = logging.getLogger(__name__)\n\nCLASS = usb1.CLASS_VENDOR_SPEC  # pylint: disable=no-member\nSUBCLASS = 0x42\nPROTOCOL = 0x01\n\n\ndef get_interface(setting):  # pragma: no cover\n    \"\"\"Get the class, subclass, and protocol for the given USB setting.\n\n    Parameters\n    ----------\n    setting : TODO\n        TODO\n\n    Returns\n    -------\n    TODO\n        TODO\n    TODO\n        TODO\n    TODO\n        TODO\n\n    \"\"\"\n    return (setting.getClass(), setting.getSubClass(), setting.getProtocol())\n\n\ndef interface_matcher(clazz, subclass, protocol):   # pragma: no cover\n    \"\"\"Returns a matcher that returns the setting with the given interface.\n\n    Parameters\n    ----------\n    clazz : TODO\n        TODO\n    subclass : TODO\n        TODO\n    protocol : TODO\n        TODO\n\n    Returns\n    -------\n    matcher : function\n        TODO\n\n    \"\"\"\n    interface = (clazz, subclass, protocol)\n\n    def matcher(device):\n        \"\"\"TODO\n\n        Parameters\n        ----------\n        device : TODO\n            TODO\n\n        Returns\n        -------\n        TODO, None\n            TODO\n\n        \"\"\"\n        for setting in device.iterSettings():\n            if get_interface(setting) == interface:\n                return setting\n        return None\n\n    return matcher\n\n\nclass UsbTransport(BaseTransport):   # pragma: no cover\n    \"\"\"USB communication object. Not thread-safe.\n\n    Handles reading and writing over USB with the proper endpoints, exceptions,\n    and interface claiming.\n\n    Parameters\n    ----------\n    device : usb1.USBDevice\n        libusb_device to connect to.\n    setting : usb1.USBInterfaceSetting\n        libusb setting with the correct endpoints to communicate with.\n    usb_info : TODO, None\n        String describing the usb path/serial/device, for debugging.\n    default_transport_timeout_s : TODO, None\n        Timeout in seconds for all I/O.\n\n    Attributes\n    ----------\n    _default_transport_timeout_s : TODO, None\n        Timeout in seconds for all I/O.\n    _device : TODO\n        libusb_device to connect to.\n    _transport : TODO\n        TODO\n    _interface_number : TODO\n        TODO\n    _max_read_packet_len : TODO\n        TODO\n    _read_endpoint : TODO\n        TODO\n    _setting : TODO\n        libusb setting with the correct endpoints to communicate with.\n    _usb_info : TODO\n        String describing the usb path/serial/device, for debugging.\n    _write_endpoint : TODO, None\n        TODO\n\n    \"\"\"\n    # We maintain an idempotent `usb1` context object to ensure that device\n    # objects we hand back to callers can be used while this class exists\n    USB1_CTX = usb1.USBContext()\n    USB1_CTX.open()\n\n    _HANDLE_CACHE = weakref.WeakValueDictionary()\n    _HANDLE_CACHE_LOCK = threading.Lock()\n\n    def __init__(self, device, setting, usb_info=None, default_transport_timeout_s=None):\n        self._setting = setting\n        self._device = device\n        self._transport = None\n\n        self._interface_number = None\n        self._read_endpoint = None\n        self._write_endpoint = None\n\n        self._usb_info = usb_info or ''\n        self._default_transport_timeout_s = default_transport_timeout_s if default_transport_timeout_s is not None else DEFAULT_TIMEOUT_S\n        self._max_read_packet_len = 0\n\n    def close(self):\n        \"\"\"Close the USB connection.\n\n        \"\"\"\n        if self._transport is None:\n            return\n        try:\n            self._transport.releaseInterface(self._interface_number)\n            self._transport.close()\n        except usb1.USBError:\n            _LOGGER.info('USBError while closing transport %s: ', self.usb_info, exc_info=True)\n        finally:\n            self._transport = None\n\n    def connect(self, transport_timeout_s=None):\n        \"\"\"Create a USB connection to the device.\n\n        Parameters\n        ----------\n        transport_timeout_s : float, None\n            Set the timeout on the USB instance\n\n        \"\"\"\n        read_endpoint = None\n        write_endpoint = None\n\n        for endpoint in self._setting.iterEndpoints():\n            address = endpoint.getAddress()\n            if address & usb1.ENDPOINT_DIR_MASK:  # pylint: disable=no-member\n                read_endpoint = address\n                # max_read_packet_len = endpoint.getMaxPacketSize()\n            else:\n                write_endpoint = address\n\n        assert read_endpoint is not None\n        assert write_endpoint is not None\n\n        transport = self._device.open()\n        iface_number = self._setting.getNumber()\n        try:\n            if (platform.system() != 'Windows' and transport.kernelDriverActive(iface_number)):\n                transport.detachKernelDriver(iface_number)\n        except usb1.USBErrorNotFound:  # pylint: disable=no-member\n            warnings.warn('Kernel driver not found for interface: %s.', iface_number)\n\n        # # When this object is deleted, make sure it's closed.\n        # weakref.ref(self, self.close)\n\n        self._transport = transport\n        self._read_endpoint = read_endpoint\n        self._write_endpoint = write_endpoint\n        self._interface_number = iface_number\n\n        self._transport.claimInterface(self._interface_number)\n\n    def bulk_read(self, numbytes, transport_timeout_s=None):\n        \"\"\"Receive data from the USB device.\n\n        Parameters\n        ----------\n        numbytes : int\n            The maximum amount of data to be received\n        transport_timeout_s : float, None\n            When the timeout argument is omitted, ``select.select`` blocks until at least one file descriptor is ready. A time-out value of zero specifies a poll and never blocks.\n\n        Returns\n        -------\n        bytes\n            The received data\n\n        Raises\n        ------\n        adb_shell.exceptions.UsbReadFailedError\n            Could not receive data\n\n        \"\"\"\n        if self._transport is None:\n            raise exceptions.UsbReadFailedError('This transport has been closed, probably due to another being opened.', None)\n        try:\n            # python-libusb1 > 1.6 exposes bytearray()s now instead of bytes/str.\n            # To support older and newer versions, we ensure everything's bytearray()\n            # from here on out.\n            return bytes(self._transport.bulkRead(self._read_endpoint, numbytes, timeout=self._timeout_ms(transport_timeout_s)))\n        except usb1.USBError as e:\n            raise exceptions.UsbReadFailedError('Could not receive data from %s (timeout %sms)' % (self.usb_info, self._timeout_ms(transport_timeout_s)), e)\n\n    def bulk_write(self, data, transport_timeout_s=None):\n        \"\"\"Send data to the USB device.\n\n        Parameters\n        ----------\n        data : bytes\n            The data to be sent\n        transport_timeout_s : float, None\n            When the timeout argument is omitted, ``select.select`` blocks until at least one file descriptor is ready. A time-out value of zero specifies a poll and never blocks.\n\n        Returns\n        -------\n        int\n            The number of bytes sent\n\n        Raises\n        ------\n        adb_shell.exceptions.UsbWriteFailedError\n            This transport has been closed, probably due to another being opened\n        adb_shell.exceptions.UsbWriteFailedError\n            Could not send data\n\n        \"\"\"\n        if self._transport is None:\n            raise exceptions.UsbWriteFailedError('This transport has been closed, probably due to another being opened.', None)\n\n        try:\n            return self._transport.bulkWrite(self._write_endpoint, data, timeout=self._timeout_ms(transport_timeout_s))\n\n        except usb1.USBError as e:\n            raise exceptions.UsbWriteFailedError('Could not send data to %s (timeout %sms)' % (self.usb_info, self._timeout_ms(transport_timeout_s)), e)\n\n    def _open(self):\n        \"\"\"Opens the USB device for this setting, and claims the interface.\n\n        \"\"\"\n        # Make sure we close any previous transport open to this usb device.\n        port_path = tuple(self.port_path)\n        with self._HANDLE_CACHE_LOCK:\n            old_transport = self._HANDLE_CACHE.get(port_path)\n            if old_transport is not None:\n                old_transport.Close()\n\n        self._read_endpoint = None\n        self._write_endpoint = None\n\n        for endpoint in self._setting.iterEndpoints():\n            address = endpoint.getAddress()\n            if address & usb1.USB_ENDPOINT_DIR_MASK:  # pylint: disable=no-member\n                self._read_endpoint = address\n                self._max_read_packet_len = endpoint.getMaxPacketSize()\n            else:\n                self._write_endpoint = address\n\n        assert self._read_endpoint is not None\n        assert self._write_endpoint is not None\n\n        transport = self._device.open()\n        iface_number = self._setting.getNumber()\n        try:\n            if (platform.system() != 'Windows' and transport.kernelDriverActive(iface_number)):\n                transport.detachKernelDriver(iface_number)\n        except usb1.USBErrorNotFound:  # pylint: disable=no-member\n            warnings.warn('Kernel driver not found for interface: %s.', iface_number)\n        transport.claimInterface(iface_number)\n        self._transport = transport\n        self._interface_number = iface_number\n\n        with self._HANDLE_CACHE_LOCK:\n            self._HANDLE_CACHE[port_path] = self\n        # When this object is deleted, make sure it's closed.\n        weakref.ref(self, self.close)\n\n    def _timeout_ms(self, transport_timeout_s):\n        \"\"\"TODO\n\n        Returns\n        -------\n        TODO\n            TODO\n\n        \"\"\"\n        return int(transport_timeout_s * 1000 if transport_timeout_s is not None else self._default_transport_timeout_s * 1000)\n\n    def _flush_buffers(self):\n        \"\"\"TODO\n\n        Raises\n        ------\n        adb_shell.exceptions.UsbReadFailedError\n            TODO\n\n        \"\"\"\n        while True:\n            try:\n                self.bulk_read(self._max_read_packet_len, transport_timeout_s=10)\n            except exceptions.UsbReadFailedError as e:\n                if isinstance(e.usb_error, usb1.USBErrorTimeout):  # pylint: disable=no-member\n                    break\n                raise\n\n    # ======================================================================= #\n    #                                                                         #\n    #                               Properties                                #\n    #                                                                         #\n    # ======================================================================= #\n    @property\n    def port_path(self):\n        \"\"\"TODO\n\n        Returns\n        -------\n        TODO\n            TODO\n\n        \"\"\"\n        return [self._device.getBusNumber()] + self._device.getPortNumberList()\n\n    @property\n    def serial_number(self):\n        \"\"\"TODO\n\n        Returns\n        -------\n        TODO\n            TODO\n\n        \"\"\"\n        return self._device.getSerialNumber()\n\n    @property\n    def usb_info(self):\n        \"\"\"TODO\n\n        Returns\n        -------\n        TODO\n            TODO\n\n        \"\"\"\n        try:\n            sn = self.serial_number\n        except usb1.USBError:\n            sn = ''\n        if sn and sn != self._usb_info:\n            return '%s %s' % (self._usb_info, sn)\n        return self._usb_info\n\n    # ======================================================================= #\n    #                                                                         #\n    #                                Matchers                                 #\n    #                                                                         #\n    # ======================================================================= #\n    @classmethod\n    def _port_path_matcher(cls, port_path):\n        \"\"\"Returns a device matcher for the given port path.\n\n        Parameters\n        ----------\n        port_path : TODO\n            TODO\n\n        Returns\n        -------\n        function\n            TODO\n\n        \"\"\"\n        if isinstance(port_path, str):\n            # Convert from sysfs path to port_path.\n            port_path = [int(part) for part in SYSFS_PORT_SPLIT_RE.split(port_path)]\n        return lambda device: device.port_path == port_path\n\n    @classmethod\n    def _serial_matcher(cls, serial):\n        \"\"\"Returns a device matcher for the given serial.\n\n        Parameters\n        ----------\n        serial : TODO\n            TODO\n\n        Returns\n        -------\n        function\n            TODO\n\n        \"\"\"\n        return lambda device: device.serial_number == serial\n\n    # ======================================================================= #\n    #                                                                         #\n    #                                 Finders                                 #\n    #                                                                         #\n    # ======================================================================= #\n    @classmethod\n    def _find(cls, setting_matcher, port_path=None, serial=None, default_transport_timeout_s=None):\n        \"\"\"Gets the first device that matches according to the keyword args.\n\n        Parameters\n        ----------\n        setting_matcher : TODO\n            TODO\n        port_path : TODO, None\n            TODO\n        serial : TODO, None\n            TODO\n        default_transport_timeout_s : TODO, None\n            TODO\n\n        Returns\n        -------\n        TODO\n            TODO\n\n        \"\"\"\n        if port_path:\n            device_matcher = cls._port_path_matcher(port_path)\n            usb_info = port_path\n        elif serial:\n            device_matcher = cls._serial_matcher(serial)\n            usb_info = serial\n        else:\n            device_matcher = None\n            usb_info = 'first'\n        return cls._find_first(setting_matcher, device_matcher, usb_info=usb_info, default_transport_timeout_s=default_transport_timeout_s)\n\n    @classmethod\n    def _find_and_open(cls, setting_matcher, port_path=None, serial=None, default_transport_timeout_s=None):\n        \"\"\"TODO\n\n        Parameters\n        ----------\n        setting_matcher : TODO\n            TODO\n        port_path : TODO, None\n            TODO\n        serial : TODO, None\n            TODO\n        default_transport_timeout_s : TODO, None\n            TODO\n\n        Returns\n        -------\n        dev : TODO\n            TODO\n\n        \"\"\"\n        dev = cls._find(setting_matcher, port_path=port_path, serial=serial, default_transport_timeout_s=default_transport_timeout_s)\n        dev._open()  # pylint: disable=protected-access\n        dev._flush_buffers()  # pylint: disable=protected-access\n        return dev\n\n    @classmethod\n    def _find_devices(cls, setting_matcher, device_matcher=None, usb_info='', default_transport_timeout_s=None):\n        \"\"\"_find and yield the devices that match.\n\n        Parameters\n        ----------\n        setting_matcher : TODO\n            Function that returns the setting to use given a ``usb1.USBDevice``, or ``None``\n            if the device doesn't have a valid setting.\n        device_matcher : TODO, None\n            Function that returns ``True`` if the given ``UsbTransport`` is\n            valid. ``None`` to match any device.\n        usb_info : str\n            Info string describing device(s).\n        default_transport_timeout_s : TODO, None\n            Default timeout of commands in seconds.\n\n        Yields\n        ------\n        TODO\n            UsbTransport instances\n\n        \"\"\"\n        for device in cls.USB1_CTX.getDeviceIterator(skip_on_error=True):\n            setting = setting_matcher(device)\n            if setting is None:\n                continue\n\n            transport = cls(device, setting, usb_info=usb_info, default_transport_timeout_s=default_transport_timeout_s)\n            if device_matcher is None or device_matcher(transport):\n                yield transport\n\n    @classmethod\n    def _find_first(cls, setting_matcher, device_matcher=None, usb_info='', default_transport_timeout_s=None):\n        \"\"\"Find and return the first matching device.\n\n        Parameters\n        ----------\n        setting_matcher : TODO\n            Function that returns the setting to use given a ``usb1.USBDevice``, or ``None``\n            if the device doesn't have a valid setting.\n        device_matcher : TODO\n            Function that returns ``True`` if the given ``UsbTransport`` is\n            valid. ``None`` to match any device.\n        usb_info : str\n            Info string describing device(s).\n        default_transport_timeout_s : TODO, None\n            Default timeout of commands in seconds.\n\n        Returns\n        -------\n        TODO\n            An instance of `UsbTransport`\n\n        Raises\n        ------\n        adb_shell.exceptions.DeviceNotFoundError\n            Raised if the device is not available.\n\n        \"\"\"\n        try:\n            return next(cls._find_devices(setting_matcher, device_matcher=device_matcher, usb_info=usb_info, default_transport_timeout_s=default_transport_timeout_s))\n        except StopIteration:\n            raise exceptions.UsbDeviceNotFoundError('No device available, or it is in the wrong configuration.')\n\n    @classmethod\n    def find_adb(cls, serial=None, port_path=None, default_transport_timeout_s=None):\n        \"\"\"TODO\n\n        Parameters\n        ----------\n        serial : TODO\n            TODO\n        port_path : TODO\n            TODO\n        default_transport_timeout_s : TODO, None\n            Default timeout of commands in seconds.\n\n        Returns\n        -------\n        UsbTransport\n            TODO\n\n        \"\"\"\n        return cls._find(\n            interface_matcher(CLASS, SUBCLASS, PROTOCOL),\n            serial=serial,\n            port_path=port_path,\n            default_transport_timeout_s=default_transport_timeout_s\n        )\n\n    @classmethod\n    def find_all_adb_devices(cls, default_transport_timeout_s=None):\n        \"\"\"Find all ADB devices attached via USB.\n\n        Parameters\n        ----------\n        default_transport_timeout_s : TODO, None\n            Default timeout of commands in seconds.\n\n        Returns\n        -------\n        generator\n            A generator which yields each ADB device attached via USB.\n\n        \"\"\"\n        yield from cls._find_devices(interface_matcher(CLASS, SUBCLASS, PROTOCOL), default_transport_timeout_s=default_transport_timeout_s)\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nSPHINXPROJ    = adb_shell\nSOURCEDIR     = source\nBUILDDIR      = build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset SOURCEDIR=source\nset BUILDDIR=build\nset SPHINXPROJ=adb_shell\n\nif \"%1\" == \"\" goto help\n\n%SPHINXBUILD% >NUL 2>NUL\nif errorlevel 9009 (\n\techo.\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\n\techo.installed, then set the SPHINXBUILD environment variable to point\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\n\techo.may add the Sphinx directory to PATH.\n\techo.\n\techo.If you don't have Sphinx installed, grab it from\n\techo.http://sphinx-doc.org/\n\texit /b 1\n)\n\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\ngoto end\n\n:help\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\n\n:end\npopd\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "# Standard requirements\nsphinx\nsphinx-rtd-theme\n\n# Specific requirements for this project\nadb-shell[async,usb]\npycryptodome\n"
  },
  {
    "path": "docs/source/adb_shell.adb_device.rst",
    "content": "adb\\_shell.adb\\_device module\n=============================\n\n.. automodule:: adb_shell.adb_device\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.adb_device_async.rst",
    "content": "adb\\_shell.adb\\_device\\_async module\n====================================\n\n.. automodule:: adb_shell.adb_device_async\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.adb_message.rst",
    "content": "adb\\_shell.adb\\_message module\n==============================\n\n.. automodule:: adb_shell.adb_message\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.auth.keygen.rst",
    "content": "adb\\_shell.auth.keygen module\n=============================\n\n.. automodule:: adb_shell.auth.keygen\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.auth.rst",
    "content": "adb\\_shell.auth package\n=======================\n\nSubmodules\n----------\n\n.. toctree::\n\n   adb_shell.auth.keygen\n   adb_shell.auth.sign_cryptography\n   adb_shell.auth.sign_pycryptodome\n   adb_shell.auth.sign_pythonrsa\n\nModule contents\n---------------\n\n.. automodule:: adb_shell.auth\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.auth.sign_cryptography.rst",
    "content": "adb\\_shell.auth.sign\\_cryptography module\n=========================================\n\n.. automodule:: adb_shell.auth.sign_cryptography\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.auth.sign_pycryptodome.rst",
    "content": "adb\\_shell.auth.sign\\_pycryptodome module\n=========================================\n\n.. automodule:: adb_shell.auth.sign_pycryptodome\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.auth.sign_pythonrsa.rst",
    "content": "adb\\_shell.auth.sign\\_pythonrsa module\n======================================\n\n.. automodule:: adb_shell.auth.sign_pythonrsa\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.constants.rst",
    "content": "adb\\_shell.constants module\n===========================\n\n.. automodule:: adb_shell.constants\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.exceptions.rst",
    "content": "adb\\_shell.exceptions module\n============================\n\n.. automodule:: adb_shell.exceptions\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.hidden_helpers.rst",
    "content": "adb\\_shell.hidden\\_helpers module\n=================================\n\n.. automodule:: adb_shell.hidden_helpers\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.rst",
    "content": "adb\\_shell package\n==================\n\nSubpackages\n-----------\n\n.. toctree::\n\n   adb_shell.auth\n   adb_shell.transport\n\nSubmodules\n----------\n\n.. toctree::\n\n   adb_shell.adb_device\n   adb_shell.adb_device_async\n   adb_shell.adb_message\n   adb_shell.constants\n   adb_shell.exceptions\n   adb_shell.hidden_helpers\n\nModule contents\n---------------\n\n.. automodule:: adb_shell\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.transport.base_transport.rst",
    "content": "adb\\_shell.transport.base\\_transport module\n===========================================\n\n.. automodule:: adb_shell.transport.base_transport\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.transport.base_transport_async.rst",
    "content": "adb\\_shell.transport.base\\_transport\\_async module\n==================================================\n\n.. automodule:: adb_shell.transport.base_transport_async\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.transport.rst",
    "content": "adb\\_shell.transport package\n============================\n\nSubmodules\n----------\n\n.. toctree::\n\n   adb_shell.transport.base_transport\n   adb_shell.transport.base_transport_async\n   adb_shell.transport.tcp_transport\n   adb_shell.transport.tcp_transport_async\n   adb_shell.transport.usb_transport\n\nModule contents\n---------------\n\n.. automodule:: adb_shell.transport\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.transport.tcp_transport.rst",
    "content": "adb\\_shell.transport.tcp\\_transport module\n==========================================\n\n.. automodule:: adb_shell.transport.tcp_transport\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.transport.tcp_transport_async.rst",
    "content": "adb\\_shell.transport.tcp\\_transport\\_async module\n=================================================\n\n.. automodule:: adb_shell.transport.tcp_transport_async\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/adb_shell.transport.usb_transport.rst",
    "content": "adb\\_shell.transport.usb\\_transport module\n==========================================\n\n.. automodule:: adb_shell.transport.usb_transport\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# Configuration file for the Sphinx documentation builder.\n#\n# This file does only contain a selection of the most common options. For a\n# full list see the documentation:\n# http://www.sphinx-doc.org/en/master/config\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport sys\nsys.path.insert(0, os.path.abspath('../..'))\n\nimport sphinx_rtd_theme\n\n\n# -- Project information -----------------------------------------------------\n\nproject = 'adb_shell'\ncopyright = '2021, Jeff Irion and contributors'\nauthor = 'Jeff Irion'\n\n# The short X.Y version\nversion = '0.4.4'\n# The full version, including alpha/beta/rc tags\nrelease = '0.4.4'\n\n\n# -- General configuration ---------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.todo',\n    'sphinx.ext.mathjax',\n    'sphinx.ext.viewcode',\n    'sphinx.ext.autodoc',\n    'sphinx.ext.napoleon'\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path .\nexclude_patterns = []\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\nautodoc_mock_imports = ['libusb1', 'usb1']\n\nautodoc_default_options = {'members': True, 'undoc-members': True, 'private-members': True, 'show-inheritance': True}\n\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'sphinx_rtd_theme'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# Custom sidebar templates, must be a dictionary that maps document names\n# to template names.\n#\n# The default sidebars (for documents that don't match any pattern) are\n# defined by theme itself.  Builtin themes are using these templates by\n# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',\n# 'searchbox.html']``.\n#\n# html_sidebars = {}\n\n\n# -- Options for HTMLHelp output ---------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'AdbShellDoc'\n\n\n# -- Options for LaTeX output ------------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'AdbShell.tex', 'ADB Shell Documentation',\n     'Jeff Irion', 'manual'),\n]\n\n\n# -- Options for manual page output ------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'adb_shell', 'ADB Shell Documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output ----------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'AdbShell', 'ADB Shell Documentation',\n     author, 'AdbShell', 'One line description of project.',\n     'Miscellaneous'),\n]\n\n\n# -- Extension configuration -------------------------------------------------\n\n# -- Options for todo extension ----------------------------------------------\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = True\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": ".. Jeff Irion's Python package documentation master file, created by\n   sphinx-quickstart on Mon Sep 05 22:06:10 2016.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nadb\\_shell Documentation\n========================\n\n.. toctree::\n   :hidden:\n\n   self\n   modules.rst\n\n\n.. include:: ../../README.rst\n   :start-line: 15\n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/source/modules.rst",
    "content": "adb_shell\n=========\n\n.. toctree::\n   :maxdepth: 4\n\n   adb_shell\n"
  },
  {
    "path": "scripts/bumpversion.sh",
    "content": "#!/bin/bash\n\n# Make sure there is only 1 argument passed\nif [ \"$#\" -ne 1 ]; then\n    echo \"You must provide a new version\"\n    exit 1\nfi\n\n# Make sure the new version is not empty\nif [ -z \"$1\" ]; then\n    echo \"You must provide a non-empty version\"\n    exit 1\nfi\n\n# get the directory of this script\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\n# get the package name\nPACKAGE=$($DIR/get_package_name.sh)\n\n# get the current version\nVERSION=$($DIR/get_version.sh)\n\n# Announce the version bump\necho \"Bumping the version from $VERSION to $1\"\n\n# __init__.py\nsed -i \"s|__version__ = \\\"$VERSION\\\"|__version__ = \\\"$1\\\"|g\" $DIR/../$PACKAGE/__init__.py\n\n# setup.py\nsed -i \"s|version=\\\"$VERSION\\\",|version=\\\"$1\\\",|g\" $DIR/../setup.py\n\n# conf.py\nsed -i \"s|version = '$VERSION'|version = '$1'|g\" $DIR/../docs/source/conf.py\nsed -i \"s|release = '$VERSION'|release = '$1'|g\" $DIR/../docs/source/conf.py\n"
  },
  {
    "path": "scripts/get_package_name.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# get the directory of this script\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nRSTRIP='\"*'\nLSTRIP='*\"'\n\n# get the package name\nPACKAGE_LINE=$(grep \"name=\" $DIR/../setup.py || echo '')\nPACKAGE_TEMP=${PACKAGE_LINE%$RSTRIP}\nPACKAGE=${PACKAGE_TEMP##$LSTRIP}\n\n# Make sure `PACKAGE` is not empty\nif [ -z \"$PACKAGE\" ]; then\n    echo \"Package name could not be determined\" >&2\n    exit 1\nfi\n\necho \"$PACKAGE\"\n"
  },
  {
    "path": "scripts/get_version.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# get the directory of this script\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nRSTRIP='\"*'\nLSTRIP='*\"'\n\n# get the package name\nPACKAGE=$($DIR/get_package_name.sh)\n\n# get the current version\nVERSION_LINE=$(grep \"__version__\" \"$DIR/../$PACKAGE/__init__.py\" || echo '')\nVERSION_TEMP=${VERSION_LINE%'\"'}\n\nVERSION=${VERSION_TEMP##$LSTRIP}\n\n# Make sure `VERSION` is not empty\nif [ -z \"$VERSION\" ]; then\n    echo \"Version could not be determined\" >&2\n    exit 1\nfi\n\necho \"$VERSION\"\n"
  },
  {
    "path": "scripts/git_retag.sh",
    "content": "#!/bin/bash\n\n# get the directory of this script\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\n# get the package name\nPACKAGE=$($DIR/get_package_name.sh)\n\n# get the current version\nVERSION=$($DIR/get_version.sh)\n\n\n# Announce the tag\necho \"Re-tagging v$VERSION\"\n\ncd $DIR/..\n\n# https://stackoverflow.com/a/8044605\ngit push origin \":refs/tags/v$VERSION\"\ngit tag -fa \"v$VERSION\"\ngit push origin master --tags\n"
  },
  {
    "path": "scripts/git_tag.sh",
    "content": "#!/bin/bash\n\n# get the directory of this script\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\n# get the current version\nVERSION=$($DIR/get_version.sh)\n\n\n# Announce the tag\necho \"Creating tag v$VERSION\"\n\ncd $DIR/..\ngit tag v$VERSION -m \"v$VERSION\"\ngit push --tags\n"
  },
  {
    "path": "scripts/pre-commit.sh",
    "content": "#!/bin/bash\n\nset -e\n\nfunction make_pre_commit() {\n  # setup pre-commit hook\n  DIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n  echo -e \"#!/bin/bash\\n\\n./scripts/pre-commit.sh 'placeholder_argument'\" > \"$DIR/../.git/hooks/pre-commit\"\n  chmod a+x \"$DIR/../.git/hooks/pre-commit\"\n  echo \"pre-commit hook successfully configured\"\n}\n\n# if no arguments are passed, create the pre-commit hook\nif [ \"$#\" -eq 0 ]; then\n  read -p \"Do you want to setup the git pre-commit hook? [Y/n]  \" -n 1 -r\n  echo\n  if [[ $REPLY =~ ^[Yy]$ ]]; then\n    make_pre_commit\n  else\n    echo \"pre-commit hook not configured\"\n  fi\n  exit 0\nfi\n\n# if the argument passed is \"MAKE_PRECOMMIT_HOOK\", then make the pre-commit hook\nif [[ $1 == \"MAKE_PRECOMMIT_HOOK\" ]]; then\n  make_pre_commit\n  exit 0\nfi\n\n# THE PRE-COMMIT HOOK\n\n# get the directory of this script\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\n(\n  cd \"$DIR/..\"\n\n  no_unstaged_changes=true\n  echo -e \"\\n\\033[1m1. Checking for unstaged changes...\\033[0m\"\n  for staged_file in $(git diff --name-only --cached); do\n    git diff --name-only | grep -q \"${staged_file}\" && echo \"You have unstaged changes in '${staged_file}'\" && no_unstaged_changes=false || true\n  done\n\n  # modified .py files\n  pyfiles=$(git diff --cached --name-only -- '*.py')\n\n  # flake8\n  flake8_pass=true\n  if [ \"$pyfiles\" != \"\" ]; then\n    echo -e \"\\n\\033[1m2. Running flake8...\\033[0m\"\n    venv/bin/flake8 $pyfiles || flake8_pass=false\n  else\n    echo -e \"\\n\\033[1m2. Skipping flake8.\\033[0m\"\n  fi\n\n  # pylint\n  pylint_pass=true\n  if [ \"$pyfiles\" != \"\" ]; then\n    echo -e \"\\n\\033[1m3. Running pylint...\\033[0m\"\n    venv/bin/pylint $pyfiles || pylint_pass=false\n  else\n    echo -e \"\\n\\033[1m3. Skipping pylint.\\033[0m\\n\"\n  fi\n\n  if [ \"$flake8_pass\" != \"true\" ] || [ \"$pylint_pass\" != \"true\" ] || [ \"$no_unstaged_changes\" != \"true\" ]; then\n    echo -e \"\\033[1m\\033[31mSome checks failed.\\033[0m\\n\\n  NOT RECOMMENDED: If you want to skip the pre-commit hook, use the --no-verify flag.\\n\"\n    exit 1\n  fi\n  echo -e \"\\033[1m\\033[32mAll checks passed.\\033[0m\\n\"\n)\n"
  },
  {
    "path": "scripts/rename_package.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# Make sure there is only 1 argument passed\nif [ \"$#\" -ne 1 ]; then\n    echo \"You must provide a new package name\"\n    exit 1\nfi\n\n# Make sure the new package name is not empty\nif [ -z \"$1\" ]; then\n    echo \"You must provide a non-empty package name\"\n    exit 1\nfi\n\n# get the directory of this script\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\n# get the current package name\nPACKAGE=$($DIR/get_package_name.sh)\n\n# Announce the renaming\necho \"Renaming from '$PACKAGE' to '$1'\"\n\n# .gitignore\nsed -i \"s|$PACKAGE|$1|g\" $DIR/../.gitignore\n\n# Doxyfile\nsed -i \"s|$PACKAGE|$1|g\" $DIR/../Doxyfile\n\n# Makefile\nsed -i \"s|$PACKAGE|$1|g\" $DIR/../Makefile\n\n# setup.py\nsed -i \"s|$PACKAGE|$1|g\" $DIR/../setup.py\n\n# docs/Makefile\nsed -i \"s|$PACKAGE|$1|g\" $DIR/../docs/Makefile\n\n# docs/make.bat\nsed -i \"s|$PACKAGE|$1|g\" $DIR/../docs/make.bat\n\n# docs/source/conf.py\nsed -i \"s|$PACKAGE|$1|g\" $DIR/../docs/source/conf.py\n"
  },
  {
    "path": "setup.py",
    "content": "\"\"\"setup.py file for the adb_shell package.\"\"\"\n\nfrom setuptools import setup\n\nwith open(\"README.rst\") as f:\n    readme = f.read()\n\nsetup(\n    name=\"adb_shell\",\n    version=\"0.4.4\",\n    description=\"A Python implementation of ADB with shell and FileSync functionality.\",\n    long_description=readme,\n    keywords=[\"adb\", \"android\"],\n    url=\"https://github.com/JeffLIrion/adb_shell\",\n    author=\"Jeff Irion\",\n    author_email=\"jefflirion@users.noreply.github.com\",\n    packages=[\"adb_shell\", \"adb_shell.auth\", \"adb_shell.transport\"],\n    install_requires=[\"cryptography\", \"pyasn1\", \"rsa\"],\n    tests_require=[\"pycryptodome\", \"libusb1>=1.0.16\"],\n    extras_require={\"usb\": [\"libusb1>=1.0.16\"], \"async\": [\"aiofiles>=0.4.0\", \"async_timeout>=3.0.0\"]},\n    classifiers=[\n        \"Operating System :: OS Independent\",\n        \"License :: OSI Approved :: Apache Software License\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 2\",\n    ],\n    test_suite=\"tests\",\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/async_patchers.py",
    "content": "try:\n    from contextlib import asynccontextmanager\nexcept ImportError:\n    asynccontextmanager = lambda func: func\n\nfrom unittest.mock import patch\n\nfrom adb_shell import constants\nfrom adb_shell.adb_message import AdbMessage, unpack\nfrom adb_shell.transport.tcp_transport_async import TcpTransportAsync\n\ntry:\n    from unittest.mock import AsyncMock\nexcept ImportError:\n    from unittest.mock import MagicMock\n\n    class AsyncMock(MagicMock):\n        async def __call__(self, *args, **kwargs):\n            return super(AsyncMock, self).__call__(*args, **kwargs)\n\n\ndef async_mock_open(read_data=\"\"):\n    class AsyncMockFile:\n        def __init__(self, read_data):\n            self.read_data = read_data\n            _async_mock_open.written = read_data[:0]\n\n        async def read(self, size=-1):\n            if size == -1:\n                ret = self.read_data\n                self.read_data = self.read_data[:0]\n                return ret\n\n            n = min(size, len(self.read_data))\n            ret = self.read_data[:n]\n            self.read_data = self.read_data[n:]\n            return ret\n\n        async def write(self, b):\n            if _async_mock_open.written:\n                _async_mock_open.written += b\n            else:\n                _async_mock_open.written = b\n\n        def fileno(self):\n            return 123\n\n    @asynccontextmanager\n    async def _async_mock_open(*args, **kwargs):\n        try:\n            yield AsyncMockFile(read_data)\n        finally:\n            pass\n\n    return _async_mock_open\n\n\nclass FakeStreamWriter:\n    def close(self):\n        pass\n\n    async def wait_closed(self):\n        pass\n\n    def write(self, data):\n        pass\n\n    async def drain(self):\n        pass\n\n\nclass FakeStreamReader:\n    async def read(self, numbytes):\n        return b'TEST'\n\n\nclass FakeTcpTransportAsync(TcpTransportAsync):\n    def __init__(self, *args, **kwargs):\n        TcpTransportAsync.__init__(self, *args, **kwargs)\n        self.bulk_read_data = b''\n        self.bulk_write_data = b''\n\n    async def close(self):\n        self._reader = None\n        self._writer = None\n\n    async def connect(self, transport_timeout_s=None):\n        self._reader = True\n        self._writer = True\n\n    async def bulk_read(self, numbytes, transport_timeout_s=None):\n        num = min(numbytes, constants.MAX_ADB_DATA)\n        ret = self.bulk_read_data[:num]\n        self.bulk_read_data = self.bulk_read_data[num:]\n        return ret\n\n    async def bulk_write(self, data, transport_timeout_s=None):\n        self.bulk_write_data += data\n        return len(data)\n\n\n# `TcpTransport` patches\nPATCH_TCP_TRANSPORT_ASYNC = patch('adb_shell.adb_device_async.TcpTransportAsync', FakeTcpTransportAsync)\n\n\ndef async_patch(*args, **kwargs):\n    return patch(*args, new_callable=AsyncMock, **kwargs)\n"
  },
  {
    "path": "tests/async_wrapper.py",
    "content": "import asyncio\nimport warnings\n\n\n\ndef _await(coro):\n    \"\"\"Create a new event loop, run the coroutine, then close the event loop.\"\"\"\n    loop = asyncio.new_event_loop()\n\n    with warnings.catch_warnings(record=True) as warns:\n        ret = loop.run_until_complete(coro)\n        loop.close()\n\n        if warns:\n            raise RuntimeError\n\n        return ret\n\n\ndef awaiter(func):\n    def sync_func(*args, **kwargs):\n        return _await(func(*args, **kwargs))\n\n    return sync_func\n"
  },
  {
    "path": "tests/filesync_helpers.py",
    "content": "import struct\n\nfrom adb_shell import constants\n\n\nclass FileSyncMessage(object):  # pylint: disable=too-few-public-methods\n    \"\"\"A helper class for packing FileSync messages.\n\n    Parameters\n    ----------\n    command : bytes\n        TODO\n    arg0 : int\n        TODO\n    data : bytes\n        The data that will be sent\n\n    Attributes\n    ----------\n    arg0 : int\n        TODO\n    command : int\n        The input parameter ``command`` converted to an integer via :const:`adb_shell.constants.FILESYNC_ID_TO_WIRE`\n    data : bytes\n        The data that will be sent\n\n    \"\"\"\n    def __init__(self, command, arg0=None, data=b''):\n        self.command = constants.FILESYNC_ID_TO_WIRE[command]\n        self.arg0 = arg0 or len(data)\n        self.data = data\n\n    def pack(self):\n        \"\"\"Returns this message in an over-the-wire format.\n\n        Returns\n        -------\n        bytes\n            The message packed into the format required by ADB\n\n        \"\"\"\n        return struct.pack(b'<2I', self.command, self.arg0)\n\n\nclass FileSyncListMessage(object):  # pylint: disable=too-few-public-methods\n    \"\"\"A helper class for packing FileSync messages for the \"list\" service\".\n\n    Parameters\n    ----------\n    command : bytes\n        TODO\n    arg0 : int\n        TODO\n    arg1 : TODO\n        TODO\n    arg2 : TODO\n        TODO\n    data : bytes\n        The data that will be sent\n\n    Attributes\n    ----------\n    arg0 : int\n        TODO\n    arg1 : TODO\n        TODO\n    arg2 : TODO\n        TODO\n    arg3 : int\n        The size of the data\n    command : int\n        The input parameter ``command`` converted to an integer via :const:`adb_shell.constants.FILESYNC_ID_TO_WIRE`\n    data : bytes\n        TODO\n\n    \"\"\"\n    def __init__(self, command, arg0, arg1, arg2, data=b''):\n        self.command = constants.FILESYNC_ID_TO_WIRE[command]\n        self.arg0 = arg0\n        self.arg1 = arg1\n        self.arg2 = arg2\n        self.arg3 = len(data)\n        self.data = data\n\n    def pack(self):\n        \"\"\"Returns this message in an over-the-wire format.\n\n        Returns\n        -------\n        bytes\n            The message packed into the format required by ADB\n\n        \"\"\"\n        return struct.pack(b'<5I', self.command, self.arg0, self.arg1, self.arg2, self.arg3)\n\n\nclass FileSyncStatMessage(object):  # pylint: disable=too-few-public-methods\n    \"\"\"A helper class for packing FileSync messages for the \"stat\" service\".\n\n    Parameters\n    ----------\n    command : bytes\n        TODO\n    arg0 : int\n        TODO\n    arg1 : TODO\n        TODO\n    arg2 : TODO\n        TODO\n\n    Attributes\n    ----------\n    arg0 : int\n        TODO\n    arg1 : TODO\n        TODO\n    arg2 : TODO\n        TODO\n    command : int\n        The input parameter ``command`` converted to an integer via :const:`adb_shell.constants.FILESYNC_ID_TO_WIRE`\n    data : bytes\n        The data that will be sent (always empty)\n\n    \"\"\"\n    def __init__(self, command, arg0, arg1, arg2):\n        self.command = constants.FILESYNC_ID_TO_WIRE[command]\n        self.arg0 = arg0\n        self.arg1 = arg1\n        self.arg2 = arg2\n        self.data = b''\n\n    def pack(self):\n        \"\"\"Returns this message in an over-the-wire format.\n\n        Returns\n        -------\n        bytes\n            The message packed into the format required by ADB\n\n        \"\"\"\n        return struct.pack(b'<4I', self.command, self.arg0, self.arg1, self.arg2)\n"
  },
  {
    "path": "tests/keygen_stub.py",
    "content": "from contextlib import contextmanager\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\n\nclass FileReadWrite(object):\n    \"\"\"Mock an opened file that can be read and written to.\"\"\"\n    def __init__(self):\n        self._content = b''\n        self._mode = 'r'\n\n    def read(self):\n        if self._mode == 'r':\n            if not isinstance(self._content, str):\n                return self._content.decode()\n            return self._content\n\n        if isinstance(self._content, str):\n            return self._content.encode('utf-8')\n        return self._content\n        \n\n    def write(self, content):\n        self._content = content\n\n\nPRIVATE_KEY = FileReadWrite()\nPUBLIC_KEY = FileReadWrite()\n\n\n@contextmanager\ndef open_priv_pub(infile, mode='r'):\n    try:\n        if infile.endswith('.pub'):\n            PUBLIC_KEY._mode = mode\n            yield PUBLIC_KEY\n        else:\n            PRIVATE_KEY._mode = mode\n            yield PRIVATE_KEY\n    finally:\n        pass\n"
  },
  {
    "path": "tests/patchers.py",
    "content": "from collections import namedtuple\nfrom contextlib import contextmanager\nimport sys\nimport unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\nfrom adb_shell import constants\nfrom adb_shell.adb_message import AdbMessage\nfrom adb_shell.transport.tcp_transport import TcpTransport\n\n\nASYNC_SKIPPER=unittest.skipIf(sys.version_info.major < 3 or sys.version_info.minor < 7, \"Async functionality requires Python 3.7+\")\n\nMSG_CONNECT = AdbMessage(command=constants.CNXN, arg0=constants.PROTOCOL, arg1=constants.MAX_LEGACY_ADB_DATA, data=b'host::unknown\\0')\nMSG_CONNECT_WITH_AUTH_INVALID = AdbMessage(command=constants.AUTH, arg0=0, arg1=0, data=b'host::unknown\\0')\nMSG_CONNECT_WITH_AUTH1 = AdbMessage(command=constants.AUTH, arg0=constants.AUTH_TOKEN, arg1=0, data=b'host::unknown\\0')\nMSG_CONNECT_WITH_AUTH2 = AdbMessage(command=constants.CNXN, arg0=constants.PROTOCOL, arg1=2*constants.MAX_LEGACY_ADB_DATA, data=b'host::unknown\\0')\nMSG_CONNECT_WITH_AUTH_NEW_KEY2 = AdbMessage(command=constants.AUTH, arg0=0, arg1=0, data=b'host::unknown\\0')\nMSG_CONNECT_WITH_AUTH_NEW_KEY3 = AdbMessage(command=constants.CNXN, arg0=constants.PROTOCOL, arg1=3*constants.MAX_LEGACY_ADB_DATA, data=b'host::unknown\\0')\n\nBULK_READ_LIST = [MSG_CONNECT.pack(), MSG_CONNECT.data]\nBULK_READ_LIST_WITH_AUTH_INVALID = [MSG_CONNECT_WITH_AUTH_INVALID.pack(), MSG_CONNECT_WITH_AUTH_INVALID.data]\nBULK_READ_LIST_WITH_AUTH = [MSG_CONNECT_WITH_AUTH1.pack(), MSG_CONNECT_WITH_AUTH1.data, MSG_CONNECT_WITH_AUTH2.pack(), MSG_CONNECT_WITH_AUTH2.data]\nBULK_READ_LIST_WITH_AUTH_NEW_KEY = [MSG_CONNECT_WITH_AUTH1.pack(), MSG_CONNECT_WITH_AUTH1.data, MSG_CONNECT_WITH_AUTH_NEW_KEY2.pack(), MSG_CONNECT_WITH_AUTH_NEW_KEY2.data, MSG_CONNECT_WITH_AUTH_NEW_KEY3.pack(), MSG_CONNECT_WITH_AUTH_NEW_KEY3.data]\n\nStSize = namedtuple(\"StSize\", [\"st_size\"])\n\n\ndef mock_open(read_data=\"\"):\n    class MockFile:\n        def __init__(self, read_data):\n            self.read_data = read_data\n            _mock_open.written = read_data[:0]\n\n        def read(self, size=-1):\n            if size == -1:\n                ret = self.read_data\n                self.read_data = self.read_data[:0]\n                return ret\n\n            n = min(size, len(self.read_data))\n            ret = self.read_data[:n]\n            self.read_data = self.read_data[n:]\n            return ret\n\n        def write(self, b):\n            if _mock_open.written:\n                _mock_open.written += b\n            else:\n                _mock_open.written = b\n        def fileno(self):\n            return 123\n\n    @contextmanager\n    def _mock_open(*args, **kwargs):\n        try:\n            yield MockFile(read_data)\n        finally:\n            pass\n\n    return _mock_open\n\n\nclass FakeSocket(object):\n    def __init__(self):\n        self._recv = b''\n\n    def close(self):\n        pass\n\n    def recv(self, bufsize):\n        ret = self._recv[:bufsize]\n        self._recv = self._recv[bufsize:]\n        return ret\n\n    def send(self, data):\n        pass\n\n    def setblocking(self, *args, **kwargs):\n        pass\n\n    def shutdown(self, how):\n        pass\n\n\nclass FakeTcpTransport(TcpTransport):\n    def __init__(self, *args, **kwargs):\n        TcpTransport.__init__(self, *args, **kwargs)\n        self.bulk_read_data = b''\n        self.bulk_write_data = b''\n\n    def close(self):\n        self._connection = None\n\n    def connect(self, transport_timeout_s=None):\n        self._connection = True\n\n    def bulk_read(self, numbytes, transport_timeout_s=None):\n        num = min(numbytes, constants.MAX_ADB_DATA)\n        ret = self.bulk_read_data[:num]\n        self.bulk_read_data = self.bulk_read_data[num:]\n        return ret\n\n    def bulk_write(self, data, transport_timeout_s=None):\n        self.bulk_write_data += data\n        return len(data)\n\n\n# `socket` patches\nPATCH_CREATE_CONNECTION = patch('socket.create_connection', return_value=FakeSocket())\n\n\n# `select` patches\nPATCH_SELECT_SUCCESS = patch('select.select', return_value=(True, True, True))\n\nPATCH_SELECT_FAIL = patch('select.select', return_value=(False, False, False))\n\n\n# `TcpTransport` patches\nPATCH_TCP_TRANSPORT = patch('adb_shell.adb_device.TcpTransport', FakeTcpTransport)\n"
  },
  {
    "path": "tests/test_adb_device.py",
    "content": "import inspect\nimport logging\nfrom io import BytesIO\nimport struct\nimport sys\nimport time\nimport unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\nfrom adb_shell import adb_device, constants, exceptions\nfrom adb_shell.adb_device import AdbDevice, AdbDeviceTcp, DeviceFile\nfrom adb_shell.adb_message import AdbMessage\nfrom adb_shell.auth.keygen import keygen\nfrom adb_shell.auth.sign_pythonrsa import PythonRSASigner\n\nfrom . import patchers\nfrom .filesync_helpers import FileSyncMessage, FileSyncListMessage, FileSyncStatMessage\nfrom .keygen_stub import open_priv_pub\n\n\n# https://stackoverflow.com/a/7483862\n_LOGGER = logging.getLogger('adb_shell.adb_device')\n_LOGGER.setLevel(logging.DEBUG)\n_LOGGER.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef to_int(cmd):\n    return sum(c << (i * 8) for i, c in enumerate(bytearray(cmd)))\n\ndef join_messages(*messages):\n    return b''.join([message.pack() + message.data for message in messages])\n\n\nclass AdbMessageForTesting(AdbMessage):\n    def __init__(self, command, arg0=None, arg1=None, data=b''):\n        self.command = to_int(command)\n        self.magic = self.command ^ 0xFFFFFFFF\n        self.arg0 = arg0\n        self.arg1 = arg1\n        self.data = data\n\n\nclass TestAdbDevice(unittest.TestCase):\n    def setUp(self):\n        self.transport = patchers.FakeTcpTransport('host', 5555)\n        self.device = AdbDevice(transport=self.transport)\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST)\n        self.progress_callback_count = 0\n\n        def _progress_callback(device_path, current, total_bytes):\n            print(\"device_path = {}, current = {}, total_bytes = {}\".format(device_path, current, total_bytes))\n            self.progress_callback_count += 1\n\n        self.progress_callback = _progress_callback\n\n    def tearDown(self):\n        self.assertFalse(self.transport.bulk_read_data)\n        self.assertEqual(len(self.device._io_manager._packet_store._dict), 0)\n\n    @staticmethod\n    def fake_stat(*args, **kwargs):\n        return 1, 2, 3\n\n    def test_no_async_references(self):\n        \"\"\"Make sure there are no references to async code.\"\"\"\n        adb_device_source = inspect.getsource(adb_device)\n        self.assertTrue(\"base_transport_async\" not in adb_device_source)\n        self.assertTrue(\"BaseTransportAsync\" not in adb_device_source)\n        self.assertTrue(\"adb_device_async\" not in adb_device_source)\n        self.assertTrue(\"AdbDeviceAsync\" not in adb_device_source)\n        self.assertTrue(\"async\" not in adb_device_source)\n        self.assertTrue(\"Async\" not in adb_device_source)\n        self.transport.bulk_read_data = b''\n\n    def test_adb_connection_error(self):\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.exec_out('FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.root()\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.shell('FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            ''.join(self.device.streaming_shell('FAIL'))\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.reboot()\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.root()\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.list('FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.push('FAIL', 'FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.pull('FAIL', 'FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            self.device.stat('FAIL')\n\n        self.transport.bulk_read_data = b''\n\n    def test_init_tcp(self):\n        with patchers.PATCH_TCP_TRANSPORT:\n            tcp_device = AdbDeviceTcp('host')\n            tcp_device._io_manager._transport.bulk_read_data = self.transport.bulk_read_data\n\n        # Make sure that the `connect()` method works\n        self.assertTrue(tcp_device.connect())\n        self.assertTrue(tcp_device.available)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n        \n    def test_init_banner(self):\n        device_with_banner = AdbDevice(transport=patchers.FakeTcpTransport('host', 5555), banner='banner')\n        self.assertEqual(device_with_banner._banner, b'banner')\n\n        device_with_banner2 = AdbDevice(transport=patchers.FakeTcpTransport('host', 5555), banner=bytearray('banner2', 'utf-8'))\n        self.assertEqual(device_with_banner2._banner, b'banner2')\n\n        device_with_banner3 = AdbDevice(transport=patchers.FakeTcpTransport('host', 5555), banner=u'banner3')\n        self.assertEqual(device_with_banner3._banner, b'banner3')\n\n        with patch('socket.gethostname', side_effect=Exception):\n            device_banner_unknown = AdbDevice(transport=self.transport)\n            self.assertTrue(device_banner_unknown.connect())\n            self.assertEqual(device_banner_unknown._banner, b'unknown')\n\n    def test_init_invalid_transport(self):\n        with self.assertRaises(exceptions.InvalidTransportError):\n            device = AdbDevice(transport=123)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    def test_available(self):\n        self.assertFalse(self.device.available)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    def test_close(self):\n        self.assertFalse(self.device.close())\n        self.assertFalse(self.device.available)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    # ======================================================================= #\n    #                                                                         #\n    #                             `connect` tests                             #\n    #                                                                         #\n    # ======================================================================= #\n    def test_connect(self):\n        self.assertTrue(self.device.connect())\n        self.assertTrue(self.device.available)\n\n    def test_connect_no_keys(self):\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH[:2])\n        with self.assertRaises(exceptions.DeviceAuthError):\n            self.device.connect()\n\n        self.assertFalse(self.device.available)\n\n    def test_connect_with_key_invalid_response(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH_INVALID)\n\n        with self.assertRaises(exceptions.InvalidResponseError):\n            self.device.connect([signer])\n\n        self.assertFalse(self.device.available)\n\n    def test_connect_with_key(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH)\n\n        self.assertTrue(self.device.connect([signer]))\n\n    def test_connect_with_new_key(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n            signer.pub_key = u''\n\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY)\n\n        self.assertTrue(self.device.connect([signer]))\n\n    def test_connect_with_new_key_and_callback(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n            signer.pub_key = u''\n\n        self._callback_invoked = False\n        def auth_callback(device):\n            self._callback_invoked = True\n\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY)\n\n        self.assertTrue(self.device.connect([signer], auth_callback=auth_callback))\n        self.assertTrue(self._callback_invoked)\n\n    def test_connect_timeout(self):\n        self.transport.bulk_read_data = AdbMessage(command=constants.CLSE, arg0=1, arg1=1).pack()\n\n        with self.assertRaises(exceptions.AdbTimeoutError):\n            # Use a negative timeout to ensure that only one packet gets read\n            self.device.connect([], read_timeout_s=-1)\n\n    # ======================================================================= #\n    #                                                                         #\n    #                              `shell` tests                              #\n    #                                                                         #\n    # ======================================================================= #\n    def test_shell_no_return(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(self.device.shell('TEST'), '')\n\n    def test_shell_return_pass(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(self.device.shell('TEST'), 'PASS')\n\n    def test_shell_local_id_wraparound(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=2**32 - 1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=2**32 - 1, data=b'PASS1'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=2**32 - 1, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS2'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.device._local_id = 2**32 - 2\n        self.assertEqual(self.device.shell('TEST'), 'PASS1')\n        self.assertEqual(self.device.shell('TEST'), 'PASS2')\n\n    def test_shell_return_pass_with_unexpected_packet(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'),\n                                                      AdbMessage(command=constants.AUTH, arg0=1, arg1=1, data=b'UNEXPECTED'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(self.device.shell('TEST'), 'PASS')\n\n    def test_shell_dont_decode(self):\n        self.assertTrue(self.device.connect())\n        \n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(self.device.shell('TEST', decode=False), b'PASS')\n\n    def test_shell_avoid_decode_error(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\\x80abc'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        expected = '\\\\x80abc' if sys.version_info[0] > 2 else u'\\ufffdabc'\n        self.assertEqual(self.device.shell('TEST'), expected)\n\n    def test_shell_data_length_exceeds_max(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'0'*(constants.MAX_ADB_DATA+1)),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.device.shell('TEST')\n        self.assertTrue(True)\n\n    def test_shell_multibytes_sequence_exceeds_max(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'0'*(constants.MAX_ADB_DATA-1) + b'\\xe3\\x81\\x82'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(u'0'*(constants.MAX_ADB_DATA-1) + u'\\u3042', self.device.shell('TEST'))\n\n    def test_shell_with_multibytes_sequence_over_two_messages(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\\xe3'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\\x81\\x82'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(u'\\u3042', self.device.shell('TEST'))\n\n    def test_shell_multiple_clse(self):\n        # https://github.com/JeffLIrion/adb_shell/issues/15#issuecomment-536795938\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        msg1 = AdbMessage(command=constants.OKAY, arg0=2, arg1=2, data=b'')\n        msg2 = AdbMessage(command=constants.WRTE, arg0=2, arg1=2, data=b'PASS')\n        msg3 = AdbMessage(command=constants.CLSE, arg0=2, arg1=2, data=b'')\n        self.transport.bulk_read_data = b''.join([b'OKAY\\xd9R\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',\n                                                  b'WRTE\\xd9R\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x002\\x00\\x00\\x00\\xa8\\xad\\xab\\xba',\n                                                  b'2',\n                                                  b'WRTE\\xd9R\\x00\\x00\\x01\\x00\\x00\\x00\\x0c\\x02\\x00\\x00\\xc0\\x92\\x00\\x00\\xa8\\xad\\xab\\xba',\n                                                  b'Wake Locks: size=2\\ncom.google.android.tvlauncher\\n\\n- STREAM_MUSIC:\\n   Muted: true\\n   Min: 0\\n   Max: 15\\n   Current: 2 (speaker): 15, 4 (headset): 10, 8 (headphone): 10, 80 (bt_a2dp): 10, 1000 (digital_dock): 10, 4000000 (usb_headset): 3, 40000000 (default): 15\\n   Devices: speaker\\n- STREAM_ALARM:\\n   Muted: true\\n   Min: 1\\n   Max: 7\\n   Current: 2 (speaker): 7, 4 (headset): 5, 8 (headphone): 5, 80 (bt_a2dp): 5, 1000 (digital_dock): 5, 4000000 (usb_headset): 1, 40000000 (default): 7\\n   Devices: speaker\\n- STREAM_NOTIFICATION:\\n',\n                                                  b'CLSE\\xd9R\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',\n                                                  msg1.pack(),\n                                                  b'CLSE\\xdaR\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',\n                                                  msg2.pack(),\n                                                  msg2.data,\n                                                  msg3.pack()])\n\n        self.device.shell(\"dumpsys power | grep 'Display Power' | grep -q 'state=ON' && echo -e '1\\\\c' && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\\\\c' && dumpsys audio | grep paused | grep -qv 'Buffer Queue' && echo -e '1\\\\c' || (dumpsys audio | grep started | grep -qv 'Buffer Queue' && echo '2\\\\c' || echo '0\\\\c') && dumpsys power | grep Locks | grep 'size=' && CURRENT_APP=$(dumpsys window windows | grep mCurrentFocus) && CURRENT_APP=${CURRENT_APP#*{* * } && CURRENT_APP=${CURRENT_APP%%/*} && echo $CURRENT_APP && (dumpsys media_session | grep -A 100 'Sessions Stack' | grep -A 100 $CURRENT_APP | grep -m 1 'state=PlaybackState {' || echo) && dumpsys audio | grep '\\\\- STREAM_MUSIC:' -A 12\")\n        self.assertEqual(self.device.shell('TEST'), 'PASS')\n\n    def test_shell_multiple_streams(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=2, data=b'PASS2'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS1'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=2, data=b''))\n\n        self.assertEqual(self.device.shell('TEST1'), 'PASS1')\n        self.assertEqual(self.device.shell('TEST2'), 'PASS2')\n\n    def test_shell_multiple_streams2(self):\n        self.assertTrue(self.device.connect())\n\n        def fake_read_packet_from_device(*args, **kwargs):\n            # Mimic the scenario that this stream's packets get read by another stream after the first attempt to read the packet from the device\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.WRTE, data=b'\\x00')\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.OKAY, data=b'\\x00')\n            self.device._io_manager._packet_store.put(arg0=2, arg1=2, cmd=constants.OKAY, data=b'\\x00')\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.OKAY, data=b'\\x00')\n            self.device._io_manager._packet_store.put(arg0=2, arg1=2, cmd=constants.WRTE, data=b'PASS2')\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.WRTE, data=b\"PASS1\")\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.CLSE, data=b\"\")\n            self.device._io_manager._packet_store.put(arg0=2, arg1=2, cmd=constants.CLSE, data=b\"\")\n\n            return constants.OKAY, 2, 2, b\"\\x00\"\n\n        with patch.object(self.device._io_manager, \"_read_packet_from_device\", fake_read_packet_from_device):\n            # The patch function will only be called once, all subsequent packets will be retrieved from the store\n            self.assertEqual(self.device.shell('TEST1'), 'PASS1')\n            self.assertEqual(self.device.shell('TEST2'), 'PASS2')\n\n    def test_shell_local_id2(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=2, data=b'PASS2'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS1'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=2, data=b''))\n\n        self.assertEqual(self.device.shell('TEST1'), 'PASS1')\n        self.assertEqual(self.device.shell('TEST2'), 'PASS2')\n\n    def test_shell_remote_id2(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=2, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=2, arg1=2, data=b'PASS2'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS1'),\n                                                      AdbMessage(command=constants.CLSE, arg0=2, arg1=2, data=b''),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(self.device.shell('TEST1'), 'PASS1')\n        self.assertEqual(self.device.shell('TEST2'), 'PASS2')\n\n    # ======================================================================= #\n    #                                                                         #\n    #                           `shell` error tests                           #\n    #                                                                         #\n    # ======================================================================= #\n    def test_shell_error_local_id_timeout(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1234, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1234, data=b'\\x00'))\n\n        with self.assertRaises(exceptions.AdbTimeoutError):\n            self.device.shell('TEST', read_timeout_s=1)\n\n        # Close the connection so that the packet store gets cleared\n        self.device.close()\n\n    def test_shell_error_unknown_command(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessageForTesting(command=constants.FAIL, arg0=1, arg1=1, data=b''))\n\n        with self.assertRaises(exceptions.InvalidCommandError):\n            self.assertEqual(self.device.shell('TEST'), '')\n\n    def test_shell_error_transport_timeout(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b''))\n\n        with self.assertRaises(exceptions.AdbTimeoutError):\n            self.device.shell('TEST', read_timeout_s=-1)\n\n    def test_shell_error_read_timeout_multiple_clse(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.CLSE, arg0=2, arg1=1, data=b''))\n\n        with self.assertRaises(exceptions.AdbTimeoutError):\n            self.device.shell('TEST', read_timeout_s=-1)\n\n    def test_shell_error_timeout(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        def fake_read_until(*args, **kwargs):\n            time.sleep(0.2)\n            return b'WRTE', b'PA'\n\n        with patch('adb_shell.adb_device.AdbDevice._read_until', fake_read_until):\n            with self.assertRaises(exceptions.AdbTimeoutError):\n                self.device.shell('TEST', timeout_s=0.5)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    def test_shell_error_checksum(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00')\n        msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS')\n        self.transport.bulk_read_data = b''.join([msg1.pack(), msg1.data, msg2.pack(), msg2.data[:-1] + b'0'])\n\n        with self.assertRaises(exceptions.InvalidChecksumError):\n            self.device.shell('TEST')\n\n    def test_issue29(self):\n        # https://github.com/JeffLIrion/adb_shell/issues/29\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n\n        okay3 = AdbMessage(command=constants.OKAY, arg0=1, arg1=3, data=b'\\x00')\n        clse3 = AdbMessage(command=constants.CLSE, arg0=1, arg1=3, data=b'')\n        okay5 = AdbMessage(command=constants.OKAY, arg0=1, arg1=5, data=b'\\x00')\n        clse5 = AdbMessage(command=constants.CLSE, arg0=1, arg1=5, data=b'')\n        okay7 = AdbMessage(command=constants.OKAY, arg0=1, arg1=7, data=b'\\x00')\n        clse7 = AdbMessage(command=constants.CLSE, arg0=1, arg1=7, data=b'')\n\n        self.transport.bulk_read_data = b''.join([b'AUTH\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\xc5\\n\\x00\\x00\\xbe\\xaa\\xab\\xb7',  # Line 22\n                                                  b\"\\x17\\xbf\\xbf\\xff\\xc7\\xa2eo'Sh\\xdf\\x8e\\xf5\\xff\\xe0\\tJ6H\",  # Line 23\n                                                  b\"CNXN\\x00\\x00\\x00\\x01\\x00\\x10\\x00\\x00i\\x00\\x00\\x00.'\\x00\\x00\\xbc\\xb1\\xa7\\xb1\",  # Line 26\n                                                  b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2',  # Line 27\n                                                  b'OKAY\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',  # Line 290 (modified --> Line 30)\n                                                  b'CLSE\\xa2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 291\n                                                  b'CLSE\\xa2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 292\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x001\\x00\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 31\n                                                  b'1',  # Line 32\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x001\\x00\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 35\n                                                  b'1',  # Line 36\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x000\\x00\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 39\n                                                  b'0',  # Line 40\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x000\\x06\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 43\n                                                  b'Wake Locks: size=0\\n',  # Line 44\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1e\\x00\\x00\\x00V\\x0b\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 47\n                                                  b'com.google.android.youtube.tv\\n',  # Line 48\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\xa13\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 51\n                                                  b'      state=PlaybackState {state=0, position=0, buffered position=0, speed=0.0, updated=0, actions=0, custom actions=[], active item id=-1, error=null}\\n',  # Line 52\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00.\\x01\\x00\\x00\\xceP\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 55\n                                                  b'- STREAM_MUSIC:\\n   Muted: false\\n   Min: 0\\n   Max: 15\\n   Current: 2 (speaker): 11, 4 (headset): 10, 8 (headphone): 10, 400 (hdmi): 6, 40000000 (default): 11\\n   Devices: hdmi\\n- STREAM_ALARM:\\n   Muted: false\\n   Min: 0\\n   Max: 7\\n   Current: 40000000 (default): 6\\n   Devices: speaker\\n- STREAM_NOTIFICATION:\\n',  # Line 56\n                                                  b'CLSE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 59\n                                                  b'AUTH\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x94\\t\\x00\\x00\\xbe\\xaa\\xab\\xb7',  # Line 297\n                                                  b'P\\xa5\\x86\\x97\\xe8\\x01\\xb09\\x8c>F\\x9d\\xc6\\xbd\\xc0J\\x80!\\xbb\\x1a',  # Line 298\n                                                  b\"CNXN\\x00\\x00\\x00\\x01\\x00\\x10\\x00\\x00i\\x00\\x00\\x00.'\\x00\\x00\\xbc\\xb1\\xa7\\xb1\",  # Line 301\n                                                  b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2',  # Line 302\n                                                  b'OKAY\\xa5\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',  # Line 305\n                                                  b'CLSE\\xa5\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 306\n                                                  okay3.pack(),\n                                                  okay3.data,\n                                                  clse3.pack(),\n                                                  b'AUTH\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00e\\x0c\\x00\\x00\\xbe\\xaa\\xab\\xb7',  # Line 315\n                                                  b'\\xd3\\xef\\x7f_\\xa6\\xc0`b\\x19\\\\z\\xe4\\xf3\\xe2\\xed\\x8d\\xe1W\\xfbH',  # Line 316\n                                                  b\"CNXN\\x00\\x00\\x00\\x01\\x00\\x10\\x00\\x00i\\x00\\x00\\x00.'\\x00\\x00\\xbc\\xb1\\xa7\\xb1\",  # Line 319\n                                                  b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2',  # Line 320\n                                                  b'OKAY\\xa7\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',  # Line 323\n                                                  b'CLSE\\xa7\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 324\n                                                  okay5.pack(),\n                                                  okay5.data,\n                                                  clse5.pack(),\n                                                  b'AUTH\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x93\\x08\\x00\\x00\\xbe\\xaa\\xab\\xb7',  # Line 333\n                                                  b's\\xd4_e\\xa4s\\x02\\x95\\x0f\\x1e\\xec\\n\\x95Y9[`\\x8e\\xe1f',  # Line 334\n                                                  b\"CNXN\\x00\\x00\\x00\\x01\\x00\\x10\\x00\\x00i\\x00\\x00\\x00.'\\x00\\x00\\xbc\\xb1\\xa7\\xb1\",  # Line 337\n                                                  b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2',  # Line 338\n                                                  b'OKAY\\xa9\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',  # Line 341\n                                                  b'CLSE\\xa9\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 342\n                                                  okay7.pack(),\n                                                  okay7.data,\n                                                  clse7.pack()])\n\n        self.assertTrue(self.device.connect([signer]))\n\n        self.device.shell('Android TV update command')\n        \n        self.assertTrue(self.device.connect([signer]))\n        self.device.shell('Android TV update command')\n        self.device.shell('Android TV update command')\n        self.assertTrue(self.device.connect([signer]))\n        self.device.shell('Android TV update command')\n        self.device.shell('Android TV update command')\n        self.assertTrue(self.device.connect([signer]))\n        self.device.shell('Android TV update command')\n        self.device.shell('Android TV update command')\n\n    # ======================================================================= #\n    #                                                                         #\n    #                      `streaming_shell` tests                            #\n    #                                                                         #\n    # ======================================================================= #\n    def test_streaming_shell_decode(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(\n            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'ABC'),\n            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'123'),\n        )\n\n        generator = self.device.streaming_shell('TEST', decode=True)\n        self.assertEqual('ABC', next(generator))\n        self.assertEqual('123', next(generator))\n\n    def test_streaming_shell_dont_decode(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(\n            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'ABC'),\n            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'123'),\n        )\n\n        generator = self.device.streaming_shell('TEST', decode=False)\n        self.assertEqual(b'ABC', next(generator))\n        self.assertEqual(b'123', next(generator))\n\n\n    # ======================================================================= #\n    #                                                                         #\n    #                              `reboot` test                              #\n    #                                                                         #\n    # ======================================================================= #\n    def test_reboot(self):\n        self.assertTrue(self.device.connect())\n\n        with patch('adb_shell.adb_device.AdbDevice._open') as patch_open:\n            self.device.reboot()\n            assert patch_open.call_count == 1\n\n\n    # ======================================================================= #\n    #                                                                         #\n    #                               `root` test                               #\n    #                                                                         #\n    # ======================================================================= #\n    def test_root(self):\n        self.assertTrue(self.device.connect())\n\n        with patch('adb_shell.adb_device.AdbDevice._service') as patch_service:\n            self.device.root()\n            assert patch_service.call_count == 1\n\n\n    # ======================================================================= #\n    #                                                                         #\n    #                          `exec_out` test                                #\n    #                                                                         #\n    # ======================================================================= #\n    def test_exec_out(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = b''.join([b'OKAY\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',\n                                                  b'WRTE\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00J\\x01\\x00\\x00\\xa8\\xad\\xab\\xba',\n                                                  b'TEST\\n',\n                                                  b'',\n                                                  b'CLSE\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba'])\n\n        self.assertEqual(self.device.exec_out(\"echo 'TEST'\"), \"TEST\\n\")\n\n    # ======================================================================= #\n    #                                                                         #\n    #                         `filesync` tests                                #\n    #                                                                         #\n    # ======================================================================= #\n    def test_list(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncListMessage(constants.DENT, 1, 2, 3, data=b'file1'),\n                                                                                                                            FileSyncListMessage(constants.DENT, 4, 5, 6, data=b'file2'),\n                                                                                                                            FileSyncListMessage(constants.DONE, 0, 0, 0))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.LIST, data=b'/dir'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        expected_result = [DeviceFile(filename=bytearray(b'file1'), mode=1, size=2, mtime=3),\n                           DeviceFile(filename=bytearray(b'file2'), mode=4, size=5, mtime=6)]\n\n        self.assertEqual(expected_result, self.device.list('/dir'))\n        self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_list_empty_path(self):\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.list(\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.list(b\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.list(u\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.list(None)\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    def test_push_fail(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.FAIL, data=b''))))\n\n        with self.assertRaises(exceptions.PushFailedError), patch('adb_shell.adb_device.open', patchers.mock_open(read_data=filedata)):\n            self.device.push('TEST_FILE', '/data', mtime=mtime)\n\n    def test_push_file(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=FileSyncMessage(constants.OKAY).pack()),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                                                                                  FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                  FileSyncMessage(command=constants.DONE, arg0=mtime, data=b''))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('adb_shell.adb_device.open', patchers.mock_open(read_data=filedata)):\n            self.assertEqual(self.progress_callback_count, 0)\n            with patch(\"adb_shell.adb_device.os.fstat\", return_value=patchers.StSize(12345)):\n                self.device.push('TEST_FILE', '/data', mtime=mtime, progress_callback=self.progress_callback)\n            self.assertEqual(self.progress_callback_count, 1)\n            self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_push_bytesio(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=FileSyncMessage(constants.OKAY).pack()),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                                                                                  FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                  FileSyncMessage(command=constants.DONE, arg0=mtime, data=b''))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        stream = BytesIO(filedata)\n        self.device.push(stream, '/data', mtime=mtime)\n        self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_push_file_exception(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=FileSyncMessage(constants.OKAY).pack()),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                                                                                  FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                  FileSyncMessage(command=constants.DONE, arg0=mtime, data=b''))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('adb_shell.adb_device.open', patchers.mock_open(read_data=filedata)):\n            # Set self.progress_callback_count to None so that an exception occurs when self.progress_callback tries to increment it\n            self.progress_callback_count = None\n            with patch(\"adb_shell.adb_device.os.fstat\", return_value=patchers.StSize(12345)):\n                self.device.push('TEST_FILE', '/data', mtime=mtime, progress_callback=self.progress_callback)\n            \n            self.assertIsNone(self.progress_callback_count)\n            self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_push_file_mtime0(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 0\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.OKAY, data=b''))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                                                                                  FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                  FileSyncMessage(command=constants.DONE, arg0=mtime))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('adb_shell.adb_device.open', patchers.mock_open(read_data=filedata)), patch('time.time', return_value=mtime):\n            self.device.push('TEST_FILE', '/data', mtime=mtime)\n            self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_push_big_file(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'0' * int(3.5 * self.device.max_chunk_size)\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.OKAY))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        mcs0, mcs1, mcs2, mcs3 = 0, self.device.max_chunk_size, 2*self.device.max_chunk_size, 3*self.device.max_chunk_size\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(\n                                                FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                FileSyncMessage(command=constants.DATA, data=filedata[mcs0:mcs1]))),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(\n                                                FileSyncMessage(command=constants.DATA, data=filedata[mcs1:mcs2]))),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(\n                                                FileSyncMessage(command=constants.DATA, data=filedata[mcs2:mcs3]),\n                                                FileSyncMessage(command=constants.DATA, data=filedata[mcs3:]),\n                                                FileSyncMessage(command=constants.DONE, arg0=mtime))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('adb_shell.adb_device.open', patchers.mock_open(read_data=filedata)):\n            self.assertEqual(self.progress_callback_count, 0)\n            with patch(\"adb_shell.adb_device.os.fstat\", return_value=patchers.StSize(12345)):\n                self.device.push('TEST_FILE', '/data', mtime=mtime, progress_callback=self.progress_callback)\n            self.assertEqual(self.progress_callback_count, 4)\n            self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_push_dir(self):\n        self.assertTrue(self.device.connect())\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=2, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=2, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=2, arg1=2, data=join_messages(FileSyncMessage(constants.OKAY))),\n                                                      AdbMessage(command=constants.CLSE, arg0=2, arg1=2, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=3, arg1=3, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=3, arg1=3, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=3, arg1=3, data=join_messages(FileSyncMessage(constants.OKAY))),\n                                                      AdbMessage(command=constants.CLSE, arg0=3, arg1=3, data=b''))\n\n        # Expected `bulk_write` values\n        #TODO\n\n        with patch('adb_shell.adb_device.open', patchers.mock_open(read_data=filedata)), patch('os.path.isdir', lambda x: x == 'TEST_DIR/'), patch('os.listdir', return_value=['TEST_FILE1', 'TEST_FILE2']):\n            self.device.push('TEST_DIR/', '/data', mtime=mtime)\n\n    def test_push_empty_path(self):\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.push(\"NOTHING\", \"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.push(\"NOTHING\", b\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.push(\"NOTHING\", u\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.push(\"NOTHING\", None)\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    def test_pull_file(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                            FileSyncMessage(command=constants.DONE))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('adb_shell.adb_device.open', patchers.mock_open()) as m:\n            self.assertEqual(self.progress_callback_count, 0)\n            with patch(\"adb_shell.adb_device.AdbDevice.stat\", self.fake_stat):\n                self.device.pull('/data', 'TEST_FILE', progress_callback=self.progress_callback)\n\n            self.assertEqual(self.progress_callback_count, 1)\n            self.assertEqual(m.written, filedata)\n            self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_pull_bytesio(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                            FileSyncMessage(command=constants.DONE))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        stream = BytesIO()\n        self.device.pull('/data', stream)\n        \n        self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n        self.assertEqual(stream.getvalue(), filedata)    \n\n    def test_pull_file_exception(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                            FileSyncMessage(command=constants.DONE))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('adb_shell.adb_device.open', patchers.mock_open()) as m:\n            # Set self.progress_callback_count to None so that an exception occurs when self.progress_callback tries to increment it\n            self.progress_callback_count = None\n            with patch(\"adb_shell.adb_device.AdbDevice.stat\", self.fake_stat):\n                self.device.pull('/data', 'TEST_FILE', progress_callback=self.progress_callback)\n\n            self.assertIsNone(self.progress_callback_count)\n            self.assertEqual(m.written, filedata)\n            self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_pull_big_file(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        filedata = b'0' * int(1.5 * constants.MAX_ADB_DATA)\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                            FileSyncMessage(command=constants.DONE))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('adb_shell.adb_device.open', patchers.mock_open()) as m:\n            self.assertEqual(self.progress_callback_count, 0)\n            with patch(\"adb_shell.adb_device.AdbDevice.stat\", self.fake_stat):\n                self.device.pull('/data', 'TEST_FILE', progress_callback=self.progress_callback)\n            \n            self.assertEqual(self.progress_callback_count, 1)\n            self.assertEqual(m.written, filedata)\n            self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_pull_empty_path(self):\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.pull(\"\", \"NOWHERE\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.pull(b\"\", \"NOWHERE\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.pull(u\"\", \"NOWHERE\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.pull(None, \"NOWHERE\")\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    def test_pull_non_existant_path(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'FAIL&\\x00\\x00\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'open failed: No such file or directory'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/does/not/exist'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n        with self.assertRaises(exceptions.AdbCommandFailureException):\n            self.device.pull(\"/does/not/exist\", \"NOWHERE\")\n        self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_pull_non_existant_path_2(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'FAIL&\\x00\\x00\\x00open failed: No such file or directory'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/does/not/exist'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n        with self.assertRaises(exceptions.AdbCommandFailureException):\n            self.device.pull(\"/does/not/exist\", \"NOWHERE\")\n        self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_stat(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncStatMessage(constants.STAT, 1, 2, 3),\n                                                                                                                            FileSyncStatMessage(constants.DONE, 0, 0, 0))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.STAT, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual((1, 2, 3), self.device.stat('/data'))\n        self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    def test_stat_empty_path(self):\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.stat(\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.stat(b\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.stat(u\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            self.device.stat(None)\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    def test_stat_issue155(self):\n        self.assertTrue(self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = b\"\".join([b'CLSE\\n\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',\n                                                  b'OKAY\\x0b\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',\n                                                  b'OKAY\\x0b\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',\n                                                  b'WRTE\\x0b\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x96\\x04\\x00\\x00\\xa8\\xad\\xab\\xba',\n                                                  b'STAT\\xedA\\x00\\x00\\x00\\x10\\x00\\x00\\xf0\\x88[I',\n                                                  b'CLSE\\x0b\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba'])\n\n        # This is where the expected values come from\n        mode = 16877\n        size = 4096\n        mtime = 1230735600\n        self.assertEqual(FileSyncStatMessage(constants.STAT, mode, size, mtime).pack(), b'STAT\\xedA\\x00\\x00\\x00\\x10\\x00\\x00\\xf0\\x88[I')\n\n        self.assertEqual((mode, size, mtime), self.device.stat('/'))\n\n    # ======================================================================= #\n    #                                                                         #\n    #                  `filesync` hidden methods tests                        #\n    #                                                                         #\n    # ======================================================================= #\n    def test_filesync_read_adb_command_failure_exceptions(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncStatMessage(constants.FAIL, 1, 2, 3),\n                                                                                                                            FileSyncStatMessage(constants.DONE, 0, 0, 0))))\n\n        with self.assertRaises(exceptions.AdbCommandFailureException):\n            self.device.stat('/data')\n\n    def test_filesync_read_invalid_response_error(self):\n        self.assertTrue(self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncStatMessage(constants.DENT, 1, 2, 3),\n                                                                                                                            FileSyncStatMessage(constants.DONE, 0, 0, 0))))\n\n        with self.assertRaises(exceptions.InvalidResponseError):\n            self.device.stat('/data')\n"
  },
  {
    "path": "tests/test_adb_device_async.py",
    "content": "import asyncio\nimport inspect\nimport logging\nfrom io import BytesIO\nimport sys\nimport unittest\nfrom unittest.mock import mock_open, patch\n\nfrom adb_shell import adb_device_async, constants, exceptions\nfrom adb_shell.adb_device_async import AdbDeviceAsync, AdbDeviceTcpAsync, DeviceFile\nfrom adb_shell.adb_message import AdbMessage\nfrom adb_shell.auth.keygen import keygen\nfrom adb_shell.auth.sign_pythonrsa import PythonRSASigner\n\nfrom . import patchers\nfrom .async_patchers import PATCH_TCP_TRANSPORT_ASYNC, FakeTcpTransportAsync, async_patch, async_mock_open\nfrom .async_wrapper import awaiter\nfrom .filesync_helpers import FileSyncMessage, FileSyncListMessage, FileSyncStatMessage\nfrom .keygen_stub import open_priv_pub\n\n\n# https://stackoverflow.com/a/7483862\n_LOGGER = logging.getLogger('adb_shell.adb_device_async')\n_LOGGER.setLevel(logging.DEBUG)\n_LOGGER.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef to_int(cmd):\n    return sum(c << (i * 8) for i, c in enumerate(bytearray(cmd)))\n\ndef join_messages(*messages):\n    return b''.join([message.pack() + message.data for message in messages])\n\n\nclass AdbMessageForTesting(AdbMessage):\n    def __init__(self, command, arg0=None, arg1=None, data=b''):\n        self.command = to_int(command)\n        self.magic = self.command ^ 0xFFFFFFFF\n        self.arg0 = arg0\n        self.arg1 = arg1\n        self.data = data\n\n\n@patchers.ASYNC_SKIPPER\nclass TestAdbDeviceAsync(unittest.TestCase):\n    def setUp(self):\n        self.transport = FakeTcpTransportAsync('host', 5555)\n        self.device = AdbDeviceAsync(self.transport)\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST)\n        self.progress_callback_count = 0\n\n        async def _progress_callback(device_path, current, total_bytes):\n            print(\"device_path = {}, current = {}, total_bytes = {}\".format(device_path, current, total_bytes))\n            self.progress_callback_count += 1\n\n        self.progress_callback = _progress_callback\n\n    def tearDown(self):\n        self.assertFalse(self.transport.bulk_read_data)\n        self.assertEqual(len(self.device._io_manager._packet_store._dict), 0)\n\n    @staticmethod\n    async def fake_stat(*args, **kwargs):\n        return 1, 2, 3\n\n    def test_no_sync_references(self):\n        \"\"\"Make sure there are no references to sync code.\"\"\"\n        adb_device_async_source = inspect.getsource(adb_device_async)\n        self.assertTrue(\"base_transport.\" not in adb_device_async_source)\n        self.assertTrue(\"BaseTransport.\" not in adb_device_async_source)\n        self.assertTrue(\"adb_device.\" not in adb_device_async_source)\n        self.assertTrue(\"AdbDevice.\" not in adb_device_async_source)\n        self.transport.bulk_read_data = b''\n\n    @awaiter\n    async def test_adb_connection_error(self):\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.exec_out('FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.root()\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.shell('FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            async_generator = self.device.streaming_shell('FAIL')\n            await async_generator.__anext__()\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.reboot()\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.root()\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.list('FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.push('FAIL', 'FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.pull('FAIL', 'FAIL')\n\n        with self.assertRaises(exceptions.AdbConnectionError):\n            await self.device.stat('FAIL')\n\n        self.transport.bulk_read_data = b''\n\n    @awaiter\n    async def test_init_tcp(self):\n        with PATCH_TCP_TRANSPORT_ASYNC:\n            tcp_device = AdbDeviceTcpAsync('host')\n            tcp_device._io_manager._transport.bulk_read_data = self.transport.bulk_read_data\n\n        # Make sure that the `connect()` method works\n        self.assertTrue(await tcp_device.connect())\n        self.assertTrue(tcp_device.available)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n\n    @awaiter\n    async def test_init_banner(self):\n        device_with_banner = AdbDeviceAsync(transport=FakeTcpTransportAsync('host', 5555), banner='banner')\n        self.assertEqual(device_with_banner._banner, b'banner')\n\n        device_with_banner2 = AdbDeviceAsync(transport=FakeTcpTransportAsync('host', 5555), banner=bytearray('banner2', 'utf-8'))\n        self.assertEqual(device_with_banner2._banner, b'banner2')\n\n        device_with_banner3 = AdbDeviceAsync(transport=FakeTcpTransportAsync('host', 5555), banner=u'banner3')\n        self.assertEqual(device_with_banner3._banner, b'banner3')\n\n        with patch('socket.gethostname', side_effect=Exception):\n            device_banner_unknown = AdbDeviceAsync(self.transport)\n            self.assertTrue(await device_banner_unknown.connect())\n            self.assertEqual(device_banner_unknown._banner, b'unknown')\n\n    @awaiter\n    async def test_init_invalid_transport(self):\n        with self.assertRaises(exceptions.InvalidTransportError):\n            device = AdbDeviceAsync(transport=123)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    @awaiter\n    async def test_available(self):\n        self.assertFalse(self.device.available)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    @awaiter\n    async def test_close(self):\n        self.assertFalse(await self.device.close())\n        self.assertFalse(self.device.available)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    # ======================================================================= #\n    #                                                                         #\n    #                             `connect` tests                             #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_connect(self):\n        self.assertTrue(await self.device.connect())\n        self.assertTrue(self.device.available)\n\n    @awaiter\n    async def test_connect_no_keys(self):\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH[:2])\n        with self.assertRaises(exceptions.DeviceAuthError):\n            await self.device.connect()\n\n        self.assertFalse(self.device.available)\n\n    @awaiter\n    async def test_connect_with_key_invalid_response(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH_INVALID)\n\n        with self.assertRaises(exceptions.InvalidResponseError):\n            await self.device.connect([signer])\n\n        self.assertFalse(self.device.available)\n\n    @awaiter\n    async def test_connect_with_key(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH)\n\n        self.assertTrue(await self.device.connect([signer]))\n\n    @awaiter\n    async def test_connect_with_new_key(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n            signer.pub_key = u''\n\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY)\n\n        self.assertTrue(await self.device.connect([signer]))\n\n    @awaiter\n    async def test_connect_with_new_key_and_callback(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n            signer.pub_key = u''\n\n        self._callback_invoked = False\n        def auth_callback(device):\n            self._callback_invoked = True\n\n        self.transport.bulk_read_data = b''.join(patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY)\n\n        self.assertTrue(await self.device.connect([signer], auth_callback=auth_callback))\n        self.assertTrue(self._callback_invoked)\n\n    @awaiter\n    async def test_connect_timeout(self):\n        self.transport.bulk_read_data = AdbMessage(command=constants.CLSE, arg0=1, arg1=1).pack()\n\n        with self.assertRaises(exceptions.AdbTimeoutError):\n            # Use a negative timeout to ensure that only one packet gets read\n            await self.device.connect([], read_timeout_s=-1)\n\n    # ======================================================================= #\n    #                                                                         #\n    #                              `shell` tests                              #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_shell_no_return(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST'), '')\n\n    @awaiter\n    async def test_shell_return_pass(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST'), 'PASS')\n\n    @awaiter\n    async def test_shell_local_id_wraparound(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=2**32 - 1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=2**32 - 1, data=b'PASS1'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=2**32 - 1, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS2'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.device._local_id = 2**32 - 2\n        self.assertEqual(await self.device.shell('TEST'), 'PASS1')\n        self.assertEqual(await self.device.shell('TEST'), 'PASS2')\n\n    @awaiter\n    async def test_shell_return_pass_with_unexpected_packet(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'),\n                                                      AdbMessage(command=constants.AUTH, arg0=1, arg1=1, data=b'UNEXPECTED'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST'), 'PASS')\n\n    @awaiter\n    async def test_shell_dont_decode(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST', decode=False), b'PASS')\n\n    @awaiter\n    async def test_shell_avoid_decode_error(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\\x80abc'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST'), '\\\\x80abc')\n\n    @awaiter\n    async def test_shell_data_length_exceeds_max(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'0'*(self.device.max_chunk_size+1)),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        await self.device.shell('TEST')\n        self.assertTrue(True)\n\n    @awaiter\n    async def test_shell_multibytes_sequence_exceeds_max(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'0'*(self.device.max_chunk_size-1) + b'\\xe3\\x81\\x82'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST'), u'0'*(self.device.max_chunk_size-1) + u'\\u3042')\n\n    @awaiter\n    async def test_shell_with_multibytes_sequence_over_two_messages(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\\xe3'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'\\x81\\x82'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST'), u'\\u3042')\n\n    @awaiter\n    async def test_shell_multiple_clse(self):\n        # https://github.com/JeffLIrion/adb_shell/issues/15#issuecomment-536795938\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        msg1 = AdbMessage(command=constants.OKAY, arg0=2, arg1=2, data=b'')\n        msg2 = AdbMessage(command=constants.WRTE, arg0=2, arg1=2, data=b'PASS')\n        msg3 = AdbMessage(command=constants.CLSE, arg0=2, arg1=2, data=b'')\n        self.transport.bulk_read_data = b''.join([b'OKAY\\xd9R\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',\n                                                  b'WRTE\\xd9R\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x002\\x00\\x00\\x00\\xa8\\xad\\xab\\xba',\n                                                  b'2',\n                                                  b'WRTE\\xd9R\\x00\\x00\\x01\\x00\\x00\\x00\\x0c\\x02\\x00\\x00\\xc0\\x92\\x00\\x00\\xa8\\xad\\xab\\xba',\n                                                  b'Wake Locks: size=2\\ncom.google.android.tvlauncher\\n\\n- STREAM_MUSIC:\\n   Muted: true\\n   Min: 0\\n   Max: 15\\n   Current: 2 (speaker): 15, 4 (headset): 10, 8 (headphone): 10, 80 (bt_a2dp): 10, 1000 (digital_dock): 10, 4000000 (usb_headset): 3, 40000000 (default): 15\\n   Devices: speaker\\n- STREAM_ALARM:\\n   Muted: true\\n   Min: 1\\n   Max: 7\\n   Current: 2 (speaker): 7, 4 (headset): 5, 8 (headphone): 5, 80 (bt_a2dp): 5, 1000 (digital_dock): 5, 4000000 (usb_headset): 1, 40000000 (default): 7\\n   Devices: speaker\\n- STREAM_NOTIFICATION:\\n',\n                                                  b'CLSE\\xd9R\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',\n                                                  msg1.pack(),\n                                                  b'CLSE\\xdaR\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',\n                                                  msg2.pack(),\n                                                  msg2.data,\n                                                  msg3.pack()])\n\n        await self.device.shell(\"dumpsys power | grep 'Display Power' | grep -q 'state=ON' && echo -e '1\\\\c' && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\\\\c' && dumpsys audio | grep paused | grep -qv 'Buffer Queue' && echo -e '1\\\\c' || (dumpsys audio | grep started | grep -qv 'Buffer Queue' && echo '2\\\\c' || echo '0\\\\c') && dumpsys power | grep Locks | grep 'size=' && CURRENT_APP=$(dumpsys window windows | grep mCurrentFocus) && CURRENT_APP=${CURRENT_APP#*{* * } && CURRENT_APP=${CURRENT_APP%%/*} && echo $CURRENT_APP && (dumpsys media_session | grep -A 100 'Sessions Stack' | grep -A 100 $CURRENT_APP | grep -m 1 'state=PlaybackState {' || echo) && dumpsys audio | grep '\\\\- STREAM_MUSIC:' -A 12\")\n        self.assertEqual(await self.device.shell('TEST'), 'PASS')\n\n    @awaiter\n    async def test_shell_multiple_streams(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=2, data=b'PASS2'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS1'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=2, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST1'), 'PASS1')\n        self.assertEqual(await self.device.shell('TEST2'), 'PASS2')\n\n    @awaiter\n    async def test_shell_multiple_streams2(self):\n        self.assertTrue(await self.device.connect())\n\n        async def fake_read_packet_from_device(*args, **kwargs):\n            # Mimic the scenario that this stream's packets get read by another stream after the first attempt to read the packet from the device\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.WRTE, data=b'\\x00')\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.OKAY, data=b'\\x00')\n            self.device._io_manager._packet_store.put(arg0=2, arg1=2, cmd=constants.OKAY, data=b'\\x00')\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.OKAY, data=b'\\x00')\n            self.device._io_manager._packet_store.put(arg0=2, arg1=2, cmd=constants.WRTE, data=b'PASS2')\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.WRTE, data=b\"PASS1\")\n            self.device._io_manager._packet_store.put(arg0=1, arg1=1, cmd=constants.CLSE, data=b\"\")\n            self.device._io_manager._packet_store.put(arg0=2, arg1=2, cmd=constants.CLSE, data=b\"\")\n\n            return constants.OKAY, 2, 2, b\"\\x00\"\n\n        with patch.object(self.device._io_manager, \"_read_packet_from_device\", fake_read_packet_from_device):\n            # The patch function will only be called once, all subsequent packets will be retrieved from the store\n            self.assertEqual(await self.device.shell('TEST1'), 'PASS1')\n            self.assertEqual(await self.device.shell('TEST2'), 'PASS2')\n\n    @awaiter\n    async def test_shell_local_id2(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=2, data=b'PASS2'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS1'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=2, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST1'), 'PASS1')\n        self.assertEqual(await self.device.shell('TEST2'), 'PASS2')\n\n    @awaiter\n    async def test_shell_remote_id2(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=2, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=2, arg1=2, data=b'PASS2'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS1'),\n                                                      AdbMessage(command=constants.CLSE, arg0=2, arg1=2, data=b''),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.shell('TEST1'), 'PASS1')\n        self.assertEqual(await self.device.shell('TEST2'), 'PASS2')\n\n\n    # ======================================================================= #\n    #                                                                         #\n    #                           `shell` error tests                           #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_shell_error_local_id_timeout(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1234, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1234, data=b'\\x00'))\n\n        with self.assertRaises(exceptions.AdbTimeoutError):\n            await self.device.shell('TEST', read_timeout_s=1)\n\n        # Close the connection so that the packet store gets cleared\n        await self.device.close()\n\n    @awaiter\n    async def test_shell_error_unknown_command(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessageForTesting(command=constants.FAIL, arg0=1, arg1=1, data=b''))\n\n        with self.assertRaises(exceptions.InvalidCommandError):\n            self.assertEqual(await self.device.shell('TEST'), '')\n\n    @awaiter\n    async def test_shell_error_transport_timeout(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b''))\n\n        with self.assertRaises(exceptions.AdbTimeoutError):\n            await self.device.shell('TEST', read_timeout_s=-1)\n\n    @awaiter\n    async def test_shell_error_read_timeout_multiple_clse(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.CLSE, arg0=2, arg1=1, data=b''))\n\n        with self.assertRaises(exceptions.AdbTimeoutError):\n            await self.device.shell('TEST', read_timeout_s=-1)\n\n    @awaiter\n    async def test_shell_error_timeout(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PA'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'SS'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        async def fake_read_until(*args, **kwargs):\n            await asyncio.sleep(0.2)\n            return b'WRTE', b'PA'\n\n        with patch('adb_shell.adb_device_async.AdbDeviceAsync._read_until', fake_read_until):\n            with self.assertRaises(exceptions.AdbTimeoutError):\n                await self.device.shell('TEST', timeout_s=0.5)\n\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    @awaiter\n    async def test_shell_error_checksum(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00')\n        msg2 = AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'PASS')\n        self.transport.bulk_read_data = b''.join([msg1.pack(), msg1.data, msg2.pack(), msg2.data[:-1] + b'0'])\n\n        with self.assertRaises(exceptions.InvalidChecksumError):\n            await self.device.shell('TEST')\n\n    @awaiter\n    async def test_issue29(self):\n        # https://github.com/JeffLIrion/adb_shell/issues/29\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n\n        okay3 = AdbMessage(command=constants.OKAY, arg0=1, arg1=3, data=b'\\x00')\n        clse3 = AdbMessage(command=constants.CLSE, arg0=1, arg1=3, data=b'')\n        okay5 = AdbMessage(command=constants.OKAY, arg0=1, arg1=5, data=b'\\x00')\n        clse5 = AdbMessage(command=constants.CLSE, arg0=1, arg1=5, data=b'')\n        okay7 = AdbMessage(command=constants.OKAY, arg0=1, arg1=7, data=b'\\x00')\n        clse7 = AdbMessage(command=constants.CLSE, arg0=1, arg1=7, data=b'')\n\n        self.transport.bulk_read_data = b''.join([b'AUTH\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\xc5\\n\\x00\\x00\\xbe\\xaa\\xab\\xb7',  # Line 22\n                                                  b\"\\x17\\xbf\\xbf\\xff\\xc7\\xa2eo'Sh\\xdf\\x8e\\xf5\\xff\\xe0\\tJ6H\",  # Line 23\n                                                  b\"CNXN\\x00\\x00\\x00\\x01\\x00\\x10\\x00\\x00i\\x00\\x00\\x00.'\\x00\\x00\\xbc\\xb1\\xa7\\xb1\",  # Line 26\n                                                  b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2',  # Line 27\n                                                  b'OKAY\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',  # Line 290 (modified --> Line 30)\n                                                  b'CLSE\\xa2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 291\n                                                  b'CLSE\\xa2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 292\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x001\\x00\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 31\n                                                  b'1',  # Line 32\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x001\\x00\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 35\n                                                  b'1',  # Line 36\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x000\\x00\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 39\n                                                  b'0',  # Line 40\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x000\\x06\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 43\n                                                  b'Wake Locks: size=0\\n',  # Line 44\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1e\\x00\\x00\\x00V\\x0b\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 47\n                                                  b'com.google.android.youtube.tv\\n',  # Line 48\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\xa13\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 51\n                                                  b'      state=PlaybackState {state=0, position=0, buffered position=0, speed=0.0, updated=0, actions=0, custom actions=[], active item id=-1, error=null}\\n',  # Line 52\n                                                  b'WRTE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00.\\x01\\x00\\x00\\xceP\\x00\\x00\\xa8\\xad\\xab\\xba',  # Line 55\n                                                  b'- STREAM_MUSIC:\\n   Muted: false\\n   Min: 0\\n   Max: 15\\n   Current: 2 (speaker): 11, 4 (headset): 10, 8 (headphone): 10, 400 (hdmi): 6, 40000000 (default): 11\\n   Devices: hdmi\\n- STREAM_ALARM:\\n   Muted: false\\n   Min: 0\\n   Max: 7\\n   Current: 40000000 (default): 6\\n   Devices: speaker\\n- STREAM_NOTIFICATION:\\n',  # Line 56\n                                                  b'CLSE\\x99\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 59\n                                                  b'AUTH\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x94\\t\\x00\\x00\\xbe\\xaa\\xab\\xb7',  # Line 297\n                                                  b'P\\xa5\\x86\\x97\\xe8\\x01\\xb09\\x8c>F\\x9d\\xc6\\xbd\\xc0J\\x80!\\xbb\\x1a',  # Line 298\n                                                  b\"CNXN\\x00\\x00\\x00\\x01\\x00\\x10\\x00\\x00i\\x00\\x00\\x00.'\\x00\\x00\\xbc\\xb1\\xa7\\xb1\",  # Line 301\n                                                  b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2',  # Line 302\n                                                  b'OKAY\\xa5\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',  # Line 305\n                                                  b'CLSE\\xa5\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 306\n                                                  okay3.pack(),\n                                                  okay3.data,\n                                                  clse3.pack(),\n                                                  b'AUTH\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00e\\x0c\\x00\\x00\\xbe\\xaa\\xab\\xb7',  # Line 315\n                                                  b'\\xd3\\xef\\x7f_\\xa6\\xc0`b\\x19\\\\z\\xe4\\xf3\\xe2\\xed\\x8d\\xe1W\\xfbH',  # Line 316\n                                                  b\"CNXN\\x00\\x00\\x00\\x01\\x00\\x10\\x00\\x00i\\x00\\x00\\x00.'\\x00\\x00\\xbc\\xb1\\xa7\\xb1\",  # Line 319\n                                                  b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2',  # Line 320\n                                                  b'OKAY\\xa7\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',  # Line 323\n                                                  b'CLSE\\xa7\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 324\n                                                  okay5.pack(),\n                                                  okay5.data,\n                                                  clse5.pack(),\n                                                  b'AUTH\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x93\\x08\\x00\\x00\\xbe\\xaa\\xab\\xb7',  # Line 333\n                                                  b's\\xd4_e\\xa4s\\x02\\x95\\x0f\\x1e\\xec\\n\\x95Y9[`\\x8e\\xe1f',  # Line 334\n                                                  b\"CNXN\\x00\\x00\\x00\\x01\\x00\\x10\\x00\\x00i\\x00\\x00\\x00.'\\x00\\x00\\xbc\\xb1\\xa7\\xb1\",  # Line 337\n                                                  b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2',  # Line 338\n                                                  b'OKAY\\xa9\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',  # Line 341\n                                                  b'CLSE\\xa9\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',  # Line 342\n                                                  okay7.pack(),\n                                                  okay7.data,\n                                                  clse7.pack()])\n\n        self.assertTrue(await self.device.connect([signer]))\n\n        await self.device.shell('Android TV update command')\n\n        self.assertTrue(await self.device.connect([signer]))\n        await self.device.shell('Android TV update command')\n        await self.device.shell('Android TV update command')\n        self.assertTrue(await self.device.connect([signer]))\n        await self.device.shell('Android TV update command')\n        await self.device.shell('Android TV update command')\n        self.assertTrue(await self.device.connect([signer]))\n        await self.device.shell('Android TV update command')\n        await self.device.shell('Android TV update command')\n\n    # ======================================================================= #\n    #                                                                         #\n    #                      `streaming_shell` tests                            #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_streaming_shell_decode(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(\n            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'ABC'),\n            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'123'),\n        )\n\n        async_generator = self.device.streaming_shell('TEST', decode=True)\n        self.assertEqual(await async_generator.__anext__(), 'ABC')\n        self.assertEqual(await async_generator.__anext__(), '123')\n\n    @awaiter\n    async def test_streaming_shell_dont_decode(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(\n            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'ABC'),\n            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'123'),\n        )\n\n        async_generator = self.device.streaming_shell('TEST', decode=False)\n        self.assertEqual(await async_generator.__anext__(), b'ABC')\n        self.assertEqual(await async_generator.__anext__(), b'123')\n\n\n    # ======================================================================= #\n    #                                                                         #\n    #                              `reboot` test                              #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_reboot(self):\n        self.assertTrue(await self.device.connect())\n\n        with async_patch('adb_shell.adb_device_async.AdbDeviceAsync._open') as patch_open:\n            await self.device.reboot()\n            patch_open.assert_called_once()\n\n\n    # ======================================================================= #\n    #                                                                         #\n    #                               `root` test                               #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_root(self):\n        self.assertTrue(await self.device.connect())\n\n        with async_patch('adb_shell.adb_device_async.AdbDeviceAsync._service') as patch_service:\n            await self.device.root()\n            patch_service.assert_called_once()\n\n\n    # ======================================================================= #\n    #                                                                         #\n    #                         `exec_out` test                                 #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_exec_out(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = b''.join([b'OKAY\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',\n                                                  b'WRTE\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00J\\x01\\x00\\x00\\xa8\\xad\\xab\\xba',\n                                                  b'TEST\\n',\n                                                  b'',\n                                                  b'CLSE\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba'])\n\n        self.assertEqual(await self.device.exec_out(\"echo 'TEST'\"), \"TEST\\n\")\n\n\n    # ======================================================================= #\n    #                                                                         #\n    #                         `filesync` tests                                #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_list(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncListMessage(constants.DENT, 1, 2, 3, data=b'file1'),\n                                                                                                                            FileSyncListMessage(constants.DENT, 4, 5, 6, data=b'file2'),\n                                                                                                                            FileSyncListMessage(constants.DONE, 0, 0, 0))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.LIST, data=b'/dir'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        expected_result = [DeviceFile(filename=bytearray(b'file1'), mode=1, size=2, mtime=3),\n                           DeviceFile(filename=bytearray(b'file2'), mode=4, size=5, mtime=6)]\n\n        self.assertEqual(await self.device.list('/dir'), expected_result)\n        self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @awaiter\n    async def test_list_empty_path(self):\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.list(\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.list(b\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.list(u\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.list(None)\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_push_fail(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.FAIL, data=b''))))\n\n        with self.assertRaises(exceptions.PushFailedError), patch('aiofiles.open', async_mock_open(read_data=filedata)):\n            await self.device.push('TEST_FILE', '/data', mtime=mtime)\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_push_file(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=FileSyncMessage(constants.OKAY).pack()),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                                                                                  FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                  FileSyncMessage(command=constants.DONE, arg0=mtime, data=b''))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('aiofiles.open', async_mock_open(read_data=filedata)):\n            self.assertEqual(self.progress_callback_count, 0)\n            with patch(\"adb_shell.adb_device_async.os.fstat\", return_value=patchers.StSize(12345)):\n                await self.device.push('TEST_FILE', '/data', mtime=mtime, progress_callback=self.progress_callback)\n            self.assertEqual(self.progress_callback_count, 1)\n            self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_push_bytesio(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=FileSyncMessage(constants.OKAY).pack()),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                                                                                  FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                  FileSyncMessage(command=constants.DONE, arg0=mtime, data=b''))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        stream = BytesIO(filedata)\n        await self.device.push(stream, '/data', mtime=mtime)\n        self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_push_file_exception(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=FileSyncMessage(constants.OKAY).pack()),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                                                                                  FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                  FileSyncMessage(command=constants.DONE, arg0=mtime, data=b''))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('aiofiles.open', async_mock_open(read_data=filedata)):\n            # Set self.progress_callback_count to None so that an exception occurs when self.progress_callback tries to increment it\n            self.progress_callback_count = None\n            with patch(\"adb_shell.adb_device_async.os.fstat\", return_value=patchers.StSize(12345)):\n                await self.device.push('TEST_FILE', '/data', mtime=mtime, progress_callback=self.progress_callback)\n\n            self.assertIsNone(self.progress_callback_count)\n            self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_push_file_mtime0(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 0\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.OKAY, data=b''))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                                                                                  FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                  FileSyncMessage(command=constants.DONE, arg0=mtime))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('aiofiles.open', async_mock_open(read_data=filedata)), patch('time.time', return_value=mtime):\n            await self.device.push('TEST_FILE', '/data', mtime=mtime)\n            self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_push_big_file(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        mtime = 100\n        filedata = b'0' * int(3.5 * self.device.max_chunk_size)\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(constants.OKAY))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        mcs0, mcs1, mcs2, mcs3 = 0, self.device.max_chunk_size, 2*self.device.max_chunk_size, 3*self.device.max_chunk_size\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(\n                                                FileSyncMessage(command=constants.SEND, data=b'/data,33272'),\n                                                FileSyncMessage(command=constants.DATA, data=filedata[mcs0:mcs1]))),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(\n                                                FileSyncMessage(command=constants.DATA, data=filedata[mcs1:mcs2]))),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(\n                                                FileSyncMessage(command=constants.DATA, data=filedata[mcs2:mcs3]),\n                                                FileSyncMessage(command=constants.DATA, data=filedata[mcs3:]),\n                                                FileSyncMessage(command=constants.DONE, arg0=mtime))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('aiofiles.open', async_mock_open(read_data=filedata)):\n            self.assertEqual(self.progress_callback_count, 0)\n            with patch(\"adb_shell.adb_device_async.os.fstat\", return_value=patchers.StSize(12345)):\n                await self.device.push('TEST_FILE', '/data', mtime=mtime, progress_callback=self.progress_callback)\n            self.assertEqual(self.progress_callback_count, 4)\n            self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_push_dir(self):\n        self.assertTrue(await self.device.connect())\n\n        mtime = 100\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=2, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=2, arg1=2, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=2, arg1=2, data=join_messages(FileSyncMessage(constants.OKAY))),\n                                                      AdbMessage(command=constants.CLSE, arg0=2, arg1=2, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=3, arg1=3, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=3, arg1=3, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=3, arg1=3, data=join_messages(FileSyncMessage(constants.OKAY))),\n                                                      AdbMessage(command=constants.CLSE, arg0=3, arg1=3, data=b''))\n\n        # Expected `bulk_write` values\n        #TODO\n\n        with patch('aiofiles.open', async_mock_open(read_data=filedata)), patch('os.path.isdir', lambda x: x == 'TEST_DIR/'), patch('os.listdir', return_value=['TEST_FILE1', 'TEST_FILE2']):\n            await self.device.push('TEST_DIR/', '/data', mtime=mtime)\n\n    @awaiter\n    async def test_push_empty_path(self):\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.push(\"NOTHING\", \"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.push(\"NOTHING\", b\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.push(\"NOTHING\", u\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.push(\"NOTHING\", None)\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_pull_file(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                            FileSyncMessage(command=constants.DONE))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('aiofiles.open', async_mock_open()) as m:\n            self.assertEqual(self.progress_callback_count, 0)\n            with patch('adb_shell.adb_device_async.AdbDeviceAsync.stat', self.fake_stat):\n                await self.device.pull('/data', 'TEST_FILE', progress_callback=self.progress_callback)\n\n            self.assertEqual(self.progress_callback_count, 1)\n            self.assertEqual(m.written, filedata)\n            self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_pull_bytesio(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                            FileSyncMessage(command=constants.DONE))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        stream = BytesIO()\n        await self.device.pull('/data', stream)\n\n        self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n        self.assertEqual(stream.getvalue(), filedata)    \n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_pull_file_exception(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        filedata = b'Ohayou sekai.\\nGood morning world!'\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                            FileSyncMessage(command=constants.DONE))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('aiofiles.open', async_mock_open()) as m:\n            # Set self.progress_callback_count to None so that an exception occurs when self.progress_callback tries to increment it\n            self.progress_callback_count = None\n            with patch('adb_shell.adb_device_async.AdbDeviceAsync.stat', self.fake_stat):\n                await self.device.pull('/data', 'TEST_FILE', progress_callback=self.progress_callback)\n\n            self.assertIsNone(self.progress_callback_count)\n            self.assertEqual(m.written, filedata)\n            self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @patchers.ASYNC_SKIPPER\n    @awaiter\n    async def test_pull_big_file(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        filedata = b'0' * int(1.5 * constants.MAX_ADB_DATA)\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.DATA, data=filedata),\n                                                                                                                            FileSyncMessage(command=constants.DONE))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        with patch('aiofiles.open', async_mock_open()) as m:\n            self.assertEqual(self.progress_callback_count, 0)\n            with patch('adb_shell.adb_device_async.AdbDeviceAsync.stat', self.fake_stat):\n                await self.device.pull('/data', 'TEST_FILE', progress_callback=self.progress_callback)\n\n            self.assertEqual(self.progress_callback_count, 1)\n            self.assertEqual(m.written, filedata)\n            self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @awaiter\n    async def test_pull_empty_path(self):\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.pull(\"\", \"NOWHERE\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.pull(b\"\", \"NOWHERE\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.pull(u\"\", \"NOWHERE\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.pull(None, \"NOWHERE\")\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    @awaiter\n    async def test_pull_non_existant_path(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'FAIL&\\x00\\x00\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'open failed: No such file or directory'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/does/not/exist'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n        with self.assertRaises(exceptions.AdbCommandFailureException):\n            await self.device.pull(\"/does/not/exist\", \"NOWHERE\")\n        self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    @awaiter\n    async def test_pull_non_existant_path_2(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'FAIL&\\x00\\x00\\x00open failed: No such file or directory'),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.RECV, data=b'/does/not/exist'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n        with self.assertRaises(exceptions.AdbCommandFailureException):\n            await self.device.pull(\"/does/not/exist\", \"NOWHERE\")\n        self.assertEqual(expected_bulk_write, self.transport.bulk_write_data)\n\n    @awaiter\n    async def test_stat(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncStatMessage(constants.STAT, 1, 2, 3),\n                                                                                                                            FileSyncStatMessage(constants.DONE, 0, 0, 0))),\n                                                      AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        # Expected `bulk_write` values\n        expected_bulk_write = join_messages(AdbMessage(command=constants.OPEN, arg0=1, arg1=0, data=b'sync:\\x00'),\n                                            AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncMessage(command=constants.STAT, data=b'/data'))),\n                                            AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b''),\n                                            AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b''))\n\n        self.assertEqual(await self.device.stat('/data'), (1, 2, 3))\n        self.assertEqual(self.transport.bulk_write_data, expected_bulk_write)\n\n    @awaiter\n    async def test_stat_empty_path(self):\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.stat(\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.stat(b\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.stat(u\"\")\n        with self.assertRaises(exceptions.DevicePathInvalidError):\n            await self.device.stat(None)\n        # Clear the `_bulk_read` buffer so that `self.tearDown()` passes\n        self.transport.bulk_read_data = b''\n\n    @awaiter\n    async def test_stat_issue155(self):\n        self.assertTrue(await self.device.connect())\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = b\"\".join([b'CLSE\\n\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba',\n                                                  b'OKAY\\x0b\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',\n                                                  b'OKAY\\x0b\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xb0\\xb4\\xbe\\xa6',\n                                                  b'WRTE\\x0b\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x96\\x04\\x00\\x00\\xa8\\xad\\xab\\xba',\n                                                  b'STAT\\xedA\\x00\\x00\\x00\\x10\\x00\\x00\\xf0\\x88[I',\n                                                  b'CLSE\\x0b\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xbc\\xb3\\xac\\xba'])\n\n        # This is where the expected values come from\n        mode = 16877\n        size = 4096\n        mtime = 1230735600\n        self.assertEqual(FileSyncStatMessage(constants.STAT, mode, size, mtime).pack(), b'STAT\\xedA\\x00\\x00\\x00\\x10\\x00\\x00\\xf0\\x88[I')\n\n        self.assertEqual((mode, size, mtime), await self.device.stat('/'))\n\n    # ======================================================================= #\n    #                                                                         #\n    #                  `filesync` hidden methods tests                        #\n    #                                                                         #\n    # ======================================================================= #\n    @awaiter\n    async def test_filesync_read_adb_command_failure_exceptions(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncStatMessage(constants.FAIL, 1, 2, 3),\n                                                                                                                            FileSyncStatMessage(constants.DONE, 0, 0, 0))))\n\n        with self.assertRaises(exceptions.AdbCommandFailureException):\n            await self.device.stat('/data')\n\n    @awaiter\n    async def test_filesync_read_invalid_response_error(self):\n        self.assertTrue(await self.device.connect())\n        self.transport.bulk_write_data = b''\n\n        # Provide the `bulk_read` return values\n        self.transport.bulk_read_data = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\\x00'),\n                                                      AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=join_messages(FileSyncStatMessage(constants.DENT, 1, 2, 3),\n                                                                                                                            FileSyncStatMessage(constants.DONE, 0, 0, 0))))\n\n        with self.assertRaises(exceptions.InvalidResponseError):\n            await self.device.stat('/data')\n"
  },
  {
    "path": "tests/test_adb_message.py",
    "content": "import os\nimport unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\nfrom adb_shell import constants\nfrom adb_shell.adb_device import AdbDevice\nfrom adb_shell.adb_message import AdbMessage, checksum, int_to_cmd, unpack\n\n\nclass TestAdbMessage(unittest.TestCase):\n    def test_checksum_bytearray(self):\n        cs = checksum(bytearray('TEST', 'utf-8'))\n        self.assertEqual(cs, 320)\n\n    def test_checksum_bytes(self):\n        cs = checksum(b'TEST')\n        self.assertEqual(cs, 320)\n\n    def test_checksum_unicode(self):\n        cs = checksum(u'TEST')\n        self.assertEqual(cs, 320)\n\n    def test_unpack_error(self):\n        with self.assertRaises(ValueError):\n            unpack(b'TEST')\n\n    def test_constants(self):\n        for key, val in constants.ID_TO_WIRE.items():\n            self.assertEqual(key, int_to_cmd(val))\n"
  },
  {
    "path": "tests/test_exceptions.py",
    "content": "import functools\nimport inspect\nimport pickle\nimport re\nimport unittest\ntry:\n    from unittest import mock\nexcept ImportError:\n    import mock\n\nimport adb_shell.exceptions\n\ntry:\n    getargspec = inspect.getfullargspec\nexcept AttributeError:\n    getargspec = inspect.getargspec\n\ntry:\n    _assertRegex = unittest.TestCase.assertRegex\nexcept AttributeError:\n    _assertRegex = unittest.TestCase.assertRegexpMatches\n\n\nclass TestExceptionSerialization(unittest.TestCase):\n    def __test_serialize_one_exc_cls(exc_cls):\n        # Work out how many args we need to instantiate this object\n        try:\n            exc_required_arity = len(getargspec(exc_cls.__init__).args)\n        except TypeError:\n            # In Python 2.7 this could be a slot wrapper which means `__init__`\n            # wasn't overridden by the exception subclass - use 0 arity.\n            exc_required_arity = 0\n        # Don't try to provide `self` - we assume strings will be fine here\n        fake_args = (\"foo\", ) * (exc_required_arity - 1)\n        # Instantiate the exception object and then attempt a serializion cycle\n        # using `pickle` - we mainly care about whether this blows up or not\n        exc_obj = exc_cls(*fake_args)\n        pickled_exc_data = pickle.dumps(exc_obj)\n        depickled_exc_obj = pickle.loads(pickled_exc_data)\n\n    for __obj in adb_shell.exceptions.__dict__.values():\n        if isinstance(__obj, type) and issubclass(__obj, BaseException):\n            __test_method = functools.partial(\n                __test_serialize_one_exc_cls, __obj\n            )\n            __test_name = \"test_serialize_{}\".format(__obj.__name__)\n            locals()[__test_name] = __test_method\n\n    # We want to confirm what the stringification and representation of\n    # `UsbReadFailedError` look like since it's a non-trivial subclass of\n    # `Exception`\n    def test_usbreadfailederror_as_str(self):\n        exc_args = (mock.sentinel.error_msg, mock.sentinel.usb1_exc_obj)\n        exc_obj = adb_shell.exceptions.UsbReadFailedError(*exc_args)\n        expected_str = \"{}: {}\".format(*exc_args)\n        _assertRegex(self, str(exc_obj), re.escape(expected_str))\n\n    def test_usbreadfailederror_as_repr(self):\n        exc_args = (mock.sentinel.error_msg, mock.sentinel.usb1_exc_obj)\n        exc_obj = adb_shell.exceptions.UsbReadFailedError(*exc_args)\n        expected_repr = \"{}{!r}\".format(\n            adb_shell.exceptions.UsbReadFailedError.__name__, exc_args\n        )\n        _assertRegex(self, repr(exc_obj), re.escape(expected_repr))\n"
  },
  {
    "path": "tests/test_hidden_helpers.py",
    "content": "import unittest\n\nfrom adb_shell import constants\nfrom adb_shell.hidden_helpers import _AdbPacketStore, _AdbTransactionInfo\n\n\nclass TestAdbPacketStore(unittest.TestCase):\n    def setUp(self):\n        self.packet_store = _AdbPacketStore()\n\n    def test_init(self):\n        self.assertEqual(len(self.packet_store), 0)\n        self.assertFalse((None, None) in self.packet_store)\n\n    def test_contains(self):\n        self.assertFalse((None, None) in self.packet_store)\n\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd1\", data=b\"data1\")\n        self.assertTrue((0, 1) in self.packet_store)\n        self.assertTrue((None, 1) in self.packet_store)\n        self.assertFalse((None, 0) in self.packet_store)\n        self.assertTrue((0, None) in self.packet_store)\n        self.assertFalse((1, None) in self.packet_store)\n        self.assertFalse((1, 1) in self.packet_store)\n        self.assertTrue((None, None) in self.packet_store)\n\n    def test_put(self):\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd1\", data=b\"data1\")\n        self.assertTrue((0, 1) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 1)\n\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd2\", data=b\"data2\")\n        self.assertTrue((0, 1) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 1)\n\n        self.packet_store.put(arg0=1, arg1=0, cmd=b\"cmd3\", data=b\"data3\")\n        self.assertTrue((1, 0) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 2)\n\n        self.packet_store.put(arg0=1, arg1=1, cmd=b\"cmd4\", data=b\"data4\")\n        self.assertTrue((1, 1) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 3)\n\n        self.packet_store.put(arg0=5, arg1=5, cmd=constants.CLSE, data=b\"data5\")\n        self.assertEqual(len(self.packet_store), 3)\n        self.assertFalse((5, 5) in self.packet_store)\n        \n        self.packet_store.put(arg0=5, arg1=1, cmd=constants.CLSE, data=b\"data5\")\n        self.assertEqual(len(self.packet_store), 3)\n        self.assertFalse((5, 1) in self.packet_store)\n\n    def test_get(self):\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd1\", data=b\"data1\")\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd2\", data=b\"data2\")\n        self.packet_store.put(arg0=1, arg1=0, cmd=b\"cmd3\", data=b\"data3\")\n        self.packet_store.put(arg0=1, arg1=1, cmd=b\"cmd4\", data=b\"data4\")\n        self.packet_store.put(arg0=2, arg1=3, cmd=b\"cmd5\", data=b\"data5\")\n        self.packet_store.put(arg0=4, arg1=5, cmd=b\"cmd6\", data=b\"data6\")\n\n        self.assertTrue((0, 1) in self.packet_store)\n        cmd1, arg0, arg1, data1 = self.packet_store.get(arg0=0, arg1=1)\n        self.assertEqual(arg0, 0)\n        self.assertEqual(arg1, 1)\n        self.assertEqual(cmd1, b\"cmd1\")\n        self.assertEqual(data1, b\"data1\")\n        self.assertTrue((0, 1) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 5)\n\n        self.assertTrue((0, 1) in self.packet_store)\n        cmd2, arg0, arg1, data2 = self.packet_store.get(arg0=0, arg1=1)\n        self.assertEqual(arg0, 0)\n        self.assertEqual(arg1, 1)\n        self.assertEqual(cmd2, b\"cmd2\")\n        self.assertEqual(data2, b\"data2\")\n        self.assertFalse((0, 1) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 4)\n\n        self.assertTrue((1, 0) in self.packet_store)\n        cmd3, arg0, arg1, data3 = self.packet_store.get(arg0=1, arg1=0)\n        self.assertEqual(arg0, 1)\n        self.assertEqual(arg1, 0)\n        self.assertEqual(cmd3, b\"cmd3\")\n        self.assertEqual(data3, b\"data3\")\n        self.assertEqual(len(self.packet_store), 3)\n\n        self.assertTrue((1, None) in self.packet_store)\n        cmd4, arg0, arg1, data4 = self.packet_store.get(arg0=1, arg1=None)\n        self.assertEqual(arg0, 1)\n        self.assertEqual(arg1, 1)\n        self.assertEqual(cmd4, b\"cmd4\")\n        self.assertEqual(data4, b\"data4\")\n        self.assertEqual(len(self.packet_store), 2)\n\n        self.assertTrue((None, 3) in self.packet_store)\n        cmd5, arg0, arg1, data5 = self.packet_store.get(arg0=None, arg1=3)\n        self.assertEqual(arg0, 2)\n        self.assertEqual(arg1, 3)\n        self.assertEqual(cmd5, b\"cmd5\")\n        self.assertEqual(data5, b\"data5\")\n        self.assertEqual(len(self.packet_store), 1)\n\n        self.assertTrue((None, None) in self.packet_store)\n        cmd6, arg0, arg1, data6 = self.packet_store.get(arg0=None, arg1=None)\n        self.assertEqual(arg0, 4)\n        self.assertEqual(arg1, 5)\n        self.assertEqual(cmd6, b\"cmd6\")\n        self.assertEqual(data6, b\"data6\")\n        self.assertEqual(len(self.packet_store), 0)\n\n        self.assertEqual(len(self.packet_store._dict), 4)\n        self.assertEqual(len(self.packet_store._dict[1]), 2)\n        self.assertEqual(len(self.packet_store._dict[0]), 1)\n        self.assertEqual(len(self.packet_store._dict[3]), 1)\n        self.assertEqual(len(self.packet_store._dict[5]), 1)\n        \n    def test_get_clse(self):\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd1\", data=b\"data1\")\n        self.packet_store.put(arg0=0, arg1=1, cmd=constants.CLSE, data=b\"data2\")\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd3\", data=b\"data3\")\n\n        self.assertTrue((0, 1) in self.packet_store)\n        cmd1, arg0, arg1, data1 = self.packet_store.get(arg0=0, arg1=1)\n        self.assertEqual(arg0, 0)\n        self.assertEqual(arg1, 1)\n        self.assertEqual(cmd1, b\"cmd1\")\n        self.assertEqual(data1, b\"data1\")\n        self.assertTrue((0, 1) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 1)\n\n        self.assertTrue((0, 1) in self.packet_store)\n        cmd2, arg0, arg1, data2 = self.packet_store.get(arg0=0, arg1=1)\n        self.assertEqual(arg0, 0)\n        self.assertEqual(arg1, 1)\n        self.assertEqual(cmd2, constants.CLSE)\n        self.assertEqual(data2, b\"data2\")\n        self.assertFalse((0, 1) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 0)\n\n        self.assertEqual(len(self.packet_store._dict), 0)\n\n    def test_clear(self):\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd1\", data=b\"data1\")\n        self.packet_store.put(arg0=2, arg1=1, cmd=b\"cmd2\", data=b\"data2\")\n\n        self.packet_store.clear(arg0=None, arg1=None)\n        self.assertEqual(len(self.packet_store), 2)\n\n        self.packet_store.clear(arg0=1, arg1=0)\n        self.assertEqual(len(self.packet_store), 2)\n\n        self.packet_store.clear(arg0=0, arg1=1)\n        self.assertEqual(len(self.packet_store), 1)\n        self.assertEqual(len(self.packet_store._dict), 1)\n\n        self.packet_store.clear(arg0=2, arg1=1)\n        self.assertEqual(len(self.packet_store), 0)\n        self.assertEqual(len(self.packet_store._dict), 0)\n\n    def test_clear_all(self):\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd1\", data=b\"data1\")\n\n        self.packet_store.clear_all()\n        self.assertFalse((0, 1) in self.packet_store)\n        self.assertEqual(len(self.packet_store), 0)\n\n    def test_find_allow_zeros(self):\n        self.packet_store.put(arg0=0, arg1=1, cmd=b\"cmd\", data=b\"data\")\n        self.assertEqual(self.packet_store.find_allow_zeros(arg0=2, arg1=1), (0, 1))\n        self.assertIsNone(self.packet_store.find_allow_zeros(arg0=2, arg1=2))\n\n\nclass TestAdbTransactionInfo(unittest.TestCase):\n\n    def test_args_match(self):\n        adb_info_1_None = _AdbTransactionInfo(1, None, 123, 456, 789)\n        adb_info_1_2 = _AdbTransactionInfo(1, 2, 123, 456, 789)\n\n        # (1, None) -> exact matches\n        self.assertTrue(adb_info_1_None.args_match(6, 1))\n        self.assertTrue(adb_info_1_None.args_match(7, 1))\n        self.assertTrue(adb_info_1_None.args_match(6, 1, allow_zeros=True))\n        self.assertTrue(adb_info_1_None.args_match(7, 1, allow_zeros=True))\n\n        # (1, None) -> no match\n        self.assertFalse(adb_info_1_None.args_match(0, 0))\n        self.assertFalse(adb_info_1_None.args_match(1, 0))\n        self.assertFalse(adb_info_1_None.args_match(2, 0))\n        self.assertFalse(adb_info_1_None.args_match(3, 0))\n        self.assertFalse(adb_info_1_None.args_match(4, 5, allow_zeros=True))\n\n        # (1, None) -> zero matches\n        self.assertTrue(adb_info_1_None.args_match(0, 0, allow_zeros=True))\n        self.assertTrue(adb_info_1_None.args_match(1, 0, allow_zeros=True))\n        self.assertTrue(adb_info_1_None.args_match(2, 0, allow_zeros=True))\n        self.assertTrue(adb_info_1_None.args_match(3, 0, allow_zeros=True))\n\n        # (1, 2) -> exact matches\n        self.assertTrue(adb_info_1_2.args_match(2, 1))\n        self.assertTrue(adb_info_1_2.args_match(2, 1, allow_zeros=True))\n\n        # (1, 2) -> no match\n        self.assertFalse(adb_info_1_2.args_match(0, 0))\n        self.assertFalse(adb_info_1_2.args_match(2, 0))\n        self.assertFalse(adb_info_1_2.args_match(0, 1))\n\n        self.assertFalse(adb_info_1_2.args_match(1, 2))\n        self.assertFalse(adb_info_1_2.args_match(1, 2, allow_zeros=True))\n\n        self.assertFalse(adb_info_1_2.args_match(3, 0))\n        self.assertFalse(adb_info_1_2.args_match(0, 4))\n        self.assertFalse(adb_info_1_2.args_match(3, 4))\n        self.assertFalse(adb_info_1_2.args_match(3, 0, allow_zeros=True))\n        self.assertFalse(adb_info_1_2.args_match(0, 4, allow_zeros=True))\n        self.assertFalse(adb_info_1_2.args_match(3, 4, allow_zeros=True))\n\n        self.assertFalse(adb_info_1_2.args_match(2, 6))\n        self.assertFalse(adb_info_1_2.args_match(2, 6, allow_zeros=True))\n        self.assertFalse(adb_info_1_2.args_match(7, 1))\n        self.assertFalse(adb_info_1_2.args_match(7, 1, allow_zeros=True))\n\n        # (1, 2) -> zero matches\n        self.assertTrue(adb_info_1_2.args_match(0, 0, allow_zeros=True))\n        self.assertTrue(adb_info_1_2.args_match(2, 0, allow_zeros=True))\n        self.assertTrue(adb_info_1_2.args_match(0, 1, allow_zeros=True))\n"
  },
  {
    "path": "tests/test_keygen.py",
    "content": "import unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\nfrom adb_shell.auth.keygen import get_user_info\n\n\nclass TestKeygen(unittest.TestCase):\n    def test_get_user_info(self):\n        with patch('adb_shell.auth.keygen.os.getlogin', side_effect=OSError), patch('adb_shell.auth.keygen.socket.gethostname', return_value=''):\n            user_host = get_user_info()\n            self.assertEqual(user_host, ' unknown@unknown')\n\n        with patch('adb_shell.auth.keygen.os.getlogin', return_value=''), patch('adb_shell.auth.keygen.socket.gethostname', return_value=''):\n            user_host = get_user_info()\n            self.assertEqual(user_host, ' unknown@unknown')\n"
  },
  {
    "path": "tests/test_sign_cryptography.py",
    "content": "import os\nimport unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.primitives import hashes\n\nfrom adb_shell.auth.keygen import keygen\nfrom adb_shell.auth.sign_cryptography import CryptographySigner\n\nfrom .keygen_stub import open_priv_pub\n\n\nclass TestCryptographySigner(unittest.TestCase):\n    def setUp(self):\n        with patch('adb_shell.auth.sign_cryptography.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            self.signer = CryptographySigner('tests/adbkey')\n\n    def test_sign(self):\n        \"\"\"Test that the ``Sign`` method does not raise an exception.\"\"\"\n        # https://www.programcreek.com/python/example/107988/cryptography.hazmat.primitives.hashes.Hash\n        hash_ctx = hashes.Hash(hashes.SHA1(), default_backend())\n        hash_ctx.update(b'notadb')\n        data = hash_ctx.finalize()\n        # For reference:\n        #   data = b'(\\x8b\\x9e\\x88|JY\\xb5\\x18\\x13b_\\xe0\\xc4\\xfb\\xa5\\x83\\xbdx\\xfc'\n\n        self.signer.Sign(data)\n        self.assertTrue(True)\n\n    def test_get_public_key(self):\n        \"\"\"Test that the ``GetPublicKey`` method works correctly.\"\"\"\n        with patch('{}.open'.format(__name__), open_priv_pub):\n            with open('tests/adbkey.pub', 'rb') as f:\n                pub = f.read()\n\n            self.assertEqual(pub, self.signer.GetPublicKey())\n"
  },
  {
    "path": "tests/test_sign_pycryptodome.py",
    "content": "import os\nimport unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\nfrom adb_shell.auth.keygen import keygen\nfrom adb_shell.auth.sign_pycryptodome import PycryptodomeAuthSigner\n\nfrom .keygen_stub import open_priv_pub\n\n\nclass TestPycryptodomeAuthSigner(unittest.TestCase):\n    def setUp(self):\n        with patch('adb_shell.auth.sign_pycryptodome.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            self.signer = PycryptodomeAuthSigner('tests/adbkey')\n\n    def test_sign(self):\n        \"\"\"Test that the ``Sign`` method does not raise an exception.\"\"\"\n        self.signer.Sign(b'notadb')\n        self.assertTrue(True)\n\n    def test_get_public_key(self):\n        \"\"\"Test that the ``GetPublicKey`` method works correctly.\"\"\"\n        with patch('{}.open'.format(__name__), open_priv_pub):\n            with open('tests/adbkey.pub', 'rb') as f:\n                pub = f.read()\n\n            self.assertEqual(pub, self.signer.GetPublicKey())\n"
  },
  {
    "path": "tests/test_sign_pythonrsa.py",
    "content": "import os\nimport unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\nfrom adb_shell.auth.keygen import keygen\nfrom adb_shell.auth.sign_pythonrsa import PythonRSASigner\n\nfrom .keygen_stub import open_priv_pub\n\n\nclass TestPythonRSASigner(unittest.TestCase):\n    def setUp(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            keygen('tests/adbkey')\n            self.signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n\n    def test_sign(self):\n        \"\"\"Test that the ``Sign`` method does not raise an exception.\"\"\"\n        self.signer.Sign(b'notadb')\n        self.assertTrue(True)\n\n    def test_get_public_key(self):\n        \"\"\"Test that the ``GetPublicKey`` method works correctly.\"\"\"\n        with patch('{}.open'.format(__name__), open_priv_pub):\n            with open('tests/adbkey.pub') as f:\n                pub = f.read()\n\n            self.assertEqual(pub, self.signer.GetPublicKey())\n\n\nclass TestPythonRSASignerExceptions(unittest.TestCase):\n    def test_value_error(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            with patch('adb_shell.auth.sign_pythonrsa.decoder.decode', return_value=([None, [None]], None)):\n                with self.assertRaises(ValueError):\n                    keygen('tests/adbkey')\n                    self.signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n\n    def test_index_error(self):\n        with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub):\n            with patch('adb_shell.auth.sign_pythonrsa.decoder.decode', side_effect=IndexError):\n                with self.assertRaises(ValueError):\n                    keygen('tests/adbkey')\n                    self.signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')\n                \n"
  },
  {
    "path": "tests/test_tcp_transport.py",
    "content": "import unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\nfrom adb_shell.exceptions import TcpTimeoutException\nfrom adb_shell.transport.tcp_transport import TcpTransport\n\nfrom . import patchers\n\n\nclass TestTcpTransport(unittest.TestCase):\n    def setUp(self):\n        \"\"\"Create a ``TcpTransport`` and connect to a TCP service.\n\n        \"\"\"\n        self.transport = TcpTransport('host', 5555)\n        with patchers.PATCH_CREATE_CONNECTION:\n            self.transport.connect(transport_timeout_s=1)\n\n    def tearDown(self):\n        \"\"\"Close the socket connection.\"\"\"\n        self.transport.close()\n\n    def test_connect_with_timeout(self):\n        \"\"\"TODO\n\n        \"\"\"\n        self.transport.close()\n        with patchers.PATCH_CREATE_CONNECTION:\n            self.transport.connect(transport_timeout_s=1)\n            self.assertTrue(True)\n\n    def test_bulk_read(self):\n        \"\"\"TODO\n\n        \"\"\"\n        # Provide the `recv` return values\n        self.transport._connection._recv = b'TEST1TEST2'\n\n        with patchers.PATCH_SELECT_SUCCESS:\n            self.assertEqual(self.transport.bulk_read(5, transport_timeout_s=1), b'TEST1')\n            self.assertEqual(self.transport.bulk_read(5, transport_timeout_s=1), b'TEST2')\n\n        with patchers.PATCH_SELECT_FAIL:\n            with self.assertRaises(TcpTimeoutException):\n                self.transport.bulk_read(4, transport_timeout_s=1)\n\n    def test_close_oserror(self):\n        \"\"\"Test that an `OSError` exception is handled when closing the socket.\n\n        \"\"\"\n        with patch('{}.patchers.FakeSocket.shutdown'.format(__name__), side_effect=OSError):\n            self.transport.close()\n\n    def test_bulk_write(self):\n        \"\"\"TODO\n\n        \"\"\"\n        with patchers.PATCH_SELECT_SUCCESS:\n            self.transport.bulk_write(b'TEST', transport_timeout_s=1)\n\n        with patchers.PATCH_SELECT_FAIL:\n            with self.assertRaises(TcpTimeoutException):\n                self.transport.bulk_write(b'FAIL', transport_timeout_s=1)\n"
  },
  {
    "path": "tests/test_tcp_transport_async.py",
    "content": "import asyncio\nimport unittest\nfrom unittest.mock import patch\n\nfrom adb_shell.exceptions import TcpTimeoutException\nfrom adb_shell.transport.tcp_transport_async import TcpTransportAsync\n\nfrom .async_patchers import FakeStreamReader, FakeStreamWriter, async_patch\nfrom .async_wrapper import awaiter\nfrom . import patchers\n\n\n@patchers.ASYNC_SKIPPER\nclass TestTcpTransportAsync(unittest.TestCase):\n    def setUp(self):\n        \"\"\"Create a ``TcpTransportAsync`` and connect to a TCP service.\n\n        \"\"\"\n        self.transport = TcpTransportAsync('host', 5555)\n\n    @awaiter\n    async def test_close(self):\n        await self.transport.close()\n\n    @awaiter\n    async def test_close2(self):\n        await self.transport.close()\n\n    @awaiter\n    async def test_connect(self):\n        with async_patch('asyncio.open_connection', return_value=(True, True)):\n            await self.transport.connect(transport_timeout_s=1)\n\n    @awaiter\n    async def test_connect_close(self):\n        with async_patch('asyncio.open_connection', return_value=(FakeStreamReader(), FakeStreamWriter())):\n            await self.transport.connect(transport_timeout_s=1)\n            self.assertIsNotNone(self.transport._writer)\n\n        await self.transport.close()\n        self.assertIsNone(self.transport._reader)\n        self.assertIsNone(self.transport._writer)\n\n    @awaiter\n    async def test_connect_close_catch_oserror(self):\n        with async_patch('asyncio.open_connection', return_value=(FakeStreamReader(), FakeStreamWriter())):\n            await self.transport.connect(transport_timeout_s=1)\n            self.assertIsNotNone(self.transport._writer)\n\n        with patch('{}.FakeStreamWriter.close'.format(__name__), side_effect=OSError):\n            await self.transport.close()\n            self.assertIsNone(self.transport._reader)\n            self.assertIsNone(self.transport._writer)\n\n    @awaiter\n    async def test_connect_with_timeout(self):\n        with self.assertRaises(TcpTimeoutException):\n            with async_patch('asyncio.open_connection', side_effect=asyncio.TimeoutError):\n                await self.transport.connect(transport_timeout_s=1)\n\n    @awaiter\n    async def test_bulk_read(self):\n        with async_patch('asyncio.open_connection', return_value=(FakeStreamReader(), FakeStreamWriter())):\n            await self.transport.connect(transport_timeout_s=1)\n\n        self.assertEqual(await self.transport.bulk_read(4, transport_timeout_s=1), b'TEST')\n\n        with self.assertRaises(TcpTimeoutException):\n            with patch('{}.FakeStreamReader.read'.format(__name__), side_effect=asyncio.TimeoutError):\n                await self.transport.bulk_read(4, transport_timeout_s=1)\n\n    @awaiter\n    async def test_bulk_write(self):\n        with async_patch('asyncio.open_connection', return_value=(FakeStreamReader(), FakeStreamWriter())):\n            await self.transport.connect(transport_timeout_s=1)\n\n        self.assertEqual(await self.transport.bulk_write(b'TEST', transport_timeout_s=1), 4)\n\n        with self.assertRaises(TcpTimeoutException):\n            with patch('{}.FakeStreamWriter.write'.format(__name__), side_effect=asyncio.TimeoutError):\n                await self.transport.bulk_write(b'TEST', transport_timeout_s=1)\n"
  },
  {
    "path": "tests/test_usb_importerror.py",
    "content": "import unittest\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from mock import patch\n\ntry:\n    from adb_shell.transport.usb_transport import UsbTransport\nexcept (ImportError, OSError):\n    UsbTransport = None\n\n\nclass TestUsbImportError(unittest.TestCase):\n    def test_import_error(self):\n        \"\"\"Test that the package still works when ``libusb1`` is not installed.\"\"\"\n        from adb_shell import adb_device\n        from adb_shell.exceptions import InvalidTransportError\n\n        # TODO: I can't manage to trigger an `ImportError` in adb_device.py\n        # self.assertIsNone(adb_device.UsbTransport)\n\n        # In lieu of a real `ImportError`, I'll just set this to None\n        with patch(\"adb_shell.adb_device.UsbTransport\", None):\n            with self.assertRaises(InvalidTransportError):\n                adb_device.AdbDeviceUsb('serial')\n\n    def test_import_successful(self):\n        from adb_shell import adb_device\n\n        if UsbTransport is not None:\n            # Make sure `UsbTransport` was imported\n            with patch(\"adb_shell.adb_device.UsbTransport\", UsbTransport):\n                with patch(\"adb_shell.adb_device.UsbTransport.find_adb\", return_value=UsbTransport(\"TODO\", \"TODO\")):\n                    adb_device.AdbDeviceUsb(\"serial\")\n"
  },
  {
    "path": "tests/test_usb_transport.py",
    "content": "\"\"\"Tests for the `UsbTransport` class.\"\"\"\n\nimport unittest\n\nfrom adb_shell.exceptions import TcpTimeoutException\n\ntry:\n    from adb_shell.transport.usb_transport import UsbTransport\nexcept (ImportError, OSError):\n    UsbTransport = None\n\nfrom . import patchers\n\n\n# pylint: disable=missing-class-docstring, missing-function-docstring\n@unittest.skipIf(UsbTransport is None, \"UsbTransport could not be imported\")\nclass TestUsbTransport(unittest.TestCase):\n    def setUp(self):\n        \"\"\"Create a ``UsbTransport`` and do something...\n\n        \"\"\"\n        self.transport = UsbTransport('TODO', 'TODO')\n\n        if True:\n            return\n\n        with patchers.PATCH_CREATE_CONNECTION:\n            self.transport.connect()\n\n    def tearDown(self):\n        \"\"\"Close the USB connection.\"\"\"\n        self.transport.close()\n\n    def test_connect_with_timeout(self):\n        \"\"\"TODO\n\n        \"\"\"\n        if True:\n            return\n\n        self.transport.close()\n        with patchers.PATCH_CREATE_CONNECTION:\n            self.transport.connect(transport_timeout_s=1)\n            self.assertTrue(True)\n\n    def test_bulk_read(self):\n        \"\"\"TODO\n\n        \"\"\"\n        if True:\n            return\n\n        # Provide the `recv` return values\n        self.transport._connection._recv = b'TEST1TEST2'\n\n        with patchers.PATCH_SELECT_SUCCESS:\n            self.assertEqual(self.transport.bulk_read(5), b'TEST1')\n            self.assertEqual(self.transport.bulk_read(5), b'TEST2')\n\n        with patchers.PATCH_SELECT_FAIL:\n            with self.assertRaises(TcpTimeoutException):\n                self.transport.bulk_read(4)\n\n    def test_bulk_write(self):\n        \"\"\"TODO\n\n        \"\"\"\n        if True:\n            return\n\n        with patchers.PATCH_SELECT_SUCCESS:\n            self.transport.bulk_write(b'TEST')\n\n        with patchers.PATCH_SELECT_FAIL:\n            with self.assertRaises(TcpTimeoutException):\n                self.transport.bulk_write(b'FAIL')\n"
  },
  {
    "path": "venv_requirements.txt",
    "content": "# Standard requirements\nblack\ncoverage\ncoveralls\nflake8\npylint\npytest\nsetuptools\nsphinx\nsphinx-rtd-theme\ntwine\n\n# Specific requirements for this project\naiofiles\nasync_timeout\ncryptography\nlibusb1\npyasn1\npycryptodome\nrsa\n"
  }
]