From 34d994204ba0988ef2dd5340e3fa19a2493c451f Mon Sep 17 00:00:00 2001 From: Colin Liang Date: Mon, 21 Oct 2024 13:11:29 -0700 Subject: [PATCH] Split javascript interface methods into domain objects. Change-Id: I7f43808a579e7740b23a2c154e8395e8c85cf9b4 --- .../app/src/main/assets/injected_script.js | 8 +- .../java/dev/cobalt/coat/ChrobaltWebView.java | 35 ++------ .../java/dev/cobalt/coat/StarboardBridge.java | 8 +- .../java/dev/cobalt/coat/WebAppInterface.java | 56 ++++++------- .../android_webview/H5vccPlatformService.java | 40 +++++++++ .../coat/android_webview/WebAppInterface.java | 7 ++ .../cobalt/util/SystemPropertiesHelper.java | 82 ++++++++++--------- .../main/java/dev/cobalt/util/UserAgent.java | 60 ++++++++++++++ 8 files changed, 188 insertions(+), 108 deletions(-) create mode 100644 starboard/android/apk/app/src/main/java/dev/cobalt/coat/android_webview/H5vccPlatformService.java create mode 100644 starboard/android/apk/app/src/main/java/dev/cobalt/coat/android_webview/WebAppInterface.java create mode 100644 starboard/android/apk/app/src/main/java/dev/cobalt/util/UserAgent.java diff --git a/starboard/android/apk/app/src/main/assets/injected_script.js b/starboard/android/apk/app/src/main/assets/injected_script.js index ac304afa164e..4c1c8a895c45 100644 --- a/starboard/android/apk/app/src/main/assets/injected_script.js +++ b/starboard/android/apk/app/src/main/assets/injected_script.js @@ -62,7 +62,7 @@ var platform_services = { }, has: (name) => { console.log('platformService.has(' + name + ')'); - return Android.has_platform_service(name); + return Android_H5vccPlatformService.has_platform_service(name); }, open: function(name, callback) { console.log('platformService.open(' + name + ',' + @@ -80,7 +80,7 @@ var platform_services = { name: name, callback: callback }; - Android.open_platform_service(serviceId, name); + Android_H5vccPlatformService.open_platform_service(serviceId, name); return { 'name': name, 'send': function (data) { @@ -89,11 +89,11 @@ var platform_services = { console.log('1 platformService.send(' + text + ')'); var convert_to_b64 = arrayBufferToBase64(data); console.log('sending as b64:' + convert_to_b64); - Android.platform_service_send(name, convert_to_b64); + Android_H5vccPlatformService.platform_service_send(name, convert_to_b64); }, close: () => { console.log('1 platformService.close()'); - Android.close_platform_service(name); + Android_H5vccPlatformService.close_platform_service(name); }, } }, diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/ChrobaltWebView.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/ChrobaltWebView.java index 1c87b5605402..5cfb68f85949 100644 --- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/ChrobaltWebView.java +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/ChrobaltWebView.java @@ -10,8 +10,9 @@ import androidx.annotation.NonNull; +import dev.cobalt.coat.android_webview.H5vccPlatformService; import dev.cobalt.util.Log; -import dev.cobalt.coat.BuildConfig; +import dev.cobalt.util.UserAgent; public class ChrobaltWebView extends WebView { public void evalJavaScript(String javascript) { // Make sure it's public @@ -63,32 +64,6 @@ public void onPageFinished(WebView view, String url) { } - private String createUserAgentString() { - // TODO: sanitize inputs - String brand = this.webAppInterface.getRestrictedSystemProperty("ro.product.brand","defaultBrand"); - String model = this.webAppInterface.getRestrictedSystemProperty("ro.product.model","defaultModel"); - String firmware = this.webAppInterface.getRestrictedSystemProperty("ro.build.id","defaultFirmware"); - String chipset = this.webAppInterface.getRestrictedSystemProperty("ro.board.platform","defaultChipset"); - String oemKey = this.webAppInterface.getRestrictedSystemProperty("ro.oem.key1","defaultModelYear"); - String integrator = this.webAppInterface.getRestrictedSystemProperty("ro.product.manufacturer","defaultIntegrator"); - String androidVersion = this.webAppInterface.getRestrictedSystemProperty("ro.build.version.release","defaultAndroidVersion"); - String abi = this.webAppInterface.getRestrictedSystemProperty("ro.product.cpu.abi", "defaultABI"); - String aux = this.bridge.getUserAgentAuxField(); - String modelYear = "20" + oemKey.substring(9, 11); - - // TODO: Resolve missing and hardcoded fields - String customUserAgent = String.format("Mozilla/5.0 (Linux %s; Android %s) %s (unlike Gecko)" + - " v8/8.8.278.8-jit gles Starboard/%s, %s_ATV_%s_%s/%s" + - " (%s, %s) %s", - abi, androidVersion, - "Cobalt/26.lts.99.42-gold","17", - integrator, chipset, modelYear, firmware, - brand, model, aux - ); - Log.e(TAG, "Custom User-Agent: " + customUserAgent); - return customUserAgent; - } - public ChrobaltWebView(@NonNull Context context, @NonNull StarboardBridge bridge) { super(context); @@ -101,7 +76,7 @@ public ChrobaltWebView(@NonNull Context context, @NonNull StarboardBridge bridge // Enable JavaScript webSettings.setJavaScriptEnabled(true); - webSettings.setUserAgentString(createUserAgentString()); + webSettings.setUserAgentString(new UserAgent(context).createUserAgentString()); // Set mixed content mode to allow all content to be loaded, regardless of the security origin webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); @@ -115,6 +90,10 @@ public ChrobaltWebView(@NonNull Context context, @NonNull StarboardBridge bridge // Disable transition icon webSettings.setMediaPlaybackRequiresUserGesture(false); + // Add all implementations of dev.cobalt.coat.android_webview.WebAppInterface + H5vccPlatformService h5vccPlatformService = new H5vccPlatformService(bridge); + addJavascriptInterface(h5vccPlatformService, h5vccPlatformService.getJavaScriptInterfaceName()); + addJavascriptInterface(this.webAppInterface, "Android"); setWebViewClient(this.webViewClient); diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java index ad241235f6a3..a119fb9b1214 100644 --- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java @@ -183,7 +183,7 @@ public void registerCobaltService(CobaltService.Factory factory) { } @SuppressWarnings("unused") - boolean hasCobaltService(String serviceName) { + public boolean hasCobaltService(String serviceName) { boolean weHaveIt =cobaltServiceFactories.get(serviceName) != null; Log.e(TAG, "From bridge, hasCobaltService:" + serviceName + " got? : " + weHaveIt); return weHaveIt; @@ -202,7 +202,7 @@ public void callbackFromService(long name, String foo) { } @SuppressWarnings("unused") - CobaltService openCobaltService(long nativeService, String serviceName) { + public CobaltService openCobaltService(long nativeService, String serviceName) { if (cobaltServices.get(serviceName) != null) { // Attempting to re-open an already open service fails. Log.e(TAG, String.format("Cannot open already open service %s", serviceName)); @@ -226,13 +226,13 @@ public CobaltService getOpenedCobaltService(String serviceName) { } @SuppressWarnings("unused") - void closeCobaltService(String serviceName) { + public void closeCobaltService(String serviceName) { Log.i(TAG, String.format("Close service: %s", serviceName)); cobaltServices.remove(serviceName); } // Differing impl - void sendToCobaltService(String serviceName, byte [] data) { + public void sendToCobaltService(String serviceName, byte [] data) { Log.i(TAG, String.format("Send to : %s data: %s", serviceName, Arrays.toString(data))); CobaltService service = cobaltServices.get(serviceName); if (service == null) { diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/WebAppInterface.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/WebAppInterface.java index 257afda3abdf..dda5eb3a4ab3 100644 --- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/WebAppInterface.java +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/WebAppInterface.java @@ -6,9 +6,7 @@ import android.util.Base64; import android.webkit.JavascriptInterface; import dev.cobalt.util.Log; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; +import dev.cobalt.util.SystemPropertiesHelper; /** Native call bridge */ public class WebAppInterface { @@ -21,43 +19,35 @@ public class WebAppInterface { bridge = b; } - @JavascriptInterface - public boolean has_platform_service(String servicename) { - return bridge.hasCobaltService(servicename); - } - - @JavascriptInterface - public void open_platform_service(long number, String servicename) { - bridge.openCobaltService(number, servicename); - } - - @JavascriptInterface - public void close_platform_service(String servicename) { - bridge.closeCobaltService(servicename); - } - - @JavascriptInterface - public void platform_service_send(String servicename, String base64Data) { - byte[] data = Base64.decode(base64Data, Base64.DEFAULT); - bridge.sendToCobaltService(servicename, data); - } +// @JavascriptInterface +// public boolean has_platform_service(String servicename) { +// return bridge.hasCobaltService(servicename); +// } +// +// @JavascriptInterface +// public void open_platform_service(long number, String servicename) { +// bridge.openCobaltService(number, servicename); +// } +// +// @JavascriptInterface +// public void close_platform_service(String servicename) { +// bridge.closeCobaltService(servicename); +// } +// +// @JavascriptInterface +// public void platform_service_send(String servicename, String base64Data) { +// byte[] data = Base64.decode(base64Data, Base64.DEFAULT); +// bridge.sendToCobaltService(servicename, data); +// } @JavascriptInterface public String getSystemProperty(String propertyName, String defaultValue) { - return System.getProperty(propertyName, defaultValue); + return SystemPropertiesHelper.getSystemProperty(propertyName, defaultValue); } @JavascriptInterface public String getRestrictedSystemProperty(String propName, String defaultValue) { - try { - Process process = Runtime.getRuntime().exec("getprop " + propName); - BufferedReader bufferedReader = - new BufferedReader(new InputStreamReader(process.getInputStream())); - - return bufferedReader.readLine(); - } catch (IOException e) { - return defaultValue; - } + return SystemPropertiesHelper.getRestrictedSystemProperty(propName, defaultValue); } @JavascriptInterface diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/android_webview/H5vccPlatformService.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/android_webview/H5vccPlatformService.java new file mode 100644 index 000000000000..f3174eef2462 --- /dev/null +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/android_webview/H5vccPlatformService.java @@ -0,0 +1,40 @@ +package dev.cobalt.coat.android_webview; + +import android.util.Base64; +import android.webkit.JavascriptInterface; +import dev.cobalt.coat.StarboardBridge; + +public class H5vccPlatformService implements WebAppInterface { + StarboardBridge bridge; + + // Instantiate the interface and set the context + public H5vccPlatformService(StarboardBridge b) { + bridge = b; + } + + @Override + public String getJavaScriptInterfaceName() { + return "Android_H5vccPlatformService"; + } + + @JavascriptInterface + public boolean has_platform_service(String servicename) { + return bridge.hasCobaltService(servicename); + } + + @JavascriptInterface + public void open_platform_service(long number, String servicename) { + bridge.openCobaltService(number, servicename); + } + + @JavascriptInterface + public void close_platform_service(String servicename) { + bridge.closeCobaltService(servicename); + } + + @JavascriptInterface + public void platform_service_send(String servicename, String base64Data) { + byte[] data = Base64.decode(base64Data, Base64.DEFAULT); + bridge.sendToCobaltService(servicename, data); + } +} diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/android_webview/WebAppInterface.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/android_webview/WebAppInterface.java new file mode 100644 index 000000000000..0422ce7c15e1 --- /dev/null +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/android_webview/WebAppInterface.java @@ -0,0 +1,7 @@ +package dev.cobalt.coat.android_webview; + +public interface WebAppInterface { + + // the name is used by WebView.addJavascriptInterface (Object object, String name) + public String getJavaScriptInterfaceName(); +} diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/util/SystemPropertiesHelper.java b/starboard/android/apk/app/src/main/java/dev/cobalt/util/SystemPropertiesHelper.java index 24a87169a039..9ab7d03c7223 100644 --- a/starboard/android/apk/app/src/main/java/dev/cobalt/util/SystemPropertiesHelper.java +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/util/SystemPropertiesHelper.java @@ -1,51 +1,55 @@ -// Copyright 2017 The Cobalt Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package dev.cobalt.util; import static dev.cobalt.util.Log.TAG; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; import java.lang.reflect.Method; -/** Utility class for accessing system properties via reflection. */ public class SystemPropertiesHelper { - private static Method getStringMethod; - - static { - try { - getStringMethod = - ClassLoader.getSystemClassLoader() - .loadClass("android.os.SystemProperties") - .getMethod("get", String.class); - if (getStringMethod == null) { - Log.e(TAG, "Couldn't load system properties getString"); - } - } catch (Exception exception) { - Log.e(TAG, "Exception looking up system properties methods: ", exception); + private static Method getStringMethod; + + static { + try { + getStringMethod = + ClassLoader.getSystemClassLoader() + .loadClass("android.os.SystemProperties") + .getMethod("get", String.class); + if (getStringMethod == null) { + Log.e(TAG, "Couldn't load system properties getString"); + } + } catch (Exception exception) { + Log.e(TAG, "Exception looking up system properties methods: ", exception); + } } - } - private SystemPropertiesHelper() {} + private SystemPropertiesHelper() {} + + public static String getString(String property) { + if (getStringMethod != null) { + try { + return (String) getStringMethod.invoke(null, new Object[] {property}); + } catch (Exception exception) { + Log.e(TAG, "Exception getting system property: ", exception); + } + } + return null; + } + + public static String getRestrictedSystemProperty(String propName, String defaultValue) { + try { + Process process = Runtime.getRuntime().exec("getprop " + propName); + BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(process.getInputStream())); + + return bufferedReader.readLine(); + } catch (IOException e) { + return defaultValue; + } + } - public static String getString(String property) { - if (getStringMethod != null) { - try { - return (String) getStringMethod.invoke(null, new Object[] {property}); - } catch (Exception exception) { - Log.e(TAG, "Exception getting system property: ", exception); - } + public static String getSystemProperty(String propertyName, String defaultValue) { + return System.getProperty(propertyName, defaultValue); } - return null; - } } diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/util/UserAgent.java b/starboard/android/apk/app/src/main/java/dev/cobalt/util/UserAgent.java new file mode 100644 index 000000000000..d4ff3ef587c2 --- /dev/null +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/util/UserAgent.java @@ -0,0 +1,60 @@ +package dev.cobalt.util; + +import static dev.cobalt.util.Log.TAG; + +import android.content.Context; +import android.content.pm.PackageManager; + + +public class UserAgent { + private final Context appContext; + + public UserAgent(Context context) { + this.appContext = context; + } + + public String createUserAgentString() { + // TODO: sanitize inputs + String brand = SystemPropertiesHelper.getRestrictedSystemProperty("ro.product.brand","defaultBrand"); + String model = SystemPropertiesHelper.getRestrictedSystemProperty("ro.product.model","defaultModel"); + String firmware = SystemPropertiesHelper.getRestrictedSystemProperty("ro.build.id","defaultFirmware"); + String chipset = SystemPropertiesHelper.getRestrictedSystemProperty("ro.board.platform","defaultChipset"); + String oemKey = SystemPropertiesHelper.getRestrictedSystemProperty("ro.oem.key1","defaultModelYear"); + String integrator = SystemPropertiesHelper.getRestrictedSystemProperty("ro.product.manufacturer","defaultIntegrator"); + String androidVersion = SystemPropertiesHelper.getRestrictedSystemProperty("ro.build.version.release","defaultAndroidVersion"); + String abi = SystemPropertiesHelper.getRestrictedSystemProperty("ro.product.cpu.abi", "defaultABI"); + String aux = this.getUserAgentAuxField(); + String modelYear = "20" + oemKey.substring(9, 11); + + // TODO: Resolve missing and hardcoded fields + String customUserAgent = String.format("Mozilla/5.0 (Linux %s; Android %s) %s (unlike Gecko)" + + " v8/8.8.278.8-jit gles Starboard/%s, %s_ATV_%s_%s/%s" + + " (%s, %s) %s", + abi, androidVersion, + "Cobalt/26.lts.99.42-gold","17", + integrator, chipset, modelYear, firmware, + brand, model, aux + ); + Log.e(TAG, "Custom User-Agent: " + customUserAgent); + return customUserAgent; + } + + protected String getUserAgentAuxField() { + StringBuilder sb = new StringBuilder(); + + String packageName = appContext.getApplicationInfo().packageName; + sb.append(packageName); + sb.append('/'); + + try { + sb.append(appContext.getPackageManager().getPackageInfo(packageName, 0).versionName); + } catch (PackageManager.NameNotFoundException ex) { + // Should never happen + Log.e(TAG, "Can't find our own package", ex); + } + + return sb.toString(); + } + + +}