Skip to content

Commit

Permalink
Enable capturing from Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
RenfengLiu committed Nov 8, 2023
1 parent 9d1d3b9 commit ac61dba
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 37 deletions.
13 changes: 10 additions & 3 deletions capture_service/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,24 @@ target_link_libraries(dive_grpc_proto PRIVATE
${_PROTOBUF_LIBPROTOBUF})
set_property(TARGET dive_grpc_proto PROPERTY POSITION_INDEPENDENT_CODE 1)

if(WIN32)
set(COMMAND_UTILS_SRC "command_utils_win32.cc")
add_definitions(-DUNICODE -D_UNICODE)
else()
set(COMMAND_UTILS_SRC "command_utils.cc")
endif()

if(ANDROID)
add_library(service
service.cc
trace_mgr.cc
android_trace_mgr.cc
command_utils.cc)
${COMMAND_UTILS_SRC})
else()
add_library(service
service.cc
trace_mgr.cc
command_utils.cc)
${COMMAND_UTILS_SRC})
endif()
target_link_libraries(service PRIVATE
dive_grpc_proto
Expand Down Expand Up @@ -102,7 +109,7 @@ else()
absl::algorithm
)

add_library(device_mgr device_mgr.cc command_utils.cc android_application.cc)
add_library(device_mgr device_mgr.cc ${COMMAND_UTILS_SRC} android_application.cc)
target_link_libraries(device_mgr
absl::flags
absl::flags_parse
Expand Down
13 changes: 0 additions & 13 deletions capture_service/command_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,6 @@ limitations under the License.
namespace Dive
{

#if defined(WIN32)
// TODO(renfeng): figure out how to run exe on Windows
CommandResult RunCommand(const std::string &command, bool quiet)
{
CommandResult result;
return result;
}
#else
CommandResult RunCommand(const std::string &command, bool quiet)
{
CommandResult result;
Expand Down Expand Up @@ -78,10 +70,5 @@ CommandResult RunCommand(const std::string &command, bool quiet)

return result;
}
#endif

CommandResult AdbSession::Run(const std::string &command, bool quiet) const
{
return RunCommand("adb -s " + m_serial + " " + command, quiet);
}
} // namespace Dive
5 changes: 4 additions & 1 deletion capture_service/command_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ class AdbSession
{
}

CommandResult Run(const std::string &command, bool quiet = false) const;
inline CommandResult Run(const std::string &command, bool quiet = true) const
{
return RunCommand("adb -s " + m_serial + " " + command, quiet);
}

private:
std::string m_serial;
Expand Down
156 changes: 156 additions & 0 deletions capture_service/command_utils_win32.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
Copyright 2023 Google Inc.
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.
*/

#include "command_utils.h"

#include <vector>
#include "absl/strings/ascii.h"
#include "log.h"
#ifndef WIN32
# error "Build this for Win32 platform only"
#endif
#include <windows.h>

namespace Dive
{

CommandResult RunCommand(const std::string &command, bool quiet)
{
CommandResult result;
HANDLE hChildStdOutRd = NULL;
HANDLE hChildStdOutWr = NULL;
HANDLE hChildStdErrRd = NULL;
HANDLE hChildStdErrWr = NULL;

SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;

ZeroMemory(&sa, sizeof(sa));
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;

if (!CreatePipe(&hChildStdOutRd, &hChildStdOutWr, &sa, 0))
{
LOGE("Create pipe to read stdout failed.");
return result;
}
if (!SetHandleInformation(hChildStdOutRd, HANDLE_FLAG_INHERIT, 0))
{
LOGE("SetHandleInformation for stdout failed.");
return result;
}
if (!CreatePipe(&hChildStdErrRd, &hChildStdErrWr, &sa, 0))
{
LOGE("CreatePipe for stderr failed");
return result;
}
if (!SetHandleInformation(hChildStdErrRd, HANDLE_FLAG_INHERIT, 0))
{
LOGE("SetHandleInformation for stdout failed");
return result;
}

si.cb = sizeof(si);
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdOutput = hChildStdOutWr;
si.hStdError = hChildStdErrWr;

int len = MultiByteToWideChar(CP_UTF8, 0, command.c_str(), -1, NULL, 0);
std::vector<wchar_t> cmd(len);

int res = MultiByteToWideChar(CP_UTF8, 0, command.c_str(), -1, cmd.data(), len);
if (res == 0)
{
LOGE("Failed to convert std::string to utf-8 string");
return result;
}

bool bSuccess = CreateProcessW(NULL,
cmd.data(), // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
CREATE_UNICODE_ENVIRONMENT, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&si, // STARTUPINFO pointer
&pi); // receives PROCESS_INFORMATION

if (!bSuccess)
{
LOGE("Error create process %d", GetLastError());
return result;
}
else
{

CloseHandle(hChildStdOutWr);
CloseHandle(hChildStdErrWr);
}

BOOL success = FALSE;
char buf[4096];
DWORD dwOutputRead, dwErrorRead;

for (;;)
{
success = ReadFile(hChildStdOutRd, buf, sizeof(buf), &dwOutputRead, NULL);
result.m_output += std::string(buf, dwOutputRead);
if (!success && !dwOutputRead)
break;
}
result.m_output = absl::StripAsciiWhitespace(result.m_output);

for (;;)
{
success = ReadFile(hChildStdErrRd, buf, sizeof(buf), &dwErrorRead, NULL);
result.m_err += std::string(buf, dwErrorRead);

if (!success && !dwErrorRead)
break;
}
result.m_err = absl::StripAsciiWhitespace(result.m_err);

CloseHandle(hChildStdOutRd);
CloseHandle(hChildStdErrRd);

WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, (LPDWORD)&result.m_ret);
LOGD("result->m_ret is %d\n", result.m_ret);
if (!quiet)
{
LOGI("Command: %s\n Output: %s\n", command.c_str(), result.m_output.c_str());
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

if (!result.Ok() && !quiet)
{

LOGE("Command `%s` failed with return code %d, stderr: %s \n",
command.c_str(),
result.m_ret,
result.m_output.c_str());
}
return result;
}

} // namespace Dive
19 changes: 15 additions & 4 deletions capture_service/device_mgr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ limitations under the License.
namespace Dive
{

DeviceManager &GetDeviceManager()
{
static DeviceManager mgr;
return mgr;
}

AndroidDevice::AndroidDevice(const std::string &serial) :
m_serial(serial),
m_adb(serial)
Expand All @@ -42,6 +48,7 @@ AndroidDevice::AndroidDevice(const std::string &serial) :

LOGD("enforce: %s\n", m_original_state.m_enforce.c_str());
LOGD("select: %s\n", GetDeviceDisplayName().c_str());
LOGD("AndroidDevice created.\n");
}

AndroidDevice::~AndroidDevice()
Expand All @@ -50,6 +57,7 @@ AndroidDevice::~AndroidDevice()
{
CleanupDevice();
}
LOGD("AndroidDevice destroyed.\n");
}

std::string AndroidDevice::GetDeviceDisplayName() const
Expand All @@ -72,25 +80,28 @@ std::vector<std::string> AndroidDevice::ListPackage(PackageListOptions option) c
std::vector<std::string> fields = absl::StrSplit(line, ':');
if (fields.size() == 2 && fields[0] == "package")
{
std::string package(absl::StripAsciiWhitespace(fields[1]));
if (option.debuggable_only)
{
std::string output = Adb().Run("shell dumpsys package " + fields[1]).Out();
std::string output = Adb().Run("shell dumpsys package " + package).Out();
// TODO: find out more reliable way to find if app is debuggable.
if (!absl::StrContains(output, "DEBUGGABLE"))
{
continue;
}
}
package_list.push_back(fields[1]);
package_list.push_back(package);
}
}
std::sort(package_list.begin(), package_list.end());
return package_list;
}

std::filesystem::path ResolveAndroidLibPath(std::string name)
std::filesystem::path ResolveAndroidLibPath(const std::string &name)
{
std::vector<std::filesystem::path> search_paths{ std::filesystem::path{
LOGD("cwd: %s\n", std::filesystem::current_path().c_str());
std::vector<std::filesystem::path> search_paths{ std::filesystem::path{ "./install" },
std::filesystem::path{
"../../build_android/Release/bin" },
std::filesystem::path{ "../../install" },
std::filesystem::path{ "./" } };
Expand Down
25 changes: 21 additions & 4 deletions capture_service/device_mgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class AndroidDevice
explicit AndroidDevice(const std::string &serial);
~AndroidDevice();

AndroidDevice &operator=(const AndroidDevice &) = delete;
AndroidDevice(const AndroidDevice &) = delete;

struct PackageListOptions
{
bool with_system_package;
Expand Down Expand Up @@ -75,23 +78,37 @@ class AndroidDevice
class DeviceManager
{
public:
DeviceManager() = default;
DeviceManager &operator=(const DeviceManager &) = delete;
DeviceManager(const DeviceManager &) = delete;

std::vector<std::string> ListDevice() const;
AndroidDevice *SelectDevice(const std::string &serial)
{
assert(!serial.empty());
m_device = std::make_unique<AndroidDevice>(serial);
if (!serial.empty())
{
m_device = std::make_unique<AndroidDevice>(serial);
}
return m_device.get();
}

void RemoveDevice() { m_device = nullptr; }
void RemoveDevice()
{
if (m_device != nullptr)
{
m_device = nullptr;
}
}

AndroidDevice *GetDevice() const { return m_device.get(); }
void Cleanup(const std::string &serial, const std::string &package);

private:
std::unique_ptr<AndroidDevice> m_device;
std::unique_ptr<AndroidDevice> m_device{ nullptr };
};

std::filesystem::path ResolveAndroidLibPath(std::string name);
std::filesystem::path ResolveAndroidLibPath(const std::string &name);

DeviceManager &GetDeviceManager();
} // namespace Dive
4 changes: 3 additions & 1 deletion scripts/build_android.bat
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ set startTime=%time%
pushd !BUILD_DIR!
cmake -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%/build/cmake/android.toolchain.cmake ^
-G "Ninja"^
-DCMAKE_MAKE_PROGRAM="ninja" ^
-DCMAKE_BUILD_TYPE=!build! ^
-DCMAKE_SYSTEM_NAME=Android ^
-DANDROID_ABI=arm64-v8a ^
Expand All @@ -41,7 +42,8 @@ set startTime=%time%
-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=NEVER ^
%SRC_DIR%

cmake --build . --config=!build! -j %NUMBER_OF_PROCESSORS%
cmake --build . --config=!build! -j
if "%%b" == "Release" cmake --install .
popd
))

Expand Down
1 change: 0 additions & 1 deletion ui/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ MainWindow::MainWindow()
horizontal_splitter->setStretchFactor(2, 1);

m_trace_dig = new TraceDialog(this);
m_trace_dig->setAttribute(Qt::WA_DeleteOnClose);

// Main Window requires a central widget.
// Make the horizontal splitter that central widget so it takes up the whole area.
Expand Down
Loading

0 comments on commit ac61dba

Please sign in to comment.