JavaScriptIsolate
@ThreadSafe
public final class JavaScriptIsolate implements 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 methods |
|
|---|---|
void |
addOnTerminatedCallback(@NonNull Consumer<TerminationInfo> callback)Add a callback to listen for isolate crashes. |
void |
addOnTerminatedCallback(Add a callback to listen for isolate crashes. |
void |
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")Clear any JavaScriptConsoleCallback set via setConsoleCallback. |
void |
close()Closes the |
@NonNull ListenableFuture<String> |
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")Reads and evaluates the JavaScript code in the file described by the given AssetFileDescriptor. |
@NonNull ListenableFuture<String> |
evaluateJavaScriptAsync(@NonNull String code)Evaluates the given JavaScript code and returns the result. |
@NonNull ListenableFuture<String> |
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")Reads and evaluates the JavaScript code in the file described by the given |
void |
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")Provides a byte array for consumption from the JavaScript environment. |
void |
removeOnTerminatedCallback(@NonNull Consumer<TerminationInfo> callback)Remove a callback previously registered with addOnTerminatedCallback. |
void |
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")Set a JavaScriptConsoleCallback to process console messages from the isolate. |
void |
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")Set a JavaScriptConsoleCallback to process console messages from the isolate. |
Protected methods |
|
|---|---|
void |
finalize() |
Public methods
addOnTerminatedCallback
public void addOnTerminatedCallback(@NonNull Consumer<TerminationInfo> callback)
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 | |
|---|---|
@NonNull Consumer<TerminationInfo> callback |
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
public void addOnTerminatedCallback(
@NonNull Executor executor,
@NonNull Consumer<TerminationInfo> callback
)
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 | |
|---|---|
@NonNull Executor executor |
the executor with which to run callback |
@NonNull Consumer<TerminationInfo> callback |
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
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
public void clearConsoleCallback()
Clear any JavaScriptConsoleCallback set via setConsoleCallback.
Clearing a callback is not guaranteed to take effect for any already pending evaluations.
close
public void close()
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
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
public @NonNull ListenableFuture<String> evaluateJavaScriptAsync(@NonNull AssetFileDescriptor afd)
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 | |
|---|---|
@NonNull AssetFileDescriptor afd |
an |
| Returns | |
|---|---|
@NonNull ListenableFuture<String> |
a Future that evaluates to the result String of the evaluation or an exception (see |
evaluateJavaScriptAsync
public @NonNull ListenableFuture<String> evaluateJavaScriptAsync(@NonNull String code)
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
isFeatureSupportedforJS_FEATURE_PROMISE_RETURNreturnstrue, the Java Future resolves to a Java String once the promise resolves. If it returnsfalse, 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.
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 | |
|---|---|
@NonNull String code |
JavaScript code to evaluate. The script should return a JavaScript String or, alternatively, a Promise that will resolve to a String if |
| Returns | |
|---|---|
@NonNull ListenableFuture<String> |
a Future that evaluates to the result String of the evaluation or an exception (see |
evaluateJavaScriptAsync
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
public @NonNull ListenableFuture<String> evaluateJavaScriptAsync(@NonNull ParcelFileDescriptor pfd)
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 | |
|---|---|
@NonNull ParcelFileDescriptor pfd |
a |
| Returns | |
|---|---|
@NonNull ListenableFuture<String> |
a Future that evaluates to the result String of the evaluation or an exception (see |
provideNamedData
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
public void provideNamedData(@NonNull String name, @NonNull byte[] inputBytes)
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);
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 | |
|---|---|
@NonNull String name |
identifier for the data that is passed. The same identifier should be used in the JavaScript environment to refer to the data. |
@NonNull byte[] inputBytes |
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
public void removeOnTerminatedCallback(@NonNull Consumer<TerminationInfo> callback)
Remove a callback previously registered with addOnTerminatedCallback.
| Parameters | |
|---|---|
@NonNull Consumer<TerminationInfo> callback |
the callback to unregister, if currently registered |
setConsoleCallback
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
public void setConsoleCallback(@NonNull JavaScriptConsoleCallback callback)
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 | |
|---|---|
@NonNull JavaScriptConsoleCallback callback |
the callback implementing console logging behaviour |
setConsoleCallback
@RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
public void setConsoleCallback(
@NonNull Executor executor,
@NonNull JavaScriptConsoleCallback callback
)
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 | |
|---|---|
@NonNull Executor executor |
the executor for running callback methods |
@NonNull JavaScriptConsoleCallback callback |
the callback implementing console logging behaviour |