1

After updating Android Target SDK from 33 to 34 the configuration is no longer valid (i.e platform_configuration_->Valid() == false). I get the following error messages from Google Play Games Services C++

1 GamesNativeSDK          V  Using classes from /data/user/0/com.mycompany.myapp/app_.gpg.classloader/4105d0375b9d69fe6ee3a07b7893a4f1_games.jar.
2 GamesNativeSDK          I  Using existing jar.
3 GamesNativeSDK          I  Writing 1881 bytes to jar file
a.myapp                 E  Attempt to load writable dex file: /data/user/0/com.mycompany.myapp/app_.gpg.classloader/4105d0375b9d69fe6ee3a07b7893a4f1_games.jar
4 Compatibil...geReporter D  Compat change id reported: 218865702; UID 10402; state: ENABLED
5 GamesNativeSDK          E  Exception in dalvik/system/DexClassLoader.<init>: java.lang.SecurityException: Writable dex file '/data/user/0/com.mycompany.myapp/app_.gpg.classloader/4105d0375b9d69fe6ee3a07b7893a4f1_games.jar' is not allowed..
6 GamesNativeSDK          E  Could not create class loader from file.
7 GamesNativeSDK          E  Could not load additional classes from embedded jar.

It seems to be related to this question: Problems encountered when developing apps with Android 14

As suggested in the above question, when I added

File(Context.getDataDir() + "/app_.gpg.classloader/4105d0375b9d69fe6ee3a07b7893a4f1_games.jar").setReadOnly();

to my activity onCreate method, log messages 2,3,4, and 5 do not show anymore. Still, the other log messages show, and the initialization of Game Services fail.

Does anyone know of any solution to this?

0

1 Answer 1

2

I ran into the same issue today and decided that updating to the v2 version or switching to the java version of this library is not an option.

So I tried to do something like you did but it didn't work. The reason as I realized is that the jar is written and then loaded after a single function call (Valid()) so you can't simply fix the jar after it was written.

The solution I found, while extremely hacky, ended up working: start a thread with a very tight infinite loop that waits for the jar to be written completely (as determined by querying its size) and then flips the file to read-only. With some luck (and it seems luck's on our side) this flip can happen before the OS code is reached checking the read-only condition.

There are actually two different jar files to fix up.

Here's the code in full. This is on the C++ side, the same idea may also work in Java, I'm not sure. Note that this will most likely only work with the arm64 version of the library - I didn't find the same checksum strings in other versions, so more manual work may be needed for those.

static void fixup_jar(const std::string& name, unsigned expected_size, const bool& done) {
  // Work around an issue with API level 34 and the old google play games lib we're using.
  // The SDK writes to a jar file and tries to load it. The system refuses to load non-read-only files.
  // So we do our best to make it read-only just in time...

  const std::string path = ANDROID_DEFAULT_FILES_DIR + std::string("/../app_.gpg.classloader/") + name + ".jar";
  unlink(path.c_str());
  bool thread_started = false;
  auto t = std::thread([=, &thread_started, &done]() {
    thread_started = true;
    while (!done) {
      struct stat st;
      stat(path.c_str(), &st);
      if (st.st_size == expected_size) {
        chmod(path.c_str(), S_IRUSR);
        break;
      }
    }
  });
  t.detach();
  while (!thread_started) usleep(50);
}

void android_leaderboard_init(jobject mainActivity) {
  platform_configuration.SetActivity(mainActivity);

  if (!platform_configuration.Valid()) {
    for (unsigned int i = 0; i < 10; ++i) {
      bool jars_done = false;
      fixup_jar("4105d0375b9d69fe6ee3a07b7893a4f1_games", 1881, jars_done);
      fixup_jar("6e3966b0d5618671dcdc0ea28384da37_nearby", 1066, jars_done);
      if (platform_configuration.Valid()) {
        jars_done = true;
        break;
      }
      jars_done = true;
    }
  }

  if (!platform_configuration.Valid()) {
    abort("invalid platform config");
  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

Wow! One of the hackiest thing I've ever seen, but whatever works :) I'll give it a try and see if I can get it running. (I was also thinking about rewriting it all in Java and use JNI, or going v2) (Google Play Games really has room for improvements, it's awfully buggy)
Woaw, it magically works! Big thanks! Just one question, why does it re-execute fixup_jar in every iteration (from 0 to 10)? As it's a thread, isn't it enough to run fixup_jar once before the loop from 0 to 10?
I was concerned with unexpected race conditions, but you are probably right, it could work that way too.
Have you reported this issue with Google so that vast developer community should be getting benefits in future without applying this hack.
Google quietly pulled support for this version of the C++ SDK. It's not available for download anymore except a random link in an old Google-own codebase on Github still links to it. Their officially supported v2 C++ SDK has a different API and limited functionality (only auth I believe). So I doubt they care that an SDK they no longer support isn't working well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.