@ThreadSafe
class JavaScriptIsolate : AutoCloseable


Environment within a JavaScriptSandbox where JavaScript is executed.

A single JavaScriptSandbox process can contain any number of JavaScriptIsolate instances where JS can be evaluated independently and in parallel.

Each isolate has its own state and JS global object, and cannot interact with any other isolate through JS APIs. There is only a moderate security boundary between isolates in a single JavaScriptSandbox. If the code in one JavaScriptIsolate is able to compromise the security of the JS engine then it may be able to observe or manipulate other isolates, since they run in the same process. For strong isolation multiple JavaScriptSandbox processes should be used, but it is not supported at the moment. Please find the feature request here.

This class is thread-safe.

Summary

Public functions

Unit

Add a callback to listen for isolate crashes.

Unit
addOnTerminatedCallback(
    executor: Executor,
    callback: Consumer<TerminationInfo!>
)

Add a callback to listen for isolate crashes.

Unit
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
clearConsoleCallback()

Clear any JavaScriptConsoleCallback set via setConsoleCallback.

Unit

Closes the JavaScriptIsolate object and renders it unusable.

ListenableFuture<String!>
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
evaluateJavaScriptAsync(afd: AssetFileDescriptor)

Reads and evaluates the JavaScript code in the file described by the given AssetFileDescriptor.

ListenableFuture<String!>

Evaluates the given JavaScript code and returns the result.

ListenableFuture<String!>
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
evaluateJavaScriptAsync(pfd: ParcelFileDescriptor)

Reads and evaluates the JavaScript code in the file described by the given ParcelFileDescriptor.

Unit
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
provideNamedData(name: String, inputBytes: ByteArray)

Provides a byte array for consumption from the JavaScript environment.

Unit

Remove a callback previously registered with addOnTerminatedCallback.

Unit
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
setConsoleCallback(callback: JavaScriptConsoleCallback)

Set a JavaScriptConsoleCallback to process console messages from the isolate.

Unit
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
setConsoleCallback(
    executor: Executor,
    callback: JavaScriptConsoleCallback
)

Set a JavaScriptConsoleCallback to process console messages from the isolate.

Protected functions

Unit

Public functions

addOnTerminatedCallback

Added in 1.0.0-beta01
fun addOnTerminatedCallback(callback: Consumer<TerminationInfo!>): Unit

Add a callback to listen for isolate crashes.

This is the same as calling addOnTerminatedCallback using the main executor of the context used to create the JavaScriptSandbox object.

Parameters
callback: Consumer<TerminationInfo!>

the consumer to be called with TerminationInfo when a crash occurs

Throws
java.lang.IllegalStateException

if the callback is already registered (using any executor)

addOnTerminatedCallback

Added in 1.0.0-beta01
fun addOnTerminatedCallback(
    executor: Executor,
    callback: Consumer<TerminationInfo!>
): Unit

Add a callback to listen for isolate crashes.

There is no guaranteed order to when these callbacks are triggered and unfinished evaluations' futures are rejected.

Registering a callback after the isolate has crashed will result in it being executed immediately on the supplied executor with the isolate's TerminationInfo as an argument.

Closing an isolate via close is not considered a crash, even if there are unresolved evaluations, and will not trigger termination callbacks.

Parameters
executor: Executor

the executor with which to run callback

callback: Consumer<TerminationInfo!>

the consumer to be called with TerminationInfo when a crash occurs

Throws
java.lang.IllegalStateException

if the callback is already registered (using any executor)

clearConsoleCallback

Added in 1.0.0-beta01
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
fun clearConsoleCallback(): Unit

Clear any JavaScriptConsoleCallback set via setConsoleCallback.

Clearing a callback is not guaranteed to take effect for any already pending evaluations.

close

Added in 1.0.0-beta01
fun close(): Unit

Closes the JavaScriptIsolate object and renders it unusable.

Once closed, no more method calls should be made. Pending evaluations will reject with an IsolateTerminatedException immediately.

If isFeatureSupported is true for JS_FEATURE_ISOLATE_TERMINATION, then any pending evaluations are terminated. If it is false, the isolate will not get cleaned up until the pending evaluations have run to completion and will consume resources until then.

Closing an isolate via this method does not wait on the isolate to clean up. Resources held by the isolate may remain in use for a duration after this method returns.

evaluateJavaScriptAsync

Added in 1.0.0-beta01
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
fun evaluateJavaScriptAsync(afd: AssetFileDescriptor): ListenableFuture<String!>

Reads and evaluates the JavaScript code in the file described by the given AssetFileDescriptor.

Please refer to the documentation of evaluateJavaScriptAsync as the behavior of this method is similar other than for the input type.

This API exposes the underlying file to the service. In case the service process is compromised for unforeseen reasons, it might be able to read from the AssetFileDescriptor beyond the given length and offset. This API does not close the given AssetFileDescriptor.

Note: The underlying file data must be UTF-8 encoded.

This overload is useful when the source of the data is easily readable as an AssetFileDescriptor, e.g. an asset or raw resource.

Parameters
afd: AssetFileDescriptor

an AssetFileDescriptor for a file containing UTF-8 encoded JavaScript code to be evaluated

Returns
ListenableFuture<String!>

a Future that evaluates to the result String of the evaluation or an exception (see JavaScriptException and subclasses) if there is an error

evaluateJavaScriptAsync

Added in 1.0.0-beta01
fun evaluateJavaScriptAsync(code: String): ListenableFuture<String!>

Evaluates the given JavaScript code and returns the result.

There are 3 possible behaviors based on the output of the expression:

  • If the JS expression evaluates to a JS String, then the Java Future resolves to a Java String.
  • If the JS expression evaluates to a JS Promise, and if isFeatureSupported for JS_FEATURE_PROMISE_RETURN returns true, the Java Future resolves to a Java String once the promise resolves. If it returns false, then the Future resolves to an empty Java string.
  • If the JS expression evaluates to another data type, then the Java Future resolves to an empty Java String.
The environment uses a single JS global object for all the calls to evaluateJavaScriptAsync(String) and provideNamedData methods. These calls are queued up and are run one at a time in sequence, using the single JS environment for the isolate. The global variables set by one evaluation are visible for later evaluations. This is similar to adding multiple <script> tags in HTML. The behavior is also similar to evaluateJavascript.

If isFeatureSupported for JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT returns false, the size of the expression to be evaluated and the result/error value is limited by the binder transaction limit (android.os.TransactionTooLargeException). If it returns true, they are not limited by the binder transaction limit but are bound by setMaxEvaluationReturnSizeBytes with a default size of DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES.

Do not use this method to transfer raw binary data. Scripts or results containing unpaired surrogate code units are not supported.

Parameters
code: String

JavaScript code to evaluate. The script should return a JavaScript String or, alternatively, a Promise that will resolve to a String if JS_FEATURE_PROMISE_RETURN is supported.

Returns
ListenableFuture<String!>

a Future that evaluates to the result String of the evaluation or an exception (see JavaScriptException and subclasses) if there is an error

evaluateJavaScriptAsync

Added in 1.0.0-beta01
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
fun evaluateJavaScriptAsync(pfd: ParcelFileDescriptor): ListenableFuture<String!>

Reads and evaluates the JavaScript code in the file described by the given ParcelFileDescriptor.

Please refer to the documentation of evaluateJavaScriptAsync as the behavior of this method is similar other than for the input type.

This API exposes the underlying file to the service. In case the service process is compromised for unforeseen reasons, it might be able to read from the ParcelFileDescriptor beyond the given length and offset. This API does not close the given ParcelFileDescriptor.

Note: The underlying file data must be UTF-8 encoded.

This overload is useful when the source of the data is easily readable as a ParcelFileDescriptor, e.g. a file from shared memory or the app's data directory.

Parameters
pfd: ParcelFileDescriptor

a ParcelFileDescriptor for a file containing UTF-8 encoded JavaScript code that is evaluated

Returns
ListenableFuture<String!>

a Future that evaluates to the result String of the evaluation or an exception (see JavaScriptException and subclasses) if there is an error

provideNamedData

Added in 1.0.0-beta01
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
fun provideNamedData(name: String, inputBytes: ByteArray): Unit

Provides a byte array for consumption from the JavaScript environment.

This method provides an efficient way to pass in data from Java into the JavaScript environment which can be referred to from JavaScript. This is more efficient than including data in the JS expression, and allows large data to be sent.

This data can be consumed in the JS environment using android.consumeNamedDataAsArrayBuffer(String) by referring to the data with the name that was used when calling this method. This is a one-time transfer and the calls should be paired.

A single name can only be used once in a particular JavaScriptIsolate. Clients can generate unique names for each call if they need to use this method multiple times. The same name should be included into the JS code.

This API can be used to pass a WASM module into the JS environment for compilation if isFeatureSupported returns true for JS_FEATURE_WASM_COMPILATION. In Java,

    jsIsolate.provideNamedData("id-1", byteArray);
In JS,
    android.consumeNamedDataAsArrayBuffer("id-1").then((value) => {
      return WebAssembly.compile(value).then((module) => {
         ...
      });
    });

The environment uses a single JS global object for all the calls to evaluateJavaScriptAsync and provideNamedData(String, byte[]) methods.

This method should only be called if isFeatureSupported returns true for JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER.

Parameters
name: String

identifier for the data that is passed. The same identifier should be used in the JavaScript environment to refer to the data.

inputBytes: ByteArray

bytes to be passed into the JavaScript environment. This array must not be modified until the JavaScript promise returned by consumeNamedDataAsArrayBuffer has resolved (or rejected).

Throws
java.lang.IllegalStateException

if the name has previously been used in the isolate

removeOnTerminatedCallback

Added in 1.0.0-beta01
fun removeOnTerminatedCallback(callback: Consumer<TerminationInfo!>): Unit

Remove a callback previously registered with addOnTerminatedCallback.

Parameters
callback: Consumer<TerminationInfo!>

the callback to unregister, if currently registered

setConsoleCallback

Added in 1.0.0-beta01
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
fun setConsoleCallback(callback: JavaScriptConsoleCallback): Unit

Set a JavaScriptConsoleCallback to process console messages from the isolate.

This is the same as calling setConsoleCallback using the main executor of the context used to create the JavaScriptSandbox object.

Parameters
callback: JavaScriptConsoleCallback

the callback implementing console logging behaviour

setConsoleCallback

Added in 1.0.0-beta01
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
fun setConsoleCallback(
    executor: Executor,
    callback: JavaScriptConsoleCallback
): Unit

Set a JavaScriptConsoleCallback to process console messages from the isolate.

Scripts always have access to console APIs, regardless of whether a console callback is set. By default, no console callback is set and calling a console API from JavaScript will do nothing, and will be relatively cheap. Setting a console callback allows console messages to be forwarded to the embedding application, but may negatively impact performance.

Note that console APIs may expose messages generated by both JavaScript code and V8/JavaScriptEngine internals.

Use caution if using this in production code as it may result in the exposure of debugging information or secrets through logs.

When setting a console callback, this method should be called before requesting any evaluations. Calling setConsoleCallback after requesting evaluations may result in those pending evaluations' console messages being dropped or logged to a previous console callback.

Note that delayed console messages may continue to be delivered after the isolate has been closed (or has crashed).

Parameters
executor: Executor

the executor for running callback methods

callback: JavaScriptConsoleCallback

the callback implementing console logging behaviour

Protected functions

finalize

protected fun finalize(): Unit