Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/build_and_test_sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ on:
branches:
- master
- staging
- staging-nw
pull_request:
branches:
- master
- staging
- staging-nw
jobs:
setup:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -73,8 +75,8 @@ jobs:
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
check_name: "Unit Test Results :rocket:"
comment_title: "Unit Test Results :rocket:"
check_name: "Unit Test Results (nw) :rocket:"
comment_title: "Unit Test Results (nw) :rocket:"
files: |
sdk/build/outputs/androidTest-results/**/*.xml
sdk-native/build/test-results/**/*.xml
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
## XX.XX.XX
## 26.1.5-nw
Released as `ly.count.android:sdk-nw:26.1.5`. See the "The `sdk-nw` artifact" section of the README for what this build changes.

* This build reports its SDK name as `java-native-android-nw`.
* The SDK now supports API level 37. Integrating apps must build with `compileSdk` 34 or higher.
* Added a new configuration option `setCustomSSLSocketFactory(SSLSocketFactory)` to send the SDK's HTTPS requests through a custom SSLSocketFactory.
* Improved the security of content, feedback widget, and push notification links by blocking the `data:`, `zip:`, and `intent:` URI schemes by default, both for opening links and for loading web view resources. They can be allowed with `setAllowedIntentSchemes(List)`.

* Mitigated issues in "ly.count.android:sdk-native" where:
* the native libraries were not laid out for 16 KB memory page sizes.
* a failure to load the "countly_native" library would crash the app instead of disabling native crash reporting.
* "CountlyNative.getBreakpadChecksum()" returned a stale revision instead of the Breakpad revision the native library was built from.

## 26.1.5
* The SDK now supports API level 37. Integrating apps must build with `compileSdk` 34 or higher.
* Added a new configuration option `setCustomSSLSocketFactory(SSLSocketFactory)` to send the SDK's HTTPS requests through a custom SSLSocketFactory.
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,26 @@ This SDK supports the following features:
## Security
Security is very important to us. If you discover any issue regarding security, please disclose the information responsibly by sending an email to security@count.ly and **not by creating a GitHub issue**.

### The `sdk-nw` artifact
Alongside `ly.count.android:sdk`, the SDK is published as `ly.count.android:sdk-nw`, where "nw" stands for "no web view".
It exists for organizations that need a hardened baseline to hold across a large number of applications, where relying on every application to apply the same configuration is not practical.
Two settings are enforced by the artifact itself:

* All WebView based UI is disabled, so no WebView is created or shown for the Content feature, Feedback Widgets, or the rating popup.
* The SDK's console logging is kept off in production (non-debuggable) builds, even when logging is enabled in the configuration.

Both settings are applied during `init` and can not be turned off, so no application, wrapper, or configuration mistake can weaken them.
Everything else is identical to `ly.count.android:sdk`: the same source, the same package names, and the same API.
Switching is a one line dependency change and requires no changes to your imports or code.

```gradle
implementation 'ly.count.android:sdk-nw:26.1.5'
```

Version numbers match the regular release they were built from, so `ly.count.android:sdk-nw:26.1.5` contains the same SDK as `ly.count.android:sdk:26.1.5`.
Depend on either `ly.count.android:sdk` or `ly.count.android:sdk-nw`, never both, because they contain the same classes.
Applications that need the WebView based UI, such as the Content feature or Feedback Widgets, should use `ly.count.android:sdk`.

## Badges
If you like Countly, [why not use one of our badges](https://count.ly/brand-assets) and give a link back to us so others know about this wonderful platform?

Expand Down
41 changes: 41 additions & 0 deletions sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ buildscript {
}
}

// This is the "nw" (no WebView) branch. It carries the same source as the regular SDK with two
// settings enforced at init, WebView UI off and console logging off in production builds, neither of
// which the config can turn back on, and it always publishes as 'ly.count.android:sdk-nw'. Keep this
// branch a one way target: merge from the release branch into it, never the other way around, and do
// not cut the regular 'ly.count.android:sdk' release from here.

android {
namespace 'ly.count.android.sdk'
compileSdk 37
Expand All @@ -30,6 +36,32 @@ android {
}

testInstrumentationRunner 'ly.count.android.sdk.test.InstrumentationTestRunner'

// These tests assert behavior this branch removes on purpose: content zone traffic, the rating
// popup, and console logging in production being on by default. Every one of them passes on the
// release branch, so they are excluded here rather than adapted. The rest of the suite, 965 of
// 983 tests, still runs. Keep this list in sync when the release branch adds content or
// WebView tests.
testInstrumentationRunnerArguments notClass: [
'ly.count.android.sdk.ModuleConfigurationTests#contentZoneEnabled_allFeatures',
'ly.count.android.sdk.ModuleConfigurationTests#crashReportingDisabled_allFeatures',
'ly.count.android.sdk.ModuleConfigurationTests#customEventTrackingDisabled_allFeatures',
'ly.count.android.sdk.ModuleConfigurationTests#journeyTriggerEvents_contentZoneRefreshFlow',
'ly.count.android.sdk.ModuleConfigurationTests#journeyTriggerEvents_refreshRetriesCorrectlyAfterProvidingEmptyResponse',
'ly.count.android.sdk.ModuleConfigurationTests#journeyTriggerEvents_refreshRetryStopAfterValidContentResponse',
'ly.count.android.sdk.ModuleConfigurationTests#journeyTriggerEvents_skipsRefreshWhenInContentZone',
'ly.count.android.sdk.ModuleConfigurationTests#locationTrackingDisabled_allFeatures',
'ly.count.android.sdk.ModuleConfigurationTests#refreshContentZoneDisabled_allFeatures',
'ly.count.android.sdk.ModuleConfigurationTests#serverConfig_Defaults_AllFeatures',
'ly.count.android.sdk.ModuleConfigurationTests#sessionsDisabled_allFeatures',
'ly.count.android.sdk.ModuleConfigurationTests#trackingDisabled_allFeatures',
'ly.count.android.sdk.ModuleConfigurationTests#viewTrackingDisabled_allFeatures',
'ly.count.android.sdk.ModuleContentTests#contentZone_doesNotResumeAfterExplicitExit',
'ly.count.android.sdk.ModuleContentTests#contentZone_resumesAfterTemporaryDeviceIDToggle',
'ly.count.android.sdk.ModuleContentTests#previewContent_validContentId',
'ly.count.android.sdk.ModuleLogTests#productionBuild_flagOff_loggingUnaffected',
'ly.count.android.sdk.ModuleRatingsTests#showFeedbackPopup_blockedInTemporaryDeviceIDMode'
].join(',')
testHandleProfiling true
testFunctionalTest true
}
Expand Down Expand Up @@ -58,6 +90,15 @@ android {
testNamespace 'ly.count.android.sdk.test'
}

//this branch is published under its own artifact id, everything else comes from gradle.properties
mavenPublishing {
coordinates(GROUP.toString(), 'sdk-nw', VERSION_NAME.toString())
pom {
name = 'Countly Android SDK (no WebView)'
description = 'Android SDK for Countly mobile analytics, with all of the SDK\'s WebView based UI disabled and console logging kept off in production builds, enforced and not configurable'
}
}

def mockitoVersion = "4.11.0"

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,9 @@ void assertDefaultValues(CountlyConfig config, boolean includeConstructorValues)
Assert.assertNull(config.starRatingTextMessage);
Assert.assertNull(config.starRatingTextTitle);
Assert.assertFalse(config.loggingEnabled);
Assert.assertFalse(config.disableSDKLoggingInProduction);
//this branch enforces both, so their defaults are inverted compared to the regular SDK
Assert.assertTrue(config.disableSDKLoggingInProduction);
Assert.assertFalse(config.webViewEnabled);
Assert.assertFalse(config.crashes.enableUnhandledCrashReporting);
Assert.assertFalse(config.enableAutomaticViewTracking);
Assert.assertFalse(config.autoTrackingUseShortName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public class TestUtils {
public final static String commonURL = "http://test.count.ly";
public final static String commonAppKey = "appkey";
public final static String commonDeviceId = "1234";
public final static String SDK_NAME = "java-native-android";
//matches what the SDK reports on this branch
public final static String SDK_NAME = "java-native-android-nw";
public final static String SDK_VERSION = "26.1.5";
public static final int MAX_THREAD_COUNT_PER_STACK_TRACE = 50;

Expand Down
9 changes: 8 additions & 1 deletion sdk/src/main/java/ly/count/android/sdk/Countly.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ public class Countly {
private final String DEFAULT_COUNTLY_SDK_VERSION_STRING = "26.1.5";
/**
* Used as request meta data on every request
* This is the "sdk-nw" branch, so the name carries the "-nw" marker to tell it apart on the server
*/
private final String DEFAULT_COUNTLY_SDK_NAME = "java-native-android";
private final String DEFAULT_COUNTLY_SDK_NAME = "java-native-android-nw";

/**
* Current version of the Count.ly Android SDK as a displayable string.
Expand Down Expand Up @@ -311,6 +312,10 @@ public synchronized Countly init(CountlyConfig config) {
throw new IllegalArgumentException("Can't init SDK with 'null' config");
}

//this build enforces both settings, the config can turn them on but never off
config.webViewEnabled = false;
config.disableSDKLoggingInProduction = true;

//determine whether console logging must stay off for production builds before any logging call
loggingForcedOffForProduction = shouldForceLoggingOffForProduction(config);

Expand All @@ -328,6 +333,8 @@ public synchronized Countly init(CountlyConfig config) {
L.d("[Init] Initializing Countly [" + COUNTLY_SDK_NAME + "] SDK version [" + COUNTLY_SDK_VERSION_STRING + "] default name[" + DEFAULT_COUNTLY_SDK_NAME + "] default version[" + DEFAULT_COUNTLY_SDK_VERSION_STRING + "]");
}

L.i("[Init] This is the 'nw' build of the SDK. All WebView based UI is disabled and console logging is kept off in production builds. Neither can be turned off");

if (config.context == null) {
if (config.application != null) {
L.d("[Init] No explicit context provided. Using context from the provided application class");
Expand Down
12 changes: 9 additions & 3 deletions sdk/src/main/java/ly/count/android/sdk/CountlyConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ public class CountlyConfig {

protected boolean loggingEnabled = false;

protected boolean disableSDKLoggingInProduction = false;
//always on in this "sdk-nw" build, Countly.init enforces it again so it can not be turned off
protected boolean disableSDKLoggingInProduction = true;

protected boolean enableAutomaticViewTracking = false;

Expand Down Expand Up @@ -217,7 +218,8 @@ public class CountlyConfig {
// If set to true, immediate requests will use serial AsyncTask executor instead of the thread pool
boolean useSerialExecutor = false;
WebViewDisplayOption webViewDisplayOption = WebViewDisplayOption.IMMERSIVE;
boolean webViewEnabled = true;
//always off in this "sdk-nw" build, Countly.init enforces it again so it can not be turned on
boolean webViewEnabled = false;

// If set to true, request queue cleaner will remove all overflow at once instead of gradually (loop limited) removing
boolean disableGradualRequestCleaner = false;
Expand Down Expand Up @@ -393,6 +395,8 @@ public synchronized CountlyConfig setLoggingEnabled(boolean enabled) {
* A production build is detected as one that is not flagged debuggable in its
* application info. This only affects console output. A log listener provided
* through {@link #setLogListener(ModuleLog.LogCallback)} keeps receiving logs.
* The "ly.count.android:sdk-nw" artifact applies this on its own and does not allow
* it to be turned off.
*/
public synchronized CountlyConfig disableSDKLoggingInProduction() {
this.disableSDKLoggingInProduction = true;
Expand Down Expand Up @@ -1162,7 +1166,9 @@ public synchronized CountlyConfig setWebviewDisplayOption(WebViewDisplayOption d
/**
* Disable all WebView-based UI in the SDK. When called, no WebView is ever created or shown
* for any feature. This covers the Content feature overlay, Feedback Widgets (surveys, NPS,
* and rating widgets), and the rating popup. WebView UI is enabled by default.
* and rating widgets), and the rating popup. WebView UI is enabled by default in the
* "ly.count.android:sdk" artifact. The "ly.count.android:sdk-nw" artifact keeps it disabled
* at all times and does not allow it to be turned back on.
*
* @return Returns the same config object for convenient linking
*/
Expand Down
Loading