• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 
17 #include <iostream>
18 #include <ostream>
19 #include <string>
20 #include <unordered_map>
21 #include <vector>
22 
23 #include <android-base/logging.h>
24 #include <android-base/no_destructor.h>
25 #include <android-base/parseint.h>
26 #include <android-base/strings.h>
27 
28 #include "common/libs/utils/flag_parser.h"
29 #include "common/libs/utils/result.h"
30 #include "device/google/cuttlefish/host/libs/command_util/runner/run_cvd.pb.h"
31 #include "host/libs/command_util/util.h"
32 #include "host/libs/config/cuttlefish_config.h"
33 #include "host/libs/config/display.h"
34 #include "host/libs/vm_manager/crosvm_display_controller.h"
35 
36 namespace cuttlefish {
37 namespace {
38 
39 static const char kAddUsage[] =
40     R"(
41 
42 Adds and connects a display to the given virtual device.
43 
44 usage: cvd display add \\
45         --display=width=1280,height=800 \\
46         --display=width=1920,height=1080,refresh_rate_hz=60
47 )";
48 
49 static const char kListUsage[] =
50     R"(
51 
52 Lists all of the displays currently connected to a given virtual device.
53 
54 usage: cvd display list
55 )";
56 
57 static const char kRemoveUsage[] =
58     R"(
59 
60 Disconnects and removes displays from the given virtual device.
61 
62 usage: cvd display remove \\
63         --display=<display id> \\
64         --display=<display id> ...
65 )";
66 
67 static const char kScreenshotUsage[] =
68     R"(
69 Screenshots the contents of a given display.
70 
71 Currently supported output formats: jpg, png, webp.
72 
73 usage: cvd display screenshot <display id> <screenshot path>
74 )";
75 
GetInstanceNum(std::vector<std::string> & args)76 Result<int> GetInstanceNum(std::vector<std::string>& args) {
77   int instance_num = 1;
78   CF_EXPECT(
79       ConsumeFlags({GflagsCompatFlag("instance_num", instance_num)}, args));
80   return instance_num;
81 }
82 
DoHelp(std::vector<std::string> & args)83 Result<int> DoHelp(std::vector<std::string>& args) {
84   static const android::base::NoDestructor<
85       std::unordered_map<std::string, std::string>>
86       kSubCommandUsages({
87           {"add", kAddUsage},
88           {"list", kListUsage},
89           {"remove", kRemoveUsage},
90           {"screenshot", kScreenshotUsage},
91       });
92 
93   const std::string& subcommand_str = args[0];
94   auto subcommand_usage = kSubCommandUsages->find(subcommand_str);
95   if (subcommand_usage == kSubCommandUsages->end()) {
96     std::cerr << "Unknown subcommand '" << subcommand_str
97               << "'. See `cvd display help`" << std::endl;
98     return 1;
99   }
100 
101   std::cout << subcommand_usage->second << std::endl;
102   return 0;
103 }
104 
DoAdd(std::vector<std::string> & args)105 Result<int> DoAdd(std::vector<std::string>& args) {
106   const int instance_num = CF_EXPECT(GetInstanceNum(args));
107 
108   const auto display_configs = CF_EXPECT(ParseDisplayConfigsFromArgs(args));
109   if (display_configs.empty()) {
110     std::cerr << "Must provide at least 1 display to add. Usage:" << std::endl;
111     std::cerr << kAddUsage << std::endl;
112     return 1;
113   }
114 
115   auto crosvm_display = CF_EXPECT(vm_manager::GetCrosvmDisplayController());
116   return CF_EXPECT(crosvm_display.Add(instance_num, display_configs));
117 }
118 
DoList(std::vector<std::string> & args)119 Result<int> DoList(std::vector<std::string>& args) {
120   const int instance_num = CF_EXPECT(GetInstanceNum(args));
121   auto crosvm_display = CF_EXPECT(vm_manager::GetCrosvmDisplayController());
122 
123   auto out = CF_EXPECT(crosvm_display.List(instance_num));
124   std::cout << out << std::endl;
125 
126   return 0;
127 }
128 
DoRemove(std::vector<std::string> & args)129 Result<int> DoRemove(std::vector<std::string>& args) {
130   const int instance_num = CF_EXPECT(GetInstanceNum(args));
131 
132   std::vector<std::string> displays;
133   const std::vector<Flag> remove_displays_flags = {
134       GflagsCompatFlag(kDisplayFlag)
135           .Help("Display id of a display to remove.")
136           .Setter([&](const FlagMatch& match) -> Result<void> {
137             displays.push_back(match.value);
138             return {};
139           }),
140   };
141   auto parse_res = ConsumeFlags(remove_displays_flags, args);
142   if (!parse_res.ok()) {
143     std::cerr << parse_res.error().FormatForEnv() << std::endl;
144     std::cerr << "Failed to parse flags. Usage:" << std::endl;
145     std::cerr << kRemoveUsage << std::endl;
146     return 1;
147   }
148 
149   if (displays.empty()) {
150     std::cerr << "Must specify at least one display id to remove. Usage:"
151               << std::endl;
152     std::cerr << kRemoveUsage << std::endl;
153     return 1;
154   }
155 
156   auto crosvm_display = CF_EXPECT(vm_manager::GetCrosvmDisplayController());
157   return CF_EXPECT(crosvm_display.Remove(instance_num, displays));
158 }
159 
DoScreenshot(std::vector<std::string> & args)160 Result<int> DoScreenshot(std::vector<std::string>& args) {
161   const int instance_num = CF_EXPECT(GetInstanceNum(args));
162 
163   auto config = cuttlefish::CuttlefishConfig::Get();
164   if (!config) {
165     return CF_ERR("Failed to get Cuttlefish config.");
166   }
167 
168   int display_number = 0;
169   std::string screenshot_path;
170 
171   std::vector<std::string> displays;
172   const std::vector<Flag> screenshot_flags = {
173       GflagsCompatFlag(kDisplayFlag, display_number)
174           .Help("Display id of a display to screenshot."),
175       GflagsCompatFlag("screenshot_path", screenshot_path)
176           .Help("Path for the resulting screenshot file."),
177   };
178   auto parse_res = ConsumeFlags(screenshot_flags, args);
179   if (!parse_res.ok()) {
180     std::cerr << parse_res.error().FormatForEnv() << std::endl;
181     std::cerr << "Failed to parse flags. Usage:" << std::endl;
182     std::cerr << kScreenshotUsage << std::endl;
183     return 1;
184   }
185   CF_EXPECT(!screenshot_path.empty(),
186             "Must provide --screenshot_path. Usage:" << kScreenshotUsage);
187 
188   run_cvd::ExtendedLauncherAction extended_action;
189   extended_action.mutable_screenshot_display()->set_display_number(
190       display_number);
191   extended_action.mutable_screenshot_display()->set_screenshot_path(
192       screenshot_path);
193 
194   std::cout << "Requesting to save screenshot for display " << display_number
195             << " to " << screenshot_path << "." << std::endl;
196 
197   auto socket = CF_EXPECT(
198       GetLauncherMonitor(*config, instance_num, /*timeout_seconds=*/5));
199   CF_EXPECT(RunLauncherAction(socket, extended_action, std::nullopt),
200             "Failed to get success response from launcher.");
201   return 0;
202 }
203 
204 using DisplaySubCommand = Result<int> (*)(std::vector<std::string>&);
205 
DisplayMain(int argc,char ** argv)206 int DisplayMain(int argc, char** argv) {
207   ::android::base::InitLogging(argv, android::base::StderrLogger);
208 
209   const std::unordered_map<std::string, DisplaySubCommand> kSubCommands = {
210       {"add", DoAdd},                //
211       {"list", DoList},              //
212       {"help", DoHelp},              //
213       {"remove", DoRemove},          //
214       {"screenshot", DoScreenshot},  //
215   };
216 
217   auto args = ArgsToVec(argc - 1, argv + 1);
218   if (args.empty()) {
219     args.push_back("help");
220   }
221 
222   const std::string command_str = args[0];
223   args.erase(args.begin());
224 
225   auto command_func_it = kSubCommands.find(command_str);
226   if (command_func_it == kSubCommands.end()) {
227     std::cerr << "Unknown display command: '" << command_str << "'."
228               << std::endl;
229     return 1;
230   }
231 
232   auto result = command_func_it->second(args);
233   if (!result.ok()) {
234     std::cerr << result.error().FormatForEnv();
235     return 1;
236   }
237   return result.value();
238 }
239 
240 }  // namespace
241 }  // namespace cuttlefish
242 
main(int argc,char ** argv)243 int main(int argc, char** argv) { return cuttlefish::DisplayMain(argc, argv); }
244