From c58f4815b07b7e9680111e6adcde1423ab2a2780 Mon Sep 17 00:00:00 2001 From: Chris Koying Browet Date: Fri, 23 Sep 2016 17:43:41 +0200 Subject: [PATCH] ADD: "storage" vfs Abstract storage for easier redirection when the underlying path changes Handles Droid removable storage for now. --- XBMC.files | 4 + .../packaging/xbmc/AndroidManifest.xml.in | 25 ++--- xbmc/android/activity/XBMCApp.cpp | 99 +++++++++++++++++++ xbmc/android/activity/XBMCApp.h | 7 ++ xbmc/filesystem/DirectoryFactory.cpp | 3 + xbmc/filesystem/FileFactory.cpp | 5 +- xbmc/filesystem/Makefile.in | 2 + xbmc/filesystem/StorageDirectory.cpp | 90 +++++++++++++++++ xbmc/filesystem/StorageDirectory.h | 42 ++++++++ xbmc/filesystem/StorageFile.cpp | 38 +++++++ xbmc/filesystem/StorageFile.h | 35 +++++++ .../android/AndroidStorageProvider.cpp | 76 ++------------ 12 files changed, 347 insertions(+), 79 deletions(-) create mode 100644 xbmc/filesystem/StorageDirectory.cpp create mode 100644 xbmc/filesystem/StorageDirectory.h create mode 100644 xbmc/filesystem/StorageFile.cpp create mode 100644 xbmc/filesystem/StorageFile.h diff --git a/XBMC.files b/XBMC.files index fbf8398b5a..d1133bf282 100644 --- a/XBMC.files +++ b/XBMC.files @@ -9889,3 +9889,7 @@ lib/libmpeg2/Makefile.am lib/libmpeg2/configure.ac lib/libUPnP/Makefile.in lib/UnrarXLib/Makefile +xbmc/filesystem/StorageDirectory.h +xbmc/filesystem/StorageDirectory.cpp +xbmc/filesystem/StorageFile.h +xbmc/filesystem/StorageFile.cpp diff --git a/tools/android/packaging/xbmc/AndroidManifest.xml.in b/tools/android/packaging/xbmc/AndroidManifest.xml.in index ca75f62ab6..25e5d13df0 100644 --- a/tools/android/packaging/xbmc/AndroidManifest.xml.in +++ b/tools/android/packaging/xbmc/AndroidManifest.xml.in @@ -24,11 +24,11 @@ - + + android:logo="@drawable/banner" + android:label="@string/app_name" + android:hasCode="true"> + + + @@ -112,15 +115,15 @@ android:name=".XBMCMediaContentProvider" android:authorities="@APP_PACKAGE@.media" android:exported="true" /> - + - - - - + + + - - + + diff --git a/xbmc/android/activity/XBMCApp.cpp b/xbmc/android/activity/XBMCApp.cpp index 79899bcf32..703ed11341 100644 --- a/xbmc/android/activity/XBMCApp.cpp +++ b/xbmc/android/activity/XBMCApp.cpp @@ -84,6 +84,8 @@ #include "android/jni/SystemProperties.h" #include "android/jni/Display.h" #include "android/jni/View.h" +#include "android/jni/StorageManager.h" + #include "AndroidKey.h" #include "CompileInfo.h" @@ -114,6 +116,7 @@ bool CXBMCApp::m_hasFocus = false; bool CXBMCApp::m_isResumed = false; bool CXBMCApp::m_hasAudioFocus = false; bool CXBMCApp::m_headsetPlugged = false; +CCriticalSection CXBMCApp::m_apiMutex; CCriticalSection CXBMCApp::m_applicationsMutex; std::vector CXBMCApp::m_applications; std::vector CXBMCApp::m_activityResultEvents; @@ -121,6 +124,7 @@ uint64_t CXBMCApp::m_vsynctime = 0; CEvent CXBMCApp::m_vsyncEvent; std::vector CXBMCApp::m_texturePool; CJNIAudioDeviceInfos CXBMCApp::m_audiodevices; +VECSOURCES CXBMCApp::m_removableDrives; void LogAudoDevices(const char* stage, const CJNIAudioDeviceInfos& devices) { @@ -932,6 +936,97 @@ bool CXBMCApp::GetStorageUsage(const std::string &path, std::string &usage) return true; } +void CXBMCApp::InvalidateRemovableDrives() +{ + CSingleLock lock(m_apiMutex); + m_removableDrives.clear(); +} + +bool CXBMCApp::GetRemovableDrives(VECSOURCES& removableDrives) +{ + CSingleLock lock(m_apiMutex); + + // Uses non-public API: be extra carefull + bool inError = false; + + if (m_removableDrives.empty()) + { + CJNIStorageManager manager(CJNIContext::getSystemService("storage")); + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + inError = true; + } + + if (!inError) + { + CJNIStorageVolumes vols = manager.getVolumeList(); + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + inError = true; + } + + if (!inError) + { + for (auto vol : vols) + { + // CLog::Log(LOGDEBUG, "-- Volume: %s(%s) -- %s", vol.getPath().c_str(), vol.getUserLabel().c_str(), vol.getState().c_str()); + bool removable = vol.isRemovable(); + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + inError = true; + break; + } + std::string state = vol.getState(); + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + inError = true; + break; + } + + if (removable && state == CJNIEnvironment::MEDIA_MOUNTED) + { + CMediaSource share; + share.strPath = vol.getPath(); + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + inError = true; + break; + } + share.strDiskUniqueId = vol.getUuid(); + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + inError = true; + break; + } + share.strName = vol.getUserLabel(); + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + inError = true; + break; + } + StringUtils::Trim(share.strName); + if (share.strName.empty() || share.strName == "?" || StringUtils::CompareNoCase(share.strName, "null") == 0) + share.strName = URIUtils::GetFileName(share.strPath); + share.m_ignore = true; + m_removableDrives.push_back(share); + } + } + } + } + } + if (!inError) + removableDrives.insert(removableDrives.end(), m_removableDrives.begin(), m_removableDrives.end()); + + return !inError; +} + // Used in Application.cpp to figure out volume steps int CXBMCApp::GetMaxSystemVolume() { @@ -1073,6 +1168,10 @@ void CXBMCApp::onNewIntent(CJNIIntent intent) CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ACTIVATE_WINDOW, WINDOW_MUSIC_NAV, 0, nullptr, "", params); } } + else if (action == "android.intent.action.MEDIA_MOUNTED" || action == "android.intent.action.MEDIA_REMOVED") + { + InvalidateRemovableDrives(); + } } void CXBMCApp::onActivityResult(int requestCode, int resultCode, CJNIIntent resultData) diff --git a/xbmc/android/activity/XBMCApp.h b/xbmc/android/activity/XBMCApp.h index dc6156ba5e..e8e82ab4ea 100644 --- a/xbmc/android/activity/XBMCApp.h +++ b/xbmc/android/activity/XBMCApp.h @@ -31,6 +31,7 @@ #include "IActivityHandler.h" #include "IInputHandler.h" +#include "storage/IStorageProvider.h" #include "xbmc.h" #include "android/jni/Activity.h" @@ -153,6 +154,9 @@ class CXBMCApp */ static bool GetExternalStorage(std::string &path, const std::string &type = ""); static bool GetStorageUsage(const std::string &path, std::string &usage); + static void InvalidateRemovableDrives(); + static bool GetRemovableDrives(VECSOURCES& removableDrives); + static int GetMaxSystemVolume(); static float GetSystemVolume(); static void SetSystemVolume(float percent); @@ -208,10 +212,13 @@ class CXBMCApp bool m_firstrun; bool m_exiting; pthread_t m_thread; + static CCriticalSection m_apiMutex; static CCriticalSection m_applicationsMutex; static std::vector m_applications; static std::vector m_activityResultEvents; + static VECSOURCES m_removableDrives; + static ANativeWindow* m_window; static CEvent m_windowCreated; static std::vector m_texturePool; diff --git a/xbmc/filesystem/DirectoryFactory.cpp b/xbmc/filesystem/DirectoryFactory.cpp index 0bf1ce88cd..669eb126c7 100644 --- a/xbmc/filesystem/DirectoryFactory.cpp +++ b/xbmc/filesystem/DirectoryFactory.cpp @@ -42,6 +42,8 @@ #include "HTTPDirectory.h" #include "DAVDirectory.h" #include "UDFDirectory.h" +#include "StorageDirectory.h" + #include "Application.h" #include "utils/log.h" #include "network/WakeOnAccess.h" @@ -130,6 +132,7 @@ IDirectory* CDirectoryFactory::Create(const CURL& url) #error Local directory access is not implemented for this platform #endif if (url.IsProtocol("special")) return new CSpecialProtocolDirectory(); + if (url.IsProtocol("storage")) return new CStorageDirectory(); if (url.IsProtocol("sources")) return new CSourcesDirectory(); if (url.IsProtocol("addons")) return new CAddonsDirectory(); #if defined(HAS_FILESYSTEM_CDDA) && defined(HAS_DVD_DRIVE) diff --git a/xbmc/filesystem/FileFactory.cpp b/xbmc/filesystem/FileFactory.cpp index 820db0367f..02df0a7ce3 100644 --- a/xbmc/filesystem/FileFactory.cpp +++ b/xbmc/filesystem/FileFactory.cpp @@ -85,6 +85,8 @@ #include "UDFFile.h" #include "ImageFile.h" #include "ResourceFile.h" +#include "StorageFile.h" + #include "Application.h" #include "URL.h" #include "utils/log.h" @@ -127,13 +129,14 @@ IFile* CFileFactory::CreateLoader(const CURL& url) else if (url.IsProtocol("musicdb")) return new CMusicDatabaseFile(); else if (url.IsProtocol("videodb")) return new CVideoDatabaseFile(); else if (url.IsProtocol("special")) return new CSpecialProtocolFile(); + else if (url.IsProtocol("storage")) return new CStorageFile(); else if (url.IsProtocol("multipath")) return new CMultiPathFile(); else if (url.IsProtocol("image")) return new CImageFile(); #ifdef TARGET_POSIX else if (url.IsProtocol("file") || url.GetProtocol().empty()) return new CPosixFile(); #elif defined(TARGET_WINDOWS) else if (url.IsProtocol("file") || url.GetProtocol().empty()) return new CWin32File(); -#endif // TARGET_WINDOWS +#endif // TARGET_WINDOWS else if (url.IsProtocol("filereader")) return new CFileReaderFile(); #if defined(HAS_FILESYSTEM_CDDA) && defined(HAS_DVD_DRIVE) else if (url.IsProtocol("cdda")) return new CFileCDDA(); diff --git a/xbmc/filesystem/Makefile.in b/xbmc/filesystem/Makefile.in index f9811d304b..9459f37c7f 100644 --- a/xbmc/filesystem/Makefile.in +++ b/xbmc/filesystem/Makefile.in @@ -77,6 +77,8 @@ SRCS += ZeroconfDirectory.cpp SRCS += ZipDirectory.cpp SRCS += ZipFile.cpp SRCS += ZipManager.cpp +SRCS += StorageDirectory.cpp +SRCS += StorageFile.cpp ifeq (@USE_ANDROID@,1) SRCS += APKDirectory.cpp diff --git a/xbmc/filesystem/StorageDirectory.cpp b/xbmc/filesystem/StorageDirectory.cpp new file mode 100644 index 0000000000..1e05acf264 --- /dev/null +++ b/xbmc/filesystem/StorageDirectory.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 Christian Browet + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "StorageDirectory.h" + +#include "Directory.h" +#include "FileItem.h" +#include "URL.h" + +#include "utils/URIUtils.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#ifdef TARGET_ANDROID +#include "android/activity/XBMCApp.h" +#endif + +using namespace XFILE; + +CStorageDirectory::CStorageDirectory() +{ +} + +CStorageDirectory::~CStorageDirectory() +{ +} + +bool CStorageDirectory::GetDirectory(const CURL& url, CFileItemList &items) +{ + const std::string pathToUrl(url.Get()); + std::string translatedPath = TranslatePath(url); + if (CDirectory::GetDirectory(translatedPath, items, m_strFileMask, m_flags | DIR_FLAG_GET_HIDDEN)) + { // replace our paths as necessary + items.SetURL(url); + for (int i = 0; i < items.Size(); i++) + { + CFileItemPtr item = items[i]; + if (StringUtils::StartsWith(item->GetPath(), translatedPath)) + item->SetPath(URIUtils::AddFileToFolder(pathToUrl, item->GetPath().substr(translatedPath.size()))); + } + return true; + } + return false; +} + +std::string CStorageDirectory::TranslatePath(const CURL &url) +{ + return TranslatePathImpl(url); +} + +std::string CStorageDirectory::TranslatePathImpl(const CURL& url) +{ + std::string returl; + std::string uuid = url.GetHostName(); + +#ifdef TARGET_ANDROID + VECSOURCES drives; + if (CXBMCApp::GetRemovableDrives(drives)) + { + for (auto d : drives) + { + if (d.strDiskUniqueId == uuid) + { + returl = URIUtils::AddFileToFolder(d.strPath, url.GetFileName()); +// CLog::Log(LOGDEBUG, "CStorageDirectory: translated %s to %s", url.Get().c_str(), returl.c_str()); + } + } + } +#endif + + return returl; +} + diff --git a/xbmc/filesystem/StorageDirectory.h b/xbmc/filesystem/StorageDirectory.h new file mode 100644 index 0000000000..30a55eb598 --- /dev/null +++ b/xbmc/filesystem/StorageDirectory.h @@ -0,0 +1,42 @@ +#pragma once +/* + * Copyright (C) 2016 Christian Browet + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "filesystem/OverrideDirectory.h" + +namespace XFILE +{ + +class CStorageDirectory : public COverrideDirectory +{ + friend class CStorageFile; + +public: + CStorageDirectory(); + virtual ~CStorageDirectory(void); + + virtual bool GetDirectory(const CURL& url, CFileItemList& items) override; + +protected: + std::string TranslatePath(const CURL& url); + static std::string TranslatePathImpl(const CURL& url); +}; + +} diff --git a/xbmc/filesystem/StorageFile.cpp b/xbmc/filesystem/StorageFile.cpp new file mode 100644 index 0000000000..0baa485e54 --- /dev/null +++ b/xbmc/filesystem/StorageFile.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 Christian Browet + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "StorageFile.h" +#include "URL.h" + +#include "StorageDirectory.h" + +using namespace XFILE; + +CStorageFile::CStorageFile(void) + : COverrideFile(true) +{ } + +CStorageFile::~CStorageFile(void) +{ } + +std::string CStorageFile::TranslatePath(const CURL& url) +{ + return CStorageDirectory::TranslatePathImpl(url); +} diff --git a/xbmc/filesystem/StorageFile.h b/xbmc/filesystem/StorageFile.h new file mode 100644 index 0000000000..49fd272d69 --- /dev/null +++ b/xbmc/filesystem/StorageFile.h @@ -0,0 +1,35 @@ +#pragma once +/* + * Copyright (C) 2016 Christian Browet + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "filesystem/OverrideFile.h" + +namespace XFILE +{ +class CStorageFile : public COverrideFile +{ +public: + CStorageFile(void); + virtual ~CStorageFile(void); + +protected: + virtual std::string TranslatePath(const CURL& url); +}; +} diff --git a/xbmc/storage/android/AndroidStorageProvider.cpp b/xbmc/storage/android/AndroidStorageProvider.cpp index 3465d7bb67..34ee1a8efa 100644 --- a/xbmc/storage/android/AndroidStorageProvider.cpp +++ b/xbmc/storage/android/AndroidStorageProvider.cpp @@ -35,10 +35,6 @@ #include "utils/StringUtils.h" #include "utils/URIUtils.h" -#include "android/jni/Context.h" -#include "android/jni/StorageManager.h" -#include "android/jni/Environment.h" - static const char * typeWL[] = { "vfat", "exfat", "sdcardfs", "fuse", "ntfs", "fat32", "ext3", "ext4", "esdfs" }; static const char * mountWL[] = { "/mnt", "/Removable", "/storage" }; static const char * mountBL[] = { @@ -123,75 +119,21 @@ void CAndroidStorageProvider::GetLocalDrives(VECSOURCES &localDrives) void CAndroidStorageProvider::GetRemovableDrives(VECSOURCES &removableDrives) { - // Uses non-public API: be extra carefull - bool inError = false; - VECSOURCES droidDrives; - - CJNIStorageManager manager(CJNIContext::getSystemService("storage")); - if (xbmc_jnienv()->ExceptionCheck()) - { - xbmc_jnienv()->ExceptionClear(); - inError = true; - } - if (!inError) + VECSOURCES droidDrives; + if (CXBMCApp::GetRemovableDrives(droidDrives)) { - CJNIStorageVolumes vols = manager.getVolumeList(); - if (xbmc_jnienv()->ExceptionCheck()) + for (auto d : droidDrives) { - xbmc_jnienv()->ExceptionClear(); - inError = true; - } - - if (!inError) - { - for (auto vol : vols) + auto r = d; + if (!r.strDiskUniqueId.empty()) { -// CLog::Log(LOGDEBUG, "-- Volume: %s(%s) -- %s", vol.getPath().c_str(), vol.getUserLabel().c_str(), vol.getState().c_str()); - bool removable = vol.isRemovable(); - if (xbmc_jnienv()->ExceptionCheck()) - { - xbmc_jnienv()->ExceptionClear(); - inError = true; - break; - } - std::string state = vol.getState(); - if (xbmc_jnienv()->ExceptionCheck()) - { - xbmc_jnienv()->ExceptionClear(); - inError = true; - break; - } - - if (removable && state == CJNIEnvironment::MEDIA_MOUNTED) - { - CMediaSource share; - share.strPath = vol.getPath(); - if (xbmc_jnienv()->ExceptionCheck()) - { - xbmc_jnienv()->ExceptionClear(); - inError = true; - break; - } - share.strName = vol.getUserLabel(); - if (xbmc_jnienv()->ExceptionCheck()) - { - xbmc_jnienv()->ExceptionClear(); - inError = true; - break; - } - StringUtils::Trim(share.strName); - if (share.strName.empty() || share.strName == "?" || StringUtils::CompareNoCase(share.strName, "null") == 0) - share.strName = URIUtils::GetFileName(share.strPath); - share.m_ignore = true; - droidDrives.push_back(share); - } + CLog::Log(LOGDEBUG, "CAndroidStorageProvider: translated %s to %s", r.strPath.c_str(), ("storage://" + r.strDiskUniqueId).c_str()); + r.strPath = "storage://" + r.strDiskUniqueId; } + removableDrives.push_back(r); } } - - if (!inError) - removableDrives.insert(removableDrives.end(), droidDrives.begin(), droidDrives.end()); else { for (const auto& mountStr : GetRemovableDrivesLinux()) @@ -212,7 +154,7 @@ void CAndroidStorageProvider::GetRemovableDrives(VECSOURCES &removableDrives) std::set CAndroidStorageProvider::GetRemovableDrivesLinux() { std::set result; - + // mounted usb disks char* buf = NULL; FILE* pipe;