Skip to content

refactor: migrate BlobProvider.java to Kotlin #50756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.modules.blob

import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import android.os.ParcelFileDescriptor
import com.facebook.infer.annotation.Nullsafe
import com.facebook.react.ReactApplication
import java.io.FileNotFoundException
import java.io.IOException
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

public final class BlobProvider : ContentProvider() {
private val executor: ExecutorService = Executors.newSingleThreadExecutor()

override fun onCreate(): Boolean = true

override fun query(
uri: Uri,
projection: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
sortOrder: String?
): Cursor? = null;

override fun getType(uri: Uri): String? = null

override fun insert(uri: Uri, values: ContentValues?): Uri? = null

override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0

override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<String>?
): Int = 0

@Throws(FileNotFoundException::class)
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
if (mode != "r") {
throw FileNotFoundException("Cannot open $uri in mode '$mode'")
}

var blobModule: BlobModule? = null
val context = context?.applicationContext ?: null
if (context is ReactApplication) {
val host = (context as ReactApplication).reactNativeHost
val reactContext = host.reactInstanceManager.currentReactContext
?: throw RuntimeException("No ReactContext associated with BlobProvider")
blobModule = reactContext.getNativeModule(BlobModule::class.java)
}

if (blobModule == null) {
throw RuntimeException("No blob module associated with BlobProvider")
}

val data = blobModule.resolve(uri)
?: throw FileNotFoundException("Cannot open $uri, blob not found.")

val pipe: Array<ParcelFileDescriptor>
try {
pipe = ParcelFileDescriptor.createPipe()
} catch (exception: IOException) {
return null
}
val readSide = pipe[0]
val writeSide = pipe[1]

if (data.size <= PIPE_CAPACITY) {
// If the blob length is less than or equal to pipe capacity (64 KB),
// we can write the data synchronously to the pipe buffer.
try {
ParcelFileDescriptor.AutoCloseOutputStream(writeSide).use { outputStream ->
outputStream.write(data)
}
} catch (exception: IOException) {
return null
}
} else {
// For blobs larger than 64 KB, a synchronous write would fill up the whole buffer
// and block forever, because there are no readers to empty the buffer.
// Writing from a separate thread allows us to return the read side descriptor
// immediately so that both writer and reader can work concurrently.
// Reading from the pipe empties the buffer and allows the next chunks to be written.
val writer =
Runnable {
try {
ParcelFileDescriptor.AutoCloseOutputStream(writeSide)
.use { outputStream ->
outputStream.write(data)
}
} catch (exception: IOException) {
// no-op
}
}
executor.submit(writer)
}

return readSide
}

private companion object {
private const val PIPE_CAPACITY = 65536
}
}
Loading