2525#include < cstring>
2626#include < limits>
2727#include < memory>
28+ #include < optional>
2829#include < sstream>
2930#include < string>
3031#include < utility>
3536#include " absl/log/log.h"
3637#include " absl/strings/match.h"
3738#include " absl/strings/str_join.h"
39+ #include " absl/strings/str_split.h"
3840
3941#include " cuttlefish/common/libs/utils/contains.h"
4042#include " cuttlefish/common/libs/utils/environment.h"
@@ -118,6 +120,18 @@ Result<CommandRequest> CreateStartCommand(const LocalInstanceGroup& group,
118120 .Build ());
119121}
120122
123+ Result<void > StartGroup (const LocalInstanceGroup& group,
124+ const std::vector<std::string>& subcmd_args,
125+ const cvd_common::Envs& envs,
126+ InstanceManager& instance_manager) {
127+ const CommandRequest start_cmd =
128+ CF_EXPECT (CreateStartCommand (group, subcmd_args, envs));
129+ std::unique_ptr<CvdCommandHandler> start_handler =
130+ NewCvdStartCommandHandler (instance_manager);
131+ CF_EXPECT (start_handler->Handle (start_cmd));
132+ return {};
133+ }
134+
121135Result<cvd_common::Envs> GetEnvs (const CommandRequest& request) {
122136 cvd_common::Envs envs = request.Env ();
123137 if (auto it = envs.find (" HOME" ); it != envs.end () && it->second .empty ()) {
@@ -236,6 +250,47 @@ std::vector<Flag> BuildSelectorFlagsForCreateHelp(
236250 };
237251}
238252
253+ bool IsActiveState (cvd::InstanceState state) {
254+ return state == cvd::INSTANCE_STATE_PREPARING ||
255+ state == cvd::INSTANCE_STATE_STARTING ||
256+ state == cvd::INSTANCE_STATE_RUNNING ||
257+ state == cvd::INSTANCE_STATE_STOPPING ;
258+ }
259+
260+ bool IsGroupReuseable (const LocalInstanceGroup& group) {
261+ for (const LocalInstance& instance : group.Instances ()) {
262+ if (IsActiveState (instance.State ())) {
263+ return false ;
264+ }
265+ }
266+ return true ;
267+ }
268+
269+ Result<bool > MatchPaths (const LocalInstanceGroup& group, const std::string& target_host,
270+ const std::vector<std::string>& target_products) {
271+ const std::string real_group_host = CF_EXPECT (RealPath (group.HostArtifactsPath ()));
272+ const std::string real_target_host = CF_EXPECT (RealPath (target_host));
273+
274+ if (real_group_host != real_target_host) {
275+ return false ;
276+ }
277+
278+ const std::vector<std::string> group_products =
279+ absl::StrSplit (group.ProductOutPath (), ' ,' );
280+
281+ std::vector<std::string> real_group_products;
282+ for (const auto & path : group_products) {
283+ real_group_products.push_back (CF_EXPECT (RealPath (path)));
284+ }
285+
286+ std::vector<std::string> real_target_products;
287+ for (const auto & path : target_products) {
288+ real_target_products.push_back (CF_EXPECT (RealPath (path)));
289+ }
290+
291+ return real_group_products == real_target_products;
292+ }
293+
239294} // namespace
240295
241296CvdCreateCommandHandler::CvdCreateCommandHandler (
@@ -275,18 +330,49 @@ Result<void> CvdCreateCommandHandler::Handle(const CommandRequest& request) {
275330 // CreationAnalyzer needs these to be set in the environment
276331 envs[kAndroidHostOut ] = AbsolutePath (own_flags_.host_path );
277332 envs[kAndroidProductOut ] = AbsolutePath (own_flags_.product_path );
278- auto group =
333+
334+ if (own_flags_.reuse_if_possible ) {
335+ const std::string target_host = envs[kAndroidHostOut ];
336+ const std::string target_product = envs[kAndroidProductOut ];
337+ const std::vector<std::string> target_products =
338+ ExpandProductPaths (target_product, num_instances_parser_.NumInstances ());
339+
340+ const std::vector<LocalInstanceGroup> groups =
341+ CF_EXPECT (instance_manager_.FindGroups ({}));
342+
343+ std::optional<LocalInstanceGroup> matched_group;
344+ for (const LocalInstanceGroup& g : groups) {
345+ const auto match_res = MatchPaths (g, target_host, target_products);
346+ if (!match_res.ok ()) {
347+ continue ;
348+ }
349+ if (*match_res) {
350+ matched_group = g;
351+ break ;
352+ }
353+ }
354+ if (matched_group) {
355+ if (IsGroupReuseable (*matched_group)) {
356+ LOG (INFO ) << " Reusing existing instance group: "
357+ << matched_group->GroupName ();
358+ CF_EXPECT (StartGroup (*matched_group, subcmd_args, envs, instance_manager_));
359+ return {};
360+ } else {
361+ LOG (WARNING ) << " Found matching instance group '"
362+ << matched_group->GroupName ()
363+ << " ' but it is in active state. Proceding with creation of a new group." ;
364+ }
365+ }
366+ }
367+
368+ LocalInstanceGroup group =
279369 CF_EXPECT (CreateGroup (instance_manager_, subcmd_args, envs, request));
280370
281371 group.SetAllStates (cvd::INSTANCE_STATE_STOPPED );
282372 CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
283373
284374 if (own_flags_.start ) {
285- CommandRequest start_cmd =
286- CF_EXPECT (CreateStartCommand (group, subcmd_args, envs));
287- std::unique_ptr<CvdCommandHandler> start_handler =
288- NewCvdStartCommandHandler (instance_manager_);
289- CF_EXPECT (start_handler->Handle (start_cmd));
375+ CF_EXPECT (StartGroup (group, subcmd_args, envs, instance_manager_));
290376
291377 if (CF_EXPECT (IsDefaultGroup (request))) {
292378 // For backward compatibility, we add extra symlink in system wide home
@@ -404,6 +490,7 @@ std::vector<Flag> CvdCreateCommandHandler::FlagModeFlags(
404490 own_flags_.host_path = DefaultHostPath (env);
405491 own_flags_.product_path = DefaultProductPath (env);
406492 own_flags_.start = true ;
493+ own_flags_.reuse_if_possible = false ;
407494 std::vector<Flag> flags = num_instances_parser_.Flags (selector_options);
408495 flags.emplace_back (
409496 GflagsCompatFlag (" host_path" , own_flags_.host_path )
@@ -417,6 +504,9 @@ std::vector<Flag> CvdCreateCommandHandler::FlagModeFlags(
417504 " $ANDROID_PRODUCT_OUT, $HOME or the current directory." ));
418505 flags.emplace_back (GflagsCompatFlag (" start" , own_flags_.start )
419506 .Help (" Whether to start the instance group." ));
507+ flags.emplace_back (
508+ GflagsCompatFlag (" reuse_if_possible" , own_flags_.reuse_if_possible )
509+ .Help (" Reuse an existing instance group if paths match and it is stopped." ));
420510 return flags;
421511}
422512
0 commit comments