Files
ppsspp/UI/UploadScreen.cpp
T
Henrik RydgårdandClaude Opus 5.5 19e4b77be4 Android: Ask for the local network permission for ad hoc, UPnP and servers
Part of #21585

Android 17 blocks LAN traffic in both directions (TCP and UDP, including
broadcast and multicast) without ACCESS_LOCAL_NETWORK. Ask for it when
networking, the built-in ad hoc server, UPnP, share-on-startup or the
remote debugger gets turned on, when a game initializes ad hoc with
networking enabled, and at startup if a server is set to start then.

A denied ad hoc send fails with EPERM, which the games never see, so show
a message for it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:05 +02:00

114 lines
3.9 KiB
C++

#include <cmath>
#include "Common/Net/Resolve.h"
#include "Common/System/Request.h"
#include "Common/File/DiskFree.h"
#include "Common/StringUtils.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/Data/Text/I18n.h"
#include "Common/UI/Notice.h"
#include "Core/WebServer.h"
#include "Core/Util/PathUtil.h"
#include "UI/UploadScreen.h"
#include "UI/MiscViews.h"
UploadScreen::UploadScreen(const Path &targetFolder) : UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::Default), targetFolder_(targetFolder) {
std::vector<std::string> ips;
net::GetLocalIP4List(ips);
localIPs_.clear();
for (const auto &ip : ips) {
if (!ip.empty() && !startsWith(ip, "127.") && !startsWith(ip, "169.254.")) {
localIPs_.push_back(ip);
}
}
System_RequestLocalNetworkPermission();
prevPermission_ = System_GetPermissionStatus(SYSTEM_PERMISSION_LOCAL_NETWORK);
StartServerIfPermitted();
}
UploadScreen::~UploadScreen() {
if (started_) {
StopWebServer(WebServerFlags::FILE_UPLOAD);
}
}
void UploadScreen::StartServerIfPermitted() {
if (started_ || System_GetPermissionStatus(SYSTEM_PERMISSION_LOCAL_NETWORK) != PERMISSION_STATUS_GRANTED) {
return;
}
WebServerSetUploadPath(targetFolder_);
StartWebServer(WebServerFlags::FILE_UPLOAD);
started_ = true;
}
void UploadScreen::CreateDialogViews(UI::ViewGroup *root) {
using namespace UI;
auto n = GetI18NCategory(I18NCat::NETWORKING);
LinearLayout *container = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(500, FILL_PARENT, 0.0f, UI::Gravity::G_HCENTER, Margins(10)));
root->Add(container);
container->Add(new TextWithImage(ImageID("I_FOLDER_UPLOAD"), GetFriendlyPath(targetFolder_)));
container->Add(new Spacer(20.0f));
if (prevPermission_ == PERMISSION_STATUS_DENIED) {
container->Add(new NoticeView(NoticeLevel::WARN, n->T("Local network permission is needed to receive files"), ""))->SetWrapText(true);
container->Add(new Choice(n->T("Ask for network permission")))->OnClick.Add([](UI::EventParams &e) {
System_AskForPermission(SYSTEM_PERMISSION_LOCAL_NETWORK);
});
} else if (prevRunning_) {
container->Add(new TextWithImage(ImageID("I_WIFI"), n->T("With a web browser on the same network, go to:")));
for (const auto &ip : localIPs_) {
std::string url = StringFromFormat("%s:%d/upload", ip.c_str(), WebServerPort());
container->Add(new CopyableText(ImageID("I_WEB_BROWSER"), url));
}
}
statusContainer_ = new LinearLayout(ORIENT_VERTICAL);
container->Add(statusContainer_);
}
void UploadScreen::RecreateStatus() {
if (!statusContainer_) {
return;
}
using namespace UI;
statusContainer_->Clear();
// Show information about current upload streams (there can be multiple, but normally it's just one
// where files are uploaded sequentially).
std::vector<UploadProgress> uploads = GetUploadsInProgress();
for (const auto &upload : uploads) {
int percent = upload.totalBytes == 0 ? 0 : (int)(ceil(100.0 * (double)upload.uploadedBytes / (double)upload.totalBytes));
std::string uploadText = StringFromFormat("%s: %s/%s (%d%%)", upload.filename.c_str(),
NiceSizeFormat(upload.uploadedBytes).c_str(), NiceSizeFormat(upload.totalBytes).c_str(), percent);
statusContainer_->Add(new TextWithImage(ImageID("I_FILE"), uploadText));
}
}
std::string_view UploadScreen::GetTitle() const {
auto n = GetI18NCategory(I18NCat::NETWORKING);
return n->T("Upload files");
}
void UploadScreen::update() {
UISimpleBaseDialogScreen::update();
PermissionStatus permission = System_GetPermissionStatus(SYSTEM_PERMISSION_LOCAL_NETWORK);
if (permission != prevPermission_) {
prevPermission_ = permission;
StartServerIfPermitted();
RecreateViews();
}
bool running = WebServerRunning(WebServerFlags::FILE_UPLOAD);
if (prevRunning_ != running) {
prevRunning_ = running;
RecreateViews();
}
// Update in-progress uploads approximately every quarter of a second.
if (lastUpdate_.ElapsedSeconds() > 0.25) {
RecreateStatus();
lastUpdate_ = Instant::Now();
}
}