• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 package com.google.fcp.client;
15 
16 import java.lang.Thread.UncaughtExceptionHandler;
17 import java.util.concurrent.Callable;
18 
19 /**
20  * Utility class to wrap java method calls that originate from the native layer over JNI, which
21  * ensures that any uncaught {@link Throwable} is passed to the given {@link
22  * UncaughtExceptionHandler} (which is expected to generate a crash report and terminate the
23  * process), as opposed to being passed back to the native layer.
24  */
25 public class CallFromNativeWrapper {
26 
27   private final UncaughtExceptionHandler uncaughtExceptionHandler;
28 
29   /** A {@link Callable} that does not throw checked exceptions. */
30   public interface NativeToJavaCallable<T> extends Callable<T> {
31     @Override
call()32     T call();
33   }
34 
CallFromNativeWrapper(UncaughtExceptionHandler uncaughtExceptionHandler)35   public CallFromNativeWrapper(UncaughtExceptionHandler uncaughtExceptionHandler) {
36     this.uncaughtExceptionHandler = uncaughtExceptionHandler;
37   }
38 
39   /**
40    * Wraps a java method call from native code on an arbitrary thread (i.e. one created by
41    * TensorFlow). If a {@link Throwable} is thrown the exception will be passed to the {@code
42    * uncaughtExceptionHandler}.
43    */
wrapCall(NativeToJavaCallable<T> callable)44   public <T> T wrapCall(NativeToJavaCallable<T> callable) {
45     try {
46       return callable.call();
47     } catch (Throwable t) {
48       uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), t);
49       // The uncaught exception handler generally will have killed us by here.
50       //
51       // On Android, the system should see our thread crash and kill the process before reaching
52       // here. Just in case we make it this far, we wrap the exception in a runtime exception and
53       // let it pass to the native layer (which will generally then abort the process upon detecting
54       // the exception).
55       throw new CallFromNativeRuntimeException(t);
56     }
57   }
58 
wrapVoidCall(Runnable runnable)59   public void wrapVoidCall(Runnable runnable) {
60     wrapCall(
61         () -> {
62           runnable.run();
63           return null;
64         });
65   }
66 
67   /**
68    * A {@link RuntimeException} signifying there was an unchecked exception when calling from native
69    * to java.
70    */
71   public static class CallFromNativeRuntimeException extends RuntimeException {
CallFromNativeRuntimeException(Throwable t)72     CallFromNativeRuntimeException(Throwable t) {
73       super(t);
74     }
75   }
76 }
77