Add an abstracted interface around android-specific handling

* This makes it easier to use the same kind of interface to manage other kinds
  of devices.
This commit is contained in:
baldurk
2019-07-31 17:51:13 +01:00
parent 06f2e61b8f
commit e2704fa2eb
34 changed files with 2340 additions and 1492 deletions
+35 -18
View File
@@ -259,15 +259,8 @@ void PersistantConfig::RemoveRemoteHost(RemoteHost host)
}
}
void PersistantConfig::AddAndroidHosts()
void PersistantConfig::UpdateEnumeratedProtocolDevices()
{
QMutexLocker autolock(&RemoteHostLock);
QMap<rdcstr, RemoteHost> oldHosts;
for(int i = RemoteHostList.count() - 1; i >= 0; i--)
if(RemoteHostList[i].IsADB())
oldHosts[RemoteHostList[i].Hostname()] = RemoteHostList.takeAt(i);
QString androidSDKPath = (!Android_SDKPath.isEmpty() && QFile::exists(Android_SDKPath))
? QString(Android_SDKPath)
: QString();
@@ -282,19 +275,39 @@ void PersistantConfig::AddAndroidHosts()
SetConfigSetting("MaxConnectTimeout", QString::number(Android_MaxConnectTimeout));
rdcstr androidHosts;
RENDERDOC_EnumerateAndroidDevices(androidHosts);
for(const QString &hostName :
QString(androidHosts).split(QLatin1Char(','), QString::SkipEmptyParts))
rdcarray<RemoteHost> enumeratedDevices;
rdcarray<rdcstr> protocols;
RENDERDOC_GetSupportedDeviceProtocols(&protocols);
for(const rdcstr &p : protocols)
{
RemoteHost host((rdcstr)hostName);
IDeviceProtocolController *protocol = RENDERDOC_GetDeviceProtocolController(p);
if(oldHosts.contains(hostName))
host = oldHosts.take(hostName);
rdcarray<rdcstr> devices = protocol->GetDevices();
rdcstr friendly;
RENDERDOC_GetAndroidFriendlyName(hostName.toUtf8().data(), friendly);
host.SetFriendlyName(friendly);
for(const rdcstr &d : devices)
{
RemoteHost newhost(protocol->GetProtocolName() + "://" + d);
enumeratedDevices.push_back(newhost);
}
}
QMutexLocker autolock(&RemoteHostLock);
QMap<rdcstr, RemoteHost> oldHosts;
for(int i = RemoteHostList.count() - 1; i >= 0; i--)
if(RemoteHostList[i].Protocol())
oldHosts[RemoteHostList[i].Hostname()] = RemoteHostList.takeAt(i);
for(RemoteHost host : enumeratedDevices)
{
// if we already had this host, use that one.
if(oldHosts.contains(host.Hostname()))
host = oldHosts.take(host.Hostname());
host.SetFriendlyName(host.Protocol()->GetFriendlyName(host.Hostname()));
// Just a command to display in the GUI and allow Launch() to be called.
host.SetRunCommand("Automatically handled");
RemoteHostList.push_back(host);
@@ -363,6 +376,10 @@ bool PersistantConfig::Load(const rdcstr &filename)
if(!host.IsValid())
continue;
// backwards compatibility - skip old adb hosts that were adb:
if(host.Hostname().find("adb:") > 0 && host.Protocol() == NULL)
continue;
RemoteHostList.push_back(host);
if(host.IsLocalhost())
+2 -2
View File
@@ -791,8 +791,8 @@ R)");
:param RemoteHost host: The remote host to remove.
R)");
void RemoveRemoteHost(RemoteHost host);
DOCUMENT("If configured, queries ``adb`` to add android hosts to :data:`RemoteHosts`.");
void AddAndroidHosts();
DOCUMENT("If configured, queries available device protocols to update auto-configured hosts.");
void UpdateEnumeratedProtocolDevices();
DOCUMENT("");
CONFIG_SETTINGS()
+10 -7
View File
@@ -60,6 +60,8 @@ RemoteHost::RemoteHost(const QVariant &var)
m_data->m_runCommand = map[lit("runCommand")].toString();
if(map.contains(lit("lastCapturePath")))
m_data->m_lastCapturePath = map[lit("lastCapturePath")].toString();
m_protocol = RENDERDOC_GetDeviceProtocolController(m_hostname);
}
RemoteHost::RemoteHost()
@@ -72,6 +74,8 @@ RemoteHost::RemoteHost(const rdcstr &host)
// create a new host
m_hostname = host;
m_data = new RemoteHostData();
m_protocol = RENDERDOC_GetDeviceProtocolController(m_hostname);
}
RemoteHost::RemoteHost(const RemoteHost &o)
@@ -82,6 +86,7 @@ RemoteHost::RemoteHost(const RemoteHost &o)
RemoteHost &RemoteHost::operator=(const RemoteHost &o)
{
m_hostname = o.m_hostname;
m_protocol = o.m_protocol;
// deref old data
if(m_data)
@@ -123,7 +128,7 @@ void RemoteHost::CheckStatus()
// to avoid doing complex work while holding the remote host lock, we check the status here then
// call into the internal function that will propagate that data to the proper storage if needed.
IRemoteServer *rend = NULL;
ReplayStatus status = RENDERDOC_CreateRemoteServerConnection(m_hostname.c_str(), 0, &rend);
ReplayStatus status = RENDERDOC_CreateRemoteServerConnection(m_hostname.c_str(), &rend);
if(rend)
rend->ShutdownConnection();
@@ -187,12 +192,10 @@ ReplayStatus RemoteHost::Launch()
{
ReplayStatus status = ReplayStatus::Succeeded;
int WAIT_TIME = 2000;
if(IsADB())
if(m_protocol)
{
status = RENDERDOC_StartAndroidRemoteServer(m_hostname.c_str());
QThread::msleep(WAIT_TIME);
// this is blocking
status = m_protocol->StartRemoteServer(m_hostname);
return status;
}
@@ -205,7 +208,7 @@ ReplayStatus RemoteHost::Launch()
RDProcess process;
process.start(run);
process.waitForFinished(WAIT_TIME);
process.waitForFinished(2000);
process.detach();
return status;
+5 -6
View File
@@ -93,6 +93,9 @@ public:
)");
void SetLastCapturePath(const rdcstr &path);
DOCUMENT(
"The :class:`DeviceProtocolController` for this host, or ``None`` if no protocol is in use");
IDeviceProtocolController *Protocol() const { return m_protocol; }
DOCUMENT(R"(
Returns the name to display for this host in the UI, either :meth:`FriendlyName` if it is valid, or
:meth:`Hostname` if not.
@@ -102,12 +105,6 @@ Returns the name to display for this host in the UI, either :meth:`FriendlyName`
rdcstr friendlyName = FriendlyName();
return !friendlyName.isEmpty() ? friendlyName : m_hostname;
}
DOCUMENT("Returns ``True`` if this host represents a connected ADB (Android) device.");
bool IsADB() const
{
return m_hostname.count() > 4 && m_hostname[0] == 'a' && m_hostname[1] == 'd' &&
m_hostname[2] == 'b' && m_hostname[3] == ':';
}
DOCUMENT("Returns ``True`` if this host represents the special localhost device.");
bool IsLocalhost() const { return m_hostname == "localhost"; }
DOCUMENT("Returns ``True`` if this host represents a valid remote host.");
@@ -117,6 +114,8 @@ private:
// are created with it
rdcstr m_hostname;
IDeviceProtocolController *m_protocol = NULL;
// self-deleting shared and locked data store
RemoteHostData *m_data = NULL;
+2 -2
View File
@@ -307,9 +307,9 @@ void ReplayManager::CloseThread()
ReplayStatus ReplayManager::ConnectToRemoteServer(RemoteHost host)
{
ReplayStatus status = RENDERDOC_CreateRemoteServerConnection(host.Hostname().c_str(), 0, &m_Remote);
ReplayStatus status = RENDERDOC_CreateRemoteServerConnection(host.Hostname().c_str(), &m_Remote);
if(host.IsADB())
if(host.Protocol() && host.Protocol()->GetProtocolName() == "adb")
{
ANALYTIC_SET(UIFeatures.AndroidRemoteReplay, true);
}
@@ -143,7 +143,7 @@ void PersistantConfig::RemoveRemoteHost(RemoteHost host)
{
}
void PersistantConfig::AddAndroidHosts()
void PersistantConfig::UpdateEnumeratedProtocolDevices()
{
}
+23
View File
@@ -85,6 +85,29 @@
$result = SWIG_NewPointerObj($1, $descriptor(struct CaptureOptions*), SWIG_POINTER_OWN);
}
// same for RENDERDOC_GetSupportedDeviceProtocols
%typemap(in, numinputs=0) rdcarray<rdcstr> *supportedProtocols { $1 = new rdcarray<rdcstr>; }
%typemap(argout) rdcarray<rdcstr> *supportedProtocols {
$result = ConvertToPy(*$1);
delete $1;
}
%typemap(freearg) rdcarray<rdcstr> *supportedProtocols { }
// same for RENDERDOC_CreateRemoteServerConnection
%typemap(in, numinputs=0) IRemoteServer **rend (IRemoteServer *outRenderer) {
outRenderer = NULL;
$1 = &outRenderer;
}
%typemap(argout) IRemoteServer **rend {
PyObject *retVal = $result;
$result = PyTuple_New(2);
if($result)
{
PyTuple_SetItem($result, 0, retVal);
PyTuple_SetItem($result, 1, SWIG_NewPointerObj(SWIG_as_voidptr(outRenderer$argnum), SWIGTYPE_p_IRemoteServer, 0));
}
}
// ignore some operators SWIG doesn't have to worry about
%ignore SDType::operator=;
%ignore StructuredObjectList::swap;
-2
View File
@@ -474,8 +474,6 @@ int main(int argc, char *argv[])
config.Save();
}
RENDERDOC_AndroidShutdown();
PythonContext::GlobalShutdown();
Formatter::shutdown();
+4 -2
View File
@@ -678,7 +678,8 @@ void CaptureDialog::on_exePathBrowse_clicked()
{
SetExecutableFilename(filename);
if(m_Ctx.Replay().CurrentRemote().IsADB())
if(m_Ctx.Replay().CurrentRemote().Protocol() &&
m_Ctx.Replay().CurrentRemote().Protocol()->GetProtocolName() == "adb")
{
CheckAndroidSetup(filename);
}
@@ -1122,7 +1123,8 @@ void CaptureDialog::UpdateGlobalHook()
void CaptureDialog::UpdateRemoteHost()
{
if(m_Ctx.Replay().CurrentRemote().IsADB())
if(m_Ctx.Replay().CurrentRemote().Protocol() &&
m_Ctx.Replay().CurrentRemote().Protocol()->GetProtocolName() == "adb")
ui->cmdLineLabel->setText(tr("Intent Arguments"));
else
ui->cmdLineLabel->setText(tr("Command-line Arguments"));
+3 -5
View File
@@ -105,8 +105,6 @@ RemoteManager::RemoteManager(ICaptureContext &ctx, MainWindow *main)
vertical->addWidget(lookupsProgressFlow);
vertical->addWidget(ui->bottomLayout->parentWidget());
m_Ctx.Config().AddAndroidHosts();
for(RemoteHost h : m_Ctx.Config().GetRemoteHosts())
addHost(h);
@@ -446,9 +444,9 @@ void RemoteManager::on_hosts_itemSelectionChanged()
ui->addUpdateHost->setText(tr("Update"));
if(host.IsLocalhost() || host.IsADB())
if(host.IsLocalhost() || host.Protocol())
{
// localhost and android hosts cannot be updated or have their run command changed
// localhost and protocol-configured hosts cannot be updated or have their run command changed
ui->addUpdateHost->setEnabled(false);
ui->runCommand->setEnabled(false);
}
@@ -601,7 +599,7 @@ void RemoteManager::on_connect_clicked()
{
IRemoteServer *server = NULL;
ReplayStatus status =
RENDERDOC_CreateRemoteServerConnection(host.Hostname().c_str(), 0, &server);
RENDERDOC_CreateRemoteServerConnection(host.Hostname().c_str(), &server);
if(server)
server->ShutdownServerAndConnection();
setRemoteServerLive(node, false, false);
+83 -35
View File
@@ -160,11 +160,15 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai
m_RemoteProbeSemaphore.release();
m_RemoteProbe = new LambdaThread([this]() {
RENDERDOC_AndroidInitialise();
// fetch all device protocols to start them processing
rdcarray<rdcstr> protocols;
RENDERDOC_GetSupportedDeviceProtocols(&protocols);
for(const rdcstr &p : protocols)
RENDERDOC_GetDeviceProtocolController(p);
while(m_RemoteProbeSemaphore.available())
{
// do a remoteProbe immediately to populate the android hosts list on startup.
// do a remoteProbe immediately to populate the device list on startup.
remoteProbe();
// do several small sleeps so we can respond quicker when we need to shut down
@@ -510,7 +514,7 @@ void MainWindow::OnCaptureTrigger(const QString &exe, const QString &workingDir,
LambdaThread *th = new LambdaThread([this, exe, workingDir, cmdLine, env, opts, callback]() {
if(isCapturableAppRunningOnAndroid())
if(isUnshareableDeviceInUse())
{
RDDialog::warning(this, tr("RenderDoc is already capturing an app on this device"),
tr("A running app on this device is already being captured with RenderDoc. "
@@ -1583,25 +1587,10 @@ void MainWindow::remoteProbe()
{
if(!m_Ctx.IsCaptureLoaded() && !m_Ctx.IsCaptureLoading())
{
GUIInvoke::call(this, [this] {
m_Ctx.Config().AddAndroidHosts();
// update the latest list by copy. Note this lock only protects m_ProbeRemoteHosts, not the
// actual RemoteHosts list itself - that is only accessed on the UI thread so is not locked.
{
QMutexLocker lock(&m_ProbeRemoteHostsLock);
m_ProbeRemoteHosts.clear();
for(RemoteHost host : m_Ctx.Config().GetRemoteHosts())
m_ProbeRemoteHosts.push_back(host);
}
});
m_Ctx.Config().UpdateEnumeratedProtocolDevices();
// fetch the latest list
rdcarray<RemoteHost> hosts;
{
QMutexLocker lock(&m_ProbeRemoteHostsLock);
hosts = m_ProbeRemoteHosts;
}
rdcarray<RemoteHost> hosts = m_Ctx.Config().GetRemoteHosts();
for(RemoteHost &host : hosts)
{
@@ -1832,23 +1821,64 @@ void MainWindow::setRemoteHost(int hostIdx)
RemoteHost host = h;
host.CheckStatus();
if(host.IsADB() && !RENDERDOC_IsAndroidSupported(host.Hostname().c_str()))
if(host.Protocol() && !host.Protocol()->IsSupported(host.Hostname()))
{
// check to see if we should warn the user about this unsupported android version.
GUIInvoke::call(this, [this]() {
GUIInvoke::call(this, [this, host]() {
QDateTime today = QDateTime::currentDateTimeUtc();
QDateTime compare = today.addDays(-21);
if(compare > m_Ctx.Config().UnsupportedAndroid_LastUpdate)
if(host.Protocol()->GetProtocolName() == "adb")
{
if(compare > m_Ctx.Config().UnsupportedAndroid_LastUpdate)
{
RDDialog::critical(
this, tr("Unsupported Device Android Version"),
tr("This device is older than Android 6.0, the minimum required version for "
"RenderDoc.\n\nThis may break or cause unknown problems - use at your own "
"risk."));
}
m_Ctx.Config().UnsupportedAndroid_LastUpdate = today;
}
else
{
RDDialog::critical(
this, tr("Unsupported Device Android Version"),
tr("This device is older than Android 6.0, the minimum required version for "
"RenderDoc.\n\nThis may break or cause unknown problems - use at your own "
"risk."));
this, tr("Unsupported Device"),
tr("This device is not able to support RenderDoc. Please consult the documentation "
"for this type of device to see what the problem may be."));
}
});
}
m_Ctx.Config().UnsupportedAndroid_LastUpdate = today;
if(host.Protocol() && host.IsVersionMismatch())
{
GUIInvoke::blockcall(this, [this, &host]() {
QMessageBox::StandardButton res =
RDDialog::question(this, tr("Unsupported version"),
tr("Remote server on %1 has an incompatible version.\n"
"Would you like to try to reinstall the version %2?")
.arg(host.Name())
.arg(lit(FULL_VERSION_STRING)),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if(res == QMessageBox::Yes)
{
LambdaThread *launchthread = new LambdaThread([this, &host]() {
// since we have a protocol, try to force-launch which should attempt to reinstall.
host.Launch();
// update status
host.CheckStatus();
});
launchthread->start();
ShowProgressDialog(this, tr("Attempting to update remote server, please wait..."),
[launchthread]() { return !launchthread->isRunning(); });
launchthread->deleteLater();
}
});
}
@@ -2539,11 +2569,25 @@ void MainWindow::on_action_Manage_Extensions_triggered()
void MainWindow::on_action_Manage_Remote_Servers_triggered()
{
RemoteManager *rm = new RemoteManager(m_Ctx, this);
RDDialog::show(rm);
// now that we're done with it, the manager deletes itself when all lookups terminate (or
// immediately if there are no lookups ongoing).
rm->closeWhenFinished();
LambdaThread *th = new LambdaThread([this]() {
m_Ctx.Config().UpdateEnumeratedProtocolDevices();
GUIInvoke::call(this, [this]() {
RemoteManager *rm = new RemoteManager(m_Ctx, this);
RDDialog::show(rm);
// now that we're done with it, the manager deletes itself when all lookups terminate (or
// immediately if there are no lookups ongoing).
rm->closeWhenFinished();
});
});
th->start();
th->wait(500);
if(th->isRunning())
{
ShowProgressDialog(this, tr("Updating available devices, please wait..."),
[th]() { return !th->isRunning(); });
}
th->deleteLater();
}
void MainWindow::on_action_Settings_triggered()
@@ -2895,12 +2939,16 @@ void MainWindow::showLaunchError(ReplayStatus status)
});
}
bool MainWindow::isCapturableAppRunningOnAndroid()
bool MainWindow::isUnshareableDeviceInUse()
{
if(!m_Ctx.Replay().CurrentRemote().IsADB())
if(!m_Ctx.Replay().CurrentRemote().Protocol())
return false;
rdcstr host = m_Ctx.Replay().CurrentRemote().Hostname();
if(m_Ctx.Replay().CurrentRemote().Protocol()->SupportsMultiplePrograms(host))
return false;
uint32_t ident = RENDERDOC_EnumerateRemoteTargets(host.c_str(), 0);
return ident != 0;
}
+1 -1
View File
@@ -256,5 +256,5 @@ private:
void showLaunchError(ReplayStatus status);
bool isCapturableAppRunningOnAndroid();
bool isUnshareableDeviceInUse();
};