Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

FNX-14633 ⁃ For #162 - Add OSS build flavor. #13453

Closed
wants to merge 14 commits into from

Conversation

gilbsgilbs
Copy link
Contributor

@gilbsgilbs gilbsgilbs commented Aug 10, 2020

This PR adds an OSS build flavor allowing to build fenix without proprietary components, namely:

  • play-services-ads-identifier
  • play-services-oss-licenses (the plugin is FOSS, but not the library, so it was replaced with AboutLibraries)
  • lib-push-firebase (which isn't closed-source by itself yet bundles proprietary libraries such as firebase-core and play-services)
  • leanplum (which isn't closed-source by itself but includes dependencies to firebase and gms)

I didn't do the work for other SDKs that are open-source but promote closed-source tracking services (such as Adjust), as it is not strictly necessary to release fenix on F-Droid as far as I know.

Build the oss / non-oss flavor:

./gradlew assembleOssDebug assembleNonOssDebug

Simple check for proprietary dependencies:

$ dexdump app/build/outputs/apk/nonOss/debug/app-nonOss-arm64-v8a-debug.apk  | grep -E '(firebase|gms)' | wc -l
21121
$ dexdump app/build/outputs/apk/oss/debug/app-oss-arm64-v8a-debug.apk  | grep -E '(firebase|gms)' | wc -l      
0

So I guess it works.

I think this PR even closes #162 as the work of making fenix available on F-Droid will mostly be political after that.

Pull Request checklist

  • Tests: This PR includes thorough tests or an explanation of why it does not - mostly consists in stubbing existing implementations
  • Screenshots: This PR includes screenshots or GIFs of the changes made or an explanation of why it does not - no UI changes
  • Accessibility: The code in this PR follows accessibility best practices or does not include any user facing features. In addition, it includes a screenshot of a successful accessibility scan to ensure no new defects are added to the product. - no UI changes

After merge

  • Milestone: Make sure issues finished by this pull request are added to the milestone of the version currently in development.

To download an APK when reviewing a PR:

  1. click on Show All Checks,
  2. click Details next to "Taskcluster (pull_request)" after it appears and then finishes with a green checkmark,
  3. click on the "Fenix - assemble" task, then click "Run Artifacts".
  4. the APK links should be on the left side of the screen, named for each CPU architecture

ping @pocmo

@firefoxci-taskcluster
Copy link

No Taskcluster jobs started for this pull request
The `allowPullRequests` configuration for this repository (in `.taskcluster.yml` on the
default branch) does not allow starting tasks for this pull request.

@gilbsgilbs gilbsgilbs mentioned this pull request Aug 10, 2020
@pocmo pocmo self-requested a review August 10, 2020 13:54
This is to ensure both free and non-free implementations are updated
when an interface is added to the base service, like in 77263aa.
import android.net.Uri
import java.util.UUID

class LeanplumMetricsServiceImpl(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LeanplumMetricsService is used for two cases, both of which already have interfaces. Rather than making a dummy metrics service, let's configure those two locations.

First one is easy, the service is used in MetricController.create and discarded if the Telemetry flag is set to false. No change needed.

Second location is for the DeepLinkIntentProcessor. It wants a DeepLinkVerifier instance. You could pass in a dummy version that always returns false based on build flags.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First one is easy, the service is used in MetricController.create and discarded if the Telemetry flag is set to false. No change needed.

Since you can't include LeanplumMetricsService in main sources because of the imports, you still need a dummy implementation somewhere at least to make the compiler happy when building the free variant. Or are you suggesting to defer this at runtime by using reflection? What am I missing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I think I was getting confused with BuildConfig flags and build variants. I thought this could be accomplished with an if statement.

Are we allowed to use any form of telemetry in the F-Droid version? If not, we may want to replace the entire MetricController. We have a dummy DebugMetricController already that could be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we allowed to use any form of telemetry in the F-Droid version?

I'm pretty sure that yes, telemetry is allowed as long as the frameworks being used are completely FLOSS (so Glean and Adjust should be OK, not firebase because of the dependency to gms). You can even display ads under the same conditions.

However, telemetry, especially when enabled by default like it is in Fenix, is considered an "anti-feature". This means there will be a warning prior downloading the app on the store. Such warning is already displayed for Fennec, so that's not too bad I guess.


class OssLicensesMenuImpl : OssLicensesMenu {
override fun getIntent(context: Context?, title: String): Intent {
// This menu shouldn't displayed on the free variant anyways.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? AFAIK open source libraries should be fine to leave in, and we'll need to credit them to use them in the app.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I don't know why I thought it wasn't required for OSS software 🤐 . So I replaced Google's library with AboutLibraries which is the most popular FLOSS alternative I could find and it just looks better.

Note that I had to pin android gradle because AboutLibraries has gradle:4.0.0 which isn't compatible with Fenix yet (#13262). For the records, I quickly checked what were the blockers to upgrading the android gradle plugin and I found that the glean-gradle-plugin is incompatible: getBuildConfigPackageName() now returns a Property so you have to call get() on it. Also, I also had to update the RepoMatching like this:

-    const val comAndroid = "com\\.android\\..*"
+    const val comAndroid = "com\\.android(\\..+)?"

I don't think those two changes are sufficient to update because there are more subtle issues with the components, but at least these could easily be addressed in other PRs.

Out of curiosity, I also looked how the licenses menu worked in Fennec, and it turns out Fennec just opens about:license, which doesn't list Android dependencies. We could fallback to this, but I don't think it's a fair solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still a bit confused here. Why can't we use the Google library? As far as I can tell its open source and just figures out the play services libraries we use, rather than depending on them.

If we need to change the license implementation I think it would be nice to have it split into a separate PR so its easier to review.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it is confusing. The gradle plugin that lists the dependencies is open-source and under Apache 2 license, but the library that is shipped with the APK is proprietary and, as far as I can tell, closed-source. Morever, the library has dependencies to gms (which you can easily see with dexdump) which is unsuitable for OSS anyways.

Agreed that it would make sense to split this into a separate PR though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks for breaking it down. Using the most popular FLOSS version instead makes sense to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app/build.gradle Outdated
@@ -17,6 +17,8 @@ import org.mozilla.fenix.gradle.tasks.LintUnitTestRunner

import static org.gradle.api.tasks.testing.TestResult.ResultType

def useFreeImplementation = project.properties['org.mozilla.fenix.useFreeImplementation'].toBoolean()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be better off as another build variant, like debug.

Copy link
Contributor Author

@gilbsgilbs gilbsgilbs Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my original intention, but wasn't sure how to exclude dependencies for a specific build type. Sure you can add new dependencies like debugImplementation, but how do you "not include" them?

Then I thought the best solution would be to use a specific product flavor dimension, but that changes quite a lot of things in the release process (mainly the output APK path) therefore I wasn't very confident making this change. I ended up going for this procedural solution which isn't very elegant but at least shouldn't break anything.

I'm open to changing the implementation to build types or flavors (and I'd even prefer it as it would make the OSS variant a first-class citizen), but I need help to make sure I don't break the release process.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is done. I've used build flavors which definitely makes more sense. Needing help to update taskcluster.


import android.content.Context

interface AdvertisingIDImpl : AdvertisingID {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a class instead of an interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. It's a bit more verbose but probably better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rolledback to an interface because otherwise detekt was complaining of an abstract class containing only abstract method (see UnncessaryAbstractClass). Tell me if you think it would be better off keeping a class and ignoring the warning using an annotation.

app/src/main/java/org/mozilla/fenix/components/Push.kt Outdated Show resolved Hide resolved
@data-sync-user data-sync-user changed the title For #162 - Add build flag to exclude proprietary dependencies. FNX-14633 ⁃ For #162 - Add build flag to exclude proprietary dependencies. Aug 11, 2020
Copy link
Contributor Author

@gilbsgilbs gilbsgilbs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, thanks for the quick review 👍 . I made most of the requested changes but some details are still unclear (see my responses).

app/build.gradle Outdated
@@ -17,6 +17,8 @@ import org.mozilla.fenix.gradle.tasks.LintUnitTestRunner

import static org.gradle.api.tasks.testing.TestResult.ResultType

def useFreeImplementation = project.properties['org.mozilla.fenix.useFreeImplementation'].toBoolean()
Copy link
Contributor Author

@gilbsgilbs gilbsgilbs Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my original intention, but wasn't sure how to exclude dependencies for a specific build type. Sure you can add new dependencies like debugImplementation, but how do you "not include" them?

Then I thought the best solution would be to use a specific product flavor dimension, but that changes quite a lot of things in the release process (mainly the output APK path) therefore I wasn't very confident making this change. I ended up going for this procedural solution which isn't very elegant but at least shouldn't break anything.

I'm open to changing the implementation to build types or flavors (and I'd even prefer it as it would make the OSS variant a first-class citizen), but I need help to make sure I don't break the release process.


import android.content.Context

interface AdvertisingIDImpl : AdvertisingID {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. It's a bit more verbose but probably better.

import android.net.Uri
import java.util.UUID

class LeanplumMetricsServiceImpl(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First one is easy, the service is used in MetricController.create and discarded if the Telemetry flag is set to false. No change needed.

Since you can't include LeanplumMetricsService in main sources because of the imports, you still need a dummy implementation somewhere at least to make the compiler happy when building the free variant. Or are you suggesting to defer this at runtime by using reflection? What am I missing?


class OssLicensesMenuImpl : OssLicensesMenu {
override fun getIntent(context: Context?, title: String): Intent {
// This menu shouldn't displayed on the free variant anyways.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I don't know why I thought it wasn't required for OSS software 🤐 . So I replaced Google's library with AboutLibraries which is the most popular FLOSS alternative I could find and it just looks better.

Note that I had to pin android gradle because AboutLibraries has gradle:4.0.0 which isn't compatible with Fenix yet (#13262). For the records, I quickly checked what were the blockers to upgrading the android gradle plugin and I found that the glean-gradle-plugin is incompatible: getBuildConfigPackageName() now returns a Property so you have to call get() on it. Also, I also had to update the RepoMatching like this:

-    const val comAndroid = "com\\.android\\..*"
+    const val comAndroid = "com\\.android(\\..+)?"

I don't think those two changes are sufficient to update because there are more subtle issues with the components, but at least these could easily be addressed in other PRs.

Out of curiosity, I also looked how the licenses menu worked in Fennec, and it turns out Fennec just opens about:license, which doesn't list Android dependencies. We could fallback to this, but I don't think it's a fair solution.

app/src/main/java/org/mozilla/fenix/components/Push.kt Outdated Show resolved Hide resolved
@pocmo pocmo self-assigned this Aug 11, 2020
@gilbsgilbs
Copy link
Contributor Author

I've moved to using build flavors instead of a build flag and fixed tests for both flavors. As expected, taskcluster will have to be updated to run the OSS tests suite and produce the OSS apks. I'd appreciate some help as I don't know how to test this locally.

@gilbsgilbs gilbsgilbs changed the title FNX-14633 ⁃ For #162 - Add build flag to exclude proprietary dependencies. FNX-14633 ⁃ For #162 - Add OSS build flavor. Aug 11, 2020
@gilbsgilbs
Copy link
Contributor Author

I've tried to blindly update the taskcluster config, but it'll be hard to go further without being able to test. Please tell me there's anything I can do to get this PR to the next step. Thanks!

(and thoughts for all the Mozilla employees who were laid off 😢)

@pocmo
Copy link
Contributor

pocmo commented Aug 12, 2020

Yeah, challenging times. I'll get back to that PR. Thank you so far for all that work! 👍

Copy link
Contributor

@pocmo pocmo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the confusion, I do think we want build flags for those (like in your initial PR) and not a separate flavor dimension. We just worked hard reducing all our dimensions to avoid an explosion of variants. :)

Overall I think the reason is that we want to be more fine-grained: "OSS" doesn't really capture it. For example Leanplum is OSS (code). But I understand you still want to exclude it for a fdroid build. Also lib-push itself is OSS (but depends on a closed transitive dependency).

I think we want different build flags here for (disabling push, disabling leanplum (or marketing SDKs in general, disabling telemetry?). In some combination we may end up using those too in some of our builds (e.g. not all devices come with firebase push support).

Changing the build variants as a really loooong ripple effect on automation and other systems and I would like us to not get into this.. :)

@gilbsgilbs
Copy link
Contributor Author

@pocmo thanks for your response. Although build flavors are generally the intended way to do this, I can totally understand your reasoning. I'll bring back build flags then.

@pocmo
Copy link
Contributor

pocmo commented Aug 14, 2020

Thank you! I am on PTO next week, so it may take a bit until can take another look.

@gilbsgilbs
Copy link
Contributor Author

Enjoy yourself 😄 .

@gilbsgilbs
Copy link
Contributor Author

I'll close this PR and make tinier, per-build flag PRs to make reviews easier.

@gilbsgilbs gilbsgilbs closed this Aug 15, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

F-Droid
3 participants