a8de408e65c922f93f8506067e6e7bc7824063ad
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / cmds / launch.cpp
1 /*
2  * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 3, as published
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranties of
10  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11  * PURPOSE.  See the GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17
18 #include "anbox/cmds/launch.h"
19 #include "anbox/dbus/stub/application_manager.h"
20 #include "anbox/dbus/interface.h"
21 #include "anbox/ui/splash_screen.h"
22 #include "anbox/system_configuration.h"
23 #include "anbox/logger.h"
24
25 #include "core/posix/exec.h"
26 #include "core/posix/fork.h"
27 #include "core/posix/signal.h"
28
29 #include <boost/filesystem.hpp>
30
31 #include <fcntl.h>
32 #include <sys/stat.h>
33
34 namespace fs = boost::filesystem;
35
36 namespace {
37 constexpr unsigned int max_session_mgr_wait_attempts{10};
38 const std::chrono::seconds session_mgr_wait_interval{5};
39 constexpr unsigned int max_dbus_service_wait_attempts{10};
40 const std::chrono::seconds dbus_service_wait_interval{5};
41
42 static int redirect_to_null(int flags, int fd) {
43   int fd2;
44   if ((fd2 = open("/dev/null", flags)) < 0)
45     return -1;
46
47   if (fd2 == fd)
48     return fd;
49
50   if (dup2(fd2, fd) < 0)
51     return -1;
52
53   close(fd2);
54   return fd;
55 }
56 } // namespace
57
58 bool anbox::cmds::Launch::launch_session_manager() {
59   std::vector<std::string> args = {"session-manager"};
60   const auto should_force_software_rendering = utils::get_env_value("ANBOX_FORCE_SOFTWARE_RENDERING", "false");
61   if (should_force_software_rendering == "true")
62     args.push_back("--software-rendering");
63
64   std::map<std::string,std::string> env;
65   core::posix::this_process::env::for_each([&](const std::string &name, const std::string &value) {
66     env.insert({name, value});
67   });
68
69   const auto exe_path = utils::process_get_exe_path(::getpid());
70   if (!fs::exists(exe_path)) {
71     ERROR("Can't find correct anbox executable to run. Found %s but does not exist", exe_path);
72     return false;
73   }
74
75   try {
76     auto flags = core::posix::StandardStream::empty;
77     auto child = core::posix::fork([&]() {
78
79       // We redirect all in/out/err to /dev/null as they can't be seen
80       // anywhere. All logging output will directly go to syslog as we
81       // will become a session leader below which will get us rid of a
82       // controlling terminal.
83       if (redirect_to_null(O_RDONLY, 0) < 0 ||
84           redirect_to_null(O_WRONLY, 1) < 0 ||
85           redirect_to_null(O_WRONLY, 2) < 0) {
86         ERROR("Failed to redirect stdout/stderr/stdin: %s", strerror(errno));
87         return core::posix::exit::Status::failure;
88       }
89
90       // As we forked one time already we're sure that our process is
91       // not the session leader anymore so we can safely become the
92       // new one and lead the process group.
93       if (setsid() < 0) {
94         ERROR("Failed to become new session leader: %s", strerror(errno));
95         return core::posix::exit::Status::failure;
96       }
97
98       umask(0077);
99
100       if (chdir("/") < 0) {
101         ERROR("Failed to change current directory: %s", strerror(errno));
102         return core::posix::exit::Status::failure;
103       }
104
105       auto grandchild = core::posix::exec(exe_path, args, env, flags);
106       grandchild.dont_kill_on_cleanup();
107       return core::posix::exit::Status::success;
108     }, flags);
109
110     // We don't wait for the grandchild but the child as we use double forking
111     // here to break through the process hierarchy and make the grandchild a
112     // direct child of the init process so it keeps running on its own and
113     // indepent of our short living process here.
114     child.wait_for(core::posix::wait::Flags::untraced);
115
116     DEBUG("Started session manager, will now try to connect ..");
117   }
118   catch (...) {
119     ERROR("Failed to start session manager instance");
120   }
121
122   return true;
123 }
124
125 bool anbox::cmds::Launch::try_launch_activity(const std::shared_ptr<dbus::stub::ApplicationManager> &stub) {
126   try {
127     DEBUG("Sending launch intent %s to Android ..", intent_);
128     stub->launch(intent_, graphics::Rect::Invalid, stack_);
129   } catch (const std::exception &err) {
130     ERROR("Failed to launch activity: %s", err.what());
131     return false;
132   } catch (...) {
133     ERROR("Failed to launch activity");
134     return false;
135   }
136
137   return true;
138 }
139
140 anbox::cmds::Launch::Launch()
141     : CommandWithFlagsAndAction{
142           cli::Name{"launch"}, cli::Usage{"launch"},
143           cli::Description{"Launch an Activity by sending an intent"}} {
144   flag(cli::make_flag(cli::Name{"action"},
145                       cli::Description{"Action of the intent"},
146                       intent_.action));
147   flag(cli::make_flag(cli::Name{"type"},
148                       cli::Description{"MIME type for the intent"},
149                       intent_.type));
150   flag(cli::make_flag(cli::Name{"uri"},
151                       cli::Description{"URI used as data within the intent"},
152                       intent_.uri));
153   flag(cli::make_flag(cli::Name{"package"},
154                       cli::Description{"Package the intent should go to"},
155                       intent_.package));
156   flag(cli::make_flag(cli::Name{"component"},
157                       cli::Description{"Component of a package the intent should go"},
158                       intent_.component));
159   flag(cli::make_flag(cli::Name{"stack"},
160                       cli::Description{"Which window stack the activity should be started on. Possible: default, fullscreen, freeform"},
161                       stack_));
162   flag(cli::make_flag(cli::Name{"use-system-dbus"},
163                       cli::Description{"Use system instead of session DBus"},
164                       use_system_dbus_));
165
166
167   action([this](const cli::Command::Context&) {
168     if (!intent_.valid()) {
169       ERROR("The intent you provided is invalid. Please provide a correct launch intent.");
170       ERROR("For example to launch the application manager, run:");
171       ERROR("$ anbox launch --package=org.anbox.appmgr --component=org.anbox.appmgr.AppViewActivity");
172       return EXIT_FAILURE;
173     }
174
175     auto bus_type = anbox::dbus::Bus::Type::Session;
176     if (use_system_dbus_)
177       bus_type = anbox::dbus::Bus::Type::System;
178     auto bus = std::make_shared<anbox::dbus::Bus>(bus_type);
179
180     std::shared_ptr<ui::SplashScreen> ss;
181     if (!bus->has_service_with_name(dbus::interface::Service::name())) {
182       DEBUG("Session manager is not yet running, trying to start it");
183
184       if (!launch_session_manager())
185         return EXIT_FAILURE;
186
187       // Give us a splash screen as long as we're trying to connect
188       // with the session manager so the user knows something is
189       // happening after he started Anbox.
190       ss = std::make_shared<ui::SplashScreen>();
191     }
192
193     unsigned int n = 0;
194     while (n < max_dbus_service_wait_attempts) {
195       if (bus->has_service_with_name(dbus::interface::Service::name()))
196         break;
197
198       std::this_thread::sleep_for(dbus_service_wait_interval);
199       n++;
200     }
201
202     auto app_mgr = dbus::stub::ApplicationManager::create_for_bus(bus);
203     n = 0;
204     while (n < max_session_mgr_wait_attempts) {
205       app_mgr->update_properties();
206       if (app_mgr->ready().get())
207         break;
208
209       std::this_thread::sleep_for(session_mgr_wait_interval);
210       n++;
211     }
212
213     if (!app_mgr->ready()) {
214       ERROR("Session manager failed to become ready");
215       return EXIT_FAILURE;
216     }
217
218     // If we have a splash screen now is the time to drop it as we're
219     // going to launch the real application now.
220     ss.reset();
221
222     const auto success = try_launch_activity(app_mgr);
223     return success ? EXIT_SUCCESS : EXIT_FAILURE;
224   });
225 }