Speckle Unity Problems in Android

Hi there,

I know there are already quite a few discussions about building Unity Android apps with Speckle send/receive functionality but since all these discussions suggest the same solution and this solution doesn’t work I wanted to start a new discussion based on my observations so far.

We want to include Speckle in a Mixed Reality-App for Meta Quest 3 and other Android-based platforms. At the moment we don’t use the standard Unity Connector components but custom made components which basically use the Operations.Receive() method as in the ManualReceive class (from the Speckle Unity sample project) and suggested by you in previous discussions like here.
There you made the correct assumption that the error is caused by the SQLite Transport which is used by default when no other type of Transport is provided for the localTransport property. Providing a MemoryTransport object does resolve this issue but another IOException is thrown now. Using adb logcat allows us to see the exception and some of its stack trace:

IOException: Read-only file system at System.IO.FileSystem.CreateDirectory (System.String fullPath) [0x00000] in <00000000000000000000000000000000>:0 at System.IO.Directory.CreateDirectory (System.String path) [0x00000] in <00000000000000000000000000000000>:0 at Speckle.Core.Helpers.SpecklePathProvider.EnsureFolderExists (System.String basePath, System.String folderName) [0x00008] in C:\Users\circleci\project\Core\Core\Helpers\Path.cs:155 at Speckle.Core.Helpers.SpecklePathProvider.get_UserSpeckleFolderPath () [0x0000a] in C:\Users\circleci\project\Core\Core\Helpers\Path.cs:45 at Speckle.Core.Helpers.SpecklePathProvider.LogFolderPath (System.String hostApplicationName, System.String hostApplicationVersion) [0x00000] in C:\Users\circleci\project\Core\Core\Helpers\Path.cs:166 at Speckle.Core.Logging.SpeckleLog.CreateConfiguredLogger (System.String hostApplicationName, System.String hostApplicationVersion, Speckle.Core.Logging.SpeckleLogConfiguration logConfiguration) [0x00011] in C:\Users\circ

As you can see here, the Stack Trace is incomplete and the file paths shown are garbage (“C:\Users\circleci\project\Core\Core\Helpers\Path.cs” ← this is not a valid path. Neither on the used Android device, nor on the Windows computer where the Application was built on).

But whats interesting here, is that you can see, that the SpeckleLog class seems to cause the exception. It tries to write all logs to a log file, which obviously isn’t allowed on Android. I don’t know where it tries to write the log file to since you dynamically try to build this path based on the platform. Logging to a file is by default enabled in the SpeckleLogConfiguration, but I can’t find where you create the SpeckleLog and set it’s config.

Hi @vsx-sieber ,

Thank you for the very thorough investigation.

You’re correct, by default, Speckle.Core will write logs to file.
You can disable this by calling the Setup.Init function with a custom SpeckleLogConfiguration, like so:

var logConfig= new SpeckleLogConfiguration(logToFile: false)
Setup.Initialize(HostApplications.Unity.Slug, "", logConfig)

You could place this in the Awake function of one of your components, but as long as it gets called before you start using any Speckle functions, then this hopefully should work for you.

1 Like

The CircleCi paths you’re seeing in the stacktrace, likely have nothing to do with any paths core is trying to use at runtime. They are simply the paths of the cs files of the Core.csproj before they were built on our CI system.
(I imagine you’re consuming our Core as a built SpeckleCore.dll)

Hi @Jedd ,

thanks for your quick response!

Unfortunately this doesn’t work. Speckle still tries to log to a file.

Are you sure the Setup class is the right one? It’s description says:

Anonymous telemetry to help us understand how to make a better Speckle. This really helps us to deliver a better open source project and product!

Doesn’t sound like it is related to local logging.

Is there any information in your docs about this subject? I wasn’t able to find anything related to controlling the Logging and Analytics behavior of speckle-sharp.

Thanks again for your help. It is much appreciated!

Sven

Ok, I checked the source code of the Setup class and in theory you should be right that it is used to initialize the SpeckleLog class. But as mentioned before, it just doesn’t work.

Since the “Setup was already initialized with {currentHostApp} {currentVersionedHostApp}” log is not triggered, I can be sure that my call of the Init method is done before any other initialization happens.

Ok I further checked the source of speckle-sharp and found the problem.

In the SpeckleLog.Initialize method you call the CreateConfiguredLogger method. The first thing this method does (even before checking whether or not you want to enable file logging) is this:

s_logFolderPath = SpecklePathProvider.LogFolderPath(hostApplicationName, hostApplicationVersion);

In your call of SpecklePathProvider.LogFolderPath you do:

return EnsureFolderExists(
  EnsureFolderExists(UserSpeckleFolderPath, LOG_FOLDER_NAME),
  $"{hostApplicationName}{hostApplicationVersion ?? ""}"
);

And this tries to create a folder:

private static string EnsureFolderExists(string basePath, string folderName)
{
  var path = System.IO.Path.Combine(basePath, folderName);
  Directory.CreateDirectory(path);
  return path;
}

Which then triggers the IOException that does not get catched or handled.

We aren’t directly supporting Android with Speckle, but by implication, we should allow Unity applications to perform under more runtimes.

I’m unfamiliar with the Android app security sandbox - is there no equivalent to a /tmp for a Unity app under Android? If not, then we need to review Speckle.Core can be amended to operate with no disk access at all. I think, currently, it does assume somewhere to cache things.

Sure, but as mentioned before, the problem is not a Android-specific one, but rather caused by your SpeckleLog implementation neither properly checking if logToFile is set to true before trying to create a log file folder, nor handling any exceptions caused by missing write access.
You even have a local variable called canLogToFile, but it’s just always set to true at the moment.

You can write to the system in a app-specific directory. You can get the path to this directory in Unity by calling Application.persistentDataPath.
We actually used it for the blobStoragePath you can provide in the constructor of the ServerTransport class, but the SpeckleLogConfiguration does not allow to provide such a path.

However, I just saw that your SpecklePathProvider class has a OverrideApplicationDataPath method. When I use it like this:

SpecklePathProvider.OverrideApplicationDataPath(Application.persistentDataPath);

all files and folders are written to the proper directory and no IOException is triggered anymore!

Unfortunately another - likely more severe - Exception is now triggered:

DllNotFoundException: MonoPosixHelper assembly: type: member:(null)
at (wrapper managed-to-native) System.IO.Compression.DeflateStreamNative.CreateZStream(System.IO.Compression.CompressionMode,bool,System.IO.Compression.DeflateStreamNative/UnmanagedReadOrWrite,intptr)
at System.IO.Compression.DeflateStreamNative.Create (System.IO.Stream compressedStream, System.IO.Compression.CompressionMode mode, System.Boolean gzip) [0x0004a] in <5fe808a9ce234d7cb9b1a4e14f988a80>:0
at System.IO.Compression.DeflateStream…ctor (System.IO.Stream compressedStream, System.IO.Compression.CompressionMode mode, System.Boolean leaveOpen, System.Boolean gzip) [0x0002d] in <5fe808a9ce234d7cb9b1a4e14f988a80>:0
at System.IO.Compression.DeflateStream…ctor (System.IO.Stream stream, System.IO.Compression.CompressionMode mode, System.Boolean leaveOpen, System.Int32 windowsBits) [0x00000] in <5fe808a9ce234d7cb9b1a4e14f988a80>:0
at (wrapper remoting-invoke-with-check) System.IO.Compression.Defla

I will further check what options we have to resolve all the issues. I still hope, we can manage to get it to run on Android too, even though your support does not include Android.