Skip to content

Commit

Permalink
android: reproducibility (#1848)
Browse files Browse the repository at this point in the history
* reproducibility changes

* fix: ndk ordering and CI

* integrate api changes
  • Loading branch information
zaneschepke authored Jan 6, 2025
1 parent d2f6a78 commit d0f4aa4
Show file tree
Hide file tree
Showing 78 changed files with 167 additions and 134 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/build-nym-vpn-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,18 @@ jobs:
- name: Setup Android SDK
uses: android-actions/setup-android@v3

- name: Setup Android SDK
- name: Setup NDK
uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r25c
add-to-path: false

- name: Set env
shell: bash
run: |
echo "ANDROID_NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }}" >> $GITHUB_ENV
echo "NDK_TOOLCHAIN_DIR=${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_ENV
- name: Grant execute permission for gradlew
run: chmod +x gradlew
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/build-nym-vpn-core-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,18 @@ jobs:
run: |
rustup target install aarch64-linux-android
- name: Setup Android SDK
- name: Setup NDK
uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r25c
add-to-path: false

- name: Set env
shell: bash
run: |
echo "ANDROID_NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }}" >> $GITHUB_ENV
echo "NDK_TOOLCHAIN_DIR=${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_ENV
- name: Install cargo deps
run: |
Expand Down
15 changes: 9 additions & 6 deletions .github/workflows/publish-nym-vpn-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
nym-vpn-android

- name: Install system dependencies
run: |
Expand All @@ -86,7 +83,7 @@ jobs:
- name: Set version release notes
if: ${{ inputs.release_type == 'release' }}
run: |
RELEASE_NOTES="$(cat ${{ github.workspace }}/nym-vpn-android/fastlane/metadata/android/en-US/changelogs/${{ env.VERSION_CODE }}.txt)"
RELEASE_NOTES="$(cat ${{ github.workspace }}/fastlane/metadata/android/en-US/changelogs/${{ env.VERSION_CODE }}.txt)"
echo "RELEASE_NOTES<<EOF" >> $GITHUB_ENV
echo "$RELEASE_NOTES" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
Expand Down Expand Up @@ -214,12 +211,18 @@ jobs:
- name: Setup Android SDK
uses: android-actions/setup-android@v3

- name: Setup Android SDK
- name: Setup NDK
uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r25c
link-to-sdk: true
add-to-path: false

- name: Set env
shell: bash
run: |
echo "ANDROID_NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }}" >> $GITHUB_ENV
echo "NDK_TOOLCHAIN_DIR=${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_ENV
- name: Grant execute permission for gradlew
run: chmod +x gradlew
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions fastlane/metadata/android/en-US/changelogs/11700.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
What's new:
- Reproducible builds
3 changes: 2 additions & 1 deletion nym-vpn-android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ app/release/output.json
app/signing.properties
*.so
!libjnidispatch.so
nym-vpn-client/src/main/assets/licenses_rust.json
core/src/main/assets/licenses_rust.json

11 changes: 10 additions & 1 deletion nym-vpn-android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ android {
generateLocaleConfig = true
}

// reproducibility
dependenciesInfo {
// Disables dependency metadata when building APKs.
includeInApk = false
// Disables dependency metadata when building Android App Bundles.
includeInBundle = false
}

defaultConfig {
applicationId = Constants.APP_ID
minSdk = Constants.MIN_SDK
Expand Down Expand Up @@ -71,6 +79,7 @@ android {
isDebuggable = false
isMinifyEnabled = true
isShrinkResources = true
vcsInfo.include = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
Expand Down Expand Up @@ -163,7 +172,7 @@ android {

dependencies {

implementation(project(":nym-vpn-client"))
implementation(project(":core"))
implementation(project(":logcat-util"))
implementation(libs.androidx.lifecycle.process)
coreLibraryDesugaring(libs.com.android.tools.desugar)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.nymtech.nymvpn.module

import android.content.Context
import android.os.Build
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -12,8 +11,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import net.nymtech.logcatutil.LogReader
import net.nymtech.logcatutil.LogcatReader
import net.nymtech.nymvpn.BuildConfig
import net.nymtech.nymvpn.NymVpn
import net.nymtech.nymvpn.data.GatewayRepository
import net.nymtech.nymvpn.manager.shortcut.DynamicShortcutManager
import net.nymtech.nymvpn.manager.shortcut.ShortcutManager
Expand All @@ -26,13 +23,11 @@ import net.nymtech.nymvpn.service.gateway.NymApiLibService
import net.nymtech.nymvpn.service.gateway.NymApiService
import net.nymtech.nymvpn.service.notification.NotificationService
import net.nymtech.nymvpn.service.notification.VpnAlertNotifications
import net.nymtech.nymvpn.util.Constants
import net.nymtech.nymvpn.util.FileUtils
import net.nymtech.nymvpn.util.extensions.isAndroidTV
import net.nymtech.nymvpn.util.extensions.toUserAgent
import net.nymtech.vpn.NymApi
import net.nymtech.vpn.backend.Backend
import net.nymtech.vpn.backend.NymBackend
import nym_vpn_lib.UserAgent
import javax.inject.Singleton

@InstallIn(SingletonComponent::class)
Expand All @@ -48,15 +43,9 @@ object AppModule {
@Singleton
@Provides
fun provideNymApi(@IoDispatcher dispatcher: CoroutineDispatcher, @ApplicationContext context: Context): NymApi {
val platform = if (context.isAndroidTV()) "AndroidTV" else "Android"
return NymApi(
dispatcher,
UserAgent(
Constants.APP_PROJECT_NAME,
BuildConfig.VERSION_NAME,
"$platform; ${Build.VERSION.SDK_INT}; ${NymVpn.getCPUArchitecture()}; ${BuildConfig.FLAVOR}",
BuildConfig.COMMIT_HASH,
),
context.toUserAgent(),
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package net.nymtech.nymvpn.service.gateway

import net.nymtech.vpn.NymApi
import net.nymtech.vpn.backend.Tunnel
import net.nymtech.vpn.model.Country
import nym_vpn_lib.GatewayType
import nym_vpn_lib.NetworkEnvironment
import nym_vpn_lib.SystemMessage
import javax.inject.Inject

Expand All @@ -16,11 +14,7 @@ class NymApiLibService @Inject constructor(
return nymApi.getGatewayCountries(type)
}

override suspend fun getEnvironment(environment: Tunnel.Environment): NetworkEnvironment {
return nymApi.getEnvironment(environment)
}

override suspend fun getSystemMessages(environment: Tunnel.Environment): List<SystemMessage> {
return nymApi.getSystemMessages(environment)
override suspend fun getSystemMessages(): List<SystemMessage> {
return nymApi.getSystemMessages()
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package net.nymtech.nymvpn.service.gateway

import net.nymtech.vpn.backend.Tunnel
import net.nymtech.vpn.model.Country
import nym_vpn_lib.GatewayType
import nym_vpn_lib.NetworkEnvironment
import nym_vpn_lib.SystemMessage

interface NymApiService {
suspend fun getCountries(type: GatewayType): Set<Country>
suspend fun getEnvironment(environment: Tunnel.Environment): NetworkEnvironment
suspend fun getSystemMessages(environment: Tunnel.Environment): List<SystemMessage>
suspend fun getSystemMessages(): List<SystemMessage>
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import net.nymtech.nymvpn.service.tunnel.model.BackendUiEvent
import net.nymtech.nymvpn.service.tunnel.model.MixnetConnectionState
import net.nymtech.nymvpn.util.extensions.requestTileServiceStateUpdate
import net.nymtech.nymvpn.util.extensions.toMB
import net.nymtech.nymvpn.util.extensions.toUserAgent
import net.nymtech.nymvpn.util.extensions.toUserMessage
import net.nymtech.vpn.backend.Backend
import net.nymtech.vpn.backend.Tunnel
Expand Down Expand Up @@ -83,7 +84,7 @@ class NymTunnelManager @Inject constructor(
backendEvent = ::onBackendEvent,
credentialMode = settingsRepository.isCredentialMode(),
)
backend.get().start(tunnel, fromBackground)
backend.get().start(tunnel, fromBackground, context.toUserAgent())
}.onFailure {
if (it is NymVpnInitializeException) {
when (it) {
Expand Down Expand Up @@ -144,7 +145,7 @@ class NymTunnelManager @Inject constructor(

override suspend fun getAccountLinks(): AccountLinks? {
return try {
backend.get().getAccountLinks(settingsRepository.getEnvironment())
backend.get().getAccountLinks()
} catch (_: Exception) {
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@ constructor(

private suspend fun checkSystemMessages() {
runCatching {
val env = settingsRepository.getEnvironment()
val messages = nymApiService.getSystemMessages(env)
val messages = nymApiService.getSystemMessages()
messages.firstOrNull()?.let {
_systemMessage.emit(it)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ import android.widget.Toast
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import net.nymtech.nymvpn.BuildConfig
import net.nymtech.nymvpn.NymVpn
import net.nymtech.nymvpn.NymVpn.Companion.instance
import net.nymtech.nymvpn.R
import net.nymtech.nymvpn.service.android.tile.VpnQuickTile
import net.nymtech.nymvpn.util.Constants
import net.nymtech.vpn.model.Country
import nym_vpn_lib.UserAgent
import timber.log.Timber
import java.util.Locale

Expand Down Expand Up @@ -170,3 +173,13 @@ fun Context.launchAppSettings() {
fun Context.isAndroidTV(): Boolean {
return packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
}

fun Context.toUserAgent(): UserAgent {
val platform = if (isAndroidTV()) "AndroidTV" else "Android"
return UserAgent(
Constants.APP_PROJECT_NAME,
BuildConfig.VERSION_NAME,
"$platform; ${Build.VERSION.SDK_INT}; ${NymVpn.getCPUArchitecture()}; ${BuildConfig.FLAVOR}",
BuildConfig.COMMIT_HASH,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,6 @@ fun VpnException.toUserMessage(context: Context): String {
is VpnException.RequestZkNym -> "Failed to request nyms"
is VpnException.UpdateAccountEndpointFailure -> "Account endpoint failure"
is VpnException.UpdateDeviceEndpointFailure -> "Device endpoint failure"
is VpnException.StatisticsRecipient -> "Statistics recipient error"
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ android {
disable.add("UnsafeOptInUsageError")
}

android {
ndkVersion = "25.2.9519653"
}

namespace = "${Constants.NAMESPACE}.${Constants.VPN_LIB_NAME}"
compileSdk = Constants.COMPILE_SDK

Expand Down Expand Up @@ -90,9 +94,14 @@ dependencies {

implementation(libs.kotlinx.serialization)
implementation(libs.timber)
implementation(libs.jna)
implementation(libs.relinker)

implementation(libs.jna) {
artifact {
type = "aar"
}
}

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand All @@ -109,7 +118,8 @@ tasks.register<Exec>(Constants.BUILD_LIB_TASK) {
commandLine("echo", "Skipping library build")
return@register
}
val ndkPath = android.sdkDirectory.resolve("ndk").listFilesOrdered().lastOrNull()?.path ?: System.getenv("ANDROID_NDK_HOME")
// prefer system for reproducible builds
val ndkPath = System.getenv("ANDROID_NDK_HOME") ?: android.sdkDirectory.resolve("ndk").listFilesOrdered().lastOrNull()?.path
commandLine("echo", "NDK HOME: $ndkPath")
val script = "${projectDir.path}/src/main/scripts/build-libs.sh"
// TODO find a better way to limit builds
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,16 @@ package net.nymtech.vpn

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import net.nymtech.vpn.backend.Tunnel
import net.nymtech.vpn.model.Country
import nym_vpn_lib.GatewayType
import nym_vpn_lib.NetworkEnvironment
import nym_vpn_lib.SystemMessage
import nym_vpn_lib.UserAgent
import nym_vpn_lib.fetchEnvironment
import nym_vpn_lib.fetchSystemMessages
import nym_vpn_lib.getGatewayCountries

class NymApi(
private val ioDispatcher: CoroutineDispatcher,
private val userAgent: UserAgent,
) {

suspend fun getGatewayCountries(type: GatewayType): Set<Country> {
return withContext(ioDispatcher) {
getGatewayCountries(type, userAgent, null).map {
Expand All @@ -25,15 +20,9 @@ class NymApi(
}
}

suspend fun getEnvironment(environment: Tunnel.Environment): NetworkEnvironment {
return withContext(ioDispatcher) {
fetchEnvironment(environment.networkName())
}
}

suspend fun getSystemMessages(environment: Tunnel.Environment): List<SystemMessage> {
suspend fun getSystemMessages(): List<SystemMessage> {
return withContext(ioDispatcher) {
fetchSystemMessages(environment.networkName())
nym_vpn_lib.getSystemMessages()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ package net.nymtech.vpn.backend

import nym_vpn_lib.AccountLinks
import nym_vpn_lib.AccountStateSummary
import nym_vpn_lib.UserAgent

interface Backend {

suspend fun init(environment: Tunnel.Environment, credentialMode: Boolean?)

suspend fun getAccountSummary(): AccountStateSummary

suspend fun getAccountLinks(environment: Tunnel.Environment): AccountLinks
suspend fun getAccountLinks(): AccountLinks

suspend fun storeMnemonic(credential: String)

suspend fun isMnemonicStored(): Boolean

suspend fun removeMnemonic()

suspend fun start(tunnel: Tunnel, background: Boolean)
suspend fun start(tunnel: Tunnel, background: Boolean, userAgent: UserAgent)

suspend fun stop()

Expand Down
Loading

0 comments on commit d0f4aa4

Please sign in to comment.