diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 634799ada..8bdef4f27 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug report about: Create a report to help us improve title: "BUG : " labels: bug, open source -assignees: itsdebs +assignees: "@rudderlabs/sdk-android" --- **Describe the bug** diff --git a/.github/workflows/draft_new_release-v2.yml b/.github/workflows/draft_new_release-v2.yml index f78e671e6..60b8fbea7 100644 --- a/.github/workflows/draft_new_release-v2.yml +++ b/.github/workflows/draft_new_release-v2.yml @@ -90,4 +90,4 @@ jobs: github_token: ${{ secrets.PAT }} pr_title: 'chore(release): pulling ${{ steps.create-release.outputs.branch_name }} into master-v2' pr_body: ':crown: *An automated PR*' - pr_reviewer: 'itsdebs' \ No newline at end of file + pr_reviewer: '@rudderlabs/sdk-android' \ No newline at end of file diff --git a/.github/workflows/draft_new_release.yml b/.github/workflows/draft_new_release.yml index 9c3f93f75..5c5abd3e5 100644 --- a/.github/workflows/draft_new_release.yml +++ b/.github/workflows/draft_new_release.yml @@ -82,4 +82,4 @@ jobs: github_token: ${{ secrets.PAT }} pr_title: "chore(release): pulling ${{ steps.create-release.outputs.branch_name }} into ${{ steps.create-release.outputs.main_branch }}" pr_body: ":crown: *An automated PR*" - pr_reviewer: 'itsdebs' + pr_reviewer: '@rudderlabs/sdk-android' diff --git a/.github/workflows/publish-new-github-release-v2.yml b/.github/workflows/publish-new-github-release-v2.yml index 03672322c..22004a710 100644 --- a/.github/workflows/publish-new-github-release-v2.yml +++ b/.github/workflows/publish-new-github-release-v2.yml @@ -87,7 +87,7 @@ jobs: github_token: ${{ secrets.PAT }} pr_title: 'chore(release): pulling master-v2 into develop-v2 post release v${{ steps.extract-version.outputs.release_version }}' pr_body: ':crown: *An automated PR*' - pr_reviewer: 'itsdebs' + pr_reviewer: '@rudderlabs/sdk-android' - name: Delete hotfix release branch v2 uses: koj-co/delete-merged-action@master diff --git a/.github/workflows/publish-new-github-release.yml b/.github/workflows/publish-new-github-release.yml index 5472ed312..cdfa83853 100644 --- a/.github/workflows/publish-new-github-release.yml +++ b/.github/workflows/publish-new-github-release.yml @@ -49,7 +49,7 @@ jobs: github_token: ${{ secrets.PAT }} pr_title: "chore(release): pulling main into develop post release v${{ steps.extract-version.outputs.release_version }}" pr_body: ':crown: *An automated PR*' - pr_reviewer: 'itsdebs' + pr_reviewer: '@rudderlabs/sdk-android' - name: Create pull request into develop-mpx uses: repo-sync/pull-request@v2 if: (startsWith(github.event.pull_request.head.ref, 'release-mpx/') || startsWith(github.event.pull_request.head.ref, 'hotfix-release-mpx/')) @@ -59,7 +59,7 @@ jobs: github_token: ${{ secrets.PAT }} pr_title: "chore(release): pulling main into develop post release v${{ steps.extract-version.outputs.release_version }}" pr_body: ':crown: *An automated PR*' - pr_reviewer: 'itsdebs' + pr_reviewer: '@rudderlabs/sdk-android' - name: Delete hotfix release branch uses: koj-co/delete-merged-action@master diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dfe51f4e..da123d073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.23.2](https://github.com/rudderlabs/rudder-sdk-android/compare/v1.23.1...v1.23.2) (2024-06-25) + + +### Bug Fixes + +* database crash issue ([#449](https://github.com/rudderlabs/rudder-sdk-android/issues/449)) ([9251b68](https://github.com/rudderlabs/rudder-sdk-android/commit/9251b6882162d61f6300f16d543bec9864a397cc)) + ### [1.23.1](https://github.com/rudderlabs/rudder-sdk-android/compare/v1.23.0...v1.23.1) (2024-05-28) diff --git a/CODEOWNERS b/CODEOWNERS index 394c12b4e..ce19134bc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @pallabmaiti @itsdebs @bardisg @ChryssaAliferi +* @rudderlabs/sdk-android diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c3fefcab..3908fa013 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,5 +40,5 @@ For any questions, concerns, or queries, you can start by asking a question on o <!----variables----> [issue]: https://github.com/rudderlabs/rudder-sdk-android/issues/new -[CLA]: https://rudderlabs.wufoo.com/forms/rudderlabs-contributor-license-agreement +[CLA]: https://forms.gle/845JRGVZaC6kPZy68 [Slack]: https://rudderstack.com/join-rudderstack-slack-community/ diff --git a/README.md b/README.md index a6eb2dd5b..0729ce4bc 100644 --- a/README.md +++ b/README.md @@ -117,60 +117,6 @@ The variable `it` contains the intialized nativeSDK object. [**Registering Lotame's onSync callback**](https://github.com/rudderlabs/rudder-integration-lotame-android#register-your-onsync-callback) shows one more example of registering a callback using `onIntegrationReady`. -#### Do I need to add anything to my ProGuard rules? - -If you are using Proguard full mode to optimize your app, add the following lines to your Android ProGuard rules: - -```java -# Reporter Module --keep class com.rudderstack.android.ruddermetricsreporterandroid.models.LabelEntity { *; } --keep class com.rudderstack.android.ruddermetricsreporterandroid.models.MetricEntity { *; } --keep class com.rudderstack.android.ruddermetricsreporterandroid.models.ErrorEntity { *; } - -# Required for the usage off TypeToken class in Utils.converToMap, Utils.convertToList --keep class com.google.gson.reflect.TypeToken { *; } --keep class * extends com.google.gson.reflect.TypeToken - -# Required for the serialization of SourceConfig once it is downloaded. --keep class com.google.gson.internal.LinkedTreeMap { *; } --keep class * implements java.io.Serializable { *; } --keep class com.rudderstack.rudderjsonadapter.RudderTypeAdapter { *; } --keep class * extends com.rudderstack.rudderjsonadapter.RudderTypeAdapter - -# Required to ensure the DefaultPersistenceProviderFactory is not removed by Proguard -# and works as expected even when the customer is not using encryption feature. --dontwarn net.sqlcipher.Cursor --dontwarn net.sqlcipher.database.SQLiteDatabase$CursorFactory --dontwarn net.sqlcipher.database.SQLiteDatabase --dontwarn net.sqlcipher.database.SQLiteOpenHelper --keep class com.rudderstack.android.sdk.core.persistence.DefaultPersistenceProviderFactory { *; } - -# Required for the usage of annotations across reporter and web modules --dontwarn com.fasterxml.jackson.annotation.JsonIgnore --dontwarn com.squareup.moshi.Json --dontwarn com.fasterxml.jackson.annotation.JsonProperty - -# Required for Device Mode Transformations --keep class com.rudderstack.android.sdk.core.TransformationResponse { *; } --keep class com.rudderstack.android.sdk.core.TransformationResponseDeserializer { *; } - -# to make sure that serialized name annotations are not removed by the Proguard full mode. --keepclassmembers,allowobfuscation class * { - @com.google.gson.annotations.SerializedName <fields>; -} - -# Required for proper serialization of the custom traits and custom context --keep class * implements com.google.gson.JsonSerializer { *; } - -# to make sure that the customContextMap, custom traits are sent in the proper format --keepclassmembers class com.rudderstack.android.sdk.core.RudderContext { java.util.Map customContextMap; } --keepclassmembers class com.rudderstack.android.sdk.core.RudderTraits { java.util.Map extras; } - -# Required for DBEncryption feature using SQLCipher --keep class net.sqlcipher.** { *; } --keep class net.sqlcipher.database.* { *; } -``` - ## Contribute We would love to see you contribute to this project. Get more information on how to contribute [**here**](./CONTRIBUTING.md). diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/DBPersistentManager.java b/core/src/main/java/com/rudderstack/android/sdk/core/DBPersistentManager.java index 751b88c3f..98982e6d2 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/DBPersistentManager.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/DBPersistentManager.java @@ -16,7 +16,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.rudderstack.android.ruddermetricsreporterandroid.RudderReporter; import com.rudderstack.android.sdk.core.persistence.DefaultPersistenceProviderFactory; import com.rudderstack.android.sdk.core.persistence.Persistence; import com.rudderstack.android.sdk.core.persistence.PersistenceProvider; @@ -26,11 +25,9 @@ import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Queue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; @@ -43,7 +40,6 @@ class DBPersistentManager/* extends SQLiteOpenHelper*/ { public static final String DBPERSISTENT_MANAGER_CHECK_FOR_MIGRATIONS_TAG = "DBPersistentManager: checkForMigrations: "; - public static final Object QUEUE_LOCK = new Object(); public static final ExecutorService executor = Executors.newSingleThreadExecutor(); static final String EVENT = "EVENT"; @@ -100,8 +96,7 @@ class DBPersistentManager/* extends SQLiteOpenHelper*/ { //synchronizing database access private static final Object DB_LOCK = new Object(); private static DBPersistentManager instance; - final Queue<Message> queue = new LinkedList<>(); - DBInsertionHandlerThread dbInsertionHandlerThread; + private DBInsertionHandlerThread dbInsertionHandlerThread; private Persistence persistence; private DBPersistentManager(Application application, @@ -190,18 +185,18 @@ private void createSchema(String eventSchemaSQL) { /* - * Receives message from Repository, and passes it to the Handler thread if it exists, else adds it to a queue for replay - * once Handler thread is initialized. + * Receives message from Repository, and passes it to the Handler thread if it exists else creates a new Handler thread. + * This method should be called in a synchronized way. * */ void saveEvent(String messageJson, EventInsertionCallback callback) { Message msg = createOsMessageFromJson(messageJson, callback); - synchronized (DBPersistentManager.QUEUE_LOCK) { - if (dbInsertionHandlerThread == null) { - queue.add(msg); - return; - } - addMessageToHandlerThread(msg); + if (dbInsertionHandlerThread == null) { + // Need to perform db operations on a separate thread to support strict mode. + // saveEvent method is already called on an executor thread, so we can directly call DBInsertionHandlerThread + dbInsertionHandlerThread = new DBInsertionHandlerThread("db_insertion_thread", persistence); + dbInsertionHandlerThread.start(); } + dbInsertionHandlerThread.addMessage(msg); } private Message createOsMessageFromJson(String messageJson, EventInsertionCallback callback) { @@ -213,13 +208,6 @@ private Message createOsMessageFromJson(String messageJson, EventInsertionCallba return msg; } - /* - Passes the input message to the Handler thread. - */ - void addMessageToHandlerThread(Message msg) { - dbInsertionHandlerThread.addMessage(msg); - } - @VisibleForTesting void saveEventSync(String messageJson) { ContentValues insertValues = new ContentValues(); @@ -456,32 +444,6 @@ private int getCountForCommand(String sql) { return count; } - /* - Starts the Handler thread, which is responsible for storing the messages in its internal queue, and - save them to the sqlite db sequentially. - */ - void startHandlerThread() { - Runnable runnable = () -> { - try { - synchronized (DBPersistentManager.QUEUE_LOCK) { - dbInsertionHandlerThread = new DBInsertionHandlerThread("db_insertion_thread", persistence); - dbInsertionHandlerThread.start(); - for (Message msg : queue) { - addMessageToHandlerThread(msg); - } - } - } catch (SQLiteDatabaseCorruptException | ConcurrentModificationException | - NullPointerException ex) { - RudderLogger.logError(ex); - ReportManager.reportError(ex); - - } - }; - // Need to perform db operations on a separate thread to support strict mode. - executor.execute(runnable); - } - - private boolean checkIfColumnExists(String newColumn) { String checkIfStatusExistsSqlString = "PRAGMA table_info(events)"; if (!persistence.isAccessible()) { diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/EventRepository.java b/core/src/main/java/com/rudderstack/android/sdk/core/EventRepository.java index 92a20fbd6..a3a4102f8 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/EventRepository.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/EventRepository.java @@ -214,7 +214,6 @@ private void initializeDbManager(Application application) { dbEncryption.getPersistenceProviderFactoryClassName(), dbEncryption.key); this.dbManager = DBPersistentManager.getInstance(application, dbManagerParams); dbManager.checkForMigrations(); - dbManager.startHandlerThread(); } private void initiatePreferenceManager(Application application) { diff --git a/core/src/test/java/com/rudderstack/android/sdk/core/DBPersistentManagerTest.java b/core/src/test/java/com/rudderstack/android/sdk/core/DBPersistentManagerTest.java index ce9835f0f..f16fea439 100644 --- a/core/src/test/java/com/rudderstack/android/sdk/core/DBPersistentManagerTest.java +++ b/core/src/test/java/com/rudderstack/android/sdk/core/DBPersistentManagerTest.java @@ -6,46 +6,29 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; -import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; -import org.powermock.reflect.Whitebox; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static java.util.concurrent.TimeUnit.SECONDS; - import android.os.Build; -import android.os.Message; -import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasProperty; -import static java.lang.Thread.sleep; import android.app.Application; import androidx.test.core.app.ApplicationProvider; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - import com.google.common.collect.ImmutableList; import com.rudderstack.android.sdk.core.gson.RudderGson; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicInteger; @RunWith(RobolectricTestRunner.class) @Config(sdk = Build.VERSION_CODES.O_MR1) @@ -84,8 +67,6 @@ public void setUp() throws Exception { dbPersistentManager = PowerMockito.mock(DBPersistentManager.class); PowerMockito.when(dbPersistentManager, "saveEventSync", anyString()).thenCallRealMethod(); PowerMockito.when(dbPersistentManager, "saveEvent", anyString(), any()).thenCallRealMethod(); - PowerMockito.when(dbPersistentManager, "startHandlerThread").thenCallRealMethod(); - Whitebox.setInternalState(dbPersistentManager, "queue", new LinkedList<Message>()); deviceModeManager = Mockito.mock(RudderDeviceModeManager.class); } @@ -98,57 +79,6 @@ public void tearDown() { private int addMessageCalled = 0; - @Test - public void testSynchronicity() throws Exception { - final AtomicInteger messagesSaved = new AtomicInteger(0); - // Mocking the addMessageToQueue, which is used by both the save-event-thread and Handler thread, to verify synchronization - PowerMockito.when(dbPersistentManager, "addMessageToHandlerThread", any(Message.class)) - .thenAnswer((Answer<Void>) invocation -> { - ++addMessageCalled; - System.out.println("addMessageToQueue called by: " + Thread.currentThread().getName()); - //assert if called by multiple thread - assertThat(addMessageCalled, Matchers.lessThan(2)); - sleep(500); - --addMessageCalled; - assertThat(addMessageCalled, Matchers.lessThan(1)); - System.out.println("return from addMessageToQueue by: " + Thread.currentThread().getName()); - messagesSaved.incrementAndGet(); - return null; - } - ); - - // Triggering the saveEvent method of DBPersistentManager from save-event-thread, as this method adds messages to the queue. - new Thread(new Runnable() { - @Override - public void run() { - for (int i = 0; i < messages.size(); i++) { - dbPersistentManager.saveEvent(messages.get(i), - new EventInsertionCallback(new RudderMessageBuilder().build(), - deviceModeManager)); - // Starting the Handler thread, only when some events are added to the queue, so that the replay happens, and handler - // thread starts reading from the queue. - if (i == messages.size() / 2) { - dbPersistentManager.startHandlerThread(); - try { - Thread.sleep(200); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - } - }, "save-event-thread") { - }.start(); - - - //await until finished - await().atMost(15, SECONDS).until(new Callable<Boolean>() { - @Override - public Boolean call() throws Exception { - return messagesSaved.get() == messages.size(); - } - }); - } @Test public void doneEventsTest() { final DBPersistentManager dbPersistentManager = DBPersistentManager.getInstance(ApplicationProvider @@ -216,4 +146,4 @@ private List<RudderMessage> parse(List<String> messageJsons) { } return messages; } -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index cf47e1be7..a6b4feadf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,8 +5,8 @@ android.enableJetifier=true android.enableR8.fullMode=true kotlin.code.style=official GROUP=com.rudderstack.android.sdk -VERSION_CODE=28 -VERSION_NAME=1.23.1 +VERSION_CODE=29 +VERSION_NAME=1.23.2 POM_NAME=Rudderstack SDK for android POM_DESCRIPTION=Rudderstack SDK for android POM_ARTIFACT_ID=core diff --git a/package.json b/package.json index 6f69d4581..2dc854832 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.23.1", + "version": "1.23.2", "dependencies": { "properties-reader": "^2.2.0" } diff --git a/settings.gradle b/settings.gradle index 841f5ce3a..c2619a6a1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,19 +1 @@ include ':sample-cdn', ':sample-kotlin', ':core', ':sample-segment-java', ':sample-kotlin-integration', ':dummy-impl', ':android-tv' -//include(':web') -//project(':web').projectDir = new File(rootDir, "../RudderAndroidLibs/web") -//// -//include(':rudderjsonadapter') -//project(':rudderjsonadapter').projectDir = new File(rootDir, "../RudderAndroidLibs/rudderjsonadapter") -//include(':gsonrudderadapter') -//project(':gsonrudderadapter').projectDir = new File(rootDir, "../RudderAndroidLibs/gsonrudderadapter") -//include(':moshirudderadapter') -//project(':moshirudderadapter').projectDir = new File(rootDir, "../RudderAndroidLibs/moshirudderadapter") -//include(':jacksonrudderadapter') -//project(':jacksonrudderadapter').projectDir = new File(rootDir, "../RudderAndroidLibs/jacksonrudderadapter") -//// -//include(':repository') -//project(':repository').projectDir = new File(rootDir, "../RudderAndroidLibs/repository") -//// -//include(':rudderreporter') -//project(':rudderreporter').projectDir = new File(rootDir, "../RudderAndroidLibs/rudderreporter") -