huangcm
2024-12-18 9d29be7f7249789d6ffd0440067187a9f040c2cd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/env python
#
# Copyright 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helper tool for 'oem at-write-persistent-digest' fastboot command.
 
This tool generates and stages the correct input data format, based on the
user-provided inputs, for the 'oem at-write-persistent-digest' fastboot command
for Android Things devices before running the command itself.
 
The input format is defined elsewhere to be the following:
 
  - Name length: 4 bytes (little-endian)
  - Name: 'name length' bytes
  - Digest length: 4 bytes (little-endian)
  - Digest: 'digest length' bytes
 
Digest length can be zero, indicating that any existing digest with the given
name should be deleted. This corresponds to the '--clear_digest' option for this
tool.
 
Digest names must be prefixed with 'avb.persistent_digest.', and if the
user-provided name does not include that prefix it is added automatically.
"""
 
import sys
 
ver = sys.version_info
if (ver[0] < 2) or (ver[0] == 2 and ver[1] < 7) or (ver[0] == 3 and ver[1] < 2):
  print('This script requires Python 2.7+ or 3.2+')
  sys.exit(1)
 
import argparse
import os
import shutil
import struct
import subprocess
import tempfile
 
HELP_DESCRIPTION = """Helper script for 'fastboot oem
at-write-persistent-digest' that generates and stages the required input data
format."""
 
AVB_PERSISTENT_DIGEST_PREFIX = 'avb.persistent_digest.'
 
 
def WritePersistentDigest(name,
                          digest=None,
                          clear_digest=False,
                          serial=None,
                          verbose=False):
  if not name.startswith(AVB_PERSISTENT_DIGEST_PREFIX):
    print("Automatically adding '{}' prefix to persistent value name".format(
        AVB_PERSISTENT_DIGEST_PREFIX))
    name = AVB_PERSISTENT_DIGEST_PREFIX + name
 
  tempdir = tempfile.mkdtemp()
  try:
    digest_data = os.path.join(tempdir, 'digest_data')
 
    with open(digest_data, 'wb') as out:
      out.write(struct.pack('<I', len(name)))
      out.write(name)
      if clear_digest:
        out.write(struct.pack('<I', 0))
      else:
        digest_bytes = bytearray.fromhex(digest)
        out.write(struct.pack('<I', len(digest_bytes)))
        out.write(digest_bytes)
 
    def fastboot_cmd(args):
      args = ['fastboot'] + (['-s', serial] if serial else []) + args
      if verbose:
        print('$ ' + ' '.join(args))
 
      try:
        out = subprocess.check_output(
            args, stderr=subprocess.STDOUT).decode('utf-8')
      except subprocess.CalledProcessError as e:
        print(e.output.decode('utf-8'))
        print("Command '{}' returned non-zero exit status {}".format(
            ' '.join(e.cmd), e.returncode))
        sys.exit(1)
 
      if verbose:
        print(out)
 
    fastboot_cmd(['stage', digest_data])
    fastboot_cmd(['oem', 'at-write-persistent-digest'])
 
    print("Persistent value '{}' {}".format(
        name, 'cleared' if clear_digest else 'written'))
 
  finally:
    shutil.rmtree(tempdir)
 
 
if __name__ == '__main__':
  parser = argparse.ArgumentParser(description=HELP_DESCRIPTION)
 
  # Optional arguments
  parser.add_argument(
      '-v',
      '--verbose',
      action='store_true',
      help='verbose; prints fastboot commands and their output')
  parser.add_argument(
      '-s',
      '--serial',
      help=
      "specify device to unlock, either by serial or any other valid value for fastboot's -s arg"
  )
 
  # Required arguments
  parser.add_argument(
      '--name',
      required=True,
      help=
      "persistent digest name to write, 'avb.persistent_digest.' prefix will be automatically added if not already present"
  )
  group = parser.add_mutually_exclusive_group(required=True)
  group.add_argument(
      '--clear_digest',
      action='store_true',
      help=
      'clear any existing persistent digest value, rather than writing a new value'
  )
  group.add_argument(
      '--digest',
      help='persistent digest value to write, as a hex encoded string')
 
  # Print help if no args given
  args = parser.parse_args(args=None if sys.argv[1:] else ['-h'])
 
  WritePersistentDigest(
      name=args.name,
      clear_digest=args.clear_digest,
      digest=args.digest,
      serial=args.serial,
      verbose=args.verbose)