1#
2# Copyright (C) 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the 'License');
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an 'AS IS' BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17import datetime
18import logging
19import os
20import shutil
21import tempfile
22import zipfile
23
24from host_controller import common
25from host_controller.command_processor import base_command_processor
26from host_controller.utils.gsi import img_utils
27
28from vts.utils.python.common import cmd_utils
29
30
31class CommandGsispl(base_command_processor.BaseCommandProcessor):
32    """Command processor for gsispl command.
33
34    Attributes:
35        arg_parser: ConsoleArgumentParser object, argument parser.
36        console: cmd.Cmd console object.
37        command: string, command name which this processor will handle.
38        command_detail: string, detailed explanation for the command.
39    """
40
41    command = "gsispl"
42    command_detail = "Changes security patch level on a selected GSI file."
43
44    # @Override
45    def SetUp(self):
46        """Initializes the parser for device command."""
47        self.arg_parser.add_argument(
48            "--gsi",
49            help="Path to GSI image to change security patch level. "
50            "If path is not given, the most recently fetched system.img "
51            "kept in device_image_info dictionary is used and then "
52            "device_image_info will be updated with the new GSI file.")
53        self.arg_parser.add_argument(
54            "--version", help="New version ID. It should be YYYY-mm-dd format")
55        self.arg_parser.add_argument(
56            "--version_from_path",
57            help="Path to vendor provided image file to retrieve SPL version. "
58            "If just a file name is given, the most recently fetched .img "
59            "file will be used.")
60        self.arg_parser.add_argument(
61            "--vendor_version",
62            help="The version of vendor.img that will be used (e.g., 8.1.0).")
63
64    # @Override
65    def Run(self, arg_line):
66        """Changes security patch level on a selected GSI file."""
67        args = self.arg_parser.ParseLine(arg_line)
68        if args.gsi:
69            if os.path.isfile(args.gsi):
70                gsi_path = args.gsi
71            else:
72                logging.error("Cannot find system image in given path")
73                return
74        elif "system.img" in self.console.device_image_info:
75            gsi_path = self.console.device_image_info["system.img"]
76        else:
77            logging.error("Cannot find system image.")
78            return False
79
80        if args.version:
81            try:
82                version_date = datetime.datetime.strptime(
83                    args.version, "%Y-%m-%d")
84                version = "{:04d}-{:02d}-{:02d}".format(
85                    version_date.year, version_date.month, version_date.day)
86            except ValueError as e:
87                logging.error("version ID should be YYYY-mm-dd format.")
88                return
89        elif args.version_from_path:
90            dest_path = None
91            if os.path.isabs(args.version_from_path) and os.path.exists(
92                    args.version_from_path):
93                img_path = args.version_from_path
94            elif args.version_from_path in self.console.device_image_info:
95                img_path = self.console.device_image_info[
96                    args.version_from_path]
97            elif (args.version_from_path == "boot.img"
98                  and "full-zipfile" in self.console.device_image_info):
99                tempdir_base = os.path.join(os.getcwd(), "tmp")
100                if not os.path.exists(tempdir_base):
101                    os.mkdir(tempdir_base)
102                dest_path = tempfile.mkdtemp(dir=tempdir_base)
103
104                with zipfile.ZipFile(
105                        self.console.device_image_info["full-zipfile"],
106                        'r') as zip_ref:
107                    zip_ref.extractall(dest_path)
108                    img_path = os.path.join(dest_path, "boot.img")
109                    if not os.path.exists(img_path):
110                        logging.error("No %s file in device img .zip.",
111                                      args.version_from_path)
112                        shutil.rmtree(dest_path)
113                        return
114            else:
115                logging.error("Cannot find %s file.", args.version_from_path)
116                return False
117
118            version_dict = img_utils.GetSPLVersionFromBootImg(img_path)
119            if dest_path:
120                shutil.rmtree(dest_path)
121            if "year" in version_dict and "month" in version_dict:
122                version = "{:04d}-{:02d}-{:02d}".format(
123                    version_dict["year"], version_dict["month"],
124                    common._SPL_DEFAULT_DAY)
125            else:
126                logging.error("Failed to fetch SPL version from %s file.",
127                              img_path)
128                return False
129        else:
130            logging.error("version ID or path of .img file must be given.")
131            return False
132
133        output_path = os.path.join(
134            os.path.dirname(os.path.abspath(gsi_path)),
135            "system-{}.img".format(version))
136        command = "{} {} {} {}".format(
137            os.path.join(os.getcwd(), "..", "bin",
138                         "change_security_patch_ver.sh"), gsi_path,
139            output_path, version)
140        if args.vendor_version:
141            command = command + " -v " + args.vendor_version
142        if self.console.password.value:
143            command = "echo {} | sudo -S {}".format(
144                self.console.password.value, command)
145        stdout, stderr, err_code = cmd_utils.ExecuteOneShellCommand(command)
146        if err_code is 0:
147            if not args.gsi:
148                logging.info(
149                    "system.img path is updated to : {}".format(output_path))
150                self.console.device_image_info["system.img"] = output_path
151        else:
152            logging.error("gsispl error: {} {}".format(stdout, stderr))
153            return False
154