Skip to content

Commit

Permalink
Android: Fix ContentSharer crash on Android 14
Browse files Browse the repository at this point in the history
  • Loading branch information
szarvas committed Nov 3, 2023
1 parent fa0c91d commit b800890
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 14 deletions.
81 changes: 70 additions & 11 deletions modules/juce_core/native/juce_JNIHelpers_android.h
Expand Up @@ -31,8 +31,15 @@ template <typename JavaType>
class LocalRef
{
public:
LocalRef() noexcept : obj (nullptr) {}
explicit LocalRef (JavaType o) noexcept : obj (o) {}
LocalRef() noexcept = default;

/* This constructor must not be used to wrap local references that were not created through
JNI, i.e. for native function callback parameters.
*/
explicit LocalRef (JavaType o) noexcept
: LocalRef (o, false)
{}

LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {}
LocalRef (LocalRef&& other) noexcept : obj (nullptr) { std::swap (obj, other.obj); }
~LocalRef() { clear(); }
Expand All @@ -48,31 +55,83 @@ class LocalRef

LocalRef& operator= (const LocalRef& other)
{
JavaType newObj = retain (other.obj);
clear();
obj = newObj;
auto tmp = other;
std::swap (tmp.obj, obj);
return *this;
}

LocalRef& operator= (LocalRef&& other)
LocalRef& operator= (LocalRef&& other) noexcept
{
clear();
std::swap (other.obj, obj);
auto tmp = std::move (other);
std::swap (tmp.obj, obj);
return *this;
}

bool operator== (std::nullptr_t) const noexcept { return obj == nullptr; }
bool operator!= (std::nullptr_t) const noexcept { return obj != nullptr; }

operator JavaType() const noexcept { return obj; }

JavaType get() const noexcept { return obj; }

private:
JavaType obj;
auto release()
{
return std::exchange (obj, nullptr);
}

/** Creates a new internal local reference. */
static auto addOwner (JavaType o)
{
return LocalRef { o, true };
}

/** Takes ownership of the passed in local reference, and deletes it when the LocalRef goes out
of scope.
*/
static auto becomeOwner (JavaType o)
{
return LocalRef { o, false };
}

private:
static JavaType retain (JavaType obj)
{
return obj == nullptr ? nullptr : (JavaType) getEnv()->NewLocalRef (obj);
}

/* We cannot delete local references that were not created by JNI, e.g. references that were
created by the VM and passed into the native function.
For these references we should use createNewLocalRef = true, which will create a new
local reference that this wrapper is allowed to delete.
Doing otherwise will result in an "Attempt to remove non-JNI local reference" warning in the
VM, which could even cause crashes in future VM implementations.
*/
LocalRef (JavaType o, bool createNewLocalRef) noexcept
: obj (createNewLocalRef ? retain (o) : o)
{}

JavaType obj = nullptr;
};

/* Creates a new local reference that shares ownership with the passed in pointer.
Can be used for wrapping function parameters that were created outside the JNI.
*/
template <class JavaType>
auto addLocalRefOwner (JavaType t)
{
return LocalRef<JavaType>::addOwner (t);
}

/* Wraps a local reference and destroys it when it goes out of scope. */
template <class JavaType>
auto becomeLocalRefOwner (JavaType t)
{
return LocalRef<JavaType>::becomeOwner (t);
}

//==============================================================================
template <typename JavaType>
class GlobalRefImpl
Expand Down Expand Up @@ -846,7 +905,7 @@ namespace
javaString ("").get()));

for (int i = 0; i < juceArray.size(); ++i)
env->SetObjectArrayElement (result, i, javaString (juceArray [i]).get());
env->SetObjectArrayElement (result.get(), i, javaString (juceArray [i]).get());

return result;
}
Expand Down
7 changes: 4 additions & 3 deletions modules/juce_gui_basics/native/juce_ContentSharer_android.cpp
Expand Up @@ -336,8 +336,8 @@ class ContentSharerGlobalImpl

static jobjectArray JNICALL contentSharerGetStreamTypes (JNIEnv*, jobject /*contentProvider*/, jobject uri, jstring mimeTypeFilter)
{
return getInstance().getStreamTypes (LocalRef<jobject> (static_cast<jobject> (uri)),
LocalRef<jstring> (static_cast<jstring> (mimeTypeFilter)));
return getInstance().getStreamTypes (addLocalRefOwner (uri),
addLocalRefOwner (mimeTypeFilter));
}

private:
Expand Down Expand Up @@ -482,7 +482,8 @@ class ContentSharerGlobalImpl
if (extension.isEmpty())
return nullptr;

return juceStringArrayToJava (filterMimeTypes (detail::MimeTypeTable::getMimeTypesForFileExtension (extension), juceString (mimeTypeFilter.get())));
return juceStringArrayToJava (filterMimeTypes (detail::MimeTypeTable::getMimeTypesForFileExtension (extension),
juceString (mimeTypeFilter.get()))).release();
}

std::unique_ptr<ActivityLauncher> doIntent (const LocalRef<jobject>& intent,
Expand Down

0 comments on commit b800890

Please sign in to comment.