1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.webkit.cts; 18 19 import android.os.RemoteException; 20 21 /** 22 * Binder only handles a few exceptions. Runtime exceptions are silently ignored and any errors 23 * thrown will result in crashing the entire process and meaning no further tests will pass. We deal 24 * with this in a bit of a sneaky sneaky way and wrap any throwables in one of the supported 25 * exception types (IllegalStateException seems fairly representative of if the host app environment 26 * is broken), and then catch that and re-expose it in the SharedSdkWebServer where we strip that 27 * exception type out to avoid confusion. 28 * 29 * <p>The wrap/unwrap methods from this class should be used on either side of IPC calls by the 30 * IHostAppInvoker and IWebServer. 31 * 32 * <p>This allows JUnit to deal with any major broken program flows gracefully instead of moving on 33 * or crashing the rest of the tests. 34 * 35 * <p>It should be noted that binder parcel will take the cause and stringify the stack trace so the 36 * type information of these exceptions is lost in the journey. This means that the test code will 37 * not be able to react to these exception types. This is already a limitation of communicating 38 * through binder. 39 */ 40 class ExceptionWrapper { wrap(WrappedTypedCall<T> c)41 public static <T> T wrap(WrappedTypedCall<T> c) { 42 try { 43 return c.wrap(); 44 } catch (Throwable e) { 45 throw new IllegalStateException(e); 46 } 47 } 48 wrap(WrappedVoidCall r)49 public static void wrap(WrappedVoidCall r) { 50 wrap(() -> { 51 r.wrap(); 52 return null; 53 }); 54 } 55 unwrap(UnwrappedTypedCall<T> c)56 public static <T> T unwrap(UnwrappedTypedCall<T> c) { 57 try { 58 return c.unwrap(); 59 } catch (RemoteException e) { 60 // We are handling the remote exception separately from the IllegalStateException 61 // because this is happening binder proxy side so we would like to preserve the 62 // exception information. 63 // We still wrap this in a runtime exception so that the WebServer tests don't need to 64 // throw inside the Webkit utils run on main sync method as that would mean those 65 // functions would all have to return null (it would turn them into callables instead of 66 // runnables). 67 throw new RuntimeException(e); 68 } catch (IllegalStateException e) { 69 throw new RuntimeException(e.getMessage()); 70 } 71 } 72 unwrap(UnwrappedVoidCall c)73 public static void unwrap(UnwrappedVoidCall c) { 74 unwrap(() -> { 75 c.unwrap(); 76 return null; 77 }); 78 } 79 80 interface WrappedTypedCall<T> { wrap()81 T wrap() throws Exception; 82 } 83 84 interface WrappedVoidCall { wrap()85 void wrap() throws Exception; 86 } 87 88 interface UnwrappedTypedCall<T> { unwrap()89 T unwrap() throws RemoteException; 90 } 91 92 interface UnwrappedVoidCall { unwrap()93 void unwrap() throws RemoteException; 94 } 95 } 96