Skip to content

Commit e385f0c

Browse files
Merge pull request #481 from RADAR-base/sdk-35
App updates to target SDK version 35 and use Firebase Analytics custom events
2 parents 7da78f4 + 7c3a12b commit e385f0c

File tree

15 files changed

+252
-18
lines changed

15 files changed

+252
-18
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ allprojects {
4444
version = "$project_version"
4545
group = 'org.radarbase'
4646

47-
ext.versionCode = 54
47+
ext.versionCode = 55
4848
}
4949

5050
subprojects {

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ android.defaults.buildfeatures.buildconfig=true
2424
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
2525
# org.gradle.parallel=true
2626

27-
project_version=1.4.2-SNAPSHOT
27+
project_version=1.5.0-SNAPSHOT
2828

2929
java_version=17
3030
kotlin_version=1.9.23

gradle/android.gradle

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
android {
2-
compileSdk 34
3-
buildToolsVersion '34.0.0'
2+
compileSdk 35
43

54
defaultConfig {
65
minSdkVersion 26
7-
targetSdkVersion 34
6+
targetSdkVersion 35
87
versionCode versionCode
98
versionName version
109
}

plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpensmileAudioManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import java.io.IOException
3434
import java.util.concurrent.TimeUnit
3535

3636
/** Manages Phone sensors */
37-
class OpensmileAudioManager constructor(service: OpenSmileAudioService) : AbstractSourceManager<OpenSmileAudioService, BaseSourceState>(service) {
37+
class OpensmileAudioManager(service: OpenSmileAudioService) : AbstractSourceManager<OpenSmileAudioService, BaseSourceState>(service) {
3838
private val audioTopic: DataCache<ObservationKey, OpenSmile2PhoneAudio> = createCache("android_processed_audio", OpenSmile2PhoneAudio())
3939

4040
@get:Synchronized

plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/opensmile/SmileJNI.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,19 @@ public static File init(Context c) throws IOException {
4141
File assetDirectory = new File(c.getCacheDir(), ASSET_PATH_BASE);
4242

4343
AssetManager assetManager = c.getAssets();
44-
for (String assetPath : ASSET_PATHS) {
45-
copyDirectory(assetManager, ASSET_PATH_BASE + '/' + assetPath, new File(assetDirectory, assetPath));
46-
}
44+
45+
copyDirectory(
46+
assetManager,
47+
ASSET_PATH_BASE,
48+
assetDirectory
49+
);
50+
51+
copyDirectory(
52+
assetManager,
53+
ASSET_PATH_BASE + "/shared",
54+
new File(assetDirectory, "shared")
55+
);
56+
4757
assetDir = assetDirectory;
4858
}
4959
return assetDir;
@@ -60,6 +70,15 @@ private static void copyDirectory(AssetManager assetManager, String fromDirector
6070
throw new IllegalStateException("Asset directory does not exist.");
6171
}
6272
for (String filename : fileList) {
73+
74+
String assetPath = fromDirectory + '/' + filename;
75+
String[] children = assetManager.list(assetPath);
76+
77+
if (children != null && children.length > 0) {
78+
logger.debug("Skipping directory: {}", assetPath);
79+
continue;
80+
}
81+
6382
File outFile = new File(toDirectory, filename);
6483

6584
byte[] buffer = new byte[8096];

plugins/radar-android-audio/src/main/jni/Android.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ LOCAL_CPP_FEATURES := exceptions
136136
DEFINES := -DOPENSMILE_BUILD -DBUILD_RNN -DHAVE_OPENSLES
137137
OPTIMIZE := -g -O3 -ffast-math -ftree-vectorize -funsafe-math-optimizations -fvisibility=hidden -ffunction-sections -fdata-sections
138138
LOCAL_LDFLAGS += -Wl,--gc-sections
139+
LOCAL_LDFLAGS += "-Wl,-z,max-page-size=16384"
139140

140141
LOCAL_CFLAGS := $(OPTIMIZE) -I$(OPENSMILE_ROOT)/src/include $(DEFINES)
141142
LOCAL_CPPFLAGS := $(OPTIMIZE) -I$(OPENSMILE_ROOT)/src/include $(DEFINES)

radar-commons-android/src/main/java/org/radarbase/android/IRadarBinder.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import android.os.IBinder
2020
import org.apache.avro.specific.SpecificRecord
2121
import org.radarbase.android.data.DataHandler
2222
import org.radarbase.android.kafka.ServerStatusListener
23+
import org.radarbase.android.source.PluginMetadataStore
2324
import org.radarbase.android.source.SourceProvider
2425
import org.radarbase.android.source.SourceServiceConnection
2526
import org.radarbase.android.util.TimedLong
@@ -34,6 +35,8 @@ interface IRadarBinder : IBinder {
3435

3536
val dataHandler: DataHandler<ObservationKey, SpecificRecord>?
3637

38+
val pluginMetadataStore: PluginMetadataStore
39+
3740
fun setAllowedSourceIds(connection: SourceServiceConnection<*>, allowedIds: Collection<String>)
3841

3942
fun startScanning()

radar-commons-android/src/main/java/org/radarbase/android/RadarService.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ import android.content.Intent
2424
import android.content.pm.PackageManager
2525
import android.content.pm.PackageManager.PERMISSION_GRANTED
2626
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
27-
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
2827
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH
2928
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
3029
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
30+
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
3131
import android.os.*
3232
import android.os.Build.VERSION.SDK_INT
3333
import android.os.Build.VERSION_CODES
@@ -74,6 +74,8 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
7474
var dataHandler: DataHandler<ObservationKey, SpecificRecord>? = null
7575
private set
7676

77+
val pluginMetadata = PluginMetadataStore()
78+
7779
open val cacheStore: CacheStore = CacheStore()
7880

7981
private lateinit var mHandler: SafeHandler
@@ -240,15 +242,15 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
240242
* This need to be modified it in future when setting new targetSdkVersion
241243
*/
242244
startForeground(1, createForegroundNotification(),
243-
FOREGROUND_SERVICE_TYPE_DATA_SYNC
245+
FOREGROUND_SERVICE_TYPE_SPECIAL_USE
244246
)
245247
}
246248

247249
return START_STICKY
248250
}
249251

250252
private fun startForegroundIfNeeded(grantedPermissions: Set<String>) {
251-
if (SDK_INT < Q) return
253+
if (SDK_INT < UPSIDE_DOWN_CAKE) return
252254

253255
val fgsTypePermissions: MutableSet<Int> = mutableSetOf()
254256

@@ -275,8 +277,7 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
275277
}
276278

277279
if (fgsTypePermissions.isNotEmpty()) {
278-
279-
fgsTypePermissions.add(FOREGROUND_SERVICE_TYPE_DATA_SYNC)
280+
fgsTypePermissions.add(FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
280281

281282
val combinedFgsType: Int = fgsTypePermissions.reduce { acc, type -> acc or type }
282283

@@ -657,6 +658,7 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
657658
}
658659
submitter {
659660
userId = authState.userId
661+
projectId = authState.projectId
660662
}
661663
}
662664

@@ -718,6 +720,9 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
718720
}
719721
}
720722

723+
override val pluginMetadataStore: PluginMetadataStore
724+
get() = this@RadarService.pluginMetadata
725+
721726
override val dataHandler: DataHandler<ObservationKey, SpecificRecord>?
722727
get() = this@RadarService.dataHandler
723728

radar-commons-android/src/main/java/org/radarbase/android/data/TableDataHandler.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import android.content.Context
2020
import android.os.Process.THREAD_PRIORITY_BACKGROUND
2121
import androidx.localbroadcastmanager.content.LocalBroadcastManager
2222
import org.apache.avro.specific.SpecificRecord
23+
import org.radarbase.android.RadarService
2324
import org.radarbase.android.kafka.KafkaDataSubmitter
2425
import org.radarbase.android.kafka.ServerStatusListener
2526
import org.radarbase.android.source.SourceService.Companion.CACHE_RECORDS_UNSENT_NUMBER
@@ -183,7 +184,7 @@ class TableDataHandler(
183184
sender = it
184185
}
185186

186-
this.submitter = KafkaDataSubmitter(this, sender, config.submitterConfig)
187+
this.submitter = KafkaDataSubmitter(this, sender, config.submitterConfig, context as? RadarService)
187188
}
188189

189190
/**

radar-commons-android/src/main/java/org/radarbase/android/kafka/KafkaDataSubmitter.kt

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ import android.os.Process
2020
import org.apache.avro.Schema
2121
import org.apache.avro.SchemaValidationException
2222
import org.apache.avro.generic.IndexedRecord
23+
import org.radarbase.android.RadarService
2324
import org.radarbase.android.data.DataCacheGroup
2425
import org.radarbase.android.data.DataHandler
2526
import org.radarbase.android.data.ReadableDataCache
27+
import org.radarbase.android.source.PluginMetadataStore
28+
import org.radarbase.android.util.FirebaseEventLogger
2629
import org.radarbase.android.util.SafeHandler
2730
import org.radarbase.data.AvroRecordData
31+
import org.radarbase.data.RecordData
2832
import org.radarbase.producer.AuthenticationException
2933
import org.radarbase.producer.KafkaSender
3034
import org.radarbase.producer.KafkaTopicSender
@@ -46,11 +50,13 @@ class KafkaDataSubmitter(
4650
private val dataHandler: DataHandler<*, *>,
4751
private val sender: KafkaSender,
4852
config: SubmitterConfiguration,
53+
radarService: RadarService? = null,
4954
) : Closeable {
5055

5156
private val submitHandler = SafeHandler.getInstance("KafkaDataSubmitter", Process.THREAD_PRIORITY_BACKGROUND)
5257
private val topicSenders: MutableMap<String, KafkaTopicSender<Any, Any>> = HashMap()
5358
private val connection: KafkaConnectionChecker
59+
private val pluginMetadata: PluginMetadataStore? = radarService?.pluginMetadata
5460

5561
var config: SubmitterConfiguration = config
5662
set(newValue) {
@@ -245,17 +251,52 @@ class KafkaDataSubmitter(
245251
if (recordsNotNull.isNotEmpty()) {
246252
val topic = cache.readTopic
247253

248-
val keyUserId = if (topic.keySchema.type == Schema.Type.RECORD) {
254+
val keyUserId: String? = if (topic.keySchema.type == Schema.Type.RECORD) {
249255
topic.keySchema.getField("userId")?.let { userIdField ->
250256
(data.key as IndexedRecord).get(userIdField.pos()).toString()
251257
}
252258
} else null
253259

254-
if (keyUserId == null || keyUserId == config.userId) {
260+
val keyProjectId: String? = retrieveDataFromFields(topic, data, "projectId") as? String
261+
262+
if ((keyUserId == null || keyUserId == config.userId) && (keyProjectId == null || keyProjectId == config.projectId)) {
255263
if (uploadingNotified.compareAndSet(false, true)) {
256264
dataHandler.updateServerStatus(ServerStatusListener.Status.UPLOADING)
257265
}
266+
val keySourceId = retrieveDataFromFields(topic, data, "sourceId") as? String
267+
if (pluginMetadata != null) {
268+
if (keySourceId != null && keySourceId !in pluginMetadata.sourceIds) {
269+
logger.warn(
270+
"(MismatchedId) SourceId: {} for topic: {} doesn't match with any sourceId's. Discarding the data",
271+
keySourceId,
272+
topic.name
273+
)
274+
dataHandler.let {
275+
it.updateRecordsSent(topic.name, size.toLong())
276+
// The upload has not actually failed yet, but if it proceeds, it will fail due to an incorrect sourceId.
277+
// Therefore, the status is explicitly set to UPLOADING_FAILED.
278+
it.updateServerStatus(ServerStatusListener.Status.UPLOADING_FAILED)
279+
}
280+
281+
cache.remove(size)
282+
283+
val pluginName = pluginMetadata.topicToPluginMap.get(topic.name)
284+
val sourceId = pluginMetadata.pluginToSourceIdMap[pluginName]
258285

286+
FirebaseEventLogger.reportMismatchedSourceId(
287+
keyProjectId,
288+
keySourceId,
289+
keyUserId,
290+
topic.name,
291+
pluginName,
292+
sourceId
293+
)
294+
295+
return size
296+
}
297+
} else {
298+
logger.warn("Trying to upload data without validating. PluginMetadata is null")
299+
}
259300
try {
260301
sender(topic).run {
261302
send(AvroRecordData<Any, Any>(data.topic, data.key, recordsNotNull))
@@ -272,14 +313,36 @@ class KafkaDataSubmitter(
272313
}
273314

274315
logger.debug("uploaded {} {} records", size, topic.name)
316+
} else {
317+
val pluginName = pluginMetadata?.topicToPluginMap?.get(topic.name)
318+
319+
when {
320+
keyProjectId != config.projectId -> {
321+
FirebaseEventLogger.reportMismatchedProjectId(
322+
keyProjectId, config.projectId, pluginName, topic.name, keyUserId
323+
)
324+
}
325+
326+
keyUserId != config.userId -> {
327+
FirebaseEventLogger.reportMismatchedUserId(
328+
keyUserId, config.userId, pluginName, topic.name, config.projectId
329+
)
330+
}
331+
}
275332
}
276333
}
277334

278335
cache.remove(size)
279-
280336
return size
281337
}
282338

339+
private fun retrieveDataFromFields(topic: AvroTopic<Any, Any>, data: RecordData<Any, Any?>, field: String) =
340+
if (topic.keySchema.type == Schema.Type.RECORD) {
341+
topic.keySchema.getField(field)?.let { fieldName ->
342+
(data.key as IndexedRecord).get(fieldName.pos()).toString()
343+
}
344+
} else null
345+
283346
companion object {
284347
private val logger = LoggerFactory.getLogger(KafkaDataSubmitter::class.java)
285348
}

0 commit comments

Comments
 (0)