Giter Club home page Giter Club logo

android-sdk's Introduction

Optimizely Android SDK

Apache 2.0 Build Status

This repository houses the Android SDK for use with Optimizely Feature Experimentation and Optimizely Full Stack (legacy). The Android SDK depends on the Optimizely Java SDK.

Optimizely Feature Experimentation is an A/B testing and feature management tool for product development teams, letting you experiment at every step. Using Optimizely Feature Experimentation allows for every feature on your roadmap to be an opportunity to discover hidden insights. Learn more at Optimizely.com, or see the developer documentation.

Optimizely Rollouts is free feature flags for development teams. You can easily roll out and roll back features in any application without code deploys, mitigating risk for every feature on your roadmap.

Get Started

Refer to the Android SDK's developer documentation for detailed instructions on getting started with using the SDK.

Requirements

  • Android API 14 or higher

Install the SDK

To add the android-sdk and all modules to your project, include the following in your app's build.gradle:


NOTE

Optimizely previously distributed the Android SDK through Bintray/JCenter. But, as of April 27, 2021, Bintray/JCenter will become a read-only repository indefinitely and the publish repository has been migrated to MavenCentral for the SDK version 3.10.1 and later. Older versions will still be available in JCenter.


repositories {
	mavenCentral()
  	jcenter()
}

dependencies {
	implementation 'com.optimizely.ab:android-sdk:4.0.0'
}

Samples

A sample code for SDK initialization and experiments:

OptimizelyManager optimizelyManager = OptimizelyManager.builder()
            .withSDKKey("my_sdk_key")
            .withDatafileDownloadInterval(TimeUnit.MINUTES.toSeconds(15))
            .build(getApplicationContext());

optimizelyManager.initialize(this, null, (OptimizelyClient optimizely) -> {
	OptimizelyClient optimizely = optimizelyManager.getOptimizely();

	Variation variation = optimizely.activate("background_experiment", userId);

	optimizely.track("sample_conversion", userId);
});

Use the Android SDK

See the Optimizely Feature Experimentation developer documentation to learn how to set up your first Android project and use the SDK.

Architecture

This project includes 5 library modules and a test app.

  1. Android SDK
  • Users who want all modules should declare a dependency on this module.
  • This is the outer module that depends on all other modules.
  • This builds Optimizely objects and provides all public APIs.
  1. Datafile Handler
  • This handles downloading the datafile from the Optimizely server.
  1. Event Handler
  • This handles dispatching events to the Optimizely server.
  • This uses a Service so events can be sent without the app being re-opened.
  • Events remain persistent until dispatching completed successfully.
  1. User Profile
  • Persistent bucketing
  • Once a user is bucketed in a variation, the user will remain in that variation.
  1. Shared
  • Common utils for all modules
  1. Test App
  • A simple app showing how to use the Optimizely Android SDK

SDK Development

Command Line

  1. Clone the repo
  1. Create, or use an existing, Optimizely Android project
  2. Build the project (from the project root)
  • ./gradlew assemble
  1. Run tests for all modules
  • ./gradlew testAllModules
  • A device or emulator must be connected.
  1. Install the test app onto all connected devices and emulators
  • ./gradlew test-app:installDebug
  • The test app depends on all of the other project modules.
  • The modules are built from source.
  1. Discover more gradle tasks
  • ./gradlew tasks
  • To see the task of an individual module: ./gradlew user-profile:tasks

Android Studio

Android Studio is an IDE that wraps gradle. Everything you can do in Android Studio can be done from the command line tools.

You can import this project into Android Studio by opening Android Studio and selecting Import Project from the first dialog or from the File menu. Simply select the project's root build.gradle file and Android Studio will do the rest.

Tests can be run by right clicking the file in the project pane or by clicking the method name in source and selecting run. You will be prompted to create an AVD or connect a device if one isn't connected.

Contributing

Please see CONTRIBUTING.

Credits

First-party code (under android-sdk/, datafile-handler/, event-handler/, shared/, user-profile/) is copyright Optimizely, Inc. and contributors, licensed under Apache 2.0

Additional Code

This software incorporates code from the following open source projects:

Optimizely.ab:core-api https://github.com/optimizely/java-sdk License (Apache 2.0): https://github.com/optimizely/java-sdk/blob/master/LICENSE Additional credits from java-sdk:https://github.com/optimizely/java-sdk/blob/master/README.md

Android Logger https://github.com/noveogroup/android-logger License (Public Domain): https://github.com/noveogroup/android-logger/blob/master/LICENSE.txt

Other Optimzely SDKs

android-sdk's People

Contributors

aliabbasrizvi avatar ceimaj avatar elliotykim avatar fayyazarshad avatar fingertricks avatar jaeopt avatar jamesneimanconsulting avatar jophde avatar juancarlostong avatar mikeproeng37 avatar mnoman09 avatar msohailhussain avatar muzahidul-opti avatar nchilada avatar nomanshoaib avatar oakbani avatar onufryk avatar ozayr-zaviar avatar shaharyar123 avatar shihpatrick avatar shisheng-1 avatar thomaszurkan-optimizely avatar trishahanlon avatar wangjoshuah avatar yasirfolio3 avatar yuanc avatar zashraf1985 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-sdk's Issues

ScheduledJobService & JobWorkService - ANR

we have a couple of high volume ANR's visible on Google Play Console, we are using your latest SDK 3.5.1

all of these are happening in com.optimizely.ab.android.shared.ScheduledJobService
and com.optimizely.ab.android.shared.JobWorkService

all occurrences are from Android 10 (various device brands)

we also have com.optimizely.ab.android.datafile_handler.DatafileService ANR's on OS 6 & 7

UserProfileCache.DiskCache uses deprecated project ID

I noticed while integrating the SDK that the UserProfileCache.DiskCache is using the projectId in the getFileName() method. However, project IDs have been deprecated as of 2.1.0.

We are currently initializing the OptimizelyManager with an SDK key, and not a project ID. This results in the disk cache being created with the filename optly-user-profile-service-null.json no matter what project or environment is being used. This could potentially lead to collisions.

android.os.DeadSystemException:Unable to unbind|create service

First Crash:
Fatal Exception: java.lang.RuntimeException: Unable to unbind to service com.optimizely.ab.android.datafile_handler.DatafileService@4b3f953 with Intent { cmp=com.verifly.smb/com.optimizely.ab.android.datafile_handler.DatafileService }: java.lang.RuntimeException: android.os.DeadSystemException
at android.app.ActivityThread.handleUnbindService(ActivityThread.java:4139)
at android.app.ActivityThread.access$1700(ActivityThread.java:226)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1924)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7615)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)

Second Crash:
Fatal Exception: java.lang.RuntimeException: Unable to create service com.optimizely.ab.android.shared.ScheduledJobService: java.lang.RuntimeException: android.os.DeadSystemException
at android.app.ActivityThread.handleCreateService(ActivityThread.java:4081)
at android.app.ActivityThread.access$1500(ActivityThread.java:226)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1914)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7615)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)

Event Flushing?

@thomaszurkan-optimizely Is there a way to flush events? i.e...., if my dispatch interval was configured at 30 seconds, but there are some important metrics I want to dispatch immediately. I can't find information around flushing.

Thanks.

SecurityException causing crashes

We are seeing multiple crash reports come in. Our current setup just initializes the SDK in our application onCreate. The security exception fails on different files (not always the meta-inf)

SDK version being used: 2.1.4 (still waiting on #258 to use 3.0.0)
Android versions: 7.1.2, 8.0.0

Stack trace:

Fatal Exception: java.lang.NoClassDefFoundError: com.noveogroup.android.log.LoggerManager
       at com.noveogroup.android.log.LoggerManager.getLogger(LoggerManager.java:200)
       at org.slf4j.impl.AndroidLoggerFactory.getLogger(AndroidLoggerFactory.java:41)
       at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:329)
       at com.optimizely.ab.android.sdk.OptimizelyManager$Builder.build(OptimizelyManager.java:748)
       at App.onCreate(MCMApp.java:95)
       at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1025)
       at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5405)
       at android.app.ActivityThread.-wrap2(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1546)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6121)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
Caused by java.lang.ExceptionInInitializerError
       at com.noveogroup.android.log.LoggerManager.getLogger(LoggerManager.java:200)
       at org.slf4j.impl.AndroidLoggerFactory.getLogger(AndroidLoggerFactory.java:41)
       at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:329)
       at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:349)
       at com.optimizely.ab.android.sdk.OptimizelyManager$Builder.build(OptimizelyManager.java:737)
       at App.onCreate(MCMApp.java:95)
       at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1025)
       at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5405)
       at android.app.ActivityThread.-wrap2(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1546)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6121)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
Caused by java.lang.SecurityException: cannot verify signature block file META-INF/GOOGPLAY
       at sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:220)
       at sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:193)
       at java.util.jar.JarVerifier.processEntry(JarVerifier.java:306)
       at java.util.jar.JarVerifier.update(JarVerifier.java:217)
       at java.util.jar.JarFile.initializeVerifier(JarFile.java:353)
       at java.util.jar.JarFile.getInputStream(JarFile.java:420)
       at libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection.getInputStream(ClassPathURLStreamHandler.java:162)
       at java.net.URL.openStream(URL.java:1058)
       at java.lang.ClassLoader.getResourceAsStream(ClassLoader.java:979)
       at com.noveogroup.android.log.LoggerManager.loadProperties(LoggerManager.java:77)
       at com.noveogroup.android.log.LoggerManager.loadConfiguration(LoggerManager.java:123)
       at com.noveogroup.android.log.LoggerManager.(LoggerManager.java:166)
       at com.noveogroup.android.log.LoggerManager.getLogger(LoggerManager.java:200)
       at org.slf4j.impl.AndroidLoggerFactory.getLogger(AndroidLoggerFactory.java:41)
       at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:329)
       at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:349)
       at com.optimizely.ab.android.sdk.OptimizelyManager$Builder.build(OptimizelyManager.java:737)
       at App.onCreate(MCMApp.java:95)
       at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1025)
       at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5405)
       at android.app.ActivityThread.-wrap2(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1546)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6121)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)

Espresso Idling Resource Dependency

Copying customer issue reported here from Optiverse:
https://community.optimizely.com/t5/Mobile-Apps/Espresso-Idling-Resource-Dependency/m-p/20874


Is there any reason the Android SDK depends on com.android.support.test.espresso:espresso-idling-resource:2.2.2 in the release SDK? Because of this, there's a dependency resolution conflict which is deeply unhelpful when we're trying to work on our test suite and use a different version of the Espresso libraries.

I spent some time looking at the SDK source on GitHub, and it looks like it's only used in a class called CountingIdlingResourceManager. Unless that data is read from this in some kind of very non-obvious way, it looks like data gets put into this thing, but is never read out. So that's nice.

Please remove this apparently superfluous dependency. Having release code contain a testing library in this way seems like a mistake. Use whatever you want in your test suites, just keep it out of the stuff that goes into the wild.

OptimizelyManager Builder requires projectId or sdkKey

Right now the OptimizelyManager.Builder requires either a projectId or a sdkKey. If neither are provided, build() will return null. One of these are required so that if a DatafileConfig is not provided, one can be created. However, if a DatafileConfig is provided, then neither projectId or sdkKey should be necessary.
For instance, if one was to create an OptimizelyManager as such, then sdkKey or projectId should not be required.

val sdkKey = "myKey"
val datafileConfig = DatafileConfig(null, sdkKey, OPTIMIZELY_DATAFILE_HOST)
val optimizelyManager = OptimizelyManager.builder()
  .withDatafileConfig(datafileConfig)
  .withSDKKey(sdkKey) // not needed since we provide a DatafileConfig, but if not provided, build() returns null
  .build(context)

Stale value is returned after refresh

Steps to reproduce:

  1. Initialize Optimizely SDK (3.3.0) like this:
val optimizelyClient = OptimizelyManager.builder()
    .withSDKKey("sdk key")
    .build(context)
    .initialize(context, R.raw.optimizely_data_file)
  1. Enable/disable a feature flag in Optimizely console.
  2. Wait for a few minutes just in case.
  3. Launch the app and wait for a new data file to be downloaded from CDN.
  4. Query the feature flag - isFeatureEnabled returns the previous value:
val enabled = optimizelyClient.isFeatureEnabled("test_flag", "123")
  1. Kill the application process and restart.
  2. Query the feature flag again - new value is returned.

Background datafile loading failing to close file resource

Version:
compile 'com.optimizely.ab:android-sdk:1.3.0'

Captured strict mode exception:

04-28 09:33:09.039: E/Surface(21300): getSlotFromBufferLocked: unknown buffer: 0xa9f76820
04-28 09:33:09.088: E/StrictMode(21300): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
04-28 09:33:09.088: E/StrictMode(21300): java.lang.Throwable: Explicit termination method 'close' not called
04-28 09:33:09.088: E/StrictMode(21300): 	at dalvik.system.CloseGuard.open(CloseGuard.java:180)
04-28 09:33:09.088: E/StrictMode(21300): 	at java.io.FileInputStream.<init>(FileInputStream.java:78)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.app.ContextImpl.openFileInput(ContextImpl.java:384)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.content.ContextWrapper.openFileInput(ContextWrapper.java:177)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.optimizely.ab.android.shared.Cache.load(Cache.java:61)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.optimizely.ab.android.sdk.DataFileCache.load(DataFileCache.java:47)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.optimizely.ab.android.sdk.DataFileLoader$LoadDataFileFromCacheTask.doInBackground(DataFileLoader.java:101)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.optimizely.ab.android.sdk.DataFileLoader$LoadDataFileFromCacheTask.doInBackground(DataFileLoader.java:85)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.os.AsyncTask$2.call(AsyncTask.java:295)
04-28 09:33:09.088: E/StrictMode(21300): 	at java.util.concurrent.FutureTask.run(FutureTask.java:237)
04-28 09:33:09.088: E/StrictMode(21300): 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
04-28 09:33:09.088: E/StrictMode(21300): 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
04-28 09:33:09.088: E/StrictMode(21300): 	at java.lang.Thread.run(Thread.java:818)
04-28 09:33:09.088: E/StrictMode(21300): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
04-28 09:33:09.088: E/StrictMode(21300): java.lang.Throwable: Explicit termination method 'close' not called
04-28 09:33:09.088: E/StrictMode(21300): 	at dalvik.system.CloseGuard.open(CloseGuard.java:180)
04-28 09:33:09.088: E/StrictMode(21300): 	at java.io.FileOutputStream.<init>(FileOutputStream.java:89)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.app.ContextImpl.openFileOutput(ContextImpl.java:393)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.content.ContextWrapper.openFileOutput(ContextWrapper.java:183)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.optimizely.ab.android.shared.Cache.save(Cache.java:108)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.optimizely.ab.android.sdk.BackgroundWatchersCache.save(BackgroundWatchersCache.java:122)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.optimizely.ab.android.sdk.BackgroundWatchersCache.setIsWatching(BackgroundWatchersCache.java:56)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.optimizely.ab.android.sdk.DataFileService.onStartCommand(DataFileService.java:77)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3010)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.app.ActivityThread.-wrap17(ActivityThread.java)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1442)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.os.Handler.dispatchMessage(Handler.java:102)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.os.Looper.loop(Looper.java:148)
04-28 09:33:09.088: E/StrictMode(21300): 	at android.app.ActivityThread.main(ActivityThread.java:5417)
04-28 09:33:09.088: E/StrictMode(21300): 	at java.lang.reflect.Method.invoke(Native Method)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
04-28 09:33:09.088: E/StrictMode(21300): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
04-28 09:33:09.140: E/Surface(21300): getSlotFromBufferLocked: unknown buffer: 0xae432570

Disable schedule

Disabling sync not working when previously was enabled (interval > 0). When sync interval is disabled, then previously scheduled service intent are starting.
Previously scheduled intents schould be removed when interval is <= 0.

public void schedule(Intent intent, long interval) {
        if (isScheduled(intent)) {
            unschedule(intent);
        }

        if (interval < 1) {
            logger.error("Tried to schedule an interval less than 1");
            return;
        }

        PendingIntent pendingIntent = pendingIntentFactory.getPendingIntent(intent);

        setRepeating(interval, pendingIntent, intent);

        logger.info("Scheduled {}", intent.getComponent().toShortString());
    }

com.optimizely.ab.android.datafile_handler.DatafileService - ANR

Hi,

We found an issue in our production app. There are multiple occurrences of ANR being reported.

SDK version: 2.1.0

Below is the stack trace of the crash,

`
executing service com.myapp.prod/com.optimizely.ab.android.datafile_handler.DatafileService

com.optimizely.ab.android.datafile_handler.DatafileService
group="main" sCount=1 dsCount=0 obj=0x761a2298 self=0xb47a4500

sysTid=19837 nice=0 cgrp=apps sched=0/0 handle=0xb6f4eb4c
state=S schedstat=( 2908414587 1901277872 7208 ) utm=151 stm=139 core=3 HZ=100
stack=0xbe0d6000-0xbe0d8000 stackSize=8MB
held mutexes=at java.lang.Object.wait! (Native method)waiting on <0x070ae6c0> (a java.lang.Object)at java.lang.Thread.parkFor$ (Thread.java:1220)locked <0x070ae6c0> (a java.lang.Object)at sun.misc.Unsafe.park (Unsafe.java:299)at java.util.concurrent.locks.LockSupport.park (LockSupport.java:158)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt (AbstractQueuedSynchronizer.java:810)at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly (AbstractQueuedSynchronizer.java:970)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly (AbstractQueuedSynchronizer.java:1278)at java.util.concurrent.CountDownLatch.await (CountDownLatch.java:203)at android.app.SharedPreferencesImpl$EditorImpl$1.run (SharedPreferencesImpl.java:366)at android.app.QueuedWork.waitToFinish (QueuedWork.java:88)at android.app.ActivityThread.handleServiceArgs (ActivityThread.java:4068)at android.app.ActivityThread.access$2400 (ActivityThread.java:221)at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1897)at android.os.Handler.dispatchMessage (Handler.java:102)at android.os.Looper.loop (Looper.java:158)at android.app.ActivityThread.main (ActivityThread.java:7225)at java.lang.reflect.Method.invoke! (Native method)at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1230)at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1120)"FinalizerWatchdogDaemon" tid=6 Sleeping "FinalizerWatchdogDaemon" daemon prio=5 tid=6 Sleeping
group="system" sCount=1 dsCount=0 obj=0x32c0d220 self=0xad918800
sysTid=19848 nice=0 cgrp=apps sched=0/0 handle=0xb341d930
state=S schedstat=( 1293228 15132450 9 ) utm=0 stm=0 core=0 HZ=100
stack=0xb331b000-0xb331d000 stackSize=1038KB
held mutexes=at java.lang.Thread.sleep! (Native method)sleeping on <0x0eee4d9f> (a java.lang.Object)at java.lang.Thread.sleep (Thread.java:1031)locked <0x0eee4d9f> (a java.lang.Object)at java.lang.Thread.sleep (Thread.java:985)at java.lang.Daemons$FinalizerWatchdogDaemon.sleepFor (Daemons.java:273)at java.lang.Daemons$FinalizerWatchdogDaemon.waitForFinalization (Daemons.java:284)at java.lang.Daemons$FinalizerWatchdogDaemon.run (Daemons.java:232)at java.lang.Thread.run (Thread.java:818)
`

Can someone shed some light on this ANR log?

Crash with IllegalStateException - Apps may not schedule more than 100 distinct jobs

We're seeing a crash in the Optimizely stack around scheduling jobs with the JobScheduler. The app also makes use of JobScheduler, via the WorkManager library, but the app currently only schedules 3-5 jobs, maximum.

These crashes are mostly happening in the background, according to fabric.

Relevant library versions:

implementation('com.optimizely.ab:android-sdk:2.1.0')
android.arch.work:work-runtime: 1.0.0-alpha10
android.arch.work:work-firebase: 1.0.0-alpha10
com.firebase:firebase-jobdispatcher:0.8.5

Fatal Exception: java.lang.IllegalStateException: Apps may not schedule more than 100 distinct jobs
       at android.os.Parcel.readException(Parcel.java:1967)
       at android.os.Parcel.readException(Parcel.java:1905)
       at android.app.job.IJobScheduler$Stub$Proxy.schedule(IJobScheduler.java:180)
       at android.app.JobSchedulerImpl.schedule(JobSchedulerImpl.java:44)
       at com.optimizely.ab.android.shared.ServiceScheduler.setRepeating(ServiceScheduler.java:134)
       at com.optimizely.ab.android.shared.ServiceScheduler.schedule(ServiceScheduler.java:83)
       at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler.startBackgroundUpdates(DefaultDatafileHandler.java:122)
       at com.optimizely.ab.android.sdk.OptimizelyManager.injectOptimizely(OptimizelyManager.java:442)
       at com.optimizely.ab.android.sdk.OptimizelyManager$2.onDatafileLoaded(OptimizelyManager.java:343)
       at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded(DefaultDatafileHandler.java:78)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify(DatafileLoader.java:81)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000(DatafileLoader.java:35)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute(DatafileLoader.java:134)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute(DatafileLoader.java:85)
       at android.os.AsyncTask.finish(AsyncTask.java:695)
       at android.os.AsyncTask.-wrap1(Unknown Source)
       at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:712)
       at android.os.Handler.dispatchMessage(Handler.java:105)
       at android.os.Looper.loop(Looper.java:164)
       at android.app.ActivityThread.main(ActivityThread.java:6938)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

Security Exception - permission.INTERACT_ACROSS_USERS

We have quite lot of bugs although they are mostly coming from device identified by Crashlytics as Ulefone Armor 2 with Android 7.0

Caused by java.lang.SecurityException: get package info: Neither user 1310104 nor current process has android.permission.INTERACT_ACROSS_USERS.
at android.os.Parcel.readException + 1683(Parcel.java:1683)
at android.os.Parcel.readException + 1636(Parcel.java:1636)
at android.app.ActivityManagerProxy.bindService + 4506(ActivityManagerProxy.java:4506)
at android.app.ContextImpl.bindServiceCommon + 1508(ContextImpl.java:1508)
at android.app.ContextImpl.bindService + 1459(ContextImpl.java:1459)
at android.content.ContextWrapper.bindService + 636(ContextWrapper.java:636)
at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler.downloadDatafile + 95(DefaultDatafileHandler.java:95)
at com.optimizely.ab.android.sdk.OptimizelyManager.initialize + 329(OptimizelyManager.java:329)
at com.optimizely.ab.android.sdk.OptimizelyManager.initialize + 307(OptimizelyManager.java:307)
at de.is24.mobile.abtesting.OptimizelyXABTesting.init + 35(OptimizelyXABTesting.java:35)

1.5.0-RC Multiple Dex Files define class

I am seeing the following error in our project:

Caused by: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex
Caused by: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex
        at com.android.builder.dexing.DexArchiveMergerCallable.call(DexArchiveMergerCallable.java:72)
        at com.android.builder.dexing.DexArchiveMergerCallable.call(DexArchiveMergerCallable.java:36)
Caused by: com.android.dex.DexException: Multiple dex files define Lcom/noveogroup/android/log/AbstractLogger;

Optimizely seems to be the only library with this dependency so I don't know why the clash dex merging.

sdk cannot resolve data file from Full Stack project

I'm using this android-sdk in conjunction with a Full Stack project setup on Optimizely. The SDK is trying to fetch the data file at https://cdn.optimizely.com/public/<project_id>/datafile_v3.json, and is getting a 503.

Upon further investigation, if we update DatafileService.DATAFILE_VERSION to "2" instead of "3", then this seems to work fine. Can we make DATAFILE_VERSION a configurable value?

According to the documentation we received from the Optimizely team about cross-platform experiments (https://docs.google.com/document/d/1BHcd4iv_ZHNFfmutS_qMNUpB6UBDIerKnK1ezqh-DN0/edit),

the only difference is the version number.

403 while using the new SDK_KEY

Hi,

I noticed in the logs that there is a 403 error while trying to access the URL with a SDK_KEY. This is how the URL looks like. https://cdn.optimizely.com/datafiles/<SDK_KEY>.json

Is there a missing step I'm supposed to do while using SDK_KEY?

Thanks,
Jia

Optimizely initialization without downloading Datafile

Hello,

We are using this Android SDK on a Kotlin application to control experiments and features for thousands of devices which have bandwidth restrictions. We want to control when the datafile updated, so we are not using polling to prevent wasting bandwidth. The issue here is that this SDK will download the datafile every time the client is initialized.

The methods provided do not allow initializing the manager without downloading cache file. The following method,

protected OptimizelyClient initialize(@NonNull Context context, @Nullable String datafile, boolean downloadToCache) {

is prepared to receive a flag to decide if it should download or not, but there is no path where this is called with the value false. We think this method should have been public, instead of protected.

There are two alternatives to this, which are:

  • Creating a custom DatafileHandler, which will block certain request, but since using your methods to download the cache files, this would be tricky requiring changing some flags or something to know if we are initializing and blocking those, which feels like too much on an hack
  • Call that initialization method with reflection, which is not really ideal, but is the solution we are using right now

Is there any alternative? What is the recommended approach for this? Would it be possible to make that method public?

Thank you

RemoteException

Hi, we're seeing a lot of crashes for Android 8 and above, stack trace looks like this. This may be different than the SecurityException that was reported years ago....

JobIntentService$JobServiceEngineImpl line 315
android.support.v4.app.JobIntentService$JobServiceEngineImpl.dequeueWork
Caused by android.os.RemoteException: Remote stack trace:
	at com.android.server.job.JobServiceContext.assertCallerLocked(JobServiceContext.java:481)
	at com.android.server.job.JobServiceContext.doDequeueWork(JobServiceContext.java:359)
	at com.android.server.job.JobServiceContext$JobCallback.dequeueWork(JobServiceContext.java:160)
	at android.app.job.IJobCallback$Stub.onTransact(IJobCallback.java:83)
	at android.os.Binder.execTransact(Binder.java:731)
Caused by java.lang.SecurityException: Caller no longer running, last stopped +1s424ms because: timed out while starting
       at android.os.Parcel.readException(Parcel.java:1943)
       at android.os.Parcel.readException(Parcel.java:1889)
       at android.app.job.IJobCallback$Stub$Proxy.dequeueWork(IJobCallback.java:191)
       at android.app.job.JobParameters.dequeueWork(JobParameters.java:196)
       at android.support.v4.app.JobIntentService$JobServiceEngineImpl.dequeueWork(SourceFile:315)
       at android.support.v4.app.JobIntentService.dequeueWork(SourceFile:640)
       at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(SourceFile:390)
       at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(SourceFile:383)
       at android.os.AsyncTask$2.call(AsyncTask.java:333)
       at java.util.concurrent.FutureTask.run(FutureTask.java:266)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
       at java.lang.Thread.run(Thread.java:764)

Proguard fails on latest version of sdk

Running the latest version (3.0) of the sdk fails when proguard is enabled with the below:

Warning: com.optimizely.ab.config.parser.JacksonConfigParser$ProjectConfigModule: can't find referenced method 'com.fasterxml.jackson.databind.module.SimpleModule addDeserializer(java.lang.Class,com.fasterxml.jackson.databind.JsonDeserializer)' in program class com.optimizely.ab.config.parser.JacksonConfigParser$ProjectConfigModule

IntentService -- OutOfMemoryError

Hi,

I'm seeing this error, happening only on Android 8 (Oreo) devices.

Fatal Exception: java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again
       at java.lang.Thread.nativeCreate(Thread.java)
       at java.lang.Thread.start(Thread.java:733)
       at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:970)
       at java.util.concurrent.ThreadPoolExecutor.processWorkerExit(ThreadPoolExecutor.java:1038)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1180)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
       at java.lang.Thread.run(Thread.java:764)

The crash console is showing there are 1177 threads created (doubt this is intentional!). And most of the threads look like this:

IntentService[EventHandlerService]
       at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
       at android.os.MessageQueue.next(MessageQueue.java:325)
       at android.os.Looper.loop(Looper.java:142)
       at android.os.HandlerThread.run(HandlerThread.java:65)

I'm not entirely sure this is an error from the optimizely SDK, but I saw you have this file, so it might be related.

ExceptionInInitializerError not catched

Hi there,

I'm getting an error in production while initializing Optimizely :

Fatal Exception: java.lang.ExceptionInInitializerError
at org.apache.harmony.security.x509.Validity.(Validity.java)
at org.apache.harmony.security.x509.TBSCertificate.(TBSCertificate.java)
at org.apache.harmony.security.x509.Certificate.(Certificate.java)
at org.apache.harmony.security.pkcs7.SignedData.(SignedData.java)
at org.apache.harmony.security.pkcs7.ContentInfo$1.getDecodedObject(ContentInfo.java:146)
at org.apache.harmony.security.asn1.ASN1Sequence.decode(ASN1Sequence.java:45)
at org.apache.harmony.security.utils.JarUtils.verifySignature(JarUtils.java:72)
at java.util.jar.JarVerifier.verifyCertificate(JarVerifier.java:294)
at java.util.jar.JarVerifier.readCertificates(JarVerifier.java:268)
at java.util.jar.JarFile.getInputStream(JarFile.java:380)
at libcore.net.url.JarURLConnectionImpl.getInputStream(JarURLConnectionImpl.java:222)
at java.net.URL.openStream(URL.java:470)
at java.lang.ClassLoader.getResourceAsStream(ClassLoader.java:444)
at com.noveogroup.android.log.LoggerManager.loadProperties(LoggerManager.java:77)
at com.noveogroup.android.log.LoggerManager.loadConfiguration(LoggerManager.java:123)
at com.noveogroup.android.log.LoggerManager.(LoggerManager.java)
at org.slf4j.impl.AndroidLoggerFactory.getLogger(AndroidLoggerFactory.java:41)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:329)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:349)
at com.optimizely.ab.android.sdk.OptimizelyManager$Builder.build(OptimizelyManager.java:627)
[...]

Caused by java.lang.ArithmeticException: BigInteger division by zero
at java.math.NativeBN.BN_new(NativeBN.java)
at java.math.BigInt.makeValid(BigInt.java:51)
at java.math.BigInt.putULongInt(BigInt.java:85)
at java.math.BigInteger.(BigInteger.java)
at java.math.BigInteger.valueOf(BigInteger.java:373)
at org.apache.harmony.security.asn1.ASN1Choice.addIdentifier(ASN1Choice.java:282)
at org.apache.harmony.security.asn1.ASN1Choice.(ASN1Choice.java)
at org.apache.harmony.security.x509.Time$1.(Time.java)
at org.apache.harmony.security.x509.Time.(Time.java)
at org.apache.harmony.security.x509.Validity.(Validity.java)
at org.apache.harmony.security.x509.TBSCertificate.(TBSCertificate.java)
at org.apache.harmony.security.x509.Certificate.(Certificate.java)
at org.apache.harmony.security.pkcs7.SignedData.(SignedData.java)
at org.apache.harmony.security.pkcs7.ContentInfo$1.getDecodedObject(ContentInfo.java:146)
at org.apache.harmony.security.asn1.ASN1Sequence.decode(ASN1Sequence.java:45)
at org.apache.harmony.security.utils.JarUtils.verifySignature(JarUtils.java:72)
at java.util.jar.JarVerifier.verifyCertificate(JarVerifier.java:294)
at java.util.jar.JarVerifier.readCertificates(JarVerifier.java:268)
at java.util.jar.JarFile.getInputStream(JarFile.java:380)
at libcore.net.url.JarURLConnectionImpl.getInputStream(JarURLConnectionImpl.java:222)
at java.net.URL.openStream(URL.java:470)
at java.lang.ClassLoader.getResourceAsStream(ClassLoader.java:444)
at com.noveogroup.android.log.LoggerManager.loadProperties(LoggerManager.java:77)
at com.noveogroup.android.log.LoggerManager.loadConfiguration(LoggerManager.java:123)
at com.noveogroup.android.log.LoggerManager.(LoggerManager.java)
at org.slf4j.impl.AndroidLoggerFactory.getLogger(AndroidLoggerFactory.java:41)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:329)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:349)
at com.optimizely.ab.android.sdk.OptimizelyManager$Builder.build(OptimizelyManager.java:627)
[...]

This seems to be a problem while initializing the Logger in Optimizely build() line 627 :

            try {
                logger = LoggerFactory.getLogger(OptimizelyManager.class);
            } catch (Exception e) {
                logger = LoggerFactory.getLogger("Optly.androidSdk");
                logger.error("Unable to generate logger from class");
            }

This snippet is catching Exceptions but not Errors like "java.lang.ExceptionInInitializerError".
A simple workaround is to add "catch(Error e){ [...] }" as well.

If you need any further infos, just tell me.

SecurityException: Caller no longer running, last stopped

We are seeing the following error using 1.5.1

Fatal Exception: java.lang.RuntimeException: An error occurred while executing doInBackground()
       at android.os.AsyncTask$3.done(AsyncTask.java:353)
       at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
       at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
       at java.util.concurrent.FutureTask.run(FutureTask.java:271)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
       at java.lang.Thread.run(Thread.java:764)
Caused by java.lang.SecurityException: Caller no longer running, last stopped +8s783ms because: timed out while starting
       at android.os.Parcel.readException(Parcel.java:1942)
       at android.os.Parcel.readException(Parcel.java:1888)
       at android.app.job.IJobCallback$Stub$Proxy.dequeueWork(IJobCallback.java:191)
       at android.app.job.JobParameters.dequeueWork(JobParameters.java:196)
       at com.optimizely.ab.android.shared.JobWorkService$CommandProcessor.doInBackground(JobWorkService.java:73)
       at com.optimizely.ab.android.shared.JobWorkService$CommandProcessor.doInBackground(JobWorkService.java:56)

Release 1.5.0-RC Proguard Issues with < v26 Build Tools

You should mention in your release notes about upgrading to v26.x.x for Android Build Tools for 1.5.0-RC. android.app.job.JobWorkItem is part of the v26.x.x build tools and you use this for the JobScheduler.

If you point to anything below 26 you will see these proguard warnings:

Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced method 'void completeWork(android.app.job.JobWorkItem)' in library class android.app.job.JobParameters
Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService$CommandProcessor: can't find referenced method 'android.app.job.JobWorkItem dequeueWork()' in library class android.app.job.JobParameters
Warning: com.optimizely.ab.android.shared.JobWorkService$CommandProcessor: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService$CommandProcessor: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService$CommandProcessor: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService$CommandProcessor: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService$CommandProcessor$1: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.JobWorkService$CommandProcessor$1: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.ServiceScheduler: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.ServiceScheduler: can't find referenced class android.app.job.JobWorkItem
Warning: com.optimizely.ab.android.shared.ServiceScheduler: can't find referenced method 'int enqueue(android.app.job.JobInfo,android.app.job.JobWorkItem)' in library class android.app.job.JobScheduler`

Main Thread IO done on SDK initialization

Everytime the SDK initializes we get a bunch of strict mode violations.

It seems like the SDK is doing some IO in onPostExecute of an AsyncTask. It would be great if this IO could be done on a different thread so it doesn't slow down the user experience.

I've added the stack trace below.

01-21 19:15:06.023 18420 18420 D StrictMode: at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1556)
01-21 19:15:06.023 18420 18420 D StrictMode: at libcore.io.BlockGuardOs.access(BlockGuardOs.java:69)
01-21 19:15:06.023 18420 18420 D StrictMode: at libcore.io.ForwardingOs.access(ForwardingOs.java:73)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.app.ActivityThread$AndroidOs.access(ActivityThread.java:7246)
01-21 19:15:06.023 18420 18420 D StrictMode: at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:281)
01-21 19:15:06.023 18420 18420 D StrictMode: at java.io.File.exists(File.java:815)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.app.ContextImpl.ensurePrivateDirExists(ContextImpl.java:645)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.app.ContextImpl.ensurePrivateDirExists(ContextImpl.java:636)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.app.ContextImpl.getFilesDir(ContextImpl.java:681)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.app.ContextImpl.fileList(ContextImpl.java:792)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.content.ContextWrapper.fileList(ContextWrapper.java:233)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.shared.Cache.exists(Cache.java:69)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileCache.exists(DatafileCache.java:65)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler.isDatafileSaved(DefaultDatafileHandler.java:225)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.sdk.OptimizelyManager.isDatafileCached(OptimizelyManager.java:412)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.sdk.OptimizelyManager.getDatafile(OptimizelyManager.java:281)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.sdk.OptimizelyManager$2.onDatafileLoaded(OptimizelyManager.java:343)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded(DefaultDatafileHandler.java:78)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify(DatafileLoader.java:81)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000(DatafileLoader.java:35)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute(DatafileLoader.java:134)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute(DatafileLoader.java:85)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.os.AsyncTask.finish(AsyncTask.java:755)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.os.AsyncTask.access$900(AsyncTask.java:192)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:772)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.os.Handler.dispatchMessage(Handler.java:107)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.os.Looper.loop(Looper.java:214)
01-21 19:15:06.023 18420 18420 D StrictMode: at android.app.ActivityThread.main(ActivityThread.java:7356)
01-21 19:15:06.023 18420 18420 D StrictMode: at java.lang.reflect.Method.invoke(Native Method)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
01-21 19:15:06.023 18420 18420 D StrictMode: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
01-21 19:15:06.024 18420 18420 D StrictMode: StrictMode policy violation; ~duration=106 ms: android.os.strictmode.DiskReadViolation
01-21 19:15:06.024 18420 18420 D StrictMode: at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1556)
01-21 19:15:06.024 18420 18420 D StrictMode: at java.io.UnixFileSystem.list(UnixFileSystem.java:344)
01-21 19:15:06.024 18420 18420 D StrictMode: at java.io.File.list(File.java:1131)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.os.FileUtils.listOrEmpty(FileUtils.java:1212)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.app.ContextImpl.fileList(ContextImpl.java:792)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.content.ContextWrapper.fileList(ContextWrapper.java:233)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.shared.Cache.exists(Cache.java:69)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileCache.exists(DatafileCache.java:65)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler.isDatafileSaved(DefaultDatafileHandler.java:225)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.sdk.OptimizelyManager.isDatafileCached(OptimizelyManager.java:412)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.sdk.OptimizelyManager.getDatafile(OptimizelyManager.java:281)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.sdk.OptimizelyManager$2.onDatafileLoaded(OptimizelyManager.java:343)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded(DefaultDatafileHandler.java:78)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify(DatafileLoader.java:81)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000(DatafileLoader.java:35)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute(DatafileLoader.java:134)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute(DatafileLoader.java:85)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.os.AsyncTask.finish(AsyncTask.java:755)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.os.AsyncTask.access$900(AsyncTask.java:192)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:772)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.os.Handler.dispatchMessage(Handler.java:107)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.os.Looper.loop(Looper.java:214)
01-21 19:15:06.024 18420 18420 D StrictMode: at android.app.ActivityThread.main(ActivityThread.java:7356)
01-21 19:15:06.024 18420 18420 D StrictMode: at java.lang.reflect.Method.invoke(Native Method)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
01-21 19:15:06.024 18420 18420 D StrictMode: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
01-21 19:15:06.024 18420 18690 I UserProfileCache: Loaded user profile cache from disk.```

Proguard rules are too broad

I was just upgrading SDK we use to version 1.4.0 and saw that you have this entryFix proguard rules.

I was curious what you did and looked at the consumerProguardFiles and saw the entry -keep class com.optimizely.ab.** { *; }

This is unacceptable for us. And it is really irresponsible. Just for this reason, we are not upgrading the SDK for now.

Until now, we have only the following in our proguard file and works fine for us. -keep interface com.fasterxml.jackson.annotation.** {*;}

As a library developer, you should be very careful about this. You should only keep methods (and maybe classes) where you use reflection. Other than that, proguard is intelligent enough to do the right thing.

Unfortunately Android build tools does not allow use to ignore what libraries put in consumerProguardFiles. This means that our method count will just increase for no reason when we upgrade the library to version 1.4.0

SUPPLICANT_CONNECTION_CHANGE_ACTION is deprecated in API 28

Hello,

following the SDK docs I have noticed that when registering a broadcast receive to retrieve connection change events we're required to use IntentFilter(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION).

It looks like now that SUPPLICANT_CONNECTION_CHANGE_ACTION has been deprecated since API 28, and by the looks of it there's no quick way around this. What would you suggest here?

Thanks

NoClassDefFoundError on API 17 (Basically a duplicate of issue 124)

I'm running into the same NoClassDefFoundError experienced in issue #124 . The app isn't crashing, but my variations are coming back as null, and I'm never able to assign the optimizelyClient member variable to the OptimizelyClient object passed in OptimizelStartListener.onStart.

I'm able to consistently reproduce on our devices running API 17.

09-06 16:08:52.557 3661-3661/com.clover.apps E/Optly.androidSdk: Unable to build OptimizelyClient instance java.lang.NoClassDefFoundError: javax/annotation/Nonnull at java.lang.reflect.Method.getParameterAnnotations(Native Method) at java.lang.reflect.Constructor.getParameterAnnotations(Constructor.java:229) at com.fasterxml.jackson.databind.introspect.AnnotatedClass._constructConstructor(AnnotatedClass.java:754) at com.fasterxml.jackson.databind.introspect.AnnotatedClass.resolveCreators(AnnotatedClass.java:335) at com.fasterxml.jackson.databind.introspect.AnnotatedClass.getConstructors(AnnotatedClass.java:227) at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addCreators(POJOPropertiesCollector.java:414) at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collect(POJOPropertiesCollector.java:234) at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.collectProperties(BasicClassIntrospector.java:142) at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forDeserialization(BasicClassIntrospector.java:81) at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forDeserialization(BasicClassIntrospector.java:11) at com.fasterxml.jackson.databind.DeserializationConfig.introspect(DeserializationConfig.java:507) at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:329) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:267) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:247) at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:146) at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:305) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:151) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:23) at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:328) at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:2990) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2884) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2041) at com.optimizely.ab.config.parser.ProjectConfigJacksonDeserializer.deserialize(ProjectConfigJacksonDeserializer.java:56) at com.optimizely.ab.config.parser.ProjectConfigJacksonDeserializer.deserialize(ProjectConfigJacksonDeserializer.java:38) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2034) at com.optimizely.ab.config.parser.JacksonConfigParser.parseProjectConfig(JacksonConfigParser.java:39) at com.optimizely.ab.Optimizely.getProjectConfig(Optimizely.java:496) at com.optimizely.ab.Optimizely.access$000(Optimizely.java:83) at com.optimizely.ab.Optimizely$Builder.build(Optimizely.java:760) at com.optimizely.ab.android.sdk.OptimizelyManager.buildOptimizely(OptimizelyManager.java:390) at com.optimizely.ab.android.sdk.OptimizelyManager.injectOptimizely(OptimizelyManager.java:339) at com.optimizely.ab.android.sdk.OptimizelyManager$1.onDatafileLoaded(OptimizelyManager.java:230) at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded(DefaultDatafileHandler.java:80) at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify(DatafileLoader.java:86) at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000(DatafileLoader.java:34) at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute(DatafileLoader.java:167) at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute(DatafileLoader.java:118) at android.os.AsyncTask.finish(AsyncTas

IncompatibleClassChangeError - Couldn't find com.fasterxml.jackson.annotation.JsonIgnoreProperties.ignoreUnknown

Hello,

We encounter an issue using your SDK on API 17. Here is the stacktrace:

Reason
Couldn't find com.fasterxml.jackson.annotation.JsonIgnoreProperties.ignoreUnknown

Crashed Thread

  0 java.lang.IncompatibleClassChangeError: Couldn't find com.fasterxml.jackson.annotation.JsonIgnoreProperties.ignoreUnknown
  1 at libcore.reflect.AnnotationAccess.toAnnotationInstance(AnnotationAccess.java:659)
  2 at libcore.reflect.AnnotationAccess.annotationSetToAnnotations(AnnotationAccess.java:633)
  3 at libcore.reflect.AnnotationAccess.getDeclaredAnnotations(AnnotationAccess.java:161)
  4 at java.lang.Class.getDeclaredAnnotations(Class.java:891)
! 5 at aad.q(aad.java:954)
  6 at so.o(so.java:409)
  7 at so.(so.java:158)
  8 at so.a(so.java:203)
  9 at sy.a(sy.java:190)
  10 at sy.a(sy.java:111)
  11 at sy.f(sy.java:16)
  12 at com.fasterxml.jackson.databind.f.b(f.java:900)
  13 at pk.f(pk.java:324)
  14 at pk.e(pk.java:264)
  15 at pk.d(pk.java:244)
  16 at pk.a(pk.java:142)
  17 at com.fasterxml.jackson.databind.g.b(g.java:476)
  18 at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3899)
  19 at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3794)
  20 at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
  21 at com.optimizely.ab.config.parser.JacksonConfigParser.parseProjectConfig(JacksonConfigParser.java:39)
  22 at com.optimizely.ab.Optimizely.getProjectConfig(Optimizely.java:468)
  23 at com.optimizely.ab.Optimizely.access$000(Optimizely.java:87)
  24 at com.optimizely.ab.Optimizely$Builder.build(Optimizely.java:714)
  25 at com.optimizely.ab.android.sdk.OptimizelyManager.buildOptimizely(OptimizelyManager.java:395)
  26 at com.optimizely.ab.android.sdk.OptimizelyManager.access$400(OptimizelyManager.java:62)
  27 at com.optimizely.ab.android.sdk.OptimizelyManager$1.onPostExecute(OptimizelyManager.java:364)
  28 at com.optimizely.ab.android.sdk.OptimizelyManager$1.onPostExecute(OptimizelyManager.java:350)
  29 at android.os.AsyncTask.finish(AsyncTask.java:632)
  30 at android.os.AsyncTask.access$600(AsyncTask.java:177)
  31 at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
  32 at android.os.Handler.dispatchMessage(Handler.java:102)
  33 at android.os.Looper.loop(Looper.java:145)
  34 at android.app.ActivityThread.main(ActivityThread.java:5951)
  35 at java.lang.reflect.Method.invoke(Method.java)
  36 at java.lang.reflect.Method.invoke(Method.java:372)
  37 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
  38 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)

java.lang.OutOfMemoryError

Hi,

We found an issue on our PRODUCTION app


java.lang.OutOfMemoryError: Could not allocate JNI Env
--
  | 1 | at java.lang.Thread.nativeCreate(Thread.java)
  | 2 | at java.lang.Thread.start(Thread.java:1063)
  | 3 | at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:921)
  | 4 | at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1328)
  | 5 | at java.util.concurrent.Executors$DelegatedExecutorService.execute(Executors.java:584)
  | 6 | at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:620)
! | 7 | at com.optimizely.ab.android.datafile_handler.DatafileLoader.getDatafile(DatafileLoader.java:77)
  | 8 | at com.optimizely.ab.android.datafile_handler.DatafileService.onStartCommand(DatafileService.java:73)


Thanks in advance for solving this issue

Bests

SDK isn't updating with new datafile

I think the issue is 2-fold

  1. I added a new variation onto my experiment, and verified the datafile is updated in the CDN by hitting the url on my browser. DatafileClient reports that "Data file has not been modified on the cdn"

  2. In debug mode, I've overridden urlConnection.setIfModifiedSince(lastModified) to have a value of 1 day prior. A new datafile was downloaded, but when I accessed

optimizelyClient?.projectConfig?.experimentKeyMapping.get(expKey).variations

The client itself isn't updated to use the new file.

A restart of the application seems to have refreshed the file.

ProGuard rules should be provided by the library

Having to add ProGuard rules on the application side is cumbersome and can break with library updates.

Please provide the rules via the library's build.gradle so they are automatically applied when the library is used in a proguarded app:

android { defaultConfig { consumerProguardFiles 'proguard-rules.pro' } }

Crash on API 17

Hello,

We encounter an issue using your SDK on API 17. Here is the stacktrace :

07-17 09:52:46.815 18144-18144 E/AndroidRuntime: FATAL EXCEPTION: main
                                                                        java.lang.NoClassDefFoundError: javax/annotation/Nonnull
                                                                            at java.lang.reflect.Method.getParameterAnnotations(Native Method)
                                                                            at java.lang.reflect.Constructor.getParameterAnnotations(Constructor.java:229)
                                                                            at com.fasterxml.jackson.databind.util.ClassUtil$Ctor.getParameterAnnotations(ClassUtil.java:1149)
                                                                            at com.fasterxml.jackson.databind.introspect.AnnotatedClass._constructNonDefaultConstructor(AnnotatedClass.java:925)
                                                                            at com.fasterxml.jackson.databind.introspect.AnnotatedClass.resolveCreators(AnnotatedClass.java:462)
                                                                            at com.fasterxml.jackson.databind.introspect.AnnotatedClass.getConstructors(AnnotatedClass.java:324)
                                                                            at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addCreators(POJOPropertiesCollector.java:446)
                                                                            at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:307)
                                                                            at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getPropertyMap(POJOPropertiesCollector.java:267)
                                                                            at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getProperties(POJOPropertiesCollector.java:159)
                                                                            at com.fasterxml.jackson.databind.introspect.BasicBeanDescription._properties(BasicBeanDescription.java:144)
                                                                            at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findProperties(BasicBeanDescription.java:219)
                                                                            at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._findCreatorsFromProperties(BasicDeserializerFactory.java:330)
                                                                            at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:314)
                                                                            at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:253)
                                                                            at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:219)
                                                                            at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:141)
                                                                            at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:403)
                                                                            at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
                                                                            at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
                                                                            at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
                                                                            at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
                                                                            at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:443)
                                                                            at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:206)
                                                                            at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:26)
                                                                            at com.fasterxml.jackson.databind.DeserializationContext.handleSecondaryContextualization(DeserializationContext.java:681)
                                                                            at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:481)
                                                                            at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3899)
                                                                            at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3794)
                                                                            at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2861)
                                                                            at com.optimizely.ab.config.parser.ProjectConfigJacksonDeserializer.deserialize(ProjectConfigJacksonDeserializer.java:56)
                                                                            at com.optimizely.ab.config.parser.ProjectConfigJacksonDeserializer.deserialize(ProjectConfigJacksonDeserializer.java:38)
                                                                            at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
                                                                            at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
                                                                            at com.optimizely.ab.config.parser.JacksonConfigParser.parseProjectConfig(JacksonConfigParser.java:39)
                                                                            at com.optimizely.ab.Optimizely.getProjectConfig(Optimizely.java:468)
                                                                            at com.optimizely.ab.Optimizely.access$000(Optimizely.java:87)
                                                                        	at com.optimizely.

How can we fix it ?

SDK crashing app...

Keep getting crash reports from our Crashlytics regarding error below. Please advise. Could not reproduce the issue in our test. Error seems to happen on Android O (8.0, 8.1).

Fatal Exception: java.lang.RuntimeException: Unable to start receiver com.optimizely.OptlyIoReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.optimizely.io.action.SCHEDULE cmp=com.thrivemarket.app/com.optimizely.OptlyIoService }: app is in background uid UidRecord{eef43ca u0a173 RCVR idle procs:1 seq(0,0,0)}
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3259)
at android.app.ActivityThread.-wrap17(Unknown Source)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by java.lang.IllegalStateException: Not allowed to start service Intent { act=com.optimizely.io.action.SCHEDULE cmp=com.thrivemarket.app/com.optimizely.OptlyIoService }: app is in background uid UidRecord{eef43ca u0a173 RCVR idle procs:1 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
at android.app.ContextImpl.startService(ContextImpl.java:1461)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)
at com.optimizely.OptlyIoReceiver.onReceive(OptlyIoReceiver.java:22)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3252)
at android.app.ActivityThread.-wrap17(Unknown Source)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

Remote file not overriding local file when calling initializing

Calling:

public OptimizelyClient initialize(@NonNull Context context, @NonNull String datafile) {

does not override the datafile after the datafile download is finished because no listener is being passed to the fileHandler:

public OptimizelyClient initialize(@NonNull Context context, @NonNull String datafile) {
    if (!isAndroidVersionSupported()) {
        return optimizelyClient;
    }

    try {
        optimizelyClient = buildOptimizely(context, datafile);
    } catch (ConfigParseException e) {
        logger.error("Unable to parse compiled data file", e);
    } catch (Exception e) {
        logger.error("Unable to build OptimizelyClient instance", e);
    } catch (Error e) {
        logger.error("Unable to build OptimizelyClient instance", e);
    }

    datafileHandler.downloadDatafile(context, projectId, null);

    return optimizelyClient;
}

The listener should instead be passed in:

@Nullable private OptimizelyStartListener optimizelyStartListener;

Is there any workaround we can use to make the remote file override the local file?

Thanks

Android O boot receiver support

Library version: 1.3.1, 1.4.0-alpha
Steps to reproduce:

  • Prepare a test app for API level 26
  • For 1.4.0: add the DatafileRescheduler, EventRescheduler receivers to the test app manifest
  • Install the app
  • Reboot the device

Expected: The boot receivers start without problem.
Actual: The receivers crash

Log:
java.lang.RuntimeException: Unable to start receiver com.optimizely.ab.android.event_handler.EventRescheduler: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=de.mobile.android.app.debug/com.optimizely.ab.android.event_handler.EventIntentService }: app is in background uid UidRecord{afa610a u0a140 TRNB idle procs:1 seq(0,0,0)} at android.app.ActivityThread.handleReceiver(ActivityThread.java:3259) at android.app.ActivityThread.-wrap17(Unknown Source:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6541) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)

DataFileLoader - OutOfMemory

Hi,

We are facing an issue in PRODUCTION with your SDK.

OS Version : Android 6.0

Here is the stacktrace :

DataFileLoader.java line 71

java.lang.OutOfMemoryError: Could not allocate JNI Env

  | 1 | at java.lang.Thread.nativeCreate(Thread.java)
  | 2 | at java.lang.Thread.start(Thread.java:1063)
  | 3 | at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:921)
  | 4 | at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1328)
  | 5 | at java.util.concurrent.Executors$DelegatedExecutorService.execute(Executors.java:584)
  | 6 | at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:607)
! | 7 | at com.optimizely.ab.android.sdk.DataFileLoader.getDataFile(DataFileLoader.java:71)
  | 8 | at com.optimizely.ab.android.sdk.DataFileService.onStartCommand(DataFileService.java:73)
  | 9 | at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3202)
  | 10 | at android.app.ActivityThread.-wrap17(ActivityThread.java)
  | 11 | at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1600)
  | 12 | at android.os.Handler.dispatchMessage(Handler.java:111)
  | 13 | at android.os.Looper.loop(Looper.java:207)
  | 14 | at android.app.ActivityThread.main(ActivityThread.java:5763)
  | 15 | at java.lang.reflect.Method.invoke(Method.java)
  | 16 | at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
  | 17 | at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)

Caused by java.lang.OutOfMemoryError Failed to allocate a 7408792 byte allocation with 1931064 free bytes and 1869KB until OOM, max allowed footprint 134217728, growth limit 134217728

SDK Version: 3.5.1

Screen Shot 2020-06-04 at 11 42 11

Fatal Exception: java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:353)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)

Caused by java.lang.OutOfMemoryError: Failed to allocate a 7408792 byte allocation with 1931064 free bytes and 1869KB until OOM, max allowed footprint 134217728, growth limit 134217728
at java.util.Arrays.copyOf(Arrays.java:3164)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
at java.io.OutputStream.write(OutputStream.java:75)
at sun.security.util.DerValue.encode(DerValue.java:425)
at sun.security.util.DerValue.toByteArray(DerValue.java:861)
at sun.security.x509.X509CRLImpl.parse(X509CRLImpl.java:1094)
at sun.security.x509.X509CRLImpl.(X509CRLImpl.java:146)
at sun.security.provider.X509Factory.intern(X509Factory.java:206)
at sun.security.x509.X509CRLImpl.toImpl(X509CRLImpl.java:1235)
at sun.security.provider.certpath.AlgorithmChecker.check(AlgorithmChecker.java:396)
at sun.security.provider.certpath.DistributionPointFetcher.verifyCRL(DistributionPointFetcher.java:667)
at sun.security.provider.certpath.DistributionPointFetcher.getCRLs(DistributionPointFetcher.java:209)
at sun.security.provider.certpath.DistributionPointFetcher.getCRLs(DistributionPointFetcher.java:121)
at sun.security.provider.certpath.RevocationChecker.checkCRLs(RevocationChecker.java:552)
at sun.security.provider.certpath.RevocationChecker.checkCRLs(RevocationChecker.java:465)
at sun.security.provider.certpath.RevocationChecker.check(RevocationChecker.java:394)
at sun.security.provider.certpath.RevocationChecker.check(RevocationChecker.java:337)
at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:125)
at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:222)
at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:140)
at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:79)
at java.security.cert.CertPathValidator.validate(CertPathValidator.java:301)
at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:703)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:539)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:560)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:605)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:495)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:418)
at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:339)
at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)
at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:197)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.verifyCertificateChain(ConscryptFileDescriptorSocket.java:399)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(NativeCrypto.java)
at com.android.org.conscrypt.SslWrapper.doHandshake(SslWrapper.java:374)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:217)
at com.android.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:283)
at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:156)
at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:119)
at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:184)
at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:126)
at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:95)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:314)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:257)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:478)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:140)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:89)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java)
at com.google.firebase.perf.network.zzd.connect(zzd.java:13)
at com.google.firebase.perf.network.zze.connect(zze.java:5)
at com.optimizely.ab.android.datafile_handler.DatafileClient$1.execute(DatafileClient.java:82)
at com.optimizely.ab.android.datafile_handler.DatafileClient$1.execute(DatafileClient.java:66)
at com.optimizely.ab.android.shared.Client.execute(Client.java:143)
at com.optimizely.ab.android.datafile_handler.DatafileClient.request(DatafileClient.java:111)
at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.doInBackground(DatafileLoader.java:122)
at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.doInBackground(DatafileLoader.java:86)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)

ConcurrentModificationException in Optimizely.filterAttributes()

We are seeing a low-frequency crash originating within the Optimizely Android SDK (v2.1.0). This is observed when calling OptimizelyClient.isFeatureEnabled():

Caused by java.util.ConcurrentModificationException
       at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:411)
       at java.util.LinkedHashMap$EntryIterator.next(LinkedHashMap.java:430)
       at java.util.LinkedHashMap$EntryIterator.next(LinkedHashMap.java:430)
       at com.optimizely.ab.Optimizely.filterAttributes(Optimizely.java:776)
       at com.optimizely.ab.Optimizely.isFeatureEnabled(Optimizely.java:362)
       at com.optimizely.ab.android.sdk.OptimizelyClient.isFeatureEnabled(OptimizelyClient.java:360)
...

As a temporary workaround we have added a try...catch around calls to isFeatureEnabled().

We've seen around 20 crashes in the last couple of weeks since we integrated the Optimizely SDK.

1.5.0 Proguard issue with payload objects getting sent correctly to optimizely backend

With the latest release 1.5.0 it says that the proguard file is included in the sdk.
We noticed our activate and track calls not showing up in the optimizely dashboard.
Then we saw that the payload was being obfuscated like so:

{
  "c": "UserID",
  ...
  "k": false,
  "m": "22",
  "a": "android-sdk",
  "b": "1.5.0"
}

So then looking through your src we added the following proguard rules to get it to work end to end.

Proguard Rules:

# Keep Payload classes that get sent to Optimizely's backend
-keep class com.optimizely.ab.event.internal.payload.** { *; }

# Keep these for logging purposes
-keep class com.optimizely.ab.bucketing.DecisionService { *; }
-keep class com.optimizely.ab.Optimizely { *; }

Just wanted to ask if this is because of our Proguard config or could it be a change to the libraries proguard config?

Unable to parse null datafile.

Hi,

We were previously on an older SDK 2.1.0 and made the jump to 3.0.0 and started noticing these kinds of error show up in our logs. Here is an example of a stacktrace. It's not reproducible locally, and only affects a small percentage of our users. Not sure what's causing it, but we believe the issue is not related to internet connectivity.

Caused by com.optimizely.ab.config.parser.ConfigParseException: Unable to parse null datafile.
       at com.optimizely.ab.config.ProjectConfig$Builder.build + 605(ProjectConfig.java:605)
       at com.optimizely.ab.Optimizely.initialize + 119(Optimizely.java:119)
       at com.optimizely.ab.Optimizely$Builder.build + 941(Optimizely.java:941)
       at com.optimizely.ab.android.sdk.OptimizelyManager.buildOptimizely + 501(OptimizelyManager.java:501)
       at com.optimizely.ab.android.sdk.OptimizelyManager.injectOptimizely + 445(OptimizelyManager.java:445)
       at com.optimizely.ab.android.sdk.OptimizelyManager$2.onDatafileLoaded + 343(OptimizelyManager.java:343)
       at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded + 78(DefaultDatafileHandler.java:78)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify + 81(DatafileLoader.java:81)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000 + 35(DatafileLoader.java:35)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 134(DatafileLoader.java:134)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 85(DatafileLoader.java:85)
       at android.os.AsyncTask.finish + 695(AsyncTask.java:695)
       at android.os.AsyncTask.-wrap1(AsyncTask.java)
       at android.os.AsyncTask$InternalHandler.handleMessage + 712(AsyncTask.java:712)
       at android.os.Handler.dispatchMessage + 106(Handler.java:106)
       at android.os.Looper.loop + 164(Looper.java:164)
       at android.app.ActivityThread.main + 6626(ActivityThread.java:6626)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run + 438(RuntimeInit.java:438)
       at com.android.internal.os.ZygoteInit.main + 811(ZygoteInit.java:811)

Unable to create/stop/unbind ScheduledJobService

Library version: 3.5.0
Android API: 29
Code for Android SDK initialization:
try {
OptimizelyManager manager = OptimizelyManager.builder()
.withSDKKey(BuildConfig.ABTEST_KEY)
.withDatafileDownloadInterval(TimeUnit.MINUTES.toSeconds(60))
.build(application);
mSyncOptyClient = manager.initialize(application, R.raw.datafile, true, true);
} catch (Exception e) {
e.printStackTrace();
}

Fatal Exception: java.lang.RuntimeException: Unable to create service com.optimizely.ab.android.shared.ScheduledJobService: java.lang.RuntimeException: android.os.DeadSystemException
at android.app.ActivityThread.handleCreateService(ActivityThread.java:4081)
at android.app.ActivityThread.access$1500(ActivityThread.java:226)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1914)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7615)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)

=====
Fatal Exception: java.lang.RuntimeException: Unable to stop service com.optimizely.ab.android.shared.ScheduledJobService@711222b: java.lang.RuntimeException: android.os.DeadSystemException
at android.app.ActivityThread.handleStopService(ActivityThread.java:4252)
at android.app.ActivityThread.access$1900(ActivityThread.java:226)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1935)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7615)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)

======
Fatal Exception: java.lang.RuntimeException: Unable to unbind to service com.optimizely.ab.android.shared.ScheduledJobService@b07dc23 with Intent { cmp=com.verifly.smb/com.optimizely.ab.android.shared.ScheduledJobService }: java.lang.RuntimeException: android.os.DeadSystemException
at android.app.ActivityThread.handleUnbindService(ActivityThread.java:4139)
at android.app.ActivityThread.access$1700(ActivityThread.java:226)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1924)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7615)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)

SecurityException: Caller no longer running, last stopped v2

This is a continuation of this issue: #166

We're seeing the same crash and stacktrace after implementing the intent filter in code.

Stacktrace:

Fatal Exception: java.lang.RuntimeException: An error occurred while executing doInBackground()
       at android.os.AsyncTask$3.done(AsyncTask.java:353)
       at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
       at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
       at java.util.concurrent.FutureTask.run(FutureTask.java:271)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
       at java.lang.Thread.run(Thread.java:764)
Caused by java.lang.SecurityException: Caller no longer running, last stopped +6s841ms because: timed out while starting
       at android.os.Parcel.readException(Parcel.java:1958)
       at android.os.Parcel.readException(Parcel.java:1904)
       at android.app.job.IJobCallback$Stub$Proxy.dequeueWork(IJobCallback.java:191)
       at android.app.job.JobParameters.dequeueWork(JobParameters.java:196)
       at com.optimizely.ab.android.shared.JobWorkService$CommandProcessor.doInBackground(JobWorkService.java:73)
       at com.optimizely.ab.android.shared.JobWorkService$CommandProcessor.doInBackground(JobWorkService.java:56)
       at android.os.AsyncTask$2.call(AsyncTask.java:333)
       at java.util.concurrent.FutureTask.run(FutureTask.java:266)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
       at java.lang.Thread.run(Thread.java:764)

Snippet of our intent-filter in our Application.onCreate() method:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      EventRescheduler rescheduler = new EventRescheduler();
      registerReceiver(rescheduler, new IntentFilter(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION));
    }

99% of our crashes are happening on Android O, with a large number of those users on Samsung devices (but this doesn't seem to be a Samsung-only issue, there are also a fair number of HTC, LG, Google, etc. devices)

Proguard rules not complete

Running version 1.3.0 with the proguard rules from documentation:

# Optimizely
-keep class com.optimizely.ab.** { *; }

# Gson
-keepnames class com.google.gson.Gson

# Safely ignore warnings about other libraries since we are using Gson
-dontwarn com.fasterxml.jackson.**
-dontwarn org.json.**

# Annotations
-dontwarn javax.annotation.**

# Findbugs
-dontwarn edu.umd.cs.findbugs.annotations.SuppressFBWarnings

# slf4j
-dontwarn org.slf4j.**

App crashes with missing class exception for Optimizely dependent class:
com.noveogroup.android.log.Utils

Perhaps proguard rules should be updated to address this?

I added rule to work around:

-keep class com.noveogroup.android.log.** { *; }

Datafile download fails on API 16

Hi.
When downloading a file with the default DataFileClient, urlConnection.connect(); fails with an ssl error, here is the full stacktrace.

error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x671c3d74:0x00000000)
	at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:448)
	at com.android.okhttp.Connection.upgradeToTls(Connection.java:146)
	at com.android.okhttp.Connection.connect(Connection.java:107)
	at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
	at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
	at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
	at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345)
	at com.android.okhttp.internal.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:89)
	at com.android.okhttp.internal.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:161)
	at com.optimizely.ab.android.datafile_handler.DatafileClient$1.execute(DatafileClient.java:84)
	at com.optimizely.ab.android.datafile_handler.DatafileClient$1.execute(DatafileClient.java:68)
	at com.optimizely.ab.android.shared.Client.execute(Client.java:143)
	at com.optimizely.ab.android.datafile_handler.DatafileClient.request(DatafileClient.java:113)
	at com.optimizely.ab.android.datafile_handler.DatafileLoader$1.run(DatafileLoader.java:100)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
	at java.lang.Thread.run(Thread.java:841)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x68a5f008: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x671c3d74:0x00000000)
	at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
	at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:405)

I think this is happening because android <20 don't support TLS1.2+ by default.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.