Update to jni 0.22.4 and gradle 9.3.1

This commit is contained in:
Jim Gustafson 2026-05-20 13:53:37 -07:00 committed by GitHub
parent 135652a835
commit 194de1bb9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 2247 additions and 2899 deletions

131
Cargo.lock generated
View File

@ -414,12 +414,6 @@ dependencies = [
"shlex",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "1.0.4"
@ -1804,27 +1798,32 @@ dependencies = [
[[package]]
name = "jni"
version = "0.21.1"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
dependencies = [
"cesu8",
"cfg-if",
"combine",
"jni-sys 0.3.1",
"jni-macros",
"jni-sys",
"log",
"thiserror 1.0.69",
"simd_cesu8",
"thiserror 2.0.18",
"walkdir",
"windows-sys 0.45.0",
"windows-link 0.2.1",
]
[[package]]
name = "jni-sys"
version = "0.3.1"
name = "jni-macros"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
dependencies = [
"jni-sys 0.4.1",
"proc-macro2",
"quote",
"rustc_version",
"simd_cesu8",
"syn",
]
[[package]]
@ -3350,6 +3349,22 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "simd_cesu8"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
dependencies = [
"rustc_version",
"simdutf8",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "sketches-ddsketch"
version = "0.3.1"
@ -4408,22 +4423,13 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
"windows-targets",
]
[[package]]
@ -4432,7 +4438,7 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
"windows-targets",
]
[[package]]
@ -4444,35 +4450,20 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
@ -4493,36 +4484,18 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -4535,48 +4508,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"

View File

@ -45,7 +45,7 @@
<h2>Overview of licenses:</h2>
<ul class="licenses-overview">
<li><a href="#MIT">MIT License</a> (188)</li>
<li><a href="#MIT">MIT License</a> (185)</li>
<li><a href="#AGPL-3.0-only">GNU Affero General Public License v3.0 only</a> (13)</li>
<li><a href="#Apache-2.0">Apache License 2.0</a> (13)</li>
<li><a href="#BSD-3-Clause">BSD 3-Clause &quot;New&quot; or &quot;Revised&quot; License</a> (4)</li>
@ -1888,15 +1888,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
<h3 id="MIT">MIT License</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
<li><a href="https://github.com/microsoft/windows-rs">windows-sys 0.45.0</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows-sys 0.59.0</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows-sys 0.61.2</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows-targets 0.42.2</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows-targets 0.52.6</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows_aarch64_msvc 0.42.2</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows_aarch64_msvc 0.52.6</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows_x86_64_gnu 0.52.6</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows_x86_64_msvc 0.42.2</a></li>
<li><a href="https://github.com/microsoft/windows-rs">windows_x86_64_msvc 0.52.6</a></li>
</ul>
<pre class="license-text"> MIT License
@ -2359,7 +2355,6 @@ DEALINGS IN THE SOFTWARE.
<h3 id="MIT">MIT License</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
<li><a href="https://github.com/jni-rs/jni-sys">jni-sys 0.3.1</a></li>
<li><a href="https://github.com/jni-rs/jni-sys">jni-sys 0.4.1</a></li>
</ul>
<pre class="license-text">Copyright (c) 2015 The rust-jni-sys Developers
@ -3875,8 +3870,9 @@ SOFTWARE.
<h3 id="MIT">MIT License (synthesized)</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
<li><a href="https://github.com/emk/cesu8-rs">cesu8 1.1.0</a></li>
<li><a href="https://github.com/jni-rs/jni-rs">jni-macros 0.22.4</a></li>
<li><a href="https://github.com/jni-rs/jni-sys">jni-sys-macros 0.4.1</a></li>
<li><a href="https://github.com/jni-rs/jni-rs">jni 0.22.4</a></li>
<li><a href="https://github.com/neon-bindings/neon">neon 1.1.1</a></li>
<li><a href="https://github.com/madsmtm/objc2">objc2-core-foundation 0.3.2</a></li>
<li><a href="https://github.com/madsmtm/objc2">objc2-io-kit 0.3.2</a></li>
@ -3922,6 +3918,32 @@ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
</pre>
</li>
<li class="license">
<h3 id="MIT">MIT License</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
<li><a href="https://github.com/rusticstuff/simdutf8">simdutf8 0.1.5</a></li>
</ul>
<pre class="license-text">MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.</pre>
</li>
<li class="license">
<h3 id="MIT">MIT License</h3>
@ -3980,6 +4002,7 @@ SOFTWARE.
<li><a href="https://github.com/serde-rs/serde">serde_core 1.0.228</a></li>
<li><a href="https://github.com/serde-rs/serde">serde_derive 1.0.228</a></li>
<li><a href="https://github.com/serde-rs/json">serde_json 1.0.149</a></li>
<li><a href="https://github.com/seancroach/simd_cesu8">simd_cesu8 1.1.1</a></li>
<li><a href="https://github.com/dtolnay/syn">syn 2.0.117</a></li>
<li><a href="https://github.com/dtolnay/thiserror">thiserror-impl 1.0.69</a></li>
<li><a href="https://github.com/dtolnay/thiserror">thiserror-impl 2.0.18</a></li>
@ -4323,35 +4346,6 @@ furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</pre>
</li>
<li class="license">
<h3 id="MIT">MIT License</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
<li><a href="https://github.com/jni-rs/jni-rs">jni 0.21.1</a></li>
</ul>
<pre class="license-text">The MIT License (MIT)
Copyright (c) 2016 Prevoty, Inc. and jni-rs contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

View File

@ -1783,7 +1783,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
```
## windows-sys 0.45.0, windows-sys 0.59.0, windows-sys 0.61.2, windows-targets 0.42.2, windows-targets 0.52.6, windows_aarch64_msvc 0.42.2, windows_aarch64_msvc 0.52.6, windows_x86_64_gnu 0.52.6, windows_x86_64_msvc 0.42.2, windows_x86_64_msvc 0.52.6
## windows-sys 0.59.0, windows-sys 0.61.2, windows-targets 0.52.6, windows_aarch64_msvc 0.52.6, windows_x86_64_gnu 0.52.6, windows_x86_64_msvc 0.52.6
```
MIT License
@ -2201,7 +2201,7 @@ DEALINGS IN THE SOFTWARE.
```
## jni-sys 0.3.1, jni-sys 0.4.1
## jni-sys 0.4.1
```
Copyright (c) 2015 The rust-jni-sys Developers
@ -3605,7 +3605,7 @@ SOFTWARE.
```
## cesu8 1.1.0, jni-sys-macros 0.4.1, neon 1.1.1, objc2-core-foundation 0.3.2, objc2-io-kit 0.3.2, protobuf-parse 3.7.2, tonic-prost-build 0.14.5, windows-collections 0.2.0, windows-collections 0.3.2, windows-core 0.61.2, windows-core 0.62.2, windows-future 0.2.1, windows-future 0.3.2, windows-implement 0.60.2, windows-interface 0.59.3, windows-link 0.1.3, windows-link 0.2.1, windows-numerics 0.2.0, windows-numerics 0.3.1, windows-result 0.3.4, windows-result 0.4.1, windows-strings 0.4.2, windows-strings 0.5.1, windows-threading 0.1.0, windows-threading 0.2.1, windows 0.61.3, windows 0.62.2
## jni-macros 0.22.4, jni-sys-macros 0.4.1, jni 0.22.4, neon 1.1.1, objc2-core-foundation 0.3.2, objc2-io-kit 0.3.2, protobuf-parse 3.7.2, tonic-prost-build 0.14.5, windows-collections 0.2.0, windows-collections 0.3.2, windows-core 0.61.2, windows-core 0.62.2, windows-future 0.2.1, windows-future 0.3.2, windows-implement 0.60.2, windows-interface 0.59.3, windows-link 0.1.3, windows-link 0.2.1, windows-numerics 0.2.0, windows-numerics 0.3.1, windows-result 0.3.4, windows-result 0.4.1, windows-strings 0.4.2, windows-strings 0.5.1, windows-threading 0.1.0, windows-threading 0.2.1, windows 0.61.3, windows 0.62.2
```
MIT License
@ -3629,6 +3629,30 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
```
## simdutf8 0.1.5
```
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
## ident_case 1.0.1
```
@ -3654,7 +3678,7 @@ SOFTWARE.
```
## curve25519-dalek-derive 0.1.1, anyhow 1.0.102, displaydoc 0.2.5, fastrand 2.4.1, home 0.5.12, itoa 1.0.18, linkme-impl 0.3.35, linkme 0.3.35, linux-raw-sys 0.12.1, linux-raw-sys 0.4.15, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.45, rustix 0.38.44, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, send_wrapper 0.6.0, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.149, syn 2.0.117, thiserror-impl 1.0.69, thiserror-impl 2.0.18, thiserror 1.0.69, thiserror 2.0.18, unicode-ident 1.0.24, zmij 1.0.21
## curve25519-dalek-derive 0.1.1, anyhow 1.0.102, displaydoc 0.2.5, fastrand 2.4.1, home 0.5.12, itoa 1.0.18, linkme-impl 0.3.35, linkme 0.3.35, linux-raw-sys 0.12.1, linux-raw-sys 0.4.15, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.45, rustix 0.38.44, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, send_wrapper 0.6.0, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.149, simd_cesu8 1.1.1, syn 2.0.117, thiserror-impl 1.0.69, thiserror-impl 2.0.18, thiserror 1.0.69, thiserror 2.0.18, unicode-ident 1.0.24, zmij 1.0.21
```
Permission is hereby granted, free of charge, to any
@ -3977,33 +4001,6 @@ SOFTWARE.
```
## jni 0.21.1
```
The MIT License (MIT)
Copyright (c) 2016 Prevoty, Inc. and jni-rs contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
## same-file 1.0.6, winapi-util 0.1.11
```

View File

@ -1868,7 +1868,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
<key>License</key>
<string>MIT License</string>
<key>Title</key>
<string>windows-sys 0.45.0, windows-sys 0.59.0, windows-sys 0.61.2, windows-targets 0.42.2, windows-targets 0.52.6, windows_aarch64_msvc 0.42.2, windows_aarch64_msvc 0.52.6, windows_x86_64_gnu 0.52.6, windows_x86_64_msvc 0.42.2, windows_x86_64_msvc 0.52.6</string>
<string>windows-sys 0.59.0, windows-sys 0.61.2, windows-targets 0.52.6, windows_aarch64_msvc 0.52.6, windows_x86_64_gnu 0.52.6, windows_x86_64_msvc 0.52.6</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
@ -2340,7 +2340,7 @@ SOFTWARE.
<key>License</key>
<string>MIT License</string>
<key>Title</key>
<string>jni-sys 0.3.1, jni-sys 0.4.1</string>
<string>jni-sys 0.4.1</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
@ -3931,7 +3931,35 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
<key>License</key>
<string>MIT License</string>
<key>Title</key>
<string>cesu8 1.1.0, jni-sys-macros 0.4.1, neon 1.1.1, objc2-core-foundation 0.3.2, objc2-io-kit 0.3.2, protobuf-parse 3.7.2, tonic-prost-build 0.14.5, windows-collections 0.2.0, windows-collections 0.3.2, windows-core 0.61.2, windows-core 0.62.2, windows-future 0.2.1, windows-future 0.3.2, windows-implement 0.60.2, windows-interface 0.59.3, windows-link 0.1.3, windows-link 0.2.1, windows-numerics 0.2.0, windows-numerics 0.3.1, windows-result 0.3.4, windows-result 0.4.1, windows-strings 0.4.2, windows-strings 0.5.1, windows-threading 0.1.0, windows-threading 0.2.1, windows 0.61.3, windows 0.62.2</string>
<string>jni-macros 0.22.4, jni-sys-macros 0.4.1, jni 0.22.4, neon 1.1.1, objc2-core-foundation 0.3.2, objc2-io-kit 0.3.2, protobuf-parse 3.7.2, tonic-prost-build 0.14.5, windows-collections 0.2.0, windows-collections 0.3.2, windows-core 0.61.2, windows-core 0.62.2, windows-future 0.2.1, windows-future 0.3.2, windows-implement 0.60.2, windows-interface 0.59.3, windows-link 0.1.3, windows-link 0.2.1, windows-numerics 0.2.0, windows-numerics 0.3.1, windows-result 0.3.4, windows-result 0.4.1, windows-strings 0.4.2, windows-strings 0.5.1, windows-threading 0.1.0, windows-threading 0.2.1, windows 0.61.3, windows 0.62.2</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.</string>
<key>License</key>
<string>MIT License</string>
<key>Title</key>
<string>simdutf8 0.1.5</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
@ -3993,7 +4021,7 @@ DEALINGS IN THE SOFTWARE.
<key>License</key>
<string>MIT License</string>
<key>Title</key>
<string>curve25519-dalek-derive 0.1.1, anyhow 1.0.102, displaydoc 0.2.5, fastrand 2.4.1, home 0.5.12, itoa 1.0.18, linkme-impl 0.3.35, linkme 0.3.35, linux-raw-sys 0.12.1, linux-raw-sys 0.4.15, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.45, rustix 0.38.44, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, send_wrapper 0.6.0, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.149, syn 2.0.117, thiserror-impl 1.0.69, thiserror-impl 2.0.18, thiserror 1.0.69, thiserror 2.0.18, unicode-ident 1.0.24, zmij 1.0.21</string>
<string>curve25519-dalek-derive 0.1.1, anyhow 1.0.102, displaydoc 0.2.5, fastrand 2.4.1, home 0.5.12, itoa 1.0.18, linkme-impl 0.3.35, linkme 0.3.35, linux-raw-sys 0.12.1, linux-raw-sys 0.4.15, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.45, rustix 0.38.44, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, send_wrapper 0.6.0, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.149, simd_cesu8 1.1.1, syn 2.0.117, thiserror-impl 1.0.69, thiserror-impl 2.0.18, thiserror 1.0.69, thiserror 2.0.18, unicode-ident 1.0.24, zmij 1.0.21</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
@ -4339,37 +4367,6 @@ SOFTWARE.
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2016 Prevoty, Inc. and jni-rs contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT License</string>
<key>Title</key>
<string>jni 0.21.1</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2017 Andrew Gallant
Permission is hereby granted, free of charge, to any person obtaining a copy

Binary file not shown.

View File

@ -1,7 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

15
gradlew vendored
View File

@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -112,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@ -170,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@ -203,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.

5
gradlew.bat vendored
View File

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@ -68,11 +70,10 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell

View File

@ -1,5 +1,5 @@
plugins {
id 'com.android.library' version '8.4.0'
id 'com.android.library' version '9.1.1'
id 'maven-publish'
id 'signing'
}
@ -15,66 +15,73 @@ ext.release_jni_lib_dirs = project.hasProperty("releaseRingrtcLibDir") ? [releas
ext.webrtc_jar = project.hasProperty("webrtcJar") ? webrtcJar : "libs/libwebrtc.jar"
ext.asset_dir = project.hasProperty("assetDir") ? assetDir : "assets"
android {
namespace 'org.signal.ringrtc'
base {
archivesName = "ringrtc-android"
}
compileSdk 34
android {
namespace = 'org.signal.ringrtc'
compileSdk = 34
defaultConfig {
minSdk 21
targetSdk 33
versionName ringrtcVersion
archivesBaseName = "ringrtc-android"
minSdk = 21
targetSdk = 33
versionName = ringrtcVersion
consumerProguardFiles "proguard-rules.pro"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets {
release {
jniLibs.srcDirs = release_jni_lib_dirs
java.srcDirs = ['api/']
assets.srcDir asset_dir
assets.srcDirs = [asset_dir]
}
debug {
jniLibs.srcDirs = debug_jni_lib_dirs
java.srcDirs = ['api/']
assets.srcDir asset_dir
assets.srcDirs = [asset_dir]
}
}
compileOptions {
coreLibraryDesugaringEnabled true
coreLibraryDesugaringEnabled = true
}
packagingOptions {
packaging {
// Libraries are already stripped if necessary when linked.
doNotStrip "**/*.so"
jniLibs.keepDebugSymbols.add('**/*.so')
}
publishing {
singleVariant("release")
singleVariant("release")
}
}
dependencies {
api files(webrtc_jar)
api 'androidx.annotation:annotation:1.4.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6'
androidTestImplementation "androidx.test:runner:1.4.0"
androidTestImplementation "androidx.test:core:1.4.0"
androidTestImplementation "org.mockito:mockito-android:5.1.1"
api 'androidx.annotation:annotation:1.10.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
androidTestImplementation "androidx.test:runner:1.5.0"
androidTestImplementation "androidx.test:core:1.5.0"
androidTestImplementation "org.mockito:mockito-android:5.12.0"
}
task javadoc(type: Javadoc) {
source = android.sourceSets.release.java.sourceFiles
classpath += files(android.bootClasspath)
// There doesn't seem to be a convenient way to do this with just one variant.
android.libraryVariants.all { v ->
classpath += v.getCompileClasspath(null)
tasks.register('javadoc', Javadoc) {
source = fileTree(dir: 'api', include: '**/*.java')
failOnError = false
if (project.hasProperty('docsDir')) {
destinationDir = new File(docsDir, 'javadoc')
}
// Normally this is set by the 'java' plugin, but that's not compatible with 'android-library'
if (project.hasProperty("docsDir")) {
destinationDir = new File(docsDir, "javadoc")
}
androidComponents {
onVariants(selector().withBuildType('release')) { variant ->
tasks.named('javadoc').configure { task ->
task.classpath = files(androidComponents.sdkComponents.bootClasspath) +
project.configurations.named("${variant.name}CompileClasspath").get()
}
}
}
@ -85,7 +92,7 @@ afterEvaluate {
from components.release
group = 'org.signal'
artifactId = archivesBaseName
artifactId = base.archivesName.get()
version = ringrtcVersion
pom {
@ -137,7 +144,7 @@ afterEvaluate {
}
signing {
required { isReleaseVersion && gradle.taskGraph.hasTask(getPath() + ":publish") }
required = { isReleaseVersion && gradle.taskGraph.hasTask(getPath() + ":publish") }
def signingKeyId = findProperty("signingKeyId")
def signingKey = findProperty("signingKey")
@ -150,8 +157,8 @@ afterEvaluate {
}
}
task version {
group 'Info'
tasks.register('version') {
group = 'Info'
description = 'Prints the versions as read from the version config file.'
doLast {
println "RingRTC version: " + ringrtcVersion

View File

@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import org.signal.ringrtc.CallException;
import org.signal.ringrtc.CallId;
import org.signal.ringrtc.CallManager;
import org.signal.ringrtc.GroupCall;
@ -18,8 +20,10 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.mockito.Mockito.*;
public class CallManagerTest extends CallTestBase {
@ -147,4 +151,78 @@ public class CallManagerTest extends CallTestBase {
callManager.receivedHttpResponse(requestId.getValue(), 404, new byte[] {});
latch.await();
}
@Test
public void testFromEraIdRoundTrip() {
CallId a1 = CallId.fromEra("mesozoic");
CallId a2 = CallId.fromEra("mesozoic");
CallId b = CallId.fromEra("cenozoic");
errors.checkThat(a1, is(a2));
errors.checkThat(a1, is(not(b)));
errors.checkThat(a1.longValue(), is(not(0L)));
}
@Test
public void testAddAssetWithNoSourceThrows() throws Exception {
CallManager callManager = CallManager.createCallManager(mock());
try {
callManager.addAsset("group", (byte[]) null);
errors.addError(new AssertionError("expected CallException"));
} catch (CallException ex) {
errors.checkThat(ex.getMessage(),
is("addAsset requires either a filePath or content"));
}
}
@Test
public void testSetAudioEnableNoActiveCallThrows() throws Exception {
CallManager callManager = CallManager.createCallManager(mock());
try {
callManager.setAudioEnable(true);
errors.addError(new AssertionError("expected CallException"));
} catch (CallException ex) {
}
}
@Test
public void testPeekWithManyMembers() throws Exception {
final int GROUP_SIZE = 256;
final int PARTICIPANTS = 100;
CallManager.Observer observer = mock();
CallManager callManager = CallManager.createCallManager(observer);
GroupCall.GroupMemberInfo[] memberInfos = new GroupCall.GroupMemberInfo[GROUP_SIZE];
StringBuilder participants = new StringBuilder("[");
for (int i = 0; i < GROUP_SIZE; i++) {
byte[] opaque = fakeOpaqueUserId(i);
memberInfos[i] = new GroupCall.GroupMemberInfo(UUID.nameUUIDFromBytes(opaque), opaque);
if (i < PARTICIPANTS) {
if (i > 0) participants.append(',');
participants.append("{\"opaqueUserId\":\"")
.append(sha256Hex(opaque))
.append("\",\"demuxId\":")
.append(32 * (i + 1))
.append('}');
}
}
participants.append(']');
String body = "{\"conferenceId\":\"jurassic\",\"maxDevices\":" + (PARTICIPANTS + 1)
+ ",\"participants\":" + participants + ",\"pendingClients\":[]}";
CountDownLatch latch = new CountDownLatch(1);
callManager.peekGroupCall("sfu.example", new byte[] { 1, 2, 3 }, Arrays.asList(memberInfos), result -> {
errors.checkThat(result.getEraId(), is("jurassic"));
errors.checkThat(result.getDeviceCountIncludingPendingDevices(), is((long) PARTICIPANTS));
errors.checkThat(result.getJoinedMembers().size(), is(PARTICIPANTS));
latch.countDown();
});
ArgumentCaptor<Long> requestId = ArgumentCaptor.forClass(Long.class);
verify(observer).onSendHttpRequest(requestId.capture(), startsWith("sfu.example"), eq(CallManager.HttpMethod.GET), any(), any());
callManager.receivedHttpResponse(requestId.getValue(), 200, body.getBytes("UTF-8"));
errors.checkThat("peek result lambda fired",
latch.await(30, TimeUnit.SECONDS), is(true));
}
}

View File

@ -74,7 +74,7 @@ zkgroup = { git = "https://github.com/signalapp/libsignal", tag = "v0.89.2" }
neon = { version = "1.1.1", optional = true, default-features = false, features = ["napi-6"] }
# Optional, needed to check Android-specific code when not targeting Android
jni = { version = "0.21.1", optional = true, default-features = false }
jni = { version = "0.22.4", optional = true, default-features = false }
# Optional, needed by the "http" feature
ureq = { version = "3.1.4", optional = true }
@ -138,7 +138,7 @@ path = "tests/outgoing.rs"
required-features = ["sim"]
[target.'cfg(target_os="android")'.dependencies]
jni = { version = "0.21.1", default-features = false }
jni = { version = "0.22.4", default-features = false }
[dev-dependencies]
uuid = { version = "1.19.0" }

File diff suppressed because it is too large Load Diff

View File

@ -3,122 +3,106 @@
// SPDX-License-Identifier: AGPL-3.0-only
//
use std::borrow::Cow;
use anyhow::Result;
use jni::{
JNIEnv,
EnvUnowned, jni_str,
objects::{JByteArray, JClass, JObject, JString},
};
use crate::{
android::{error, jni_util::*},
core::util::try_scoped,
lite::call_links::CallLinkRootKey,
};
use crate::{android::error::ThrowCallException, lite::call_links::CallLinkRootKey};
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn Java_org_signal_ringrtc_CallLinkRootKey_nativeParseKeyString<'local>(
mut env: JNIEnv<'local>,
mut unowned_env: EnvUnowned<'local>,
_class: JClass,
string: JString,
) -> JByteArray<'local> {
try_scoped(|| {
let string = env.get_string(&string)?;
let key = CallLinkRootKey::try_from(Cow::from(&string).as_ref())?;
Ok(env.byte_array_from_slice(key.as_slice())?)
})
.unwrap_or_else(|e| {
error::throw_error(&mut env, e);
JByteArray::default()
})
unowned_env
.with_env(|env| -> Result<_> {
let string = string.try_to_string(env)?;
let key = CallLinkRootKey::try_from(string.as_str())?;
Ok(env.byte_array_from_slice(key.as_slice())?)
})
.resolve::<ThrowCallException>()
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn Java_org_signal_ringrtc_CallLinkRootKey_nativeValidateKeyBytes(
mut env: JNIEnv,
mut unowned_env: EnvUnowned,
_class: JClass,
bytes: JByteArray,
) {
try_scoped(|| {
let bytes = env.convert_byte_array(bytes)?;
let _ = CallLinkRootKey::try_from(bytes.as_slice())?;
Ok(())
})
.unwrap_or_else(|e| error::throw_error(&mut env, e))
unowned_env
.with_env(|env| -> Result<()> {
let bytes = env.convert_byte_array(bytes)?;
let _ = CallLinkRootKey::try_from(bytes.as_slice())?;
Ok(())
})
.resolve::<ThrowCallException>();
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn Java_org_signal_ringrtc_CallLinkRootKey_generate<'local>(
mut env: JNIEnv<'local>,
mut unowned_env: EnvUnowned<'local>,
_class: JClass,
) -> JObject<'local> {
try_scoped(|| {
let key = CallLinkRootKey::generate(rand::rngs::OsRng);
let bytes = env.byte_array_from_slice(key.as_slice())?;
let object = jni_new_object(
&mut env,
jni_class_name!(org.signal.ringrtc.CallLinkRootKey),
jni_args!((bytes => [byte]) -> void),
)?;
Ok(object)
})
.unwrap_or_else(|e| {
error::throw_error(&mut env, e);
JObject::default()
})
unowned_env
.with_env(|env| -> Result<_> {
let key = CallLinkRootKey::generate(rand::rngs::OsRng);
let bytes = env.byte_array_from_slice(key.as_slice())?;
let object = jni_new_object!(env, jni_str!("org/signal/ringrtc/CallLinkRootKey"), (
bytes => [byte],
))?;
Ok(object)
})
.resolve::<ThrowCallException>()
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn Java_org_signal_ringrtc_CallLinkRootKey_generateAdminPasskey<'local>(
mut env: JNIEnv<'local>,
mut unowned_env: EnvUnowned<'local>,
_class: JClass,
) -> JByteArray<'local> {
try_scoped(|| {
let passkey = CallLinkRootKey::generate_admin_passkey(rand::rngs::OsRng);
Ok(env.byte_array_from_slice(&passkey)?)
})
.unwrap_or_else(|e| {
error::throw_error(&mut env, e);
JByteArray::default()
})
unowned_env
.with_env(|env| -> Result<_> {
let passkey = CallLinkRootKey::generate_admin_passkey(rand::rngs::OsRng);
Ok(env.byte_array_from_slice(&passkey)?)
})
.resolve::<ThrowCallException>()
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn Java_org_signal_ringrtc_CallLinkRootKey_nativeDeriveRoomId<'local>(
mut env: JNIEnv<'local>,
mut unowned_env: EnvUnowned<'local>,
_class: JClass,
key_bytes: JByteArray,
) -> JByteArray<'local> {
try_scoped(|| {
let key_bytes = env.convert_byte_array(key_bytes)?;
let key = CallLinkRootKey::try_from(key_bytes.as_slice())?;
Ok(env.byte_array_from_slice(&key.derive_room_id())?)
})
.unwrap_or_else(|e| {
error::throw_error(&mut env, e);
JByteArray::default()
})
unowned_env
.with_env(|env| -> Result<_> {
let key_bytes = env.convert_byte_array(key_bytes)?;
let key = CallLinkRootKey::try_from(key_bytes.as_slice())?;
Ok(env.byte_array_from_slice(&key.derive_room_id())?)
})
.resolve::<ThrowCallException>()
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn Java_org_signal_ringrtc_CallLinkRootKey_nativeToFormattedString<'local>(
mut env: JNIEnv<'local>,
mut unowned_env: EnvUnowned<'local>,
_class: JClass,
key_bytes: JByteArray,
) -> JString<'local> {
try_scoped(|| {
let key_bytes = env.convert_byte_array(key_bytes)?;
let key = CallLinkRootKey::try_from(key_bytes.as_slice())?;
Ok(env.new_string(key.to_formatted_string())?)
})
.unwrap_or_else(|e| {
error::throw_error(&mut env, e);
JString::default()
})
unowned_env
.with_env(|env| -> Result<_> {
let key_bytes = env.convert_byte_array(key_bytes)?;
let key = CallLinkRootKey::try_from(key_bytes.as_slice())?;
Ok(env.new_string(key.to_formatted_string())?)
})
.resolve::<ThrowCallException>()
}

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,10 @@
use std::{borrow::Cow, convert::TryFrom, panic, sync::Arc, time::Duration};
use jni::{
JNIEnv,
objects::{GlobalRef, JByteArray, JClass, JObject, JString},
sys::{jbyte, jint, jlong},
Env, jni_sig, jni_str,
objects::{JByteArray, JClass, JObject, JString},
signature::FieldSignature,
sys::{jbyte, jint, jlong, jobject},
};
use log::Level;
@ -18,7 +19,6 @@ use crate::{
android::{
android_platform::{AndroidCallContext, AndroidPlatform},
error::AndroidError,
jni_util::*,
logging::init_logging,
webrtc_peer_connection_factory::*,
},
@ -50,25 +50,23 @@ use crate::{
pub type AndroidCallManager = CallManager<AndroidPlatform>;
/// CMI request for build time information
pub fn get_build_info<'a>(env: &mut JNIEnv<'a>) -> Result<JObject<'a>> {
pub fn get_build_info<'a>(env: &mut Env<'a>) -> Result<JObject<'a>> {
#[cfg(all(debug_assertions, not(test)))]
let debug = true;
#[cfg(any(not(debug_assertions), test))]
let debug = false;
let result = jni_new_object(
Ok(jni_new_object!(
env,
jni_class_name!(org.signal.ringrtc.BuildInfo),
jni_args!((debug => boolean) -> void),
)?;
Ok(result)
jni_str!("org/signal/ringrtc/BuildInfo"),
(debug => boolean)
)?)
}
/// Library initialization routine.
///
/// Sets up the logging infrastructure.
pub fn initialize(env: &mut JNIEnv) -> Result<()> {
pub fn initialize(env: &mut Env) -> Result<()> {
init_logging(env, Level::Debug)?;
// Set a custom panic handler that uses the logger instead of
@ -81,7 +79,7 @@ pub fn initialize(env: &mut JNIEnv) -> Result<()> {
}
/// Creates a new AndroidCallManager object.
pub fn create_call_manager(env: &mut JNIEnv, jni_call_manager: JObject) -> Result<jlong> {
pub fn create_call_manager(env: &mut Env, jni_call_manager: JObject) -> Result<jlong> {
let platform = AndroidPlatform::new(env, env.new_global_ref(jni_call_manager)?)?;
let http_client = http::DelegatingClient::new(platform.try_clone()?);
@ -94,7 +92,7 @@ pub fn create_call_manager(env: &mut JNIEnv, jni_call_manager: JObject) -> Resul
/// Create a org.webrtc.PeerConnection object
pub fn create_peer_connection(
env: &mut JNIEnv,
env: &mut Env,
peer_connection_factory: jlong,
mut native_connection: webrtc::ptr::Borrowed<Connection<AndroidPlatform>>,
jni_rtc_config: JObject,
@ -121,8 +119,8 @@ pub fn create_peer_connection(
// construct JNI OwnedPeerConnection object
let jni_owned_pc = unsafe {
Java_org_webrtc_PeerConnectionFactory_nativeCreatePeerConnection(
env.unsafe_clone(),
JClass::from(JObject::null()),
jni::EnvUnowned::from_raw(env.get_raw()),
JClass::default(),
peer_connection_factory,
jni_rtc_config,
jni_media_constraints,
@ -161,7 +159,7 @@ pub fn create_peer_connection(
/// Application notification updating the current user's UUID
pub fn set_self_uuid(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
uuid: JByteArray,
) -> Result<()> {
@ -171,7 +169,7 @@ pub fn set_self_uuid(
/// Adds an asset to the asset manager.
pub fn add_asset(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
asset_group: JString,
file_path: JString,
@ -179,12 +177,12 @@ pub fn add_asset(
) -> Result<()> {
use crate::core::assets::AssetHandle;
let asset_group: String = env.get_string(&asset_group)?.into();
let asset_group: String = asset_group.try_to_string(env)?;
let handle = if !content.is_null() {
AssetHandle::Content(env.convert_byte_array(content)?)
} else if !file_path.is_null() {
let path: String = env.get_string(&file_path)?.into();
let path: String = file_path.try_to_string(env)?;
AssetHandle::FilePath(path)
} else {
return Err(anyhow::anyhow!(
@ -198,20 +196,20 @@ pub fn add_asset(
/// Application notification to start a new call
pub fn call(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
jni_remote: JObject,
call_media_type: CallMediaType,
local_device_id: DeviceId,
) -> Result<()> {
let call_manager = unsafe { ptr_as_mut(call_manager)? };
let app_remote_peer = env.new_global_ref(jni_remote)?;
let app_remote_peer = Arc::new(env.new_global_ref(jni_remote)?);
call_manager.call(app_remote_peer, call_media_type, local_device_id)
}
/// Application notification to proceed with a new call
pub fn proceed(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
call_id: jlong,
jni_call_context: JObject,
@ -254,7 +252,7 @@ pub fn hangup(call_manager: *mut AndroidCallManager) -> Result<()> {
/// Application notification cancelling a group call ring
pub fn cancel_group_ring(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
group_id: JByteArray,
ring_id: jlong,
@ -277,7 +275,7 @@ pub fn cancel_group_ring(
/// Application notification of received answer message
#[allow(clippy::too_many_arguments)]
pub fn received_answer(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
call_id: jlong,
remote_peer: JObject,
@ -288,7 +286,7 @@ pub fn received_answer(
) -> Result<()> {
let call_manager = unsafe { ptr_as_mut(call_manager)? };
let call_id = CallId::from(call_id);
let remote_peer = env.new_global_ref(remote_peer)?;
let remote_peer = Arc::new(env.new_global_ref(remote_peer)?);
let opaque = if opaque.is_null() {
return Err(RingRtcError::OptionValueNotSet(
@ -317,7 +315,7 @@ pub fn received_answer(
/// Application notification of received offer message
#[allow(clippy::too_many_arguments)]
pub fn received_offer(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
call_id: jlong,
remote_peer: JObject,
@ -331,7 +329,7 @@ pub fn received_offer(
) -> Result<()> {
let call_manager = unsafe { ptr_as_mut(call_manager)? };
let call_id = CallId::from(call_id);
let remote_peer = env.new_global_ref(remote_peer)?;
let remote_peer = Arc::new(env.new_global_ref(remote_peer)?);
let opaque = if opaque.is_null() {
return Err(RingRtcError::OptionValueNotSet(
@ -361,7 +359,7 @@ pub fn received_offer(
/// Application notification to add ICE candidates to a Connection
pub fn received_ice(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
call_id: jlong,
remote_peer: JObject,
@ -370,14 +368,15 @@ pub fn received_ice(
) -> Result<()> {
let call_manager = unsafe { ptr_as_mut(call_manager)? };
let call_id = CallId::from(call_id);
let remote_peer = env.new_global_ref(remote_peer)?;
let remote_peer = Arc::new(env.new_global_ref(remote_peer)?);
// Convert Java list of byte[] into Rust Vector of IceCandidate
let jni_ice_candidates = env.get_list(&candidates)?;
let jni_ice_candidates = jni::objects::JList::cast_local(env, candidates)?;
let mut ice_candidates = Vec::new();
let mut iterator = jni_ice_candidates.iter(env)?;
let iterator = jni_ice_candidates.iter(env)?;
while let Some(jni_ice_candidate) = iterator.next(env)? {
let jni_ice_candidate: JByteArray<'_> = jni_ice_candidate.into();
// SAFETY: Java side must provide a `List<byte[]>`, so each element is `byte[]`.
let jni_ice_candidate = unsafe { JByteArray::from_raw(env, jni_ice_candidate.as_raw()) };
let opaque = env.convert_byte_array(jni_ice_candidate)?;
ice_candidates.push(signaling::IceCandidate::new(opaque));
}
@ -396,7 +395,7 @@ pub fn received_ice(
/// Application notification of received Hangup message
pub fn received_hangup(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
call_id: jlong,
remote_peer: JObject,
@ -406,7 +405,7 @@ pub fn received_hangup(
) -> Result<()> {
let call_manager = unsafe { ptr_as_mut(call_manager)? };
let call_id = CallId::from(call_id);
let remote_peer = env.new_global_ref(remote_peer)?;
let remote_peer = Arc::new(env.new_global_ref(remote_peer)?);
call_manager.received_hangup(
remote_peer,
@ -420,7 +419,7 @@ pub fn received_hangup(
/// Application notification of received Busy message
pub fn received_busy(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
call_id: jlong,
remote_peer: JObject,
@ -428,7 +427,7 @@ pub fn received_busy(
) -> Result<()> {
let call_manager = unsafe { ptr_as_mut(call_manager)? };
let call_id = CallId::from(call_id);
let remote_peer = env.new_global_ref(remote_peer)?;
let remote_peer = Arc::new(env.new_global_ref(remote_peer)?);
call_manager.received_busy(
remote_peer,
@ -439,7 +438,7 @@ pub fn received_busy(
/// Application notification of received call message.
pub fn received_call_message(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
sender_uuid: JByteArray,
sender_device_id: DeviceId,
@ -474,7 +473,7 @@ pub fn received_call_message(
/// Application notification of received HTTP response.
pub fn received_http_response(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
request_id: jlong,
status_code: jint,
@ -512,22 +511,22 @@ pub fn accept_call(call_manager: *mut AndroidCallManager, call_id: jlong) -> Res
call_manager.accept_call(call_id)
}
/// CMI request for the active Connection object
pub fn get_active_connection(call_manager: *mut AndroidCallManager) -> Result<GlobalRef> {
/// CMI request to get the active Connection object (a raw jobject pointing to the Global ref)
pub fn get_active_connection(call_manager: *mut AndroidCallManager) -> Result<jobject> {
let call_manager = unsafe { ptr_as_mut(call_manager)? };
let connection = call_manager.active_connection()?;
let android_connection = connection.app_connection()?;
Ok(android_connection.to_jni())
Ok(android_connection.to_jni().as_raw())
}
/// CMI request for the active CallContext object
pub fn get_active_call_context(call_manager: *mut AndroidCallManager) -> Result<GlobalRef> {
/// CMI request to get the active CallContext object (a raw jobject pointing to the Global ref)
pub fn get_active_call_context(call_manager: *mut AndroidCallManager) -> Result<jobject> {
let call_manager = unsafe { ptr_as_mut(call_manager)? };
let call = call_manager.active_call()?;
let android_call_context = call.call_context()?;
Ok(android_call_context.to_jni())
Ok(android_call_context.to_jni().as_raw())
}
/// CMI request to set the audio status
@ -605,14 +604,14 @@ pub fn close(call_manager: *mut AndroidCallManager) -> Result<()> {
// Call Links
pub fn read_call_link(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
sfu_url: JString,
auth_credential_presentation: JByteArray,
root_key: JByteArray,
request_id: jlong,
) -> Result<()> {
let sfu_url = env.get_string(&sfu_url)?;
let sfu_url = sfu_url.try_to_string(env)?;
let auth_credential_presentation = env.convert_byte_array(auth_credential_presentation)?;
let root_key =
call_links::CallLinkRootKey::try_from(env.convert_byte_array(root_key)?.as_slice())?;
@ -634,7 +633,7 @@ pub fn read_call_link(
#[allow(clippy::too_many_arguments)]
pub fn create_call_link(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
sfu_url: JString,
create_credential_presentation: JByteArray,
@ -644,7 +643,7 @@ pub fn create_call_link(
restrictions: jint,
request_id: jlong,
) -> Result<()> {
let sfu_url = env.get_string(&sfu_url)?;
let sfu_url = sfu_url.try_to_string(env)?;
let create_credential_presentation = env.convert_byte_array(create_credential_presentation)?;
let root_key =
call_links::CallLinkRootKey::try_from(env.convert_byte_array(root_key)?.as_slice())?;
@ -672,7 +671,7 @@ pub fn create_call_link(
#[allow(clippy::too_many_arguments)]
pub fn update_call_link(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
sfu_url: JString,
auth_credential_presentation: JByteArray,
@ -683,7 +682,7 @@ pub fn update_call_link(
new_revoked: jint,
request_id: jlong,
) -> Result<()> {
let sfu_url = env.get_string(&sfu_url)?;
let sfu_url = sfu_url.try_to_string(env)?;
let auth_credential_presentation = env.convert_byte_array(auth_credential_presentation)?;
let root_key =
call_links::CallLinkRootKey::try_from(env.convert_byte_array(root_key)?.as_slice())?;
@ -691,10 +690,10 @@ pub fn update_call_link(
let new_name = if new_name.is_null() {
None
} else {
Some(env.get_string(&new_name)?)
Some(new_name.try_to_string(env)?)
};
let encrypted_name = new_name.map(|name| {
let name = Cow::from(&name);
let name: &str = &name;
if name.is_empty() {
vec![]
} else {
@ -731,7 +730,7 @@ pub fn update_call_link(
#[allow(clippy::too_many_arguments)]
pub fn delete_call_link(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
sfu_url: JString,
auth_credential_presentation: JByteArray,
@ -739,7 +738,7 @@ pub fn delete_call_link(
admin_passkey: JByteArray,
request_id: jlong,
) -> Result<()> {
let sfu_url = env.get_string(&sfu_url)?;
let sfu_url = sfu_url.try_to_string(env)?;
let auth_credential_presentation = env.convert_byte_array(auth_credential_presentation)?;
let root_key =
call_links::CallLinkRootKey::try_from(env.convert_byte_array(root_key)?.as_slice())?;
@ -789,7 +788,7 @@ fn deserialize_to_group_member_info(
}
pub fn peek_group_call(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
request_id: jlong,
sfu_url: JString,
@ -798,7 +797,7 @@ pub fn peek_group_call(
) -> Result<()> {
let request_id = request_id as u32;
let sfu_url = env.get_string(&sfu_url)?.into();
let sfu_url = sfu_url.try_to_string(env)?;
let membership_proof = env.convert_byte_array(membership_proof)?;
@ -811,7 +810,7 @@ pub fn peek_group_call(
}
pub fn peek_call_link_call(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
request_id: jlong,
sfu_url: JString,
@ -820,7 +819,7 @@ pub fn peek_call_link_call(
) -> Result<()> {
let request_id = request_id as u32;
let sfu_url = env.get_string(&sfu_url)?;
let sfu_url = sfu_url.try_to_string(env)?;
let auth_credential_presentation = env.convert_byte_array(auth_credential_presentation)?;
let root_key =
@ -844,7 +843,7 @@ pub fn peek_call_link_call(
#[allow(clippy::too_many_arguments)]
pub fn create_group_call_client(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
group_id: JByteArray,
sfu_url: JString,
@ -856,7 +855,7 @@ pub fn create_group_call_client(
native_video_track_borrowed_rc: jlong,
) -> Result<group_call::ClientId> {
let group_id = env.convert_byte_array(group_id)?;
let sfu_url = env.get_string(&sfu_url)?.into();
let sfu_url = sfu_url.try_to_string(env)?;
let hkdf_extra_info = env.convert_byte_array(hkdf_extra_info)?;
let peer_connection_factory = unsafe {
@ -909,7 +908,7 @@ pub fn create_group_call_client(
#[allow(clippy::too_many_arguments)]
pub fn create_call_link_call_client(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
sfu_url: JString,
endorsement_public_key: JByteArray,
@ -923,7 +922,7 @@ pub fn create_call_link_call_client(
native_audio_track_borrowed_rc: jlong,
native_video_track_borrowed_rc: jlong,
) -> Result<group_call::ClientId> {
let sfu_url = env.get_string(&sfu_url)?.into();
let sfu_url = sfu_url.try_to_string(env)?;
let endorsement_public_key = env.convert_byte_array(endorsement_public_key)?;
let auth_presentation = env.convert_byte_array(auth_presentation)?;
let root_key =
@ -1086,7 +1085,7 @@ pub fn set_outgoing_group_call_video_is_screenshare(
}
pub fn group_ring(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
client_id: group_call::ClientId,
recipient: JByteArray,
@ -1122,51 +1121,53 @@ pub fn set_data_mode(
}
pub fn request_video(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
client_id: group_call::ClientId,
jni_rendered_resolutions: JObject,
active_speaker_height: jint,
) -> Result<()> {
// Convert Java list of VideoRequest into Rust Vec<group_call::VideoRequest>.
let jni_rendered_resolution_list = env.get_list(&jni_rendered_resolutions)?;
let jni_rendered_resolution_list =
jni::objects::JList::cast_local(env, jni_rendered_resolutions)?;
let mut rendered_resolutions: Vec<group_call::VideoRequest> = Vec::new();
let mut iterator = jni_rendered_resolution_list.iter(env)?;
let iterator = jni_rendered_resolution_list.iter(env)?;
while let Some(jni_rendered_resolution) = iterator.next(env)? {
const LONG_TYPE: &str = jni_signature!(long);
const INT_TYPE: &str = jni_signature!(int);
const NULLABLE_INT_TYPE: &str = jni_signature!(java.lang.Integer);
const LONG_TYPE: FieldSignature<'static> = jni_sig!(long);
const INT_TYPE: FieldSignature<'static> = jni_sig!(int);
const NULLABLE_INT_TYPE: FieldSignature<'static> = jni_sig!(java.lang.Integer);
const DEMUX_ID_FIELD: &str = "demuxId";
let demux_id =
jni_get_field(env, &jni_rendered_resolution, DEMUX_ID_FIELD, LONG_TYPE)?.j()?;
let demux_id = env
.get_field(&jni_rendered_resolution, jni_str!("demuxId"), &LONG_TYPE)?
.into_long()?;
let demux_id = demux_id as u32;
const WIDTH_FIELD: &str = "width";
let width = jni_get_field(env, &jni_rendered_resolution, WIDTH_FIELD, INT_TYPE)?.i()?;
let width = env
.get_field(&jni_rendered_resolution, jni_str!("width"), &INT_TYPE)?
.into_int()?;
let width = width as u16;
const HEIGHT_FIELD: &str = "height";
let height = jni_get_field(env, &jni_rendered_resolution, HEIGHT_FIELD, INT_TYPE)?.i()?;
let height = env
.get_field(&jni_rendered_resolution, jni_str!("height"), &INT_TYPE)?
.into_int()?;
let height = height as u16;
const FRAMERATE_FIELD: &str = "framerate";
let framerate = jni_get_field(
env,
&jni_rendered_resolution,
FRAMERATE_FIELD,
NULLABLE_INT_TYPE,
)?
.l()?;
let framerate = env
.get_field(
&jni_rendered_resolution,
jni_str!("framerate"),
&NULLABLE_INT_TYPE,
)?
.into_object()?;
let framerate = if framerate.is_null() {
None
} else {
// We have java.lang.Integer, so we need to invoke the function to get the actual
// int value that is attached to it.
match env.call_method(framerate, "intValue", jni_signature!(() -> int), &[]) {
match env.call_method(framerate, jni_str!("intValue"), jni_sig!(() -> int), &[]) {
Ok(jvalue) => {
match jvalue.i() {
match jvalue.into_int() {
Ok(int) => Some(int.to_owned() as u16),
Err(_) => {
// The framerate can be ignored.
@ -1201,7 +1202,7 @@ pub fn request_video(
}
pub fn approve_user(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
client_id: group_call::ClientId,
other_user_id: JByteArray,
@ -1213,7 +1214,7 @@ pub fn approve_user(
}
pub fn deny_user(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
client_id: group_call::ClientId,
other_user_id: JByteArray,
@ -1245,7 +1246,7 @@ pub fn block_client(
}
pub fn set_group_members(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
client_id: group_call::ClientId,
jni_serialized_group_members: JByteArray,
@ -1258,7 +1259,7 @@ pub fn set_group_members(
}
pub fn set_membership_proof(
env: &JNIEnv,
env: &Env,
call_manager: *mut AndroidCallManager,
client_id: group_call::ClientId,
proof: JByteArray,
@ -1270,12 +1271,12 @@ pub fn set_membership_proof(
}
pub fn react(
env: &mut JNIEnv,
env: &mut Env,
call_manager: *mut AndroidCallManager,
client_id: group_call::ClientId,
value: JString,
) -> Result<()> {
let value = env.get_string(&value)?.into();
let value: String = value.try_to_string(env)?;
let call_manager = unsafe { ptr_as_mut(call_manager)? };
call_manager.react(client_id, value);
Ok(())

View File

@ -5,40 +5,76 @@
//! Android Error Codes and Utilities.
use anyhow::Error;
use jni::{JNIEnv, errors, objects::JThrowable};
use std::panic::{AssertUnwindSafe, catch_unwind};
use jni::{
Env,
errors::ErrorPolicy,
jni_str,
objects::JThrowable,
strings::{JNIStr, JNIString},
};
use thiserror::Error;
use crate::{android::jni_util::*, core::util::try_scoped};
use crate::core::util::try_scoped;
const CALL_EXCEPTION_CLASS: &str = jni_class_name!(org.signal.ringrtc.CallException);
const CALL_EXCEPTION_CLASS: &JNIStr = jni_str!("org/signal/ringrtc/CallException");
/// Convert a `Error` into a Java `org.signal.ringrtc.CallException`
/// and throw it.
///
/// This is used to communicate synchronous errors to the client
/// application.
pub fn throw_error(env: &mut JNIEnv, error: Error) {
if let Ok(exception) = env.exception_occurred() {
if env.exception_clear().is_ok() {
/// `ErrorPolicy` that converts Rust errors and panics into a Java
/// `org.signal.ringrtc.CallException` and throws it to the client.
#[derive(Debug, Default)]
pub struct ThrowCallException;
impl<T: Default> ErrorPolicy<T, anyhow::Error> for ThrowCallException {
type Captures<'unowned_env_local: 'native_method, 'native_method> = ();
fn on_error<'unowned_env_local: 'native_method, 'native_method>(
env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
err: anyhow::Error,
) -> jni::errors::Result<T> {
// If the closure returned an error while a Java exception was already
// pending, preserve that exception as the cause.
if let Some(exception) = env.exception_occurred() {
env.exception_clear();
let _ = try_scoped(|| {
let message = env.new_string(error.to_string())?;
let call_exception: JThrowable = jni_new_object(
env,
CALL_EXCEPTION_CLASS,
jni_args!((
message => java.lang.String,
exception => java.lang.Throwable,
) -> void),
)?
.into();
Ok(env.throw(call_exception)?)
let message = env.new_string(err.to_string())?;
let call_exception = jni_new_object!(env, CALL_EXCEPTION_CLASS, (
message => java.lang.String,
exception => java.lang.Throwable,
))?;
// SAFETY: We just constructed CallException, which extends Throwable.
let throwable = unsafe { JThrowable::from_raw(env, call_exception.as_raw()) };
Ok(env.throw(throwable)?)
});
} else {
// Don't try to throw our own exception on top of another exception.
let jni_msg = JNIString::from(format!("{}", err));
let _ = env.throw_new(CALL_EXCEPTION_CLASS, &jni_msg);
}
} else {
let _ = env.throw_new(CALL_EXCEPTION_CLASS, format!("{}", error));
Ok(T::default())
}
fn on_panic<'unowned_env_local: 'native_method, 'native_method>(
env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
payload: Box<dyn std::any::Any + Send + 'static>,
) -> jni::errors::Result<T> {
let panic_string = match payload.downcast::<&'static str>() {
Ok(s) => (*s).to_string(),
Err(payload) => match payload.downcast::<String>() {
Ok(s) => *s,
Err(payload) => {
if let Err(drop_panic) = catch_unwind(AssertUnwindSafe(|| drop(payload))) {
log::error!("Panic while dropping panic payload: {:?}", drop_panic);
std::mem::forget(drop_panic);
}
"".to_string()
}
},
};
let jni_msg = JNIString::from(format!("Rust panic: {panic_string}"));
let _ = env.throw_new(CALL_EXCEPTION_CLASS, &jni_msg);
Ok(T::default())
}
}
@ -48,29 +84,9 @@ pub enum AndroidError {
// Android JNI error codes
#[error("JNI: static method lookup failed. Class: {0}, Method: {1}, Sig: {2}")]
JniStaticMethodLookup(String, String, String),
#[error("JNI: calling method failed. Method: {0}, Sig: {1}, Error: {2}")]
JniCallMethod(String, String, errors::Error),
#[error("JNI: calling static method failed. Class: {0}, Method: {1}, Sig: {2}")]
JniCallStaticMethod(String, String, String),
#[error("JNI: calling constructor failed. Constructor: {0}, Sig: {1}")]
JniCallConstructor(String, String),
#[error("JNI: getting field failed. Field: {0}, Type: {1}")]
JniGetField(String, String),
#[error("JNI: class not found. Type: {0} Add to the cache?")]
JniGetLangClassNotFound(String),
#[error("JNI: new object failed. Type: {0}")]
JniNewLangObjectFailed(String),
#[error("JNI: invalid serialized buffer.")]
JniInvalidSerializedBuffer,
// Android Class Cache error codes
#[error("ClassCache: Class is already in cache: {0}")]
ClassCacheDuplicate(String),
#[error("ClassCache: class not found in jvm: {0}")]
ClassCacheNotFound(String),
#[error("ClassCache: class not found in cache: {0}")]
ClassCacheLookup(String),
// Android Misc error codes
#[error("Creating JNI PeerConnection failed")]
CreateJniPeerConnection,

View File

@ -1,31 +1,11 @@
//
// Copyright 2019-2021 Signal Messenger, LLC
// Copyright 2019-2026 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
//! Utility helpers for JNI access
use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
marker::PhantomData,
};
use jni::{
JNIEnv,
objects::{GlobalRef, JClass, JObject, JValue, JValueOwned},
};
use crate::{android::error::AndroidError, common::Result, core::util::try_scoped};
pub use crate::{jni_class_name, jni_signature};
macro_rules! jni_arg {
( $arg:expr => boolean ) => {
<jni::objects::JValue as From<bool>>::from($arg)
};
// jni_signature will reject this, but having it do something reasonable avoids multiple errors.
( $arg:expr => bool ) => {
<jni::objects::JValue as From<bool>>::from($arg)
jni::objects::JValue::Bool($arg)
};
( $arg:expr => byte ) => {
jni::objects::JValue::Byte($arg)
@ -54,306 +34,58 @@ macro_rules! jni_arg {
};
}
macro_rules! jni_return_type {
// Unfortunately there's not a conversion directly from JValue to bool, only jboolean.
(boolean) => {
jni::sys::jboolean
};
// jni_signature will reject this, but having it do something reasonable avoids multiple errors.
(bool) => {
jni::sys::jboolean
};
(byte) => {
jni::sys::jbyte
};
(char) => {
jni::sys::jchar
};
(short) => {
jni::sys::jshort
};
(int) => {
jni::sys::jint
};
(long) => {
jni::sys::jlong
};
(float) => {
jni::sys::jfloat
};
(double) => {
jni::sys::jdouble
};
(void) => {
()
};
// Assume anything else is an object. This includes arrays and classes.
($($_:tt)+) => {
jni::objects::JObject
};
}
/// Represents a return type, used by [`JniArgs`].
///
/// This is an implementation detail of [`jni_args`] and [`JniArgs`]. Using a function type makes
/// `JniArgs` covariant, which allows the compiler to be less strict about the lifetime marker.
pub type PhantomReturnType<R> = PhantomData<fn() -> R>;
/// A JNI argument list, type-checked with its signature.
#[derive(Debug, Clone, Copy)]
pub struct JniArgs<'local, 'obj_ref, R, const LEN: usize> {
pub sig: &'static str,
pub args: [JValue<'local, 'obj_ref>; LEN],
pub _return: PhantomReturnType<R>,
}
/// Produces a JniArgs struct from the given arguments and return type.
///
/// # Example
///
/// ```
/// let args = jni_args!((name => java.lang.String, 0x3FFF => short) -> void);
/// assert_eq!(args.sig, "(Ljava/lang/String;S)V");
/// assert_eq!(args.args.len(), 2);
/// ```
/// Builds a `(MethodSignature, [JValue; N])` tuple from a Java-style arg list.
macro_rules! jni_args {
(
(
$( $arg:expr => $arg_base:tt $(. $arg_rest:ident)* $(:: $arg_nested:ident)* ),* $(,)?
) -> $ret_base:tt $(. $ret_rest:ident)* $(:: $ret_nested:ident)*
) => {
JniArgs {
sig: jni_signature!(
(
jni::jni_sig!(
(
$( $arg_base $(. $arg_rest)* $(:: $arg_nested)* ),*
) -> $ret_base $(. $ret_rest)* $(:: $ret_nested)*
),
args: [$(jni_arg!($arg => $arg_base)),*],
_return: PhantomReturnType::<jni_return_type!($ret_base)> {},
}
[$(jni_arg!($arg => $arg_base)),*],
)
}
}
/// Wrapper around JNIEnv::call_method() with logging.
pub fn jni_call_method<
'input,
'output,
O: AsRef<JObject<'input>>,
R: TryFrom<JValueOwned<'output>, Error = jni::errors::Error>,
const LEN: usize,
>(
env: &mut JNIEnv<'output>,
object: O,
name: &'static str,
args: JniArgs<R, LEN>,
) -> Result<R> {
env.call_method(object, name, args.sig, &args.args)
.and_then(|v| v.try_into())
.map_err(|e| AndroidError::JniCallMethod(name.to_string(), args.sig.to_string(), e).into())
}
/// Wrapper around JNIEnv::call_static_method() with logging.
#[allow(dead_code)]
pub fn jni_call_static_method<
'output,
R: TryFrom<JValueOwned<'output>, Error = jni::errors::Error>,
const LEN: usize,
>(
env: &mut JNIEnv<'output>,
class: &'static str,
name: &'static str,
args: JniArgs<R, LEN>,
) -> Result<R> {
env.call_static_method(class, name, args.sig, &args.args)
.and_then(|v| v.try_into())
.map_err(|e| AndroidError::JniCallMethod(name.to_string(), args.sig.to_string(), e).into())
}
/// Wrapper around JNIEnv::new_object() with logging.
pub fn jni_new_object<'output, const LEN: usize>(
env: &mut JNIEnv<'output>,
class: &'static str,
args: JniArgs<(), LEN>,
) -> Result<JObject<'output>> {
match env.new_object(class, args.sig, &args.args) {
Ok(v) => Ok(v),
Err(_) => {
Err(AndroidError::JniCallConstructor(class.to_string(), args.sig.to_string()).into())
}
}
}
/// Wrapper around JNIEnv::get_field() with logging.
pub fn jni_get_field<'input, 'output, O: AsRef<JObject<'input>>>(
env: &mut JNIEnv<'output>,
object: O,
name: &'static str,
ty: &'static str,
) -> Result<JValueOwned<'output>> {
env.get_field(object, name, ty)
.map_err(|_| AndroidError::JniGetField(name.to_string(), ty.to_string()).into())
}
/// Creates a new java.util.ArrayList object
pub fn jni_new_arraylist<'output>(
env: &mut JNIEnv<'output>,
initial_capacity: usize,
) -> Result<JObject<'output>> {
jni_new_object(
env,
jni_class_name!(java.util.ArrayList),
jni_args!((initial_capacity.try_into().expect("too big for Java") => int) -> void),
)
}
/// Creates a new java.util.HashMap object
pub fn jni_new_hashmap<'output>(
env: &mut JNIEnv<'output>,
initial_capacity: usize,
) -> Result<JObject<'output>> {
jni_new_object(
env,
jni_class_name!(java.util.HashMap),
jni_args!((initial_capacity.try_into().expect("too big for Java") => int) -> void),
)
}
/// Prints local and global references to the log.
#[allow(dead_code)]
pub fn dump_references(env: &mut JNIEnv) {
let _ = env.with_local_frame(5, |env| -> Result<()> {
info!("Dumping references ->");
let _ = env.call_static_method(
jni_class_name!(dalvik.system.VMDebug),
"dumpReferenceTables",
jni_signature!(() -> void),
&[],
/// Calls `Env::call_method` with the given args. Returns `Result<JValueOwned>`.
///
/// Use trailing `.into_<type>` to unwrap a typed return.
macro_rules! jni_call_method {
(
$env:expr, $obj:expr, $name:expr,
( $($arg_list:tt)* ) -> $ret_base:tt $(. $ret_rest:ident)* $(:: $ret_nested:ident)*
) => {{
let (sig, args) = jni_args!(
( $($arg_list)* ) -> $ret_base $(. $ret_rest)* $(:: $ret_nested)*
);
info!("<- Done with references");
Ok(())
});
$env.call_method($obj, $name, &sig, &args)
}};
}
/// A cache of Java class objects
/// Calls `Env::call_static_method` with the given args. Returns `Result<JValueOwned>`.
macro_rules! jni_call_static_method {
(
$env:expr, $class:expr, $name:expr,
( $($arg_list:tt)* ) -> $ret_base:tt $(. $ret_rest:ident)* $(:: $ret_nested:ident)*
) => {{
let (sig, args) = jni_args!(
( $($arg_list)* ) -> $ret_base $(. $ret_rest)* $(:: $ret_nested)*
);
$env.call_static_method($class, $name, &sig, &args)
}};
}
/// Calls `Env::new_object` with the given args. Returns `Result<JObject>`.
///
/// JNI cannot lookup classes by name from threads other than the main
/// thread. See this FAQ for background:
/// https://developer.android.com/training/articles/perf-jni#faq:-why-didnt-findclass-find-my-class
///
/// The solution here is to look up the class objects at init time on
/// the main thread and cache a global reference to the object for
/// later use.
#[derive(Clone)]
pub struct ClassCache {
/// HashMap mapping the class name (String) to Java object
map: HashMap<String, GlobalRef>,
}
impl ClassCache {
/// Returns an empty cache
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
/// Look up the class specified by `class_name` and store a global
/// reference to the class object result in the cache.
///
/// * Adding the same class twice is treated as an error.
/// * If the class lookup fails, return an error.
pub fn add_class(&mut self, env: &mut JNIEnv, class_name: &str) -> Result<()> {
if self.map.contains_key(class_name) {
return Err(AndroidError::ClassCacheDuplicate(class_name.to_string()).into());
}
let class_object = match env.find_class(class_name) {
Ok(v) => v,
Err(_) => return Err(AndroidError::ClassCacheNotFound(class_name.to_string()).into()),
};
let class_ref = env.new_global_ref(JObject::from(class_object))?;
self.map.insert(String::from(class_name), class_ref);
Ok(())
}
/// Retrieve the class object specified by `class_name` and return it.
///
/// * If the class is not in the cache, return an error.
pub fn get_class(&self, class_name: &str) -> Result<&JClass<'_>> {
if let Some(class_ref) = self.map.get(class_name) {
let object = class_ref.as_obj();
Ok(<&JClass>::from(object))
} else {
Err(AndroidError::ClassCacheLookup(class_name.to_string()).into())
}
}
}
/// A wrapper around [`JNIEnv`] that reports uncaught exceptions on destruction.
///
/// Normally JNI handles uncaught exceptions when a native thread is "detached" from the JVM, but
/// RingRTC treats all its callback threads as "daemon" threads that are only detached when the
/// native thread exits. This regains that functionality.
///
/// Because `ExceptionCheckingJNIEnv` implements `Deref`, it should be a drop-in replacement for
/// most uses of JNIEnv as a value. References to JNIEnv should continue as references.
pub struct ExceptionCheckingJNIEnv<'a>(JNIEnv<'a>);
impl Drop for ExceptionCheckingJNIEnv<'_> {
fn drop(&mut self) {
let result = try_scoped(|| {
let exception = self.exception_occurred()?;
if exception.is_null() {
return Ok(());
}
self.exception_clear()?;
let thread = jni_call_static_method(
self,
jni_class_name!(java.lang.Thread),
"currentThread",
jni_args!(() -> java.lang.Thread),
)?;
let handler = jni_call_method(
self,
&thread,
"getUncaughtExceptionHandler",
jni_args!(() -> java.lang.Thread::UncaughtExceptionHandler),
)?;
jni_call_method(
self,
handler,
"uncaughtException",
jni_args!((thread => java.lang.Thread, exception => java.lang.Throwable) -> void),
)?;
Ok(())
});
match result {
Ok(()) => {}
Err(e) => {
error!("unable to rethrow exception: {e}");
}
}
}
}
impl<'a> From<JNIEnv<'a>> for ExceptionCheckingJNIEnv<'a> {
fn from(env: JNIEnv<'a>) -> Self {
Self(env)
}
}
impl<'a> std::ops::Deref for ExceptionCheckingJNIEnv<'a> {
type Target = JNIEnv<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for ExceptionCheckingJNIEnv<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
/// The constructor's JNI return type is always `void`; this macro embeds that.
macro_rules! jni_new_object {
( $env:expr, $class:expr, ( $($arg_list:tt)* ) $(,)? ) => {{
let (sig, args) = jni_args!(( $($arg_list)* ) -> void);
$env.new_object($class, &sig, &args)
}};
}

View File

@ -6,39 +6,27 @@
//! Setup Android logging object
use jni::{
JNIEnv, JavaVM,
objects::{GlobalRef, JObject},
Env, JavaVM, jni_sig, jni_str,
objects::{Global, JObject},
signature::MethodSignature,
strings::JNIStr,
};
use log::{Level, Log, Metadata, Record};
use crate::{
android::{error::AndroidError, jni_util::*},
common::Result,
};
use crate::{android::error::AndroidError, common::Result};
/// Log object for interfacing with existing Android logger.
struct AndroidLogger {
level: Level,
jvm: JavaVM,
logger_class: GlobalRef,
logger_class: Global<jni::objects::JClass<'static>>,
}
// Method name and signature required of Java logger class
// void log(int level, String message)
const LOGGER_CLASS: &str = jni_class_name!(org.signal.ringrtc.Log);
const LOGGER_METHOD: &str = "log";
const LOGGER_SIG: &str = jni_signature!((int, java.lang.String) -> void);
impl AndroidLogger {
// This is specifically *not* using ExceptionCheckingJNIEnv:
// - We may be logging in the middle of some other operation, which might be able to handle
// uncaught exceptions.
// - ExceptionCheckingJNIEnv itself can log in failure cases, and we don't want an infinite
// loop.
fn get_java_env(&self) -> Result<JNIEnv<'_>> {
Ok(self.jvm.attach_current_thread_as_daemon()?)
}
}
const LOGGER_CLASS: &JNIStr = jni_str!("org/signal/ringrtc/Log");
const LOGGER_METHOD: &JNIStr = jni_str!("log");
const LOGGER_SIG: MethodSignature<'static, 'static> = jni_sig!((int, java.lang.String) -> void);
impl Log for AndroidLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
@ -54,25 +42,6 @@ impl Log for AndroidLogger {
return;
}
// Use the `JavaVM` interface to attach a `JNIEnv` to the current thread.
let mut env = match self.get_java_env() {
Ok(v) => v,
Err(_) => return,
};
// Attempt to clear any exception before we log anything.
// We'll rethrow it after logging.
let exception = env
.exception_occurred()
.unwrap_or_else(|_| JObject::null().into());
if !exception.is_null() {
let exception_cleared = env.exception_clear();
if exception_cleared.is_err() {
// If we can't clear the exception, skip the log.
return;
}
}
let get_file_name = || -> Option<&str> {
let file = record.file()?;
file.split(std::path::MAIN_SEPARATOR_STR).last()
@ -89,43 +58,61 @@ impl Log for AndroidLogger {
let level = record.level() as i32;
let _ = env.with_local_frame(5, |env| -> Result<()> {
let msg = match env.new_string(message) {
Ok(v) => JObject::from(v),
Err(_) => return Ok(()),
};
let _ = self.jvm.attach_current_thread(|env| -> Result<()> {
// Attempt to clear any exception before we log anything.
// We'll rethrow it after logging.
let exception = env.exception_occurred();
let had_exception = exception.is_some();
if had_exception {
env.exception_clear();
}
let values = [level.into(), (&msg).into()];
let _ = env.with_local_frame(5, |env| -> Result<()> {
let msg = match env.new_string(&message) {
Ok(v) => JObject::from(v),
Err(_) => return Ok(()),
};
let values = [level.into(), (&msg).into()];
// Ignore the result here, can't do anything about it anyway.
let _ = env.call_static_method(
&self.logger_class,
LOGGER_METHOD,
&LOGGER_SIG,
&values,
);
Ok(())
});
// If we put an exception "on hold" earlier, try to throw it again now.
if let Some(exception) = exception
&& had_exception
{
// But check that there hasn't been *another* exception thrown.
if env.exception_occurred().is_none() {
let _ = env.throw(exception);
}
}
// Ignore the result here, can't do anything about it anyway.
let _ =
env.call_static_method(&self.logger_class, LOGGER_METHOD, LOGGER_SIG, &values);
Ok(())
});
// If we put an exception "on hold" earlier, try to throw it again now.
if !exception.is_null() {
// But check that there hasn't been *another* exception thrown.
if let Ok(false) = env.exception_check() {
let _ = env.throw(exception);
}
}
}
}
fn flush(&self) {}
}
pub fn init_logging(env: &mut JNIEnv, level: Level) -> Result<()> {
pub fn init_logging(env: &mut Env, level: Level) -> Result<()> {
// Check if the Logger class contains a good logger method and signature
if env
.get_static_method_id(LOGGER_CLASS, LOGGER_METHOD, LOGGER_SIG)
.get_static_method_id(LOGGER_CLASS, LOGGER_METHOD, &LOGGER_SIG)
.is_err()
{
return Err(AndroidError::JniStaticMethodLookup(
String::from(LOGGER_CLASS),
String::from(LOGGER_METHOD),
String::from(LOGGER_SIG),
LOGGER_CLASS.to_string(),
LOGGER_METHOD.to_string(),
LOGGER_SIG.sig().to_string(),
)
.into());
}
@ -134,11 +121,11 @@ pub fn init_logging(env: &mut JNIEnv, level: Level) -> Result<()> {
// main thread, so stash a global ref to the class now, while
// we're on the main thread.
let logger_class = env.find_class(LOGGER_CLASS)?;
let logger_class = env.new_global_ref(JObject::from(logger_class))?;
let logger_class = env.new_global_ref(logger_class)?;
// `JNIEnv` cannot be sent across thread boundaries. To be able to use JNI
// `Env` cannot be sent across thread boundaries. To be able to use JNI
// functions in other threads, we must first obtain the `JavaVM` interface
// which, unlike `JNIEnv` is `Send`.
// which, unlike `Env` is `Send`.
let jvm = env.get_java_vm()?;
let logger = AndroidLogger {
level,

View File

@ -0,0 +1,114 @@
//
// Copyright 2026 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
use jni::{
Env, bind_java_type,
refs::{LoaderContext, Reference as _},
};
// RingRTC App Classes
bind_java_type! { pub CallSummary => org.signal.ringrtc.CallSummary }
bind_java_type! { pub QualityStats => org.signal.ringrtc.CallSummary::QualityStats }
bind_java_type! { pub MediaQualityStats => org.signal.ringrtc.CallSummary::MediaQualityStats }
bind_java_type! { pub CallLinkState => org.signal.ringrtc.CallLinkState }
bind_java_type! { pub CallLinkRootKey => org.signal.ringrtc.CallLinkRootKey }
bind_java_type! { pub HttpHeader => org.signal.ringrtc.HttpHeader }
bind_java_type! { pub HttpResult => org.signal.ringrtc.CallManager::HttpResult }
bind_java_type! { pub PeekInfo => org.signal.ringrtc.PeekInfo }
bind_java_type! { pub Reaction => org.signal.ringrtc.GroupCall::Reaction }
bind_java_type! { pub RemoteDeviceState => org.signal.ringrtc.GroupCall::RemoteDeviceState }
bind_java_type! { pub ReceivedAudioLevel => org.signal.ringrtc.GroupCall::ReceivedAudioLevel }
// RingRTC Enum Classes
bind_java_type! {
pub CallEvent => org.signal.ringrtc.CallManager::CallEvent,
methods { static fn from_native_index(value: jint) -> CallEvent }
}
bind_java_type! {
pub CallMediaType => org.signal.ringrtc.CallManager::CallMediaType,
methods { static fn from_native_index(value: jint) -> CallMediaType }
}
bind_java_type! {
pub HangupType => org.signal.ringrtc.CallManager::HangupType,
methods { static fn from_native_index(value: jint) -> HangupType }
}
bind_java_type! {
pub HttpMethod => org.signal.ringrtc.CallManager::HttpMethod,
methods { static fn from_native_index(value: jint) -> HttpMethod }
}
bind_java_type! {
pub CallEndReason => org.signal.ringrtc.CallManager::CallEndReason,
methods { static fn from_native_index(value: jint) -> CallEndReason }
}
bind_java_type! {
pub ConnectionState => org.signal.ringrtc.GroupCall::ConnectionState,
methods { static fn from_native_index(value: jint) -> ConnectionState }
}
bind_java_type! {
pub JoinState => org.signal.ringrtc.GroupCall::JoinState,
methods { static fn from_native_index(value: jint) -> JoinState }
}
bind_java_type! {
pub SpeechEvent => org.signal.ringrtc.GroupCall::SpeechEvent,
methods { static fn from_native_index(value: jint) -> SpeechEvent }
}
// JDK Primitives
bind_java_type! {
pub JBoolean => java.lang.Boolean,
constructors { fn new(value: jboolean) }
}
bind_java_type! {
pub JFloat => java.lang.Float,
constructors { fn new(value: jfloat) }
}
bind_java_type! {
pub JInteger => java.lang.Integer,
constructors { fn new(value: jint) }
}
bind_java_type! {
pub JLong => java.lang.Long,
constructors { fn new(value: jlong) }
}
// JDK Collections
bind_java_type! {
pub JArrayList => java.util.ArrayList,
constructors { fn with_capacity(initial_capacity: jint) }
}
bind_java_type! {
pub JHashMap => java.util.HashMap,
constructors { fn with_capacity(initial_capacity: jint) }
}
pub fn init_class_cache(env: &mut Env) -> jni::errors::Result<()> {
let ctx = LoaderContext::default();
CallSummary::lookup_class(env, &ctx)?;
QualityStats::lookup_class(env, &ctx)?;
MediaQualityStats::lookup_class(env, &ctx)?;
CallLinkState::lookup_class(env, &ctx)?;
CallLinkRootKey::lookup_class(env, &ctx)?;
HttpHeader::lookup_class(env, &ctx)?;
HttpResult::lookup_class(env, &ctx)?;
PeekInfo::lookup_class(env, &ctx)?;
Reaction::lookup_class(env, &ctx)?;
RemoteDeviceState::lookup_class(env, &ctx)?;
ReceivedAudioLevel::lookup_class(env, &ctx)?;
CallEvent::lookup_class(env, &ctx)?;
CallMediaType::lookup_class(env, &ctx)?;
HangupType::lookup_class(env, &ctx)?;
HttpMethod::lookup_class(env, &ctx)?;
CallEndReason::lookup_class(env, &ctx)?;
ConnectionState::lookup_class(env, &ctx)?;
JoinState::lookup_class(env, &ctx)?;
SpeechEvent::lookup_class(env, &ctx)?;
JBoolean::lookup_class(env, &ctx)?;
JFloat::lookup_class(env, &ctx)?;
JInteger::lookup_class(env, &ctx)?;
JLong::lookup_class(env, &ctx)?;
JArrayList::lookup_class(env, &ctx)?;
JHashMap::lookup_class(env, &ctx)?;
Ok(())
}

View File

@ -6,8 +6,8 @@
//! webrtc::jni::JavaMediaStream Interface.
use jni::{
JNIEnv,
objects::{GlobalRef, JObject},
Env,
objects::{Global, JObject},
sys::jobject,
};
@ -51,11 +51,11 @@ impl JavaMediaStream {
Ok(Self { rffi })
}
/// Return a JNI GlobalRef to the JavaMediaStream object
pub fn global_ref(&self, env: &JNIEnv) -> Result<GlobalRef> {
/// Return a JNI Global to the JavaMediaStream object
pub fn global_ref(&self, env: &Env) -> Result<Global<JObject<'static>>> {
unsafe {
let object = Rust_getJavaMediaStreamObject(self.rffi.borrow());
Ok(env.new_global_ref(JObject::from_raw(object))?)
Ok(env.new_global_ref(JObject::from_raw(env, object))?)
}
}
}

View File

@ -6,7 +6,7 @@
//! Re-exports WebRTC JNI interfaces
use jni::{
JNIEnv,
EnvUnowned,
objects::{JClass, JObject},
sys::jlong,
};
@ -17,7 +17,7 @@ unsafe extern "C" {
/// Export the nativeCreatePeerConnection() call from the
/// org.webrtc.PeerConnectionFactory class.
pub fn Java_org_webrtc_PeerConnectionFactory_nativeCreatePeerConnection(
env: JNIEnv,
unowned_env: EnvUnowned,
class: JClass,
factory: jlong,
rtcConfig: JObject,

View File

@ -1,121 +0,0 @@
//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
//! Macros to generate JNI signature strings.
//!
//! Located outside of the `android` module so that the tests get run.
#![allow(unused_macros)]
/// Takes a Java-esque class name of the form `org.signal.Outer::Inner` and turns it into a
/// JNI-style name `org/signal/Outer$Inner`.
#[macro_export]
macro_rules! jni_class_name {
( $arg_base:tt $(. $arg_rest:ident)+ $(:: $arg_nested:ident)* ) => {
concat!(
stringify!($arg_base),
$("/", stringify!($arg_rest),)+
$("$", stringify!($arg_nested),)*
)
}
}
#[test]
fn test_jni_class_name() {
assert_eq!(jni_class_name!(foo.bar), "foo/bar");
assert_eq!(jni_class_name!(foo.bar.baz), "foo/bar/baz");
assert_eq!(jni_class_name!(foo.bar.baz::garply), "foo/bar/baz$garply");
assert_eq!(
jni_class_name!(foo.bar.baz::garply::qux),
"foo/bar/baz$garply$qux"
);
}
/// Converts a function or type signature to a JNI signature string.
///
/// This macro uses Rust function syntax `(Foo, Bar) -> Baz`, and uses Rust syntax for Java arrays
/// `[Foo]`, but otherwise uses Java names for types: `boolean`, `byte`, `void`. Like
/// [`jni_class_name`], inner classes are indicated with `::` rather than `.`.
#[macro_export]
macro_rules! jni_signature {
( boolean ) => ("Z");
( bool ) => (compile_error!("use Java type 'boolean'"));
( byte ) => ("B");
( char ) => ("C");
( short ) => ("S");
( int ) => ("I");
( long ) => ("J");
( float ) => ("F");
( double ) => ("D");
( void ) => ("V");
// Escape hatch: provide a literal string.
( $x:literal ) => ($x);
// Arrays
( [$($contents:tt)+] ) => {
concat!("[", jni_signature!($($contents)+))
};
// Classes
( $arg_base:tt $(. $arg_rest:ident)+ $(:: $arg_nested:ident)* ) => {
concat!(
"L",
jni_class_name!($arg_base $(. $arg_rest)+ $(:: $arg_nested)*),
";"
)
};
// Functions
(
(
$( $arg_base:tt $(. $arg_rest:ident)* $(:: $arg_nested:ident)* ),* $(,)?
) -> $ret_base:tt $(. $ret_rest:ident)* $(:: $ret_nested:ident)*
) => {
concat!(
"(",
$( jni_signature!($arg_base $(. $arg_rest)* $(:: $arg_nested)*), )*
")",
jni_signature!($ret_base $(. $ret_rest)* $(:: $ret_nested)*)
)
};
}
#[test]
fn test_jni_signature() {
// Literals
assert_eq!(jni_signature!("Lfoo/bar;"), "Lfoo/bar;");
// Classes
assert_eq!(jni_signature!(foo.bar), "Lfoo/bar;");
assert_eq!(jni_signature!(foo.bar.baz), "Lfoo/bar/baz;");
assert_eq!(jni_signature!(foo.bar.baz::garply), "Lfoo/bar/baz$garply;");
assert_eq!(
jni_signature!(foo.bar.baz::garply::qux),
"Lfoo/bar/baz$garply$qux;"
);
// Arrays
assert_eq!(jni_signature!([byte]), "[B");
assert_eq!(jni_signature!([[byte]]), "[[B");
assert_eq!(jni_signature!([foo.bar]), "[Lfoo/bar;");
assert_eq!(
jni_signature!([foo.bar.baz::garply::qux]),
"[Lfoo/bar/baz$garply$qux;"
);
// Functions
assert_eq!(jni_signature!(() -> void), "()V");
assert_eq!(jni_signature!((byte, int) -> float), "(BI)F");
assert_eq!(
jni_signature!(([byte], foo.bar, foo.bar.baz::garply::qux) -> [byte]),
"([BLfoo/bar;Lfoo/bar/baz$garply$qux;)[B"
);
assert_eq!(jni_signature!(() -> foo.bar), "()Lfoo/bar;");
assert_eq!(
jni_signature!(() -> foo.bar.baz::garply::qux),
"()Lfoo/bar/baz$garply$qux;"
);
}

View File

@ -6,7 +6,6 @@
//! Common types used throughout the library.
pub mod actor;
pub mod jni_signature;
pub mod slice;
pub mod time;
pub mod units;

View File

@ -70,6 +70,7 @@ mod android {
mod call_manager;
mod error;
mod logging;
mod types;
mod webrtc_java_media_stream;
mod webrtc_peer_connection_factory;
}