Marmalade Core Storage (s3eSecureStoragePut & s3eSecureStorageGet) not persisting

73 views Asked by At

I have a minimal app made using Marmalade Core. It creates a webview then sends data to be stored and retrieved using s3eSecureStoragePut & s3eSecureStorageGet.

This works fine until I close the app and reopen it. After it loads up again and I try to load the data it returns as blank. Does anyone have any ideas why this might be happening? It is built/deployed using GCC ARM Debug for Android

Here is my c++

#include "s3e.h"
#include "s3eDevice.h"
#include "IwDebug.h"
#include "s3eWebView.h"
#include "IwGx.h"
#include "Iw2D.h"
#include <string>
#include <sstream>

s3eWebView* webView;
bool pageLoaded;
const char* rom = "rom://index.html";

struct SStoreData {
    std::string key;
};

static int loadedCallback(s3eWebView* instance, void* sysData, void* userData) {
    pageLoaded = true;
    return 1;
}

static int javaScriptCallback(s3eWebView* instance, void* systemData, void* userData) {
//cast the data to string for easier handling
std::string data = (char*) systemData;
char jsFunc[128];
//check if page has loaded to prevent errors
if (pageLoaded) {
    if (!std::strncmp(data.c_str(), "saveKey", std::strlen("saveKey")))  {
        std::string key = data.substr(data.find("|") + 1, data.length());
        SStoreData *toSave = new SStoreData;
        toSave->key = key;
        s3eSecureStoragePut (toSave, sizeof (*toSave));
    } else if ("loadKey" == data) {
        SStoreData *toLoad = new SStoreData;
        s3eSecureStorageGet (toLoad,sizeof(*toLoad));
        sprintf(jsFunc, "dataLoaded(\"%s\", \"key\");", toLoad->key.c_str());
        s3eWebViewSendJavaScript(webView, jsFunc);
    } else {

        IwTrace(JS_CALLBACK, ("DID NOT RECEIVE ANYTHING"));
    }
}
return 1;
}


void init() {
//init vars
webView = s3eWebViewCreate(false);
pageLoaded = false;

//register callbacks
s3eWebViewRegister(S3E_WEBVIEW_FINISHED_LOADING, loadedCallback, NULL, webView);
s3eWebViewRegister(S3E_WEBVIEW_FROM_JAVASCRIPT, javaScriptCallback, NULL, webView);

//navigate to the webpage
s3eWebViewNavigate(webView, rom);
//display the webview
s3eWebViewShow(webView, 0, 0, IwGxGetDisplayWidth(), IwGxGetDisplayHeight());
}

void destroy() {
if (webView) {
    s3eWebViewDestroy(webView);
    webView = NULL;

    //unregister callbacks
    s3eWebViewUnRegister(S3E_WEBVIEW_FINISHED_LOADING, loadedCallback, webView);
    s3eWebViewUnRegister(S3E_WEBVIEW_FROM_JAVASCRIPT, javaScriptCallback, webView);
}
delete rom;

//gives me warning so I'm leaving it out.
//delete pipHandler;
}

// Main entry point for the application
int main()
{
    //Initialise graphics system(s)
    //Initialise graphics system(s)
    Iw2DInit();

    //Init
    init();

    // Loop forever, until the user or the OS performs some action to quit the app
    while (!s3eDeviceCheckQuitRequest()) {
        //Update the input systems
        s3eKeyboardUpdate();
        s3ePointerUpdate();


    // Your rendering/app code goes here.


    // Sleep for 0ms to allow the OS to process events etc.
        s3eDeviceYield(0);
    }

    destroy();

    //Terminate modules being used

    // Return
    return 0;
}

and here is the html/js for the webview

<!DOCTYPE html>
<html>
<head>
    <title>Storage Spike</title>
</head>
<body onload="main();">
  <div>Loaded value:<span id="loadedKey">(loaded value goes here)</span></div>
  <input type="text" id="inputKey" value="123key" placeholder="enter key to save"/>
  <button id="saveKey">Save</button>
  <button id="loadKey">Load</button>
  <br>
  <script>
    function main()
    {
      var saveKey = document.getElementById("saveKey");
      var loadKey = document.getElementById("loadKey");
      var inputKey = document.getElementById("inputKey");

      saveKey.addEventListener("click", function() {
        var key = inputKey.value;
        s3e.exec("saveKey|" + key);
      });

      loadKey.addEventListener("click", function() {
         s3e.exec("loadKey");
      });

    }

    function dataLoaded(data, type)
    {
      console.log(data);
      console.log(type);
      var loadedKey = document.getElementById("loadedKey");
      if(type === "key")
        loadedKey.innerHTML = data;
      else
        console.log("type error");
    }

  </script>
</body>
</html>

Any help would be greatly appreciated,

Kind Regards

1

There are 1 answers

0
Aleksi Grön On

The problem is that you are trying to directly save an instance of std::string.

A std::string instance is nothing but three pointers that describe the memory that it is using to store the actual string data. When the application closes or the memory used to store the data is released, those pointers lose all meaning.

You need to save the actual string data to s3eSecureStorage for it to persist. To do this, you need to serialize the std::string instance. A simple way of doing the serialization is to first save the length of the string, then save the actual string data.

To serialize and save the string to s3eSecureStorage, you could do this:

std::string input = "exampledata";
uint16 inputSize = static_cast<uint16>(input.size());
uint16 bufferSize = static_cast<uint16>(sizeof(uint16) + inputSize);
buffer = new char[bufferSize];
std::memcpy(buffer, &inputSize, sizeof(uint16));
std::memcpy(buffer + sizeof(uint16), input.c_str(), inputSize);
s3eSecureStoragePut(buffer, bufferSize);
delete[] buffer;

And to get the data from s3eSecureStorage and deserialize the string, you could do this:

std::string output;
uint16 dataSize = 0;
s3eSecureStorageGet(&dataSize, sizeof(uint16));
if (dataSize > 0) {
    uint16 bufferSize = static_cast<uint16>(dataSize + sizeof(uint16));
    char* buffer = new char[bufferSize];
    s3eSecureStorageGet(buffer, bufferSize);
    output.resize(dataSize);
    std::memcpy(&output[0], buffer + sizeof(uint16), dataSize);
    delete[] buffer;
}

If you want to store multiple values, the serialization needs to be slightly more complicated but the basic idea is the same.

Obviously you also need to use some kind of error handling for when there is nothing saved to s3eSecureStorage.

The reason the code you posted worked within a single execution of your app is because you allocated the SStoreData instance with new and never released it. When you loaded the pointers back from s3eSecureStorage, they would still point to the allocated memory that held the string data.