diff options
author | Mukilan Thiyagarajan <mukilan@igalia.com> | 2024-01-22 18:30:15 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-22 13:00:15 +0000 |
commit | d7de206dbd459e8c8bf121f73755d12569c6cc55 (patch) | |
tree | 34af7250352bd0b5c00aa56bfac6b086d30713a4 | |
parent | 8e6bdb69b1b5110ac7fce5801d8ee3e9fd7c6354 (diff) | |
download | servo-d7de206dbd459e8c8bf121f73755d12569c6cc55.tar.gz servo-d7de206dbd459e8c8bf121f73755d12569c6cc55.zip |
Preliminary Android build support (#31086)
* Android build
* Fixes
* More fixes
- Still failing in the linking step
* More work on getting linking working
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* android: use mozjs with ndk r25c. loads servo.org
more android build fixes.
* fix ./mach run for android and make it follow logs
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* add experimental logic for compositor pause/resume
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* pass DPI from android to simpleservo
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* ci: add android workflow
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* switch to ANDROID_SDK_ROOT and ANDROID_NDK_ROOT vars
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* upgrade gradle to 4.10.1
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* upgrade to gradle 5.1.1
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* upgrade to gradle 8 and agp 8
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* make compositing work again with external present
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* android: improve mach support for non-NixOS and CI
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* fix sampler compilation bug introduced in #30490
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* ci: add android build to main workflow
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* gradle: set MinSdk = targetSdk = 30
NDK requires we compile against the minSdk API level
which is 30 in our case.
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* add instructions for android in README.md
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* apk: move servosurface to servoview
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* apk: uncomment the mediasession callbacks on MainActivity
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* apk: fix crash on MainAtivity.onDestroy
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* apk: drop VR, arm 5 and unused code
This commit drops:
* support for google, oculusvr
* support for arm5 architecture
and also removes
* fakeld scripts
* unused java code
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* cleanup shell.nix
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* android: add FIXMEs for gstreamer code
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* apk: remove commented code and debug logs
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* cleanup ServoView.java
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* mach: comment call to download gstreamer deps for android
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* disable bluetooth for jniapi as blurdroid is broken
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* fixup! README.md
* fixup! remove change in Cargo.toml
* fixup! move shell variables together
* fixup! cleanup jniapi/Cargo.toml comments
* delete commented gstreamer related android code
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* remove unused config variable in servbuild
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* android: more cleanup
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* force no_static_freetype only for android
* use actions to manage sdk, ndk and java
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* rename embedder event names to be more clear.
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* link to startup crash issue
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* fix lint issues
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* upgrade env_logger to 0.10 with duplicate exception
libservo and android_logger can use env_logger 0.10
but quickcheck is still stuck on 0.8 and has not seen
any activity in the last 2 years. This commit adds
a duplicate exception until the quickcheck dependency
can be upgraded (or replaced)
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* android: fix comments
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* disable jemalloc on android
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
* fixup! replace linux with android in cfg
---------
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
58 files changed, 905 insertions, 1364 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml index 836726e6d8e..e1bdd061199 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,26 +1,17 @@ -[target.arm-linux-androideabi] -linker = "./support/android/fakeld/fake-ld-arm.sh" -ar = "arm-linux-androideabi-ar" +[target.aarch64-linux-android] +linker = "aarch64-linux-android30-clang" [target.armv7-linux-androideabi] -linker = "./support/android/fakeld/fake-ld-armv7.sh" -ar = "arm-linux-androideabi-ar" +linker = "armv7a-linux-androideabi30-clang" -[target.aarch64-linux-android] -linker = "./support/android/fakeld/fake-ld-arm64.sh" -ar = "aarch64-linux-android-ar" +[target.armv-linux-androideabi] +linker = "armv7a-linux-androideabi30-clang" [target.i686-linux-android] -linker = "./support/android/fakeld/fake-ld-x86.sh" -ar = "i686-linux-android-ar" - -[target.arm-unknown-linux-gnueabihf] -linker = "arm-linux-gnueabihf-gcc" -ar = "arm-linux-gnueabihf-ar" +linker = "i686-linux-android30-clang" -[target.aarch64-unknown-linux-gnu] -linker = "aarch64-linux-gnu-gcc" -ar = "aarch64-linux-gnu-ar" +[target.x86_64-linux-android] +linker = "x86_64-linux-android30-clang" [target.x86_64-pc-windows-msvc] linker = "lld-link.exe" diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 00000000000..db4cbde755d --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,107 @@ +name: Android +on: + workflow_call: + inputs: + profile: + required: false + default: "release" + type: string + workflow_dispatch: + inputs: + profile: + required: false + default: "release" + type: string + options: ["release", "debug", "production"] + push: + branches: ["try-android"] + +env: + RUST_BACKTRACE: 1 + SHELL: /bin/bash + SCCACHE_GHA_ENABLED: "true" + CCACHE: "sccache" + CARGO_INCREMENTAL: 0 + +jobs: + build: + name: Android Build + runs-on: ubuntu-22.04 + strategy: + matrix: + arch: ['armv7-linux-androideabi', 'i686-linux-android'] + steps: + - uses: actions/checkout@v3 + if: github.event_name != 'issue_comment' && github.event_name != 'pull_request_target' + with: + fetch-depth: 2 + # This is necessary to checkout the pull request if this run was triggered + # via an `issue_comment` action on a pull request. + - uses: actions/checkout@v3 + if: github.event_name == 'issue_comment' || github.event_name == 'pull_request_target' + with: + ref: refs/pull/${{ github.event.issue.number || github.event.number }}/head + fetch-depth: 2 + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + - name: Install taplo + uses: baptiste0928/cargo-install@v2 + with: + crate: taplo-cli + locked: true + - name: Bootstrap Python + run: python3 -m pip install --upgrade pip virtualenv + - name: Bootstrap dependencies + run: sudo apt update && python3 ./mach bootstrap + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + with: + packages: 'tools platform-tools platforms;android-33' + - name: Install Android NDK + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r25c + - name: Build (arch ${{ matrix.arch }} profile ${{ inputs.profile }}) + env: + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} + run: | + python3 ./mach build --android --target ${{ matrix.arch }} --${{ inputs.profile }} + # TODO: This is disabled since APK crashes during startup. + # See https://github.com/servo/servo/issues/31134 + # - name: Script tests + # run: ./mach test-android-startup + - name: Rename build timing + run: cp -r target/cargo-timings target/cargo-timings-android-${{ matrix.arch }} + - name: Archive build timing + uses: actions/upload-artifact@v3 + with: + name: cargo-timings + # Using a wildcard here ensures that the archive includes the path. + path: target/cargo-timings-* + - name: Upload APK artifact for mach package + uses: actions/upload-artifact@v3 + with: + name: android-${{ matrix.arch }}-${{ inputs.profile }} + path: target/android/${{ matrix.arch }}/${{ inputs.profile }}/servoapp.apk + + result: + name: Result + runs-on: ubuntu-latest + if: always() + # `needs: 'build'` is necessary to detect cancellation + needs: + - "build" + steps: + - name: Mark the job as successful + run: exit 0 + if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + - name: Mark the job as unsuccessful + run: exit 1 + if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 82defcc2f6c..649f66a4d82 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -76,7 +76,7 @@ jobs: let platforms = []; if (platform == "all") { - platforms = [ "linux", "windows", "macos" ]; + platforms = [ "linux", "windows", "macos", "android" ]; } else { platforms = [ platform ]; } @@ -125,6 +125,15 @@ jobs: unit-tests: ${{ fromJson(needs.decision.outputs.configuration).unit_tests }} secrets: inherit + build-android: + name: Android + needs: ["decision"] + if: ${{ contains(fromJson(needs.decision.outputs.configuration).platforms, 'android') }} + uses: ./.github/workflows/linux.yml + with: + profile: "release" + secrets: inherit + build-result: name: Result runs-on: ubuntu-latest @@ -135,7 +144,7 @@ jobs: - "build-win" - "build-mac" - "build-linux" - + - "build-android" steps: - name: Mark skipped jobs as successful if: ${{ fromJson(needs.decision.outputs.configuration).platforms[0] != null }} diff --git a/Cargo.lock b/Cargo.lock index 18a1040e12b..0a787cd600a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,20 +128,20 @@ checksum = "80b9e34fcbf29c0563547cb2ecce9b49504597cad6166769b1e4efb45c6c2951" [[package]] name = "android_log-sys" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" [[package]] name = "android_logger" -version = "0.10.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9ed09b18365ed295d722d0b5ed59c01b79a826ff2d2a8f73d5ecca8e6fb2f66" +checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" dependencies = [ "android_log-sys", - "env_logger", - "lazy_static", + "env_logger 0.10.1", "log", + "once_cell", ] [[package]] @@ -1604,8 +1604,18 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ - "atty", + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ "humantime", + "is-terminal", "log", "regex", "termcolor", @@ -2941,6 +2951,17 @@ dependencies = [ ] [[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.2", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3379,7 +3400,7 @@ dependencies = [ "devtools", "devtools_traits", "embedder_traits", - "env_logger", + "env_logger 0.10.1", "euclid", "gaol", "gfx", @@ -4568,7 +4589,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ - "env_logger", + "env_logger 0.8.4", "log", "rand", ] @@ -5379,6 +5400,7 @@ version = "0.0.1" dependencies = [ "jemalloc-sys", "jemallocator", + "libc", "winapi", ] @@ -5592,7 +5614,7 @@ version = "0.0.1" dependencies = [ "backtrace", "cbindgen", - "env_logger", + "env_logger 0.10.1", "keyboard-types", "lazy_static", "libc", @@ -5606,10 +5628,8 @@ dependencies = [ name = "simpleservo_jniapi" version = "0.0.1" dependencies = [ - "android_injected_glue", "android_logger", "cc", - "gstreamer", "jni", "libc", "log", @@ -5906,9 +5926,9 @@ dependencies = [ [[package]] name = "surfman" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca39befaf946247c5d3323465a9ec86c4a05523bb87a0d3eb07e71c15181a338" +checksum = "db2e4280229411d6eb8a8f873152dece1904df2682003bdc748adc181e003568" dependencies = [ "bitflags 1.3.2", "cfg_aliases", diff --git a/Cargo.toml b/Cargo.toml index 5495afa005a..c5f7aa60a9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ data-url = "0.1.0" devtools_traits = { path = "components/shared/devtools" } embedder_traits = { path = "components/shared/embedder" } encoding_rs = "0.8" -env_logger = "0.8" +env_logger = "0.10" euclid = "0.22" fnv = "1.0" fxhash = "0.2" diff --git a/README.md b/README.md index f935df82b4a..2c3a56144f6 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,27 @@ though of course it doesn’t produce a binary you can run. ### Building for Android target +Prerequisites: +Servo's build system assumes that both the Android SDK & NDK are +already installed and expects the paths to be specified via the +environment variables `ANDROID_SDK_ROOT` and `ANDROID_NDK_ROOT`. + +Servo also expects the following components are installed via +sdkmanager: + +For building: + +``` sh +sdkmanager install platform-tools platforms;android-33 +``` + +To run in emulator, also install the related components: + +``` sh +sdkmanager install emulator system-images;android-33;google_apis;x86 +``` + +Build commands: For ARM (`armv7-linux-androideabi`, most phones): ``` sh @@ -131,6 +152,12 @@ For x86 (typically for the emulator): ./mach package --release --target i686-linux-android ``` +Install the APK to the device or emulator: + +``` sh +./mach install --release --android +``` + ## Running Run Servo with the command: diff --git a/components/allocator/Cargo.toml b/components/allocator/Cargo.toml index 3d29fca5cf8..7a017e4cf92 100644 --- a/components/allocator/Cargo.toml +++ b/components/allocator/Cargo.toml @@ -9,9 +9,12 @@ publish = false [lib] path = "lib.rs" -[target.'cfg(not(windows))'.dependencies] +[target.'cfg(not(any(windows, target_os = "android")))'.dependencies] jemallocator = { workspace = true } jemalloc-sys = { workspace = true } [target.'cfg(windows)'.dependencies] winapi = { workspace = true, features = ["heapapi"] } + +[target.'cfg(target_os = "android")'.dependencies] +libc = { workspace = true } diff --git a/components/allocator/lib.rs b/components/allocator/lib.rs index d4e8c73b318..9d7c0b466f0 100644 --- a/components/allocator/lib.rs +++ b/components/allocator/lib.rs @@ -9,7 +9,7 @@ static ALLOC: Allocator = Allocator; pub use crate::platform::*; -#[cfg(not(windows))] +#[cfg(not(any(windows, target_os = "android")))] mod platform { use std::os::raw::c_void; @@ -28,6 +28,21 @@ mod platform { } } +#[cfg(target_os = "android")] +mod platform { + pub use std::alloc::System as Allocator; + use std::os::raw::c_void; + + /// Get the size of a heap block. + pub unsafe extern "C" fn usable_size(ptr: *const c_void) -> usize { + libc::malloc_usable_size(ptr) + } + + pub mod libc_compat { + pub use libc::{free, malloc, realloc}; + } +} + #[cfg(windows)] mod platform { pub use std::alloc::System as Allocator; diff --git a/components/background_hang_monitor/background_hang_monitor.rs b/components/background_hang_monitor/background_hang_monitor.rs index f8e26972a0f..1aacadfa19a 100644 --- a/components/background_hang_monitor/background_hang_monitor.rs +++ b/components/background_hang_monitor/background_hang_monitor.rs @@ -100,9 +100,9 @@ impl BackgroundHangMonitorRegister for HangMonitorRegister { not(any(target_arch = "arm", target_arch = "aarch64")) ))] let sampler = crate::sampler_linux::LinuxSampler::new(); - #[cfg(all( - any(target_os = "android", target_os = "linux"), - any(target_arch = "arm", target_arch = "aarch64") + #[cfg(any( + target_os = "android", + all(target_os = "linux", any(target_arch = "arm", target_arch = "aarch64")) ))] let sampler = crate::sampler::DummySampler::new(); diff --git a/components/bluetooth/Cargo.toml b/components/bluetooth/Cargo.toml index 4046b256a1b..536a29e583f 100644 --- a/components/bluetooth/Cargo.toml +++ b/components/bluetooth/Cargo.toml @@ -22,6 +22,7 @@ servo_rand = { path = "../rand" } uuid = { workspace = true } [features] +default = ["bluetooth-test"] native-bluetooth = ["blurz", "blurdroid", "blurmac", "bluetooth-test"] bluetooth-test = ["blurmock"] diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index d0af82067fc..5ef8ffc20a6 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -501,6 +501,33 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { self.shutdown_state = ShutdownState::FinishedShuttingDown; } + /// The underlying native surface can be lost during servo's lifetime. + /// On Android, for example, this happens when the app is sent to background. + /// We need to unbind the surface so that we don't try to use it again. + pub fn invalidate_native_surface(&mut self) { + debug!("Invalidating native surface in compositor"); + if let Err(e) = self.webrender_surfman.unbind_native_surface_from_context() { + warn!("Unbinding native surface from context failed ({:?})", e); + } + } + + /// On Android, this function will be called when the app moves to foreground + /// and the system creates a new native surface that needs to bound to the current + /// context. + #[allow(unsafe_code)] + pub fn replace_native_surface(&mut self, native_widget: *mut c_void, coords: DeviceIntSize) { + debug!("Replacing native surface in compositor: {native_widget:?}"); + let connection = self.webrender_surfman.connection(); + let native_widget = + unsafe { connection.create_native_widget_from_ptr(native_widget, coords.to_untyped()) }; + if let Err(e) = self + .webrender_surfman + .bind_native_surface_to_context(native_widget) + { + warn!("Binding native surface to context failed ({:?})", e); + } + } + fn handle_browser_message(&mut self, msg: CompositorMsg) -> bool { match (msg, self.shutdown_state) { (_, ShutdownState::FinishedShuttingDown) => { diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 3aebe8d874c..c78ddb6ba85 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -10,6 +10,7 @@ use std::time::Duration; use embedder_traits::{EmbedderProxy, EventLoopWaker}; use euclid::Scale; use keyboard_types::KeyboardEvent; +use libc::c_void; use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId, TraversalDirection}; use script_traits::{MediaSessionActionType, MouseButton, TouchEventType, TouchId, WheelDelta}; use servo_geometry::DeviceIndependentPixel; @@ -105,6 +106,14 @@ pub enum EmbedderEvent { ChangeBrowserVisibility(TopLevelBrowsingContextId, bool), /// Virtual keyboard was dismissed IMEDismissed, + /// Sent on platforms like Android where the native widget surface can be + /// automatically destroyed by the system, for example when the app + /// is sent to background. + InvalidateNativeSurface, + /// Sent on platforms like Android where system recreates a new surface for + /// the native widget when it is brough back to foreground. This event + /// carries the pointer to the native widget and its new size. + ReplaceNativeSurface(*mut c_void, DeviceIntSize), } impl Debug for EmbedderEvent { @@ -139,6 +148,8 @@ impl Debug for EmbedderEvent { EmbedderEvent::ChangeBrowserVisibility(..) => write!(f, "ChangeBrowserVisibility"), EmbedderEvent::IMEDismissed => write!(f, "IMEDismissed"), EmbedderEvent::ClearCache => write!(f, "ClearCache"), + EmbedderEvent::InvalidateNativeSurface => write!(f, "InvalidateNativeSurface"), + EmbedderEvent::ReplaceNativeSurface(..) => write!(f, "ReplaceNativeSurface"), } } } diff --git a/components/gfx/platform/freetype/android/font_list.rs b/components/gfx/platform/freetype/android/font_list.rs index 8c4b2e38a17..44c6640468c 100644 --- a/components/gfx/platform/freetype/android/font_list.rs +++ b/components/gfx/platform/freetype/android/font_list.rs @@ -4,12 +4,13 @@ use std::path::Path; +use log::warn; use ucd::{Codepoint, UnicodeBlock}; use super::xml::{Attribute, Node}; use crate::text::util::is_cjk; -lazy_static! { +lazy_static::lazy_static! { static ref FONT_LIST: FontList = FontList::new(); } diff --git a/components/profile/Cargo.toml b/components/profile/Cargo.toml index 173fdbcbc2b..da55faa6a00 100644 --- a/components/profile/Cargo.toml +++ b/components/profile/Cargo.toml @@ -23,6 +23,6 @@ task_info = { path = "../../support/rust-task_info" } [target.'cfg(target_os = "linux")'.dependencies] regex = { workspace = true } -[target.'cfg(not(target_os = "windows"))'.dependencies] +[target.'cfg(not(any(target_os = "windows", target_os = "android")))'.dependencies] libc = { workspace = true } jemalloc-sys = { workspace = true } diff --git a/components/profile/mem.rs b/components/profile/mem.rs index 11542045f67..53990d7f05f 100644 --- a/components/profile/mem.rs +++ b/components/profile/mem.rs @@ -387,16 +387,16 @@ impl ReportsForest { //--------------------------------------------------------------------------- mod system_reporter { - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "android")))] use std::ffi::CString; - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "android")))] use std::mem::size_of; - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "android")))] use std::ptr::null_mut; #[cfg(target_os = "linux")] use libc::c_int; - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "android")))] use libc::{c_void, size_t}; use profile_traits::mem::{Report, ReportKind, ReporterRequest}; use profile_traits::path; @@ -499,10 +499,10 @@ mod system_reporter { None } - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "android")))] use jemalloc_sys::mallctl; - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "android")))] fn jemalloc_stat(value_name: &str) -> Option<usize> { // Before we request the measurement of interest, we first send an "epoch" // request. Without that jemalloc gives cached statistics(!) which can be @@ -549,7 +549,7 @@ mod system_reporter { Some(value as usize) } - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "android"))] fn jemalloc_stat(_value_name: &str) -> Option<usize> { None } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 424f3fc647e..662a958460c 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -503,7 +503,14 @@ where EmbedderEvent::Resize => { return self.compositor.on_resize_window_event(); }, - + EmbedderEvent::InvalidateNativeSurface => { + self.compositor.invalidate_native_surface(); + }, + EmbedderEvent::ReplaceNativeSurface(native_widget, coords) => { + self.compositor + .replace_native_surface(native_widget, coords); + self.compositor.composite(); + }, EmbedderEvent::AllowNavigationResponse(pipeline_id, allowed) => { let msg = ConstellationMsg::AllowNavigationResponse(pipeline_id, allowed); if let Err(e) = self.constellation_chan.send(msg) { @@ -1091,6 +1098,9 @@ fn default_user_agent_string_for(agent: UserAgent) -> &'static str { const DESKTOP_UA_STRING: &'static str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Servo/1.0 Firefox/111.0"; + #[cfg(target_os = "android")] + const DESKTOP_UA_STRING: &'static str = ""; + match agent { UserAgent::Desktop => DESKTOP_UA_STRING, UserAgent::Android => "Mozilla/5.0 (Android; Mobile; rv:109.0) Servo/1.0 Firefox/111.0", diff --git a/components/webrender_surfman/lib.rs b/components/webrender_surfman/lib.rs index 4d2d4346d2c..8e4dc75cb79 100644 --- a/components/webrender_surfman/lib.rs +++ b/components/webrender_surfman/lib.rs @@ -216,4 +216,28 @@ impl WebrenderSurfman { let ref context = self.0.context.borrow(); device.get_proc_address(context, name) } + + pub fn unbind_native_surface_from_context(&self) -> Result<(), Error> { + let device = self.0.device.borrow_mut(); + let mut context = self.0.context.borrow_mut(); + let mut surface = device.unbind_surface_from_context(&mut context)?.unwrap(); + let _ = device.destroy_surface(&mut context, &mut surface)?; + Ok(()) + } + + pub fn bind_native_surface_to_context(&self, native_widget: NativeWidget) -> Result<(), Error> { + let mut device = self.0.device.borrow_mut(); + let mut context = self.0.context.borrow_mut(); + let surface_access = SurfaceAccess::GPUOnly; + let surface_type = SurfaceType::Widget { native_widget }; + let surface = device.create_surface(&context, surface_access, surface_type)?; + device + .bind_surface_to_context(&mut context, surface) + .map_err(|(err, mut surface)| { + let _ = device.destroy_surface(&mut context, &mut surface); + err + })?; + device.make_context_current(&context)?; + Ok(()) + } } diff --git a/etc/run_in_headless_android_emulator.py b/etc/run_in_headless_android_emulator.py index a3b5d573ef9..758af9b5a5d 100755 --- a/etc/run_in_headless_android_emulator.py +++ b/etc/run_in_headless_android_emulator.py @@ -79,8 +79,8 @@ def main(avd_name, apk_path, *args): def tool_path(directory, bin_name): - if "ANDROID_SDK" in os.environ: - path = os.path.join(os.environ["ANDROID_SDK"], directory, bin_name) + if "ANDROID_SDK_ROOT" in os.environ: + path = os.path.join(os.environ["ANDROID_SDK_ROOT"], directory, bin_name) if os.path.exists(path): return path diff --git a/etc/shell.nix b/etc/shell.nix index 39c8ff2eea9..3c55c1ab491 100644 --- a/etc/shell.nix +++ b/etc/shell.nix @@ -10,6 +10,10 @@ with import (builtins.fetchTarball { url = "https://github.com/oxalica/rust-overlay/archive/a0df72e106322b67e9c6e591fe870380bd0da0d5.tar.gz"; })) ]; + config = { + android_sdk.accept_license = true; + allowUnfree = true; + }; }; let rustToolchain = rust-bin.fromRustupToolchainFile ../rust-toolchain.toml; @@ -27,6 +31,25 @@ let # - glibc 2.38 (#31054) llvmPackages = llvmPackages_14; stdenv = llvmPackages.stdenv; + + buildToolsVersion = "33.0.2"; + androidComposition = androidenv.composeAndroidPackages { + buildToolsVersions = [ buildToolsVersion ]; + includeEmulator = true; + platformVersions = [ "33" ]; + includeSources = false; + includeSystemImages = true; + systemImageTypes = [ "google_apis" ]; + abiVersions = [ "x86" "armeabi-v7a" ]; + includeNDK = true; + ndkVersion = "25.2.9519653"; + useGoogleAPIs = false; + useGoogleTVAddOns = false; + includeExtras = [ + "extras;google;gcm" + ]; + }; + androidSdk = androidComposition.androidsdk; in stdenv.mkDerivation rec { name = "servo-env"; @@ -111,22 +134,33 @@ stdenv.mkDerivation rec { RUSTC_BOOTSTRAP = "crown"; })) + + # for android builds + # TODO: make this optional + openjdk17_headless + androidSdk ] ++ (lib.optionals stdenv.isDarwin [ darwin.apple_sdk.frameworks.AppKit ]); LIBCLANG_PATH = llvmPackages.clang-unwrapped.lib + "/lib/"; + # Required by ./mach build --android + ANDROID_SDK_ROOT = "${androidSdk}/libexec/android-sdk"; + ANDROID_NDK_ROOT = "${ANDROID_SDK_ROOT}/ndk-bundle"; + GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_SDK_ROOT}/build-tools/${buildToolsVersion}/aapt2"; + # Allow cargo to download crates SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt"; # Enable colored cargo and rustc output TERMINFO = "${ncurses.out}/share/terminfo"; + # Provide libraries that aren’t linked against but somehow required LD_LIBRARY_PATH = lib.makeLibraryPath [ # Fixes missing library errors - xorg.libXcursor xorg.libXrandr xorg.libXi libxkbcommon + zlib xorg.libXcursor xorg.libXrandr xorg.libXi libxkbcommon # [WARN script::dom::gpu] Could not get GPUAdapter ("NotFound") # TLA Err: Error: Couldn't request WebGPU adapter. diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs index cd9abc9b511..5a3d1e4cd74 100644 --- a/ports/libsimpleservo/api/src/lib.rs +++ b/ports/libsimpleservo/api/src/lib.rs @@ -18,6 +18,7 @@ use servo::compositing::windowing::{ AnimationState, EmbedderCoordinates, EmbedderEvent, EmbedderMethods, MouseWindowEvent, WindowMethods, }; +use servo::compositing::CompositeTarget; use servo::config::prefs::pref_map; pub use servo::config::prefs::{add_user_prefs, PrefValue}; use servo::embedder_traits::resources::{self, Resource, ResourceReaderMethods}; @@ -298,7 +299,12 @@ pub fn init( gl: gl.clone(), }); - let servo = Servo::new(embedder_callbacks, window_callbacks.clone(), None); + let servo = Servo::new( + embedder_callbacks, + window_callbacks.clone(), + None, + CompositeTarget::Window, + ); SERVO.with(|s| { let mut servo_glue = ServoGlue { @@ -569,6 +575,24 @@ impl ServoGlue { self.process_event(EmbedderEvent::Keyboard(key_event)) } + pub fn pause_compositor(&mut self) -> Result<(), &'static str> { + self.process_event(EmbedderEvent::InvalidateNativeSurface) + } + + pub fn resume_compositor( + &mut self, + native_surface: *mut c_void, + coords: Coordinates, + ) -> Result<(), &'static str> { + if native_surface.is_null() { + panic!("null passed for native_surface"); + } + self.process_event(EmbedderEvent::ReplaceNativeSurface( + native_surface, + coords.framebuffer, + )) + } + pub fn media_session_action( &mut self, action: MediaSessionActionType, @@ -789,6 +813,9 @@ impl ServoGlue { EmbedderMsg::Panic(reason, backtrace) => { self.callbacks.host_callbacks.on_panic(reason, backtrace); }, + EmbedderMsg::ReadyToPresent => { + self.servo.present(); + }, EmbedderMsg::Status(..) | EmbedderMsg::SelectFiles(..) | EmbedderMsg::MoveTo(..) | @@ -798,7 +825,6 @@ impl ServoGlue { EmbedderMsg::NewFavicon(..) | EmbedderMsg::HeadParsed | EmbedderMsg::SetFullscreenState(..) | - EmbedderMsg::ReadyToPresent | EmbedderMsg::ReportProfile(..) | EmbedderMsg::EventDelivered(..) => {}, } diff --git a/ports/libsimpleservo/jniapi/Cargo.toml b/ports/libsimpleservo/jniapi/Cargo.toml index 020767c41be..b74591ff7bf 100644 --- a/ports/libsimpleservo/jniapi/Cargo.toml +++ b/ports/libsimpleservo/jniapi/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "simpleservo_jniapi" version = "0.0.1" -build = "build.rs" authors = ["The Servo Project Developers"] license = "MPL-2.0" edition = "2018" @@ -14,21 +13,21 @@ test = false bench = false [dependencies] -android_injected_glue = "0.2" -android_logger = "0.10" -gstreamer = { workspace = true } +android_logger = "0.13" jni = "0.18.0" libc = { workspace = true } log = { workspace = true } serde_json = { workspace = true } -simpleservo = { path = "../api" } +# TODO: Once the native-bluetooth feature works for +# Android, remove the explicit feature list here. +simpleservo = { path = "../api", default-features = false, features = ["max_log_level", "webdriver"] } [build-dependencies] cc = "1.0" [features] debugmozjs = ["simpleservo/debugmozjs"] -default = ["max_log_level", "native-bluetooth", "webdriver"] +default = ["max_log_level", "webdriver", "no_static_freetype"] googlevr = ["simpleservo/googlevr"] js_backtrace = ["simpleservo/js_backtrace"] max_log_level = ["simpleservo/max_log_level"] @@ -36,3 +35,4 @@ media-gstreamer = ["simpleservo/media-gstreamer"] native-bluetooth = ["simpleservo/native-bluetooth"] webdriver = ["simpleservo/webdriver"] webgl_backtrace = ["simpleservo/webgl_backtrace"] +no_static_freetype = ["simpleservo/no_static_freetype"] diff --git a/ports/libsimpleservo/jniapi/build.rs b/ports/libsimpleservo/jniapi/build.rs deleted file mode 100644 index e97cb310111..00000000000 --- a/ports/libsimpleservo/jniapi/build.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::env; -use std::path::Path; - -fn main() { - // Get the NDK path from NDK_HOME env. - let ndk_path = - env::var_os("ANDROID_NDK").expect("Please set the ANDROID_NDK environment variable"); - let ndk_path = Path::new(&ndk_path); - - // compiling android_native_app_glue.c - let c_file = ndk_path - .join("sources") - .join("android") - .join("native_app_glue") - .join("android_native_app_glue.c"); - cc::Build::new() - .file(c_file) - .warnings(false) - .compile("android_native_app_glue"); - - // Get the output directory. - let out_dir = - env::var("OUT_DIR").expect("Cargo should have set the OUT_DIR environment variable"); - - println!("cargo:rustc-link-lib=static=android_native_app_glue"); - println!("cargo:rustc-link-search=native={}", out_dir); - println!("cargo:rustc-link-lib=log"); - println!("cargo:rustc-link-lib=android"); -} diff --git a/ports/libsimpleservo/jniapi/src/lib.rs b/ports/libsimpleservo/jniapi/src/lib.rs index 53dc59047eb..0293a8332e4 100644 --- a/ports/libsimpleservo/jniapi/src/lib.rs +++ b/ports/libsimpleservo/jniapi/src/lib.rs @@ -5,21 +5,18 @@ #![allow(non_snake_case)] use std::os::raw::{c_char, c_int, c_void}; -use std::ptr::{null, null_mut}; use std::sync::Arc; use std::thread; -use android_logger::{self, Filter}; -use gstreamer::debug_set_threshold_from_string; +use android_logger::{self, Config, FilterBuilder}; use jni::objects::{GlobalRef, JClass, JObject, JString, JValue}; use jni::sys::{jboolean, jfloat, jint, jstring, JNI_TRUE}; -use jni::{errors, JNIEnv, JavaVM}; +use jni::{JNIEnv, JavaVM}; use libc::{dup2, pipe, read}; -use log::Level; +use log::{debug, error, info, warn}; use simpleservo::{ - self, self, deinit, gl_glue, gl_glue, Coordinates, DeviceIntRect, EventLoopWaker, HostTrait, - InitOptions, InputMethodType, MediaSessionPlaybackState, MouseButton, PromptResult, ServoGlue, - ServoGlue, VRInitOptions, SERVO, SERVO, + self, gl_glue, Coordinates, DeviceIntRect, EventLoopWaker, HostTrait, InitOptions, + InputMethodType, MediaSessionPlaybackState, PromptResult, ServoGlue, SERVO, }; struct HostCallbacks { @@ -27,6 +24,19 @@ struct HostCallbacks { jvm: JavaVM, } +extern "C" { + fn ANativeWindow_fromSurface(env: *mut jni::sys::JNIEnv, surface: JObject) -> *mut c_void; +} + +#[no_mangle] +pub fn android_main() { + // FIXME(mukilan): this android_main is only present to stop + // the java side 'System.loadLibrary('simpleservo') call from + // failing due to undefined reference to android_main introduced + // by winit's android-activity crate. There is no way to disable + // this currently. +} + fn call<F>(env: &JNIEnv, f: F) where F: Fn(&mut ServoGlue) -> Result<(), &str>, @@ -51,11 +61,12 @@ pub fn Java_org_mozilla_servoview_JNIServo_version(env: JNIEnv, _class: JClass) pub fn Java_org_mozilla_servoview_JNIServo_init( env: JNIEnv, _: JClass, - activity: JObject, + _activity: JObject, opts: JObject, callbacks_obj: JObject, + surface: JObject, ) { - let (mut opts, log, log_str, gst_debug_str) = match get_options(&env, opts) { + let (mut opts, log, log_str, _gst_debug_str) = match get_options(&env, opts, surface) { Ok((opts, log, log_str, gst_debug_str)) => (opts, log, log_str, gst_debug_str), Err(err) => { throw(&env, &err); @@ -79,26 +90,26 @@ pub fn Java_org_mozilla_servoview_JNIServo_init( "compositing::compositor", "constellation::constellation", ]; - let mut filter = Filter::default().with_min_level(Level::Debug); + let mut filter_builder = FilterBuilder::new(); for &module in &filters { - filter = filter.with_allowed_module_path(module); + filter_builder.filter_module(module, log::LevelFilter::Debug); } if let Some(log_str) = log_str { for module in log_str.split(',') { - filter = filter.with_allowed_module_path(module); + filter_builder.filter_module(module, log::LevelFilter::Debug); } } - if let Some(gst_debug_str) = gst_debug_str { - debug_set_threshold_from_string(&gst_debug_str, true); - } - - android_logger::init_once(filter, Some("simpleservo")); + android_logger::init_once( + Config::default() + .with_max_level(log::LevelFilter::Debug) + .with_filter(filter_builder.build()) + .with_tag("simpleservo"), + ) } info!("init"); - initialize_android_glue(&env, activity); redirect_stdout_to_logcat(); let callbacks_ref = match env.new_global_ref(callbacks_obj) { @@ -329,12 +340,34 @@ pub fn Java_org_mozilla_servoview_JNIServo_pinchZoomEnd( } #[no_mangle] -pub fn Java_org_mozilla_servoview_JNIServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) { +pub fn Java_org_mozilla_servoview_JNIServo_click(env: JNIEnv, _: JClass, x: jfloat, y: jfloat) { debug!("click"); call(&env, |s| s.click(x as f32, y as f32)); } #[no_mangle] +pub fn Java_org_mozilla_servoview_JNIServo_pauseCompositor(env: JNIEnv, _: JClass) { + debug!("pauseCompositor"); + call(&env, |s| s.pause_compositor()); +} + +#[no_mangle] +pub fn Java_org_mozilla_servoview_JNIServo_resumeCompositor( + env: JNIEnv, + _: JClass, + surface: JObject, + coordinates: JObject, +) { + debug!("resumeCompositor"); + let widget = unsafe { ANativeWindow_fromSurface(env.get_native_interface(), surface) }; + let coords = jni_coords_to_rust_coords(&env, coordinates); + match coords { + Ok(coords) => call(&env, |s| s.resume_compositor(widget, coords.clone())), + Err(error) => throw(&env, &error), + } +} + +#[no_mangle] pub fn Java_org_mozilla_servoview_JNIServo_mediaSessionAction( env: JNIEnv, _: JClass, @@ -379,20 +412,6 @@ impl HostCallbacks { } impl HostTrait for HostCallbacks { - fn flush(&self) { - debug!("flush"); - let env = self.jvm.get_env().unwrap(); - env.call_method(self.callbacks.as_obj(), "flush", "()V", &[]) - .unwrap(); - } - - fn make_current(&self) { - debug!("make_current"); - let env = self.jvm.get_env().unwrap(); - env.call_method(self.callbacks.as_obj(), "makeCurrent", "()V", &[]) - .unwrap(); - } - fn prompt_alert(&self, message: String, _trusted: bool) { debug!("prompt_alert"); let env = self.jvm.get_env().unwrap(); @@ -446,9 +465,10 @@ impl HostTrait for HostCallbacks { .unwrap(); } - fn on_title_changed(&self, title: String) { + fn on_title_changed(&self, title: Option<String>) { debug!("on_title_changed"); let env = self.jvm.get_env().unwrap(); + let title = title.unwrap_or_else(String::new); let s = match new_string(&env, &title) { Ok(s) => s, Err(_) => return, @@ -529,7 +549,7 @@ impl HostTrait for HostCallbacks { fn on_ime_show( &self, - _type: InputEncoding, + _input_type: InputMethodType, _text: Option<(String, i32)>, _multiline: bool, _rect: DeviceIntRect, @@ -610,65 +630,16 @@ impl HostTrait for HostCallbacks { .unwrap(); } - fn on_devtools_started(&self, port: Result<u16, ()>) { + fn on_devtools_started(&self, port: Result<u16, ()>, _token: String) { match port { Ok(p) => info!("Devtools Server running on port {}", p), Err(()) => error!("Error running devtools server"), } } -} - -fn initialize_android_glue(env: &JNIEnv, activity: JObject) { - use android_injected_glue::{ffi, ANDROID_APP}; - - // From jni-rs to android_injected_glue - let clazz = Box::leak(Box::new(env.new_global_ref(activity).unwrap())); + fn show_context_menu(&self, _title: Option<String>, _items: Vec<String>) {} - let activity = Box::into_raw(Box::new(ffi::ANativeActivity { - clazz: clazz.as_obj().into_inner() as *mut c_void, - vm: env.get_java_vm().unwrap().get_java_vm_pointer() as *mut ffi::_JavaVM, - - callbacks: null_mut(), - env: null_mut(), - internalDataPath: null(), - externalDataPath: null(), - sdkVersion: 0, - instance: null_mut(), - assetManager: null_mut(), - obbPath: null(), - })); - - extern "C" fn on_app_cmd(_: *mut ffi::android_app, _: i32) {} - extern "C" fn on_input_event(_: *mut ffi::android_app, _: *const c_void) -> i32 { - 0 - } - - let app = Box::into_raw(Box::new(ffi::android_app { - activity, - onAppCmd: on_app_cmd, - onInputEvent: on_input_event, - - userData: null_mut(), - config: null(), - savedState: null_mut(), - savedStateSize: 0, - looper: null_mut(), - inputQueue: null(), - window: null_mut(), - contentRect: ffi::ARect { - left: 0, - top: 0, - right: 0, - bottom: 0, - }, - activityState: 0, - destroyRequested: 0, - })); - - unsafe { - ANDROID_APP = app; - } + fn on_panic(&self, _reason: String, _backtrace: Option<String>) {} } extern "C" { @@ -803,28 +774,21 @@ fn jni_coords_to_rust_coords(env: &JNIEnv, obj: JObject) -> Result<Coordinates, fn get_field<'a>( env: &'a JNIEnv, - obj: JObject, + obj: JObject<'a>, field: &str, type_: &str, ) -> Result<Option<JValue<'a>>, String> { if env.get_field_id(obj, field, type_).is_err() { - return Err(format!("Can't find `{}` field", &field)); + return Err(format!("Can't find `{}` field", field)); } env.get_field(obj, field, type_) .map(|value| Some(value)) - .or_else(|e| match *e.kind() { - errors::ErrorKind::NullPtr(_) => Ok(None), - _ => Err(format!( - "Can't find `{}` field: {}", - &field, - e.description() - )), - }) + .or_else(|_| Err(format!("Can't find `{}` field", field))) } fn get_non_null_field<'a>( env: &'a JNIEnv, - obj: JObject, + obj: JObject<'a>, field: &str, type_: &str, ) -> Result<JValue<'a>, String> { @@ -851,6 +815,7 @@ fn get_string(env: &JNIEnv, obj: JObject, field: &str) -> Result<Option<String>, fn get_options( env: &JNIEnv, opts: JObject, + surface: JObject, ) -> Result<(InitOptions, bool, Option<String>, Option<String>), String> { let args = get_string(env, opts, "args")?; let url = get_string(env, opts, "url")?; @@ -862,13 +827,6 @@ fn get_options( let log = get_non_null_field(env, opts, "enableLogs", "Z")? .z() .map_err(|_| "enableLogs not a boolean")?; - let enable_subpixel_text_antialiasing = - get_non_null_field(env, opts, "enableSubpixelTextAntialiasing", "Z")? - .z() - .map_err(|_| "enableSubpixelTextAntialiasing not a boolean")?; - let vr_pointer = get_non_null_field(env, opts, "VRExternalContext", "J")? - .j() - .map_err(|_| "VRExternalContext is not a long")? as *mut c_void; let coordinates = get_non_null_field( env, opts, @@ -885,20 +843,16 @@ fn get_options( None => None, }; + let native_window = unsafe { ANativeWindow_fromSurface(env.get_native_interface(), surface) }; let opts = InitOptions { args: args.unwrap_or(vec![]), - url, coordinates, density, - enable_subpixel_text_antialiasing, - vr_init: if vr_pointer.is_null() { - VRInitOptions::None - } else { - VRInitOptions::VRExternal(vr_pointer) - }, xr_discovery: None, gl_context_pointer: None, native_display_pointer: None, + surfman_integration: simpleservo::SurfmanIntegration::Widget(native_window), + prefs: None, }; Ok((opts, log, log_str, gst_debug_str)) } diff --git a/python/servo/bootstrap_commands.py b/python/servo/bootstrap_commands.py index 1c7afc957d8..33d3b20aa2f 100644 --- a/python/servo/bootstrap_commands.py +++ b/python/servo/bootstrap_commands.py @@ -12,7 +12,6 @@ import glob import json import os import os.path as path -import platform import re import subprocess import sys @@ -30,7 +29,7 @@ from mach.decorators import ( import servo.platform from servo.command_base import CommandBase, cd, check_call -from servo.util import delete, download_bytes, download_file, extract, check_hash +from servo.util import delete, download_bytes @CommandProvider @@ -66,150 +65,6 @@ class MachCommands(CommandBase): return 1 return 0 - @Command('bootstrap-android', - description='Install the Android SDK and NDK.', - category='bootstrap') - @CommandArgument('--build', - action='store_true', - help='Install Android-specific dependencies for building') - @CommandArgument('--emulator-x86', - action='store_true', - help='Install Android x86 emulator and system image') - @CommandArgument('--accept-all-licences', - action='store_true', - help='For non-interactive use') - def bootstrap_android(self, build=False, emulator_x86=False, accept_all_licences=False): - if not (build or emulator_x86): - print("Must specify `--build` or `--emulator-x86` or both.") - - ndk = "android-ndk-r15c-{system}-{arch}" - tools = "sdk-tools-{system}-4333796" - - emulator_platform = "android-28" - emulator_image = "system-images;%s;google_apis;x86" % emulator_platform - - known_sha1 = { - # https://dl.google.com/android/repository/repository2-1.xml - "sdk-tools-darwin-4333796.zip": "ed85ea7b59bc3483ce0af4c198523ba044e083ad", - "sdk-tools-linux-4333796.zip": "8c7c28554a32318461802c1291d76fccfafde054", - "sdk-tools-windows-4333796.zip": "aa298b5346ee0d63940d13609fe6bec621384510", - - # https://developer.android.com/ndk/downloads/older_releases - "android-ndk-r15c-windows-x86.zip": "f2e47121feb73ec34ced5e947cbf1adc6b56246e", - "android-ndk-r15c-windows-x86_64.zip": "970bb2496de0eada74674bb1b06d79165f725696", - "android-ndk-r15c-darwin-x86_64.zip": "ea4b5d76475db84745aa8828000d009625fc1f98", - "android-ndk-r15c-linux-x86_64.zip": "0bf02d4e8b85fd770fd7b9b2cdec57f9441f27a2", - } - - toolchains = path.join(self.context.topdir, "android-toolchains") - if not path.isdir(toolchains): - os.makedirs(toolchains) - - def download(target_dir, name, flatten=False): - final = path.join(toolchains, target_dir) - if path.isdir(final): - return - - base_url = "https://dl.google.com/android/repository/" - filename = name + ".zip" - url = base_url + filename - archive = path.join(toolchains, filename) - - if not path.isfile(archive): - download_file(filename, url, archive) - check_hash(archive, known_sha1[filename], "sha1") - print("Extracting " + filename) - remove = True # Set to False to avoid repeated downloads while debugging this script - if flatten: - extracted = final + "_" - extract(archive, extracted, remove=remove) - contents = os.listdir(extracted) - assert len(contents) == 1 - os.rename(path.join(extracted, contents[0]), final) - os.rmdir(extracted) - else: - extract(archive, final, remove=remove) - - system = platform.system().lower() - machine = platform.machine().lower() - arch = {"i386": "x86"}.get(machine, machine) - if build: - download("ndk", ndk.format(system=system, arch=arch), flatten=True) - download("sdk", tools.format(system=system)) - - components = [] - if emulator_x86: - components += [ - "platform-tools", - "emulator", - "platforms;" + emulator_platform, - emulator_image, - ] - if build: - components += [ - "platform-tools", - "platforms;android-18", - ] - - sdkmanager = [path.join(toolchains, "sdk", "tools", "bin", "sdkmanager")] + components - if accept_all_licences: - yes = subprocess.Popen(["yes"], stdout=subprocess.PIPE) - process = subprocess.Popen( - sdkmanager, stdin=yes.stdout, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - ) - # Reduce progress bar spam by removing duplicate lines. - # Printing the same line again with \r is a no-op in a real terminal, - # but each line is shown individually in Taskcluster's log viewer. - previous_line = None - line = b"" - while 1: - # Read one byte at a time because in Python: - # * readline() blocks until "\n", which doesn't come before the prompt - # * read() blocks until EOF, which doesn't come before the prompt - # * read(n) keeps reading until it gets n bytes or EOF, - # but we don't know reliably how many bytes to read until the prompt - byte = process.stdout.read(1) - if len(byte) == 0: - print(line) - break - line += byte - if byte == b'\n' or byte == b'\r': - if line != previous_line: - print(line.decode("utf-8", "replace"), end="") - sys.stdout.flush() - previous_line = line - line = b"" - exit_code = process.wait() - yes.terminate() - if exit_code: - return exit_code - else: - subprocess.check_call(sdkmanager) - - if emulator_x86: - avd_path = path.join(toolchains, "avd", "servo-x86") - process = subprocess.Popen(stdin=subprocess.PIPE, stdout=subprocess.PIPE, args=[ - path.join(toolchains, "sdk", "tools", "bin", "avdmanager"), - "create", "avd", - "--path", avd_path, - "--name", "servo-x86", - "--package", emulator_image, - "--force", - ]) - output = b"" - while 1: - # Read one byte at a time, see comment above. - byte = process.stdout.read(1) - if len(byte) == 0: - break - output += byte - # There seems to be no way to disable this prompt: - if output.endswith(b"Do you wish to create a custom hardware profile? [no]"): - process.stdin.write("no\n") - assert process.wait() == 0 - with open(path.join(avd_path, "config.ini"), "a") as f: - f.write("disk.dataPartition.size=2G\n") - @Command('update-hsts-preload', description='Download the HSTS preload list', category='bootstrap') diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py index f61c82fe5b3..dd63aff54f3 100644 --- a/python/servo/build_commands.py +++ b/python/servo/build_commands.py @@ -98,7 +98,6 @@ class MachCommands(CommandBase): for key in env: print((key, env[key])) - self.download_and_build_android_dependencies_if_needed(env) status = self.run_cargo_build_like_command( "build", opts, env=env, verbose=verbose, libsimpleservo=libsimpleservo, **kwargs diff --git a/python/servo/command_base.py b/python/servo/command_base.py index f6d105e7837..833f0bac417 100644 --- a/python/servo/command_base.py +++ b/python/servo/command_base.py @@ -334,7 +334,7 @@ class CommandBase(object): def get_binary_path(self, build_type: BuildType, target=None, android=False, simpleservo=False): base_path = util.get_target_dir() if android: - base_path = path.join(base_path, "android", self.config["android"]["target"]) + base_path = path.join(base_path, self.config["android"]["target"]) simpleservo = True elif target: base_path = path.join(base_path, target) @@ -529,58 +529,38 @@ class CommandBase(object): # Paths to Android build tools: if self.config["android"]["sdk"]: - env["ANDROID_SDK"] = self.config["android"]["sdk"] + env["ANDROID_SDK_ROOT"] = self.config["android"]["sdk"] if self.config["android"]["ndk"]: - env["ANDROID_NDK"] = self.config["android"]["ndk"] - if self.config["android"]["toolchain"]: - env["ANDROID_TOOLCHAIN"] = self.config["android"]["toolchain"] - if self.config["android"]["platform"]: - env["ANDROID_PLATFORM"] = self.config["android"]["platform"] - - # These are set because they are the variable names that build-apk - # expects. However, other submodules have makefiles that reference - # the env var names above. Once winit is enabled and set as the - # default, we could modify the subproject makefiles to use the names - # below and remove the vars above, to avoid duplication. - if "ANDROID_SDK" in env: - env["ANDROID_HOME"] = env["ANDROID_SDK"] - if "ANDROID_NDK" in env: - env["NDK_HOME"] = env["ANDROID_NDK"] - if "ANDROID_TOOLCHAIN" in env: - env["NDK_STANDALONE"] = env["ANDROID_TOOLCHAIN"] + env["ANDROID_NDK_ROOT"] = self.config["android"]["ndk"] toolchains = path.join(self.context.topdir, "android-toolchains") for kind in ["sdk", "ndk"]: default = os.path.join(toolchains, kind) if os.path.isdir(default): - env.setdefault("ANDROID_" + kind.upper(), default) + env.setdefault(f"ANDROID_{kind.upper()}_ROOT", default) - tools = os.path.join(toolchains, "sdk", "platform-tools") - if os.path.isdir(tools): - env["PATH"] = "%s%s%s" % (tools, os.pathsep, env["PATH"]) - - if "ANDROID_NDK" not in env: - print("Please set the ANDROID_NDK environment variable.") + if "ANDROID_NDK_ROOT" not in env: + print("Please set the ANDROID_NDK_ROOT environment variable.") sys.exit(1) - if "ANDROID_SDK" not in env: - print("Please set the ANDROID_SDK environment variable.") + if "ANDROID_SDK_ROOT" not in env: + print("Please set the ANDROID_SDK_ROOT environment variable.") sys.exit(1) android_platform = self.config["android"]["platform"] android_toolchain_name = self.config["android"]["toolchain_name"] - android_toolchain_prefix = self.config["android"]["toolchain_prefix"] android_lib = self.config["android"]["lib"] - android_arch = self.config["android"]["arch"] - # Check if the NDK version is 15 - if not os.path.isfile(path.join(env["ANDROID_NDK"], 'source.properties')): + android_api = android_platform.replace('android-', '') + + # Check if the NDK version is 25 + if not os.path.isfile(path.join(env["ANDROID_NDK_ROOT"], 'source.properties')): print("ANDROID_NDK should have file `source.properties`.") - print("The environment variable ANDROID_NDK may be set at a wrong path.") + print("The environment variable ANDROID_NDK_ROOT may be set at a wrong path.") sys.exit(1) - with open(path.join(env["ANDROID_NDK"], 'source.properties'), encoding="utf8") as ndk_properties: + with open(path.join(env["ANDROID_NDK_ROOT"], 'source.properties'), encoding="utf8") as ndk_properties: lines = ndk_properties.readlines() - if lines[1].split(' = ')[1].split('.')[0] != '15': - print("Currently only support NDK 15. Please re-run `./mach bootstrap-android`.") + if lines[1].split(' = ')[1].split('.')[0] != '25': + print("Servo currently only supports NDK r25c.") sys.exit(1) # Android builds also require having the gcc bits on the PATH and various INCLUDE @@ -598,48 +578,33 @@ class CommandBase(object): host_suffix = "x86_64" host = os_type + "-" + host_suffix - host_cc = env.get('HOST_CC') or shutil.which(["clang"]) or util.whichget_exec_path(["gcc"]) - host_cxx = env.get('HOST_CXX') or util.whichget_exec_path(["clang++"]) or util.whichget_exec_path(["g++"]) - - llvm_toolchain = path.join(env['ANDROID_NDK'], "toolchains", "llvm", "prebuilt", host) - gcc_toolchain = path.join(env['ANDROID_NDK'], "toolchains", - android_toolchain_prefix + "-4.9", "prebuilt", host) - gcc_libs = path.join(gcc_toolchain, "lib", "gcc", android_toolchain_name, "4.9.x") + host_cc = env.get('HOST_CC') or shutil.which("clang") + host_cxx = env.get('HOST_CXX') or shutil.which("clang++") + llvm_toolchain = path.join(env['ANDROID_NDK_ROOT'], "toolchains", "llvm", "prebuilt", host) env['PATH'] = (path.join(llvm_toolchain, "bin") + ':' + env['PATH']) - env['ANDROID_SYSROOT'] = path.join(env['ANDROID_NDK'], "sysroot") - support_include = path.join(env['ANDROID_NDK'], "sources", "android", "support", "include") - cpufeatures_include = path.join(env['ANDROID_NDK'], "sources", "android", "cpufeatures") - cxx_include = path.join(env['ANDROID_NDK'], "sources", "cxx-stl", - "llvm-libc++", "include") - clang_include = path.join(llvm_toolchain, "lib64", "clang", "3.8", "include") - cxxabi_include = path.join(env['ANDROID_NDK'], "sources", "cxx-stl", - "llvm-libc++abi", "include") - sysroot_include = path.join(env['ANDROID_SYSROOT'], "usr", "include") - arch_include = path.join(sysroot_include, android_toolchain_name) - android_platform_dir = path.join(env['ANDROID_NDK'], "platforms", android_platform, "arch-" + android_arch) - arch_libs = path.join(android_platform_dir, "usr", "lib") - clang_include = path.join(llvm_toolchain, "lib64", "clang", "5.0", "include") - android_api = android_platform.replace('android-', '') + + def to_ndk_bin(prog): + return path.join(llvm_toolchain, "bin", prog) env["RUST_TARGET"] = self.cross_compile_target env['HOST_CC'] = host_cc env['HOST_CXX'] = host_cxx env['HOST_CFLAGS'] = '' env['HOST_CXXFLAGS'] = '' - env['CC'] = path.join(llvm_toolchain, "bin", "clang") - env['CPP'] = path.join(llvm_toolchain, "bin", "clang") + " -E" - env['CXX'] = path.join(llvm_toolchain, "bin", "clang++") - env['ANDROID_TOOLCHAIN'] = gcc_toolchain - env['ANDROID_TOOLCHAIN_DIR'] = gcc_toolchain - env['ANDROID_VERSION'] = android_api - env['ANDROID_PLATFORM_DIR'] = android_platform_dir - env['GCC_TOOLCHAIN'] = gcc_toolchain - gcc_toolchain_bin = path.join(gcc_toolchain, android_toolchain_name, "bin") - env['AR'] = path.join(gcc_toolchain_bin, "ar") - env['RANLIB'] = path.join(gcc_toolchain_bin, "ranlib") - env['OBJCOPY'] = path.join(gcc_toolchain_bin, "objcopy") - env['YASM'] = path.join(env['ANDROID_NDK'], 'prebuilt', host, 'bin', 'yasm') + env['CC'] = to_ndk_bin("clang") + env['CPP'] = to_ndk_bin("clang") + " -E" + env['CXX'] = to_ndk_bin("clang++") + + env['AR'] = to_ndk_bin("llvm-ar") + env['RANLIB'] = to_ndk_bin("llvm-ranlib") + env['OBJCOPY'] = to_ndk_bin("llvm-objcopy") + env['YASM'] = to_ndk_bin("yasm") + env['STRIP'] = to_ndk_bin("llvm-strip") + env['HARFBUZZ_SYS_NO_PKG_CONFIG'] = "true" + env['RUST_FONTCONFIG_DLOPEN'] = "on" + + env["LIBCLANG_PATH"] = path.join(llvm_toolchain, "lib64") # A cheat-sheet for some of the build errors caused by getting the search path wrong... # # fatal error: 'limits' file not found @@ -651,52 +616,28 @@ class CommandBase(object): # # Also worth remembering: autoconf uses C for its configuration, # even for C++ builds, so the C flags need to line up with the C++ flags. - env['CFLAGS'] = ' '.join([ - "--target=" + self.cross_compile_target, - "--sysroot=" + env['ANDROID_SYSROOT'], - "--gcc-toolchain=" + gcc_toolchain, - "-isystem", sysroot_include, - "-I" + arch_include, - "-B" + arch_libs, - "-L" + arch_libs, - "-D__ANDROID_API__=" + android_api, - ]) - env['CXXFLAGS'] = ' '.join([ - "--target=" + self.cross_compile_target, - "--sysroot=" + env['ANDROID_SYSROOT'], - "--gcc-toolchain=" + gcc_toolchain, - "-I" + cpufeatures_include, - "-I" + cxx_include, - "-I" + clang_include, - "-isystem", sysroot_include, - "-I" + cxxabi_include, - "-I" + clang_include, - "-I" + arch_include, - "-I" + support_include, - "-L" + gcc_libs, - "-B" + arch_libs, - "-L" + arch_libs, - "-D__ANDROID_API__=" + android_api, - "-D__STDC_CONSTANT_MACROS", - "-D__NDK_FPABI__=", - ]) - env['CPPFLAGS'] = ' '.join([ - "--target=" + self.cross_compile_target, - "--sysroot=" + env['ANDROID_SYSROOT'], - "-I" + arch_include, - ]) - env["NDK_ANDROID_VERSION"] = android_api + env['CFLAGS'] = "--target=" + android_toolchain_name + env['CXXFLAGS'] = "--target=" + android_toolchain_name + + # These two variables are needed for the mozjs compilation. + env['ANDROID_API_LEVEL'] = android_api + env["ANDROID_NDK_HOME"] = env["ANDROID_NDK_ROOT"] + + # The two variables set below are passed by our custom + # support/android/toolchain.cmake to the NDK's CMake toolchain file env["ANDROID_ABI"] = android_lib env["ANDROID_PLATFORM"] = android_platform - env["NDK_CMAKE_TOOLCHAIN_FILE"] = path.join(env['ANDROID_NDK'], "build", "cmake", "android.toolchain.cmake") - env["CMAKE_TOOLCHAIN_FILE"] = path.join(self.android_support_dir(), "toolchain.cmake") + env["NDK_CMAKE_TOOLCHAIN_FILE"] = path.join( + env['ANDROID_NDK_ROOT'], "build", "cmake", "android.toolchain.cmake") + env["CMAKE_TOOLCHAIN_FILE"] = path.join( + self.context.topdir, "support", "android", "toolchain.cmake") # Set output dir for gradle aar files - env["AAR_OUT_DIR"] = self.android_aar_dir() + env["AAR_OUT_DIR"] = path.join(self.context.topdir, "target", "android", "aar") if not os.path.exists(env['AAR_OUT_DIR']): os.makedirs(env['AAR_OUT_DIR']) - env['PKG_CONFIG_ALLOW_CROSS'] = "1" + env['PKG_CONFIG_SYSROOT_DIR'] = path.join(llvm_toolchain, 'sysroot') @staticmethod def common_command_arguments(build_configuration=False, build_type=False): @@ -863,11 +804,7 @@ class CommandBase(object): if self.config["build"]["media-stack"] != "auto": media_stack = self.config["build"]["media-stack"] assert media_stack - elif ( - not self.cross_compile_target - or ("armv7" in self.cross_compile_target and self.is_android_build) - or "x86_64" in self.cross_compile_target - ): + elif not self.cross_compile_target: media_stack = "gstreamer" else: media_stack = "dummy" @@ -943,22 +880,16 @@ class CommandBase(object): return call(["cargo", command] + args + cargo_args, env=env, verbose=verbose) - def android_support_dir(self): - return path.join(self.context.topdir, "support", "android") - - def android_aar_dir(self): - return path.join(self.context.topdir, "target", "android", "aar") - def android_adb_path(self, env): - if "ANDROID_SDK" in env: - sdk_adb = path.join(env["ANDROID_SDK"], "platform-tools", "adb") + if "ANDROID_SDK_ROOT" in env: + sdk_adb = path.join(env["ANDROID_SDK_ROOT"], "platform-tools", "adb") if path.exists(sdk_adb): return sdk_adb return "adb" def android_emulator_path(self, env): - if "ANDROID_SDK" in env: - sdk_adb = path.join(env["ANDROID_SDK"], "emulator", "emulator") + if "ANDROID_SDK_ROOT" in env: + sdk_adb = path.join(env["ANDROID_SDK_ROOT"], "emulator", "emulator") if path.exists(sdk_adb): return sdk_adb return "emulator" @@ -968,29 +899,29 @@ class CommandBase(object): build by writing the appropriate toolchain configuration values into the stored configuration.""" if target == "armv7-linux-androideabi": - self.config["android"]["platform"] = "android-21" + self.config["android"]["platform"] = "android-30" self.config["android"]["target"] = target self.config["android"]["toolchain_prefix"] = "arm-linux-androideabi" self.config["android"]["arch"] = "arm" self.config["android"]["lib"] = "armeabi-v7a" - self.config["android"]["toolchain_name"] = "arm-linux-androideabi" + self.config["android"]["toolchain_name"] = "armv7a-linux-androideabi30" return True elif target == "aarch64-linux-android": - self.config["android"]["platform"] = "android-21" + self.config["android"]["platform"] = "android-30" self.config["android"]["target"] = target self.config["android"]["toolchain_prefix"] = target self.config["android"]["arch"] = "arm64" self.config["android"]["lib"] = "arm64-v8a" - self.config["android"]["toolchain_name"] = target + self.config["android"]["toolchain_name"] = "aarch64-linux-androideabi30" return True elif target == "i686-linux-android": # https://github.com/jemalloc/jemalloc/issues/1279 - self.config["android"]["platform"] = "android-21" + self.config["android"]["platform"] = "android-30" self.config["android"]["target"] = target - self.config["android"]["toolchain_prefix"] = "x86" + self.config["android"]["toolchain_prefix"] = target self.config["android"]["arch"] = "x86" self.config["android"]["lib"] = "x86" - self.config["android"]["toolchain_name"] = target + self.config["android"]["toolchain_name"] = "i686-linux-android30" return True return False diff --git a/python/servo/package_commands.py b/python/servo/package_commands.py index 62ae60f5fa6..ab8cbacb2cd 100644 --- a/python/servo/package_commands.py +++ b/python/servo/package_commands.py @@ -160,7 +160,7 @@ class PackageCommands(CommandBase): else: raise Exception("TODO what should this be?") - flavor_name = "Main" + flavor_name = "Basic" if flavor is not None: flavor_name = flavor.title() @@ -176,10 +176,7 @@ class PackageCommands(CommandBase): variant = ":assemble" + flavor_name + arch_string + build_type_string apk_task_name = ":servoapp" + variant aar_task_name = ":servoview" + variant - maven_task_name = ":servoview:uploadArchive" argv = ["./gradlew", "--no-daemon", apk_task_name, aar_task_name] - if maven: - argv.append(maven_task_name) try: with cd(path.join("support", "android", "apk")): subprocess.check_call(argv, env=env) diff --git a/python/servo/post_build_commands.py b/python/servo/post_build_commands.py index bd9382b55a1..ebaf5a5e983 100644 --- a/python/servo/post_build_commands.py +++ b/python/servo/post_build_commands.py @@ -118,6 +118,7 @@ class PostBuildCommands(CommandBase): "am start " + extra + " org.mozilla.servo/org.mozilla.servo.MainActivity", "sleep 0.5", "echo Servo PID: $(pidof org.mozilla.servo)", + "logcat --pid=$(pidof org.mozilla.servo)", "exit" ] args = [self.android_adb_path(env)] @@ -129,7 +130,7 @@ class PostBuildCommands(CommandBase): if usb: args += ["-d"] shell = subprocess.Popen(args + ["shell"], stdin=subprocess.PIPE) - shell.communicate("\n".join(script) + "\n") + shell.communicate(bytes("\n".join(script) + "\n", "utf8")) return shell.wait() args = [bin or self.get_nightly_binary_path(nightly) or self.get_binary_path(build_type)] diff --git a/servo-tidy.toml b/servo-tidy.toml index 6e295cac954..bc235ae68c9 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -65,6 +65,10 @@ packages = [ # style (0.64) vs. webxr (0.66) vs. mozjs_sys (0.68). "bindgen", + + # quickcheck (required by layout_2020 for tests) is + # stuck on 0.8.4 with no new releases. + "env_logger", ] # Files that are ignored for all tidy and lint checks. files = [ diff --git a/servobuild.example b/servobuild.example index 1c63d91d4b0..d0d930ec202 100644 --- a/servobuild.example +++ b/servobuild.example @@ -56,8 +56,6 @@ media-stack = "auto" # Android information [android] -# Defaults to the value of $ANDROID_SDK, $ANDROID_NDK, $ANDROID_TOOLCHAIN, $ANDROID_PLATFORM respectively +# Defaults to the value of $ANDROID_SDK_ROOT, $ANDROID_NDK_ROOT respectively #sdk = "/opt/android-sdk" #ndk = "/opt/android-ndk" -#toolchain = "/opt/android-toolchain" -#platform = "android-18" diff --git a/support/android/apk/build.gradle b/support/android/apk/build.gradle index aa429b3da22..c2619efb978 100644 --- a/support/android/apk/build.gradle +++ b/support/android/apk/build.gradle @@ -1,42 +1,30 @@ -import org.apache.tools.ant.taskdefs.condition.Os - -buildscript { - repositories { - jcenter() - google() - } - dependencies { - classpath 'com.android.tools.build:gradle:3.1.3' - } -} - -allprojects { - repositories { - jcenter() - flatDir { - dirs rootDir.absolutePath + "/../../../target/android/aar" - } - google() - } +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '8.0.1' apply false + id 'com.android.library' version '8.0.1' apply false } // Utility methods -String getTargetDir(boolean debug, String arch) { +ext.getTargetDir = { boolean debug, String arch -> def basePath = project.rootDir.getParentFile().getParentFile().getParentFile().absolutePath return basePath + '/target/android/' + getSubTargetDir(debug, arch) } -String getSubTargetDir(boolean debug, String arch) { +ext.getNativeTargetDir = { boolean debug, String arch -> + def basePath = project.rootDir.getParentFile().getParentFile().getParentFile().absolutePath + return basePath + '/target/' + getSubTargetDir(debug, arch) +} + +ext.getSubTargetDir = { boolean debug, String arch -> return getRustTarget(arch) + '/' + (debug ? 'debug' : 'release') } -String getJniLibsPath(boolean debug, String arch) { - return getTargetDir(debug, arch) + '/apk/jniLibs' +ext.getJniLibsPath = { boolean debug, String arch -> + return getTargetDir(debug, arch) + '/jniLibs' } -static String getRustTarget(String arch) { +ext.getRustTarget = { String arch -> switch (arch.toLowerCase()) { - case 'arm' : return 'arm-linux-androideabi' case 'armv7' : return 'armv7-linux-androideabi' case 'arm64' : return 'aarch64-linux-android' case 'x86' : return 'i686-linux-android' @@ -44,9 +32,8 @@ static String getRustTarget(String arch) { } } -static String getNDKAbi(String arch) { +ext.getNDKAbi = { String arch -> switch (arch.toLowerCase()) { - case 'arm' : return 'armeabi' case 'armv7' : return 'armeabi-v7a' case 'arm64' : return 'arm64-v8a' case 'x86' : return 'x86' @@ -54,16 +41,10 @@ static String getNDKAbi(String arch) { } } -String getNdkDir() { +ext.getNdkDir = { -> // Read environment variable used in rust build system - String ndkDir = System.getenv('ANDROID_NDK') - if (ndkDir == null) { - ndkDir = System.getenv('ANDROID_NDK_HOME') - } - if (ndkDir == null) { - ndkDir = System.getenv('ANDROID_NDK_ROOT') - } - if (ndkDir == null) { + String ndkRoot = System.getenv('ANDROID_NDK_ROOT') + if (ndkRoot == null) { // Fallback to ndkDir in local.properties def rootDir = project.rootDir def localProperties = new File(rootDir, "local.properties") @@ -72,14 +53,13 @@ String getNdkDir() { properties.load(instr) } - ndkDir = properties.getProperty('ndk.dir') + ndkRoot = properties.getProperty('ndk.dir') } - def cmd = Os.isFamily(Os.FAMILY_WINDOWS) ? 'ndk-build.cmd' : 'ndk-build' - def ndkbuild = new File(ndkDir + '/' + cmd) - if (!ndkbuild.exists()) { - throw new GradleException("Please set a valid NDK_HOME environment variable" + + def ndkDir = ndkRoot != null ? new File(ndkRoot) : null + if (!ndkDir || !ndkDir.exists()) { + throw new GradleException("Please set a valid ANDROID_NDK_ROOT environment variable" + "or ndk.dir path in local.properties file"); } - return ndkbuild.absolutePath + return ndkDir.absolutePath } diff --git a/support/android/apk/gradle.properties b/support/android/apk/gradle.properties new file mode 100644 index 00000000000..a03b3548962 --- /dev/null +++ b/support/android/apk/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true diff --git a/support/android/apk/gradle/wrapper/gradle-wrapper.jar b/support/android/apk/gradle/wrapper/gradle-wrapper.jar Binary files differindex 13372aef5e2..87b738cbd05 100644 --- a/support/android/apk/gradle/wrapper/gradle-wrapper.jar +++ b/support/android/apk/gradle/wrapper/gradle-wrapper.jar diff --git a/support/android/apk/gradle/wrapper/gradle-wrapper.properties b/support/android/apk/gradle/wrapper/gradle-wrapper.properties index 915f189a922..da1db5f04e8 100644 --- a/support/android/apk/gradle/wrapper/gradle-wrapper.properties +++ b/support/android/apk/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed Jul 11 13:23:08 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/support/android/apk/gradlew b/support/android/apk/gradlew index 9d82f789151..af6708ff229 100755 --- a/support/android/apk/gradlew +++ b/support/android/apk/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/support/android/apk/gradlew.bat b/support/android/apk/gradlew.bat index aec99730b4e..0f8d5937c4a 100644 --- a/support/android/apk/gradlew.bat +++ b/support/android/apk/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m"
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation. goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
diff --git a/support/android/apk/jni/Android.mk b/support/android/apk/jni/Android.mk index 20a4de2d4b2..7ee4ffe58d8 100644 --- a/support/android/apk/jni/Android.mk +++ b/support/android/apk/jni/Android.mk @@ -16,13 +16,7 @@ MY_LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_PATH:= $(SERVO_TARGET_DIR) +LOCAL_PATH := $(SERVO_TARGET_DIR) LOCAL_MODULE := servojni LOCAL_SRC_FILES := libsimpleservo.so include $(PREBUILT_SHARED_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_PATH:= $(SERVO_TARGET_DIR)/../../gstreamer/gst-build-$(APP_ABI) -LOCAL_MODULE := gstreamer -LOCAL_SRC_FILES := libgstreamer_android.so -include $(PREBUILT_SHARED_LIBRARY) diff --git a/support/android/apk/jni/Application.mk b/support/android/apk/jni/Application.mk index 9944120aebe..5b349f06d44 100644 --- a/support/android/apk/jni/Application.mk +++ b/support/android/apk/jni/Application.mk @@ -1,5 +1,5 @@ NDK_TOOLCHAIN_VERSION := clang -APP_MODULES := c++_shared servojni gstreamer -APP_PLATFORM := android-21 -APP_STL:= c++_shared -APP_ABI:= armeabi-v7a x86 +APP_MODULES := c++_shared servojni +APP_PLATFORM := android-30 +APP_STL := c++_shared +APP_ABI := armeabi-v7a x86 diff --git a/support/android/apk/servoapp/build.gradle b/support/android/apk/servoapp/build.gradle index 6b440e7b2c7..6e1cb514d26 100644 --- a/support/android/apk/servoapp/build.gradle +++ b/support/android/apk/servoapp/build.gradle @@ -1,18 +1,22 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.android.application' +} import java.util.regex.Matcher import java.util.regex.Pattern android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdk 33 + buildToolsVersion "33.0.2" + + namespace 'org.mozilla.servo' buildDir = rootDir.absolutePath + "/../../../target/android/gradle/servoapp" defaultConfig { applicationId "org.mozilla.servo" - minSdkVersion 21 - targetSdkVersion 27 + minSdk 30 + targetSdk 30 versionCode 1 versionName "1.0.0" } @@ -26,11 +30,7 @@ android { flavorDimensions "default" productFlavors { - main { - } - googlevr { - } - oculusvr { + basic { } } @@ -61,19 +61,6 @@ android { } // Custom build types - armDebug { - initWith(debug) - ndk { - abiFilters getNDKAbi('arm') - } - } - - armRelease { - initWith(release) - ndk { - abiFilters getNDKAbi('arm') - } - } armv7Debug { initWith(debug) ndk { @@ -144,7 +131,6 @@ android { } dependencies { - implementation 'com.android.support.constraint:constraint-layout:1.1.2' if (findProject(':servoview-local')) { implementation project(':servoview-local') } else { diff --git a/support/android/apk/servoapp/src/googlevr/AndroidManifest.xml b/support/android/apk/servoapp/src/googlevr/AndroidManifest.xml deleted file mode 100644 index a7df4ac14a9..00000000000 --- a/support/android/apk/servoapp/src/googlevr/AndroidManifest.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- BEGIN_INCLUDE(manifest) --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" - package="org.mozilla.servo"> - <application android:label="Servo"> - <activity android:name=".MainActivity" - android:label="Servo"> - <meta-data android:name="android.app.lib_name" android:value="servo" /> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="com.google.intent.category.LAUNCHER"/> - <category android:name="com.google.intent.category.DAYDREAM"/> - <category android:name="com.google.intent.category.CARDBOARD"/> - </intent-filter> - </activity> - </application> - -</manifest> -<!-- END_INCLUDE(manifest) --> diff --git a/support/android/apk/servoapp/src/main/AndroidManifest.xml b/support/android/apk/servoapp/src/main/AndroidManifest.xml index 71db56c53a1..3bedf50ffbf 100644 --- a/support/android/apk/servoapp/src/main/AndroidManifest.xml +++ b/support/android/apk/servoapp/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- BEGIN_INCLUDE(manifest) --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" - package="org.mozilla.servo"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto"> <uses-feature android:glEsVersion="0x00030000" android:required="true" /> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> diff --git a/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java b/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java index 1e1c9dbabeb..a366e8037d2 100644 --- a/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java +++ b/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java @@ -78,8 +78,7 @@ public class MainActivity extends Activity implements Servo.Client { Intent intent = getIntent(); String args = intent.getStringExtra("servoargs"); String log = intent.getStringExtra("servolog"); - String gstdebug = intent.getStringExtra("gstdebug"); - mServoView.setServoArgs(args, log, gstdebug); + mServoView.setServoArgs(args, log); if (Intent.ACTION_VIEW.equals(intent.getAction())) { mServoView.loadUri(intent.getData()); @@ -89,8 +88,10 @@ public class MainActivity extends Activity implements Servo.Client { @Override protected void onDestroy() { - super.onDestroy(); - mMediaSession.hideMediaSessionControls(); + super.onDestroy(); + if (mMediaSession != null) { + mMediaSession.hideMediaSessionControls(); + } } private void setupUrlField() { @@ -229,31 +230,31 @@ public class MainActivity extends Activity implements Servo.Client { @Override public void onMediaSessionMetadata(String title, String artist, String album) { - if (mMediaSession == null) { - mMediaSession = new MediaSession(mServoView, this, getApplicationContext()); - } - Log.d("onMediaSessionMetadata", title + " " + artist + " " + album); - mMediaSession.updateMetadata(title, artist, album); + if (mMediaSession == null) { + mMediaSession = new MediaSession(mServoView, this, getApplicationContext()); + } + Log.d("onMediaSessionMetadata", title + " " + artist + " " + album); + mMediaSession.updateMetadata(title, artist, album); } @Override public void onMediaSessionPlaybackStateChange(int state) { - Log.d("onMediaSessionPlaybackStateChange", String.valueOf(state)); - if (mMediaSession == null) { - mMediaSession = new MediaSession(mServoView, this, getApplicationContext()); - } - - mMediaSession.setPlaybackState(state); - - if (state == MediaSession.PLAYBACK_STATE_NONE) { - mMediaSession.hideMediaSessionControls(); - return; - } - if (state == MediaSession.PLAYBACK_STATE_PLAYING || - state == MediaSession.PLAYBACK_STATE_PAUSED) { - mMediaSession.showMediaSessionControls(); - return; - } + Log.d("onMediaSessionPlaybackStateChange", String.valueOf(state)); + if (mMediaSession == null) { + mMediaSession = new MediaSession(mServoView, this, getApplicationContext()); + } + + mMediaSession.setPlaybackState(state); + + if (state == MediaSession.PLAYBACK_STATE_NONE) { + mMediaSession.hideMediaSessionControls(); + return; + } + if (state == MediaSession.PLAYBACK_STATE_PLAYING || + state == MediaSession.PLAYBACK_STATE_PAUSED) { + mMediaSession.showMediaSessionControls(); + return; + } } @Override diff --git a/support/android/apk/servoapp/src/main/res/layout/activity_main.xml b/support/android/apk/servoapp/src/main/res/layout/activity_main.xml index 2cfb3357a01..42cf7a1a4b9 100644 --- a/support/android/apk/servoapp/src/main/res/layout/activity_main.xml +++ b/support/android/apk/servoapp/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" @@ -102,4 +102,4 @@ android:focusable="true"/> </LinearLayout> -</android.support.constraint.ConstraintLayout> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/support/android/apk/servoview/build.gradle b/support/android/apk/servoview/build.gradle index 51bab4b45e2..5898bb7bca2 100644 --- a/support/android/apk/servoview/build.gradle +++ b/support/android/apk/servoview/build.gradle @@ -1,18 +1,23 @@ -apply plugin: 'com.android.library' +plugins { + id 'com.android.library' +} import groovy.io.FileType import java.util.regex.Matcher import java.util.regex.Pattern android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdk 33 + buildToolsVersion "33.0.2" + + namespace 'org.mozilla.servoview' buildDir = rootDir.absolutePath + "/../../../target/android/gradle/servoview" + ndkPath = getNdkDir() defaultConfig { - minSdkVersion 18 - targetSdkVersion 27 + minSdk 30 + targetSdk 30 versionCode 1 versionName "1.0" } @@ -26,25 +31,19 @@ android { flavorDimensions "default" productFlavors { - main { + basic { } - googlevr { - minSdkVersion 21 + } + + splits { + density { + enable false } - oculusvr { - minSdkVersion 21 + abi { + enable false } } - splits { - density { - enable false - } - abi { - enable false - } - } - buildTypes { // Default debug and release build types are used as templates @@ -59,65 +58,29 @@ android { } // Custom build types - armDebug { - initWith(debug) - ndk { - abiFilters getNDKAbi('arm') - } - } - armRelease { - initWith(release) - ndk { - abiFilters getNDKAbi('arm') - } - } armv7Debug { initWith(debug) - ndk { - abiFilters getNDKAbi('armv7') - } } armv7Release { initWith(release) - ndk { - abiFilters getNDKAbi('armv7') - } } arm64Debug { initWith(debug) - ndk { - abiFilters getNDKAbi('arm64') - } } arm64Release { initWith(release) - ndk { - abiFilters getNDKAbi('arm64') - } } x86Debug { initWith(debug) - ndk { - abiFilters getNDKAbi('x86') - } } x86Release { initWith(release) - ndk { - abiFilters getNDKAbi('x86') - } } } sourceSets { main { } - armDebug { - jniLibs.srcDirs = [getJniLibsPath(true, 'arm')] - } - armRelease { - jniLibs.srcDirs = [getJniLibsPath(false, 'arm')] - } armv7Debug { jniLibs.srcDirs = [getJniLibsPath(true, 'armv7')] } @@ -145,34 +108,49 @@ android { } } - - // Call our custom NDK Build task using flavor parameters + // Call our custom NDK Build task using flavor parameters. + // This step is needed because the Android Gradle Plugin system's + // integration with native C/C++ shared objects (based on the + // `android.externalNativeBuild` dsl object) assumes that we + // actually execute compiler commands to produced the shared + // objects. We already have the libsimpleservo.so produced by rustc. + // We could simply copy the .so to the `sourceSet.jniLibs` folder + // to make AGP bundle it with the APK, but this doesn't copy the STL + // (libc++_shared.so) as well. So we use ndk-build as a glorified + // `cp` command to copy the libsimpleservo.so from target/<arch> + // to target/android and crucially also include libc++_shared.so + // as well. + // + // FIXME(mukilan): According to the AGP docs, we should not be + // relying on task names used by the plugin system to hook into + // the build process, but instead we should use officially supported + // extension points such as `androidComponents.beforeVariants` tasks.all { compileTask -> - Pattern pattern = Pattern.compile(/^compile[A-Z][\w\d]+([A-Z][\w\d]+)(Debug|Release)Ndk/) + // This matches the task `mergeBasicArmv7DebugJniLibFolders`. + Pattern pattern = Pattern.compile(/^merge[A-Z][\w\d]+([A-Z][\w\d]+)(Debug|Release)JniLibFolders/) Matcher matcher = pattern.matcher(compileTask.name) if (!matcher.find()) { - return + return } def taskName = "ndkbuild" + compileTask.name tasks.create(name: taskName, type: Exec) { def debug = compileTask.name.contains("Debug") def arch = matcher.group(1) - commandLine getNdkDir(), - 'APP_BUILD_SCRIPT=../jni/Android.mk', - 'NDK_APPLICATION_MK=../jni/Application.mk', - 'NDK_LIBS_OUT=' + getJniLibsPath(debug, arch), - 'NDK_OUT=' + getTargetDir(debug, arch) + '/apk/obj', - 'NDK_DEBUG=' + (debug ? '1' : '0'), - 'APP_ABI=' + getNDKAbi(arch), - 'SERVO_TARGET_DIR=' + getTargetDir(debug, arch) + commandLine getNdkDir() + "/ndk-build", + 'APP_BUILD_SCRIPT=../jni/Android.mk', + 'NDK_APPLICATION_MK=../jni/Application.mk', + 'NDK_LIBS_OUT=' + getJniLibsPath(debug, arch), + 'NDK_DEBUG=' + (debug ? '1' : '0'), + 'APP_ABI=' + getNDKAbi(arch), + 'NDK_LOG=1', + 'SERVO_TARGET_DIR=' + getNativeTargetDir(debug, arch) } compileTask.dependsOn taskName } - project.afterEvaluate { android.libraryVariants.all { variant -> Pattern pattern = Pattern.compile(/^[\w\d]+([A-Z][\w\d]+)(Debug|Release)/) @@ -205,7 +183,7 @@ dependencies { ] // Iterate all build types and dependencies // For each dependency call the proper implementation command and set the correct dependency path - def list = ['arm', 'armv7', 'arm64', 'x86'] + def list = ['armv7', 'arm64', 'x86'] for (arch in list) { for (debug in [true, false]) { String basePath = getTargetDir(debug, arch) + "/build" @@ -220,10 +198,9 @@ dependencies { } } - googlevrImplementation 'com.google.vr:sdk-base:1.140.0' - googlevrImplementation(name: 'GVRService', ext: 'aar') - oculusvrImplementation(name: 'OVRService', ext: 'aar') - implementation 'com.android.support.constraint:constraint-layout:1.1.2' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.9.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' } // folderFilter can be used to improve search performance @@ -258,42 +235,3 @@ class ServoDependency { public String fileName; public String folderFilter; } - -apply plugin: 'maven' -import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact - -uploadArchives { - doFirst { - for ( arch in ["arm", "armv7", "arm64", "x86"] ) { - def target = getTargetDir(false, arch) - def aar = new File(target, "servoview.aar") - if (aar.exists()) { - def art = new DefaultPublishArtifact("servoview-" + arch, "aar", "aar", null, new Date(), aar); - project.artifacts.add('archives', art) - } - } - } - repositories.mavenDeployer { - repository(url: "file://localhost/${buildDir}/maven") - def cmd = "git rev-parse --short HEAD" - def proc = cmd.execute() - def commit = proc.text.trim() - def version = "0.0.1." + new Date().format('yyyyMMdd') + "." + commit - for ( arch_ in ["arm", "armv7", "arm64", "x86"] ) { - def arch = arch_ - addFilter(arch) {artifact, file -> artifact.name == "servoview-" + arch} - pom(arch).artifactId = "servoview-" + arch - pom(arch).groupId = 'org.mozilla.servoview' - pom(arch).version = version - pom(arch).project { - licenses { - license { - name 'The Mozilla Public License, v. 2.0' - url 'https://mozilla.org/MPL/2.0/' - distribution 'repo' - } - } - } - } - } -} diff --git a/support/android/apk/servoview/src/googlevr/AndroidManifest.xml b/support/android/apk/servoview/src/googlevr/AndroidManifest.xml deleted file mode 100644 index 7e1c7048634..00000000000 --- a/support/android/apk/servoview/src/googlevr/AndroidManifest.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- BEGIN_INCLUDE(manifest) --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.mozilla.servoview"> - <application> - <activity android:name=".MainActivity" - android:screenOrientation="landscape" - android:enableVrMode="@string/gvr_vr_mode_component" - android:resizeableActivity="false"> - <!-- Intent filter that enables this app to be launched from the -Daydream Home menu. --> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="com.google.intent.category.DAYDREAM"/> - </intent-filter> - </activity> - </application> - -</manifest> - <!-- END_INCLUDE(manifest) --> diff --git a/support/android/apk/servoview/src/main/AndroidManifest.xml b/support/android/apk/servoview/src/main/AndroidManifest.xml index 1e916f18c97..94cbbcfc396 100644 --- a/support/android/apk/servoview/src/main/AndroidManifest.xml +++ b/support/android/apk/servoview/src/main/AndroidManifest.xml @@ -1,2 +1 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.mozilla.servoview" /> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" /> diff --git a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java index 5fa91fa34f6..29dce9f01b1 100644 --- a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java +++ b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java @@ -6,7 +6,7 @@ package org.mozilla.servoview; import android.app.Activity; - +import android.view.Surface; /** * Maps /ports/libsimpleservo API */ @@ -14,13 +14,12 @@ import android.app.Activity; public class JNIServo { JNIServo() { System.loadLibrary("c++_shared"); - System.loadLibrary("gstreamer_android"); System.loadLibrary("simpleservo"); } public native String version(); - public native void init(Activity activity, ServoOptions options, Callbacks callbacks); + public native void init(Activity activity, ServoOptions options, Callbacks callbacks, Surface surface); public native void deinit(); @@ -66,6 +65,9 @@ public class JNIServo { public native void click(float x, float y); + public native void pauseCompositor(); + public native void resumeCompositor(Surface surface, ServoCoordinates coords); + public native void mediaSessionAction(int action); public static class ServoOptions { diff --git a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java index 7f8bd3fc211..e8dfd332a11 100644 --- a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java +++ b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java @@ -8,6 +8,7 @@ package org.mozilla.servoview; import android.app.Activity; import android.content.Context; import android.util.Log; +import android.view.Surface; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; @@ -30,21 +31,16 @@ public class Servo { RunCallback runCallback, GfxCallbacks gfxcb, Client client, - Activity activity) { + Activity activity, + Surface surface) { mRunCallback = runCallback; mServoCallbacks = new Callbacks(client, gfxcb); mRunCallback.inGLThread(() -> { - mJNI.init(activity, options, mServoCallbacks); + mJNI.init(activity, options, mServoCallbacks, surface); }); - - try { - GStreamer.init((Context) activity); - } catch (Exception e) { - e.printStackTrace(); - } } public void resetGfxCallbacks(GfxCallbacks gfxcb) { @@ -164,6 +160,13 @@ public class Servo { mRunCallback.inGLThread(() -> mJNI.click(x, y)); } + public void pauseCompositor() { + mRunCallback.inGLThread(() -> mJNI.pauseCompositor()); + } + public void resumeCompositor(Surface surface, ServoCoordinates coords) { + mRunCallback.inGLThread(() -> mJNI.resumeCompositor(surface, coords)); + } + public void suspend(boolean suspended) { mSuspended = suspended; } diff --git a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/ServoSurface.java b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/ServoSurface.java deleted file mode 100644 index 80f897fa4b9..00000000000 --- a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/ServoSurface.java +++ /dev/null @@ -1,302 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -package org.mozilla.servoview; - -import android.app.Activity; -import android.net.Uri; -import android.opengl.EGL14; -import android.opengl.EGLConfig; -import android.opengl.EGLContext; -import android.opengl.EGLDisplay; -import android.opengl.EGLSurface; -import android.opengl.GLUtils; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.view.Surface; - -import org.mozilla.servoview.JNIServo.ServoCoordinates; -import org.mozilla.servoview.JNIServo.ServoOptions; -import org.mozilla.servoview.Servo.Client; -import org.mozilla.servoview.Servo.GfxCallbacks; -import org.mozilla.servoview.Servo.RunCallback; - -import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION; -import static android.opengl.EGL14.EGL_NO_CONTEXT; -import static android.opengl.EGL14.EGL_NO_SURFACE; -import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT; - -public class ServoSurface { - private static final String LOGTAG = "ServoSurface"; - private final GLThread mGLThread; - private final Handler mMainLooperHandler; - private Handler mGLLooperHandler; - private Surface mASurface; - private int mPadding; - private int mWidth; - private int mHeight; - private long mVRExternalContext; - private Servo mServo; - private Client mClient = null; - private String mServoArgs; - private String mServoLog; - private String mInitialUri; - private Activity mActivity; - - public ServoSurface(Surface surface, int width, int height, int padding) { - mPadding = padding; - mWidth = width; - mHeight = height; - mASurface = surface; - mMainLooperHandler = new Handler(Looper.getMainLooper()); - mGLThread = new GLThread(); - } - - public void onSurfaceChanged(Surface surface) { - mASurface = surface; - mGLThread.onSurfaceChanged(); - } - - public void setClient(Client client) { - mClient = client; - } - - public void setServoArgs(String args, String log) { - mServoArgs = args; - mServoLog = log; - } - - public void setActivity(Activity activity) { - mActivity = activity; - } - - public void setVRExternalContext(long context) { - mVRExternalContext = context; - } - - public void runLoop() { - mGLThread.start(); - } - - public void shutdown() { - Log.d(LOGTAG, "shutdown"); - mServo.shutdown(); - mServo = null; - mGLThread.shutdown(); - try { - Log.d(LOGTAG, "Waiting for GL thread to shutdown"); - mGLThread.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public void reload() { - mServo.reload(); - } - - public void goBack() { - mServo.goBack(); - } - - public void goForward() { - mServo.goForward(); - } - - public void stop() { - mServo.stop(); - } - - public void loadUri(String uri) { - if (mServo != null) { - mServo.loadUri(uri); - } else { - mInitialUri = uri; - } - } - - public void loadUri(Uri uri) { - loadUri(uri.toString()); - } - - public void scrollStart(int dx, int dy, int x, int y) { - mServo.scrollStart(dx, dy, x, y); - } - - public void scroll(int dx, int dy, int x, int y) { - mServo.scroll(dx, dy, x, y); - } - - public void scrollEnd(int dx, int dy, int x, int y) { - mServo.scrollEnd(dx, dy, x, y); - } - - public void click(float x, float y) { - mServo.click(x, y); - } - - public void onSurfaceResized(int width, int height) { - mWidth = width; - mHeight = height; - - ServoCoordinates coords = new ServoCoordinates(); - coords.x = mPadding; - coords.y = mPadding; - coords.width = width - 2 * mPadding; - coords.height = height - 2 * mPadding; - coords.fb_width = width; - coords.fb_height = height; - - mServo.resize(coords); - } - - static class GLSurface implements GfxCallbacks { - private EGLConfig[] mEGLConfigs; - private EGLDisplay mEglDisplay; - private EGLContext mEglContext; - private EGLSurface mEglSurface; - - void throwGLError(String function) { - throwGLError(function, EGL14.eglGetError()); - } - - void throwGLError(String function, int error) { - throw new RuntimeException("Error: " + function + "() Failed " + GLUtils.getEGLErrorString(error)); - } - - GLSurface(Surface surface) { - mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); - int[] version = new int[2]; - if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) { - throwGLError("eglInitialize"); - } - mEGLConfigs = new EGLConfig[1]; - int[] configsCount = new int[1]; - int[] configSpec = new int[]{ - EGL14.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL14.EGL_RED_SIZE, 8, - EGL14.EGL_GREEN_SIZE, 8, - EGL14.EGL_BLUE_SIZE, 8, - EGL14.EGL_ALPHA_SIZE, 8, - EGL14.EGL_DEPTH_SIZE, 24, - EGL14.EGL_STENCIL_SIZE, 0, - EGL14.EGL_NONE - }; - if ((!EGL14.eglChooseConfig(mEglDisplay, configSpec, 0, mEGLConfigs, 0, 1, configsCount, 0)) || (configsCount[0] == 0)) { - throwGLError("eglChooseConfig"); - } - if (mEGLConfigs[0] == null) { - throw new RuntimeException("Error: eglConfig() not Initialized"); - } - int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE}; - mEglContext = EGL14.eglCreateContext(mEglDisplay, mEGLConfigs[0], EGL14.EGL_NO_CONTEXT, attrib_list, 0); - int glError = EGL14.eglGetError(); - if (glError != EGL14.EGL_SUCCESS) { - throwGLError("eglCreateContext", glError); - } - mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEGLConfigs[0], surface, new int[]{EGL14.EGL_NONE}, 0); - if (mEglSurface == null || mEglSurface == EGL14.EGL_NO_SURFACE) { - glError = EGL14.eglGetError(); - if (glError == EGL14.EGL_BAD_NATIVE_WINDOW) { - Log.e(LOGTAG, "Error: createWindowSurface() Returned EGL_BAD_NATIVE_WINDOW."); - return; - } - throwGLError("createWindowSurface", glError); - } - - makeCurrent(); - } - - - public void makeCurrent() { - if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { - throwGLError("eglMakeCurrent"); - } - } - - public void flushGLBuffers() { - EGL14.eglSwapBuffers(mEglDisplay, mEglSurface); - } - - public void animationStateChanged(boolean animating) { - // FIXME - } - - void destroy() { - Log.d(LOGTAG, "Destroying surface"); - if (!EGL14.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - throwGLError("eglMakeCurrent"); - } - if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) { - throwGLError("eglDestroyContext"); - } - if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) { - throwGLError("eglDestroySurface"); - } - if (!EGL14.eglTerminate(mEglDisplay)) { - throwGLError("eglTerminate"); - } - } - - } - - class GLThread extends Thread implements RunCallback { - private GLSurface mSurface; - - public void inGLThread(Runnable r) { - mGLLooperHandler.post(r); - } - - public void inUIThread(Runnable r) { - mMainLooperHandler.post(r); - } - - public void onSurfaceChanged() { - Log.d(LOGTAG, "GLThread::onSurfaceChanged"); - mSurface.destroy(); - mSurface = new GLSurface(mASurface); - mServo.resetGfxCallbacks(mSurface); - } - - public void shutdown() { - Log.d(LOGTAG, "GLThread::shutdown"); - mSurface.destroy(); - mGLLooperHandler.getLooper().quitSafely(); - } - - public void run() { - Looper.prepare(); - - mSurface = new GLSurface(mASurface); - - mGLLooperHandler = new Handler(); - - inUIThread(() -> { - ServoCoordinates coords = new ServoCoordinates(); - coords.x = mPadding; - coords.y = mPadding; - coords.width = mWidth - 2 * mPadding; - coords.height = mHeight - 2 * mPadding; - coords.fb_width = mWidth; - coords.fb_height = mHeight; - - ServoOptions options = new ServoOptions(); - options.coordinates = coords; - options.args = mServoArgs; - options.density = 1; - options.url = mInitialUri; - options.logStr = mServoLog; - options.enableLogs = true; - options.enableSubpixelTextAntialiasing = false; - options.VRExternalContext = mVRExternalContext; - - mServo = new Servo(options, this, mSurface, mClient, mActivity); - }); - - Looper.loop(); - } - } -} diff --git a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/ServoView.java b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/ServoView.java index 3784bbf58cb..19343d134d7 100644 --- a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/ServoView.java +++ b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/ServoView.java @@ -7,17 +7,15 @@ package org.mozilla.servoview; import android.app.Activity; import android.content.Context; -import android.net.Uri; -import android.opengl.GLES31; -import android.opengl.GLSurfaceView; import android.util.AttributeSet; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; import android.util.DisplayMetrics; import android.util.Log; -import android.view.Choreographer; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.widget.OverScroller; +import android.view.Surface; +import android.view.SurfaceView; +import android.view.SurfaceHolder; import org.mozilla.servoview.JNIServo.ServoCoordinates; import org.mozilla.servoview.JNIServo.ServoOptions; @@ -25,41 +23,45 @@ import org.mozilla.servoview.Servo.Client; import org.mozilla.servoview.Servo.GfxCallbacks; import org.mozilla.servoview.Servo.RunCallback; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; +import android.view.Choreographer; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.widget.OverScroller; -public class ServoView extends GLSurfaceView - implements - GestureDetector.OnGestureListener, - ScaleGestureDetector.OnScaleGestureListener, - Choreographer.FrameCallback, - GfxCallbacks, - RunCallback { +import java.util.ArrayList; +public class ServoView extends SurfaceView + implements + GfxCallbacks, + RunCallback, + Choreographer.FrameCallback, + GestureDetector.OnGestureListener, + ScaleGestureDetector.OnScaleGestureListener { private static final String LOGTAG = "ServoView"; - - private Activity mActivity; - private Servo mServo; + private GLThread mGLThread; + private Handler mGLLooperHandler; + private Surface mASurface; + protected Servo mServo = null; private Client mClient = null; - private Uri mInitialUri = null; - private boolean mAnimating; private String mServoArgs; private String mServoLog; - private String mGstDebug; + private String mInitialUri; + private Activity mActivity; private GestureDetector mGestureDetector; - private ScaleGestureDetector mScaleGestureDetector; - - private OverScroller mScroller; private int mLastX = 0; private int mCurX = 0; private int mLastY = 0; private int mCurY = 0; private boolean mFlinging; + private ScaleGestureDetector mScaleGestureDetector; + private OverScroller mScroller; private boolean mZooming; private float mZoomFactor = 1; - private boolean mRedrawing; + private boolean mAnimating; + private boolean mPaused = false; public ServoView(Context context) { super(context); @@ -71,79 +73,49 @@ public class ServoView extends GLSurfaceView init(context); } - public void onDetachedFromWindow() { - mServo.shutdown(); - mServo = null; - super.onDetachedFromWindow(); - } - private void init(Context context) { mActivity = (Activity) context; setFocusable(true); setFocusableInTouchMode(true); + setClickable(true); + ArrayList view = new ArrayList(); + view.add(this); + addTouchables(view); setWillNotCacheDrawing(false); - setEGLContextClientVersion(3); - setEGLConfigChooser(8, 8, 8, 8, 24, 0); - setPreserveEGLContextOnPause(true); - ServoGLRenderer mRenderer = new ServoGLRenderer(this); - setRenderer(mRenderer); - setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); initGestures(context); - } - public void setServoArgs(String args, String log, String gstdebug) { - mServoArgs = args; - mServoLog = log; - mGstDebug = gstdebug; + mGLThread = new GLThread(mActivity, this); + getHolder().addCallback(mGLThread); + mGLThread.start(); } - public void reload() { - mServo.reload(); - } - public void goBack() { - mServo.goBack(); - } - - public void goForward() { - mServo.goForward(); + public void setClient(Client client) { + mClient = client; } - public void stop() { - mServo.stop(); + public void setServoArgs(String args, String log) { + mServoArgs = args; + mServoLog = log; } - public void onSurfaceInvalidated(int width, int height) { - if (mServo != null) { - ServoCoordinates coords = new ServoCoordinates(); - coords.width = width; - coords.height = height; - coords.fb_width = width; - coords.fb_height = height; - mServo.resize(coords); - mServo.refresh(); - } + // RunCallback + public void inGLThread(Runnable r) { + mGLLooperHandler.post(r); } - public void loadUri(Uri uri) { - if (mServo != null) { - mServo.loadUri(uri.toString()); - } else { - mInitialUri = uri; - } + public void inUIThread(Runnable r) { + post(r); } - public void mediaSessionAction(int action) { - mServo.mediaSessionAction(action); - } + // GfxCallbacks public void flushGLBuffers() { - requestRender(); } - // Scroll and click + // Scroll and click public void animationStateChanged(boolean animating) { if (!mAnimating && animating) { post(() -> startLooping()); @@ -154,48 +126,6 @@ public class ServoView extends GLSurfaceView public void makeCurrent() { } - public void inGLThread(Runnable f) { - queueEvent(f); - } - - public void inUIThread(Runnable f) { - post(f); - } - - public void onGLReady() { - ServoCoordinates coords = new ServoCoordinates(); - coords.width = getWidth(); - coords.height = getHeight(); - coords.fb_width = getWidth(); - coords.fb_height = getHeight(); - - ServoOptions options = new ServoOptions(); - options.args = mServoArgs; - options.coordinates = coords; - options.enableLogs = true; - options.enableSubpixelTextAntialiasing = true; - - DisplayMetrics metrics = new DisplayMetrics(); - mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics); - options.density = metrics.density; - inGLThread(() -> { - String uri = mInitialUri == null ? null : mInitialUri.toString(); - options.url = uri; - options.logStr = mServoLog; - options.gstDebugStr = mGstDebug; - mServo = new Servo(options, this, this, mClient, mActivity); - }); - } - - public void setClient(Client client) { - mClient = client; - } - - private void initGestures(Context context) { - mGestureDetector = new GestureDetector(context, this); - mScaleGestureDetector = new ScaleGestureDetector(context, this); - mScroller = new OverScroller(context); - } private void startLooping() { // In case we were already drawing. @@ -253,6 +183,63 @@ public class ServoView extends GLSurfaceView } } + // Calls from Activity + public void onPause() { + if (mServo != null) { + mServo.suspend(true); + } + } + + public void onResume() { + if (mServo != null) { + mServo.suspend(false); + } + } + + public void reload() { + mServo.reload(); + } + + public void goBack() { + mServo.goBack(); + } + + public void goForward() { + mServo.goForward(); + } + + public void stop() { + mServo.stop(); + } + + public void loadUri(String uri) { + if (mServo != null) { + mServo.loadUri(uri); + } else { + mInitialUri = uri; + } + } + + public void loadUri(Uri uri) { + loadUri(uri.toString()); + } + + public void scrollStart(int dx, int dy, int x, int y) { + mServo.scrollStart(dx, dy, x, y); + } + + public void scroll(int dx, int dy, int x, int y) { + mServo.scroll(dx, dy, x, y); + } + + public void scrollEnd(int dx, int dy, int x, int y) { + mServo.scrollEnd(dx, dy, x, y); + } + + public void click(float x, float y) { + mServo.click(x, y); + } + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { mFlinging = true; @@ -275,20 +262,22 @@ public class ServoView extends GLSurfaceView return true; } + @Override public boolean onTouchEvent(final MotionEvent e) { mGestureDetector.onTouchEvent(e); mScaleGestureDetector.onTouchEvent(e); int action = e.getActionMasked(); + float x = e.getX(); - float y = e.getY(); - + int pointerIndex = e.getActionIndex(); int pointerId = e.getPointerId(pointerIndex); + switch (action) { case (MotionEvent.ACTION_DOWN): - mServo.touchDown(x, y, pointerId); + case (MotionEvent.ACTION_POINTER_DOWN): mFlinging = false; mScroller.forceFinished(true); mCurX = (int) x; @@ -299,32 +288,35 @@ public class ServoView extends GLSurfaceView case (MotionEvent.ACTION_MOVE): mCurX = (int) x; mCurY = (int) y; - mServo.touchMove(x, y, pointerId); return true; case (MotionEvent.ACTION_UP): - mServo.touchUp(x, y, pointerId); + case (MotionEvent.ACTION_POINTER_UP): + return true; case (MotionEvent.ACTION_CANCEL): - mServo.touchCancel(x, y, pointerId); return true; default: return true; } } - public boolean onSingleTapUp(MotionEvent e) { - return false; - } - + // OnGestureListener public void onLongPress(MotionEvent e) { } public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + mServo.scroll((int) -distanceX, (int) -distanceY, (int) e1.getX(), (int) e1.getY()); return true; } + public boolean onSingleTapUp(MotionEvent e) { + click(e.getX(), e.getY()); + return false; + } + public void onShowPress(MotionEvent e) { } + // OnScaleGestureListener @Override public boolean onScaleBegin(ScaleGestureDetector detector) { if (mScroller.isFinished()) { @@ -351,41 +343,81 @@ public class ServoView extends GLSurfaceView mServo.pinchZoomEnd(mZoomFactor, 0, 0); } - - @Override - public void onPause() { - super.onPause(); - if (mServo != null) { - mServo.suspend(true); - } + private void initGestures(Context context) { + mGestureDetector = new GestureDetector(context, this); + mScaleGestureDetector = new ScaleGestureDetector(context, this); + mScroller = new OverScroller(context); } - @Override - public void onResume() { - super.onResume(); - if (mServo != null) { - mServo.suspend(false); - } + public void mediaSessionAction(int action) { + mServo.mediaSessionAction(action); } - static class ServoGLRenderer implements Renderer { + class GLThread extends Thread implements SurfaceHolder.Callback { + private Activity mActivity; + private ServoView mServoView; + GLThread(Activity activity, ServoView servoView) { + mActivity = activity; + mServoView = servoView; + } - private final ServoView mView; + public void surfaceCreated(SurfaceHolder holder) { + Log.d(LOGTAG, "GLThread::surfaceCreated"); + + ServoCoordinates coords = new ServoCoordinates(); + coords.width = mServoView.getWidth(); + coords.height = mServoView.getHeight(); + coords.fb_width = mServoView.getWidth(); + coords.fb_height = mServoView.getHeight(); + + Surface surface = holder.getSurface(); + ServoOptions options = new ServoOptions(); + options.args = mServoView.mServoArgs; + options.coordinates = coords; + options.enableLogs = true; + options.enableSubpixelTextAntialiasing = true; + + DisplayMetrics metrics = new DisplayMetrics(); + mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + options.density = metrics.density; + if (mServoView.mServo == null && !mPaused) { + mServoView.mServo = new Servo( + options, mServoView, mServoView, mClient, mActivity, surface); + } else { + mPaused = false; + mServoView.mServo.resumeCompositor(surface, coords); + } - ServoGLRenderer(ServoView view) { - mView = view; } - public void onSurfaceCreated(GL10 unused, EGLConfig config) { - mView.onGLReady(); + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.d(LOGTAG, "GLThread::surfaceChanged"); + ServoCoordinates coords = new ServoCoordinates(); + coords.width = width; + coords.height = height; + coords.fb_width = width; + coords.fb_height = height; + + mServoView.mServo.resize(coords); } - public void onDrawFrame(GL10 unused) { + public void surfaceDestroyed(SurfaceHolder holder) { + Log.d(LOGTAG, "GLThread::surfaceDestroyed"); + mPaused = true; + mServoView.mServo.pauseCompositor(); } - public void onSurfaceChanged(GL10 gl, int width, int height) { - GLES31.glViewport(0, 0, width, height); - mView.onSurfaceInvalidated(width, height); + public void shutdown() { + Log.d(LOGTAG, "GLThread::shutdown"); + mGLLooperHandler.getLooper().quitSafely(); + } + + public void run() { + Looper.prepare(); + + mGLLooperHandler = new Handler(); + + Looper.loop(); } } } diff --git a/support/android/apk/servoview/src/oculusvr/AndroidManifest.xml b/support/android/apk/servoview/src/oculusvr/AndroidManifest.xml deleted file mode 100644 index 076c3dfa688..00000000000 --- a/support/android/apk/servoview/src/oculusvr/AndroidManifest.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- BEGIN_INCLUDE(manifest) --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.mozilla.servoview">> - <application> - <meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/> - <activity android:name=".MainActivity" android:screenOrientation="landscape"> - </activity> - </application> -</manifest> -<!-- END_INCLUDE(manifest) --> diff --git a/support/android/apk/settings.gradle b/support/android/apk/settings.gradle index 3d36551554c..d8826861853 100644 --- a/support/android/apk/settings.gradle +++ b/support/android/apk/settings.gradle @@ -1,3 +1,19 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + include ':servoapp' def userPropertiesFile = new File('user.properties') diff --git a/support/android/fakeld/fake-ld-arm.sh b/support/android/fakeld/fake-ld-arm.sh deleted file mode 100755 index 0e81b85fb14..00000000000 --- a/support/android/fakeld/fake-ld-arm.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -o errexit -set -o nounset -set -o pipefail - -source ./support/android/fakeld/fake-ld.sh - -export _GCC_PARAMS="${@}" -call_gcc "arch-arm" "armeabi" "arm-linux-androideabi" diff --git a/support/android/fakeld/fake-ld-arm64.sh b/support/android/fakeld/fake-ld-arm64.sh deleted file mode 100755 index eb7b0ada9a2..00000000000 --- a/support/android/fakeld/fake-ld-arm64.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -o errexit -set -o nounset -set -o pipefail - -source ./support/android/fakeld/fake-ld.sh - -export _GCC_PARAMS="${@}" -call_gcc "arch-arm64" "arm64-v8a" "aarch64-linux-android" diff --git a/support/android/fakeld/fake-ld-armv7.sh b/support/android/fakeld/fake-ld-armv7.sh deleted file mode 100755 index b1758a9b07a..00000000000 --- a/support/android/fakeld/fake-ld-armv7.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -o errexit -set -o nounset -set -o pipefail - -source ./support/android/fakeld/fake-ld.sh - -export _GCC_PARAMS="${@}" -call_gcc "arch-arm" "armeabi-v7a" "armv7-linux-androideabi" diff --git a/support/android/fakeld/fake-ld-x86.sh b/support/android/fakeld/fake-ld-x86.sh deleted file mode 100755 index 2df1d5e49e9..00000000000 --- a/support/android/fakeld/fake-ld-x86.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -o errexit -set -o nounset -set -o pipefail - -source ./support/android/fakeld/fake-ld.sh - -export _GCC_PARAMS="${@}" -call_gcc "arch-x86" "x86" "i686-linux-android" diff --git a/support/android/fakeld/fake-ld.cmd b/support/android/fakeld/fake-ld.cmd deleted file mode 100644 index b13c7e818ab..00000000000 --- a/support/android/fakeld/fake-ld.cmd +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -gcc -mwindows %* diff --git a/support/android/fakeld/fake-ld.sh b/support/android/fakeld/fake-ld.sh deleted file mode 100755 index 29eef1326ba..00000000000 --- a/support/android/fakeld/fake-ld.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -o errexit -set -o nounset -set -o pipefail - -call_gcc() -{ - TARGET_DIR="${OUT_DIR}/../../.." - - export _ANDROID_ARCH=$1 - export _ANDROID_TARGET=$3 - export ANDROID_SYSROOT="${ANDROID_NDK}/platforms/${ANDROID_PLATFORM}/${_ANDROID_ARCH}" - ANDROID_TOOLCHAIN="" - for host in "linux-x86_64" "linux-x86" "darwin-x86_64" "darwin-x86"; do - if [[ -d "${ANDROID_NDK}/toolchains/llvm/prebuilt/${host}/bin" ]]; then - ANDROID_TOOLCHAIN="${ANDROID_NDK}/toolchains/llvm/prebuilt/${host}/bin" - break - fi - done - - ANDROID_CPU_ARCH_DIR=$2 - ANDROID_CXX_LIBS="${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/${ANDROID_CPU_ARCH_DIR}" - - echo "toolchain: ${ANDROID_TOOLCHAIN}" - echo "libs dir: ${ANDROID_CXX_LIBS}" - echo "sysroot: ${ANDROID_SYSROOT}" - echo "targetdir: ${TARGET_DIR}" - - "${ANDROID_TOOLCHAIN}/clang" \ - --sysroot="${ANDROID_SYSROOT}" \ - --gcc-toolchain="${GCC_TOOLCHAIN}" \ - --target="${_ANDROID_TARGET}" \ - -L "${ANDROID_CXX_LIBS}" ${_GCC_PARAMS} -lc++ -} |