Skip to content

Commit fa890a8

Browse files
committed
fixez
1 parent 567ce8d commit fa890a8

30 files changed

Lines changed: 4245 additions & 297 deletions

CLAUDE.md

Lines changed: 348 additions & 0 deletions
Large diffs are not rendered by default.

app/src/main/java/com/flockyou/FlockYouApplication.kt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@ package com.flockyou
33
import android.app.Application
44
import android.app.NotificationChannel
55
import android.app.NotificationManager
6+
import android.content.Intent
67
import android.os.Build
8+
import android.util.Log
9+
import androidx.core.content.ContextCompat
710
import androidx.hilt.work.HiltWorkerFactory
811
import androidx.work.Configuration
912
import com.flockyou.ai.DetectionAnalyzer
1013
import com.flockyou.data.AiSettingsRepository
1114
import com.flockyou.data.NukeSettingsRepository
1215
import com.flockyou.data.OuiSettingsRepository
1316
import com.flockyou.data.repository.OuiRepository
17+
import com.flockyou.service.BootReceiver
18+
import com.flockyou.service.ScanningService
1419
import com.flockyou.util.NotificationChannelIds
1520
import com.flockyou.worker.BackgroundAnalysisWorker
1621
import com.flockyou.worker.DeadManSwitchWorker
@@ -26,6 +31,10 @@ import javax.inject.Inject
2631
@HiltAndroidApp
2732
class FlockYouApplication : Application(), Configuration.Provider {
2833

34+
companion object {
35+
private const val TAG = "FlockYouApplication"
36+
}
37+
2938
@Inject
3039
lateinit var workerFactory: HiltWorkerFactory
3140

@@ -57,6 +66,9 @@ class FlockYouApplication : Application(), Configuration.Provider {
5766
// Create all notification channels at app startup
5867
createNotificationChannels()
5968

69+
// Initialize OEM defaults and auto-start service for OEM builds
70+
initializeOemBackground()
71+
6072
// Initialize OUI database updates
6173
applicationScope.launch {
6274
initializeOuiUpdates()
@@ -73,6 +85,46 @@ class FlockYouApplication : Application(), Configuration.Provider {
7385
}
7486
}
7587

88+
/**
89+
* Initialize OEM-specific background scanning behavior.
90+
* For OEM builds, this ensures the scanning service starts automatically
91+
* and runs continuously in the background.
92+
*/
93+
private fun initializeOemBackground() {
94+
// Set OEM defaults on first run
95+
BootReceiver.initializeOemDefaults(this)
96+
97+
// For OEM builds, auto-start the scanning service
98+
if (BuildConfig.IS_OEM_BUILD) {
99+
Log.i(TAG, "OEM build detected - starting background scanning service")
100+
startScanningService()
101+
}
102+
}
103+
104+
/**
105+
* Start the scanning service as a foreground service.
106+
*/
107+
private fun startScanningService() {
108+
try {
109+
val serviceIntent = Intent(this, ScanningService::class.java).apply {
110+
action = "com.flockyou.START_SCANNING"
111+
}
112+
113+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
114+
ContextCompat.startForegroundService(this, serviceIntent)
115+
} else {
116+
startService(serviceIntent)
117+
}
118+
119+
// Mark service as enabled for restart mechanisms
120+
BootReceiver.setServiceEnabled(this, true)
121+
122+
Log.i(TAG, "Scanning service start requested")
123+
} catch (e: Exception) {
124+
Log.e(TAG, "Failed to start scanning service", e)
125+
}
126+
}
127+
76128
private fun createNotificationChannels() {
77129
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
78130
val notificationManager = getSystemService(NotificationManager::class.java)

app/src/main/java/com/flockyou/ai/CompactPromptTemplates.kt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ impossible_speed=${analysis.impossibleSpeed}
206206
cell_trust=${analysis.cellTrustScore}%
207207
sig_spike=${analysis.signalSpikeDetected}
208208
down+spike=${analysis.downgradeWithSignalSpike}
209+
down+new_tower=${analysis.downgradeWithNewTower}
210+
lac_tac_changed=${analysis.lacTacChanged}
211+
operator_changed=${analysis.operatorChanged}
212+
roaming=${analysis.isRoaming}
209213
fp_pct=${String.format("%.0f", analysis.falsePositiveLikelihood)}%
210214
normal_handoff=${analysis.isLikelyNormalHandoff}
211215
5g_beam=${analysis.isLikely5gBeamSteering}
@@ -388,6 +392,49 @@ ${detection.signalStrength.displayName}"""
388392
return wrapGemmaPrompt(content)
389393
}
390394

395+
// ==================== COMPACT BLE PROMPT ====================
396+
397+
/**
398+
* Compact BLE analysis prompt - ~350 tokens
399+
*/
400+
fun compactBlePrompt(
401+
detection: Detection,
402+
analysis: BleAnalysisData
403+
): String {
404+
val trackerInfo = if (analysis.isTrackerDevice) {
405+
"follow=${analysis.isFollowingUser},locs=${analysis.distinctLocationCount},dur=${analysis.durationMinutes}min,susp=${analysis.suspicionScore}/100,possess=${analysis.isPossessionSignal}"
406+
} else ""
407+
408+
val spamInfo = if (analysis.isBleSpam) {
409+
"spam=${analysis.spamType},events=${analysis.spamEventsCount},rate=${String.format("%.1f", analysis.spamEventsPerSecond)}/s"
410+
} else ""
411+
412+
val content = """$ABBREVIATION_LEGEND
413+
414+
BLE dev analysis. Respond JSON:
415+
{"is_threat":true/false,"dev_type":"${analysis.deviceCategory.displayName}","conf":0-100,"fp_pct":0-100,"explain":"1-2 sentences","action":"what to do"}
416+
417+
BLE_DATA:
418+
name=${sanitize(analysis.deviceName) ?: "unknown"}
419+
mac=${sanitize(analysis.macAddress)}
420+
cat=${analysis.deviceCategory.displayName}
421+
sig=${analysis.rssi}dBm
422+
adv_rate=${String.format("%.1f", analysis.advertisingRate)}pps
423+
connect=${analysis.isConnectable}
424+
${analysis.estimatedDistanceMeters?.let { "dist=${String.format("%.1f", it)}m" } ?: ""}
425+
${if (trackerInfo.isNotEmpty()) "TRACKER:$trackerInfo" else ""}
426+
${if (spamInfo.isNotEmpty()) "SPAM:$spamInfo" else ""}
427+
fp_pct=${String.format("%.0f", analysis.falsePositiveLikelihood)}%
428+
consumer=${analysis.isLikelyConsumerDevice}
429+
passing=${analysis.isPassingBy}
430+
431+
RULES:fp_pct>50=likely benign BLE dev
432+
433+
JSON:"""
434+
435+
return wrapGemmaPrompt(content)
436+
}
437+
391438
// ==================== HELPER FUNCTIONS ====================
392439

393440
/**
@@ -414,6 +461,19 @@ ${detection.signalStrength.displayName}"""
414461
is EnrichedDetectorData.Satellite -> {
415462
"SAT:type=${data.detectorType},${data.riskIndicators.take(2).joinToString(",")}"
416463
}
464+
is EnrichedDetectorData.Ble -> {
465+
val a = data.analysis
466+
"BLE:cat=${a.deviceCategory.displayName},sig=${a.rssi}dBm,adv=${String.format("%.1f", a.advertisingRate)}pps" +
467+
(if (a.isTrackerDevice) ",follow=${a.isFollowingUser},susp=${a.suspicionScore}" else "") +
468+
(if (a.isBleSpam) ",spam=${a.spamType}" else "") +
469+
",fp=${String.format("%.0f", a.falsePositiveLikelihood)}%"
470+
}
471+
is EnrichedDetectorData.WifiSsidMatch -> {
472+
"WIFI_SSID:ssid=${data.ssid.ifEmpty { "(hidden)" }},ch=${data.channel},sig=${data.rssiDbm}dBm,band=${data.frequencyBand},oui=${data.ouiVendor ?: "unk"},aps=${data.nearbyApCount},method=${data.detectionMethodName}"
473+
}
474+
is EnrichedDetectorData.RfEnvironment -> {
475+
"RF:type=${data.anomalyType},score=${data.rfThreatScore},nets=${data.totalNetworks},hidden=${data.hiddenNetworkCount},fp=${String.format("%.0f", data.falsePositiveLikelihood)}%"
476+
}
417477
}
418478
}
419479

@@ -429,6 +489,8 @@ ${detection.signalStrength.displayName}"""
429489
is EnrichedDetectorData.Gnss -> return enrichedData.analysis.falsePositiveLikelihood.toInt()
430490
is EnrichedDetectorData.Ultrasonic -> return enrichedData.analysis.falsePositiveLikelihood.toInt()
431491
is EnrichedDetectorData.WifiFollowing -> return enrichedData.analysis.falsePositiveLikelihood.toInt()
492+
is EnrichedDetectorData.Ble -> return enrichedData.analysis.falsePositiveLikelihood.toInt()
493+
is EnrichedDetectorData.RfEnvironment -> return enrichedData.falsePositiveLikelihood.toInt()
432494
else -> {}
433495
}
434496

app/src/main/java/com/flockyou/ai/DetectionAnalyzer.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,12 @@ class DetectionAnalyzer @Inject constructor(
13761376
PromptTemplates.buildWifiFollowingEnrichedPrompt(detection, enrichedData.analysis)
13771377
is EnrichedDetectorData.Satellite ->
13781378
PromptTemplates.buildSatelliteEnrichedPrompt(detection, enrichedData)
1379+
is EnrichedDetectorData.RfEnvironment ->
1380+
PromptTemplates.buildRfEnvironmentEnrichedPrompt(detection, enrichedData)
1381+
is EnrichedDetectorData.Ble ->
1382+
PromptTemplates.buildBleEnrichedPrompt(detection, enrichedData.analysis)
1383+
is EnrichedDetectorData.WifiSsidMatch ->
1384+
PromptTemplates.buildWifiSsidEnrichedPrompt(detection, enrichedData)
13791385
}
13801386
}
13811387

app/src/main/java/com/flockyou/ai/EnrichedDataCache.kt

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@ class EnrichedDataCache @Inject constructor() {
108108
put(detectionId, EnrichedDetectorData.WifiFollowing(analysis))
109109
}
110110

111+
/**
112+
* Store WiFi SSID/MAC match enriched data for a detection.
113+
*
114+
* This is for standard WiFi pattern match detections (SSID regex, MAC OUI prefix)
115+
* that need scan context for LLM analysis.
116+
*/
117+
fun putWifiSsidMatch(detectionId: String, data: EnrichedDetectorData.WifiSsidMatch) {
118+
put(detectionId, data)
119+
}
120+
121+
/**
122+
* Store BLE analysis data for a detection.
123+
*/
124+
fun putBle(detectionId: String, analysis: BleAnalysisData) {
125+
put(detectionId, EnrichedDetectorData.Ble(analysis))
126+
}
127+
111128
/**
112129
* Store satellite/NTN enriched data for a detection.
113130
*/
@@ -128,6 +145,13 @@ class EnrichedDataCache @Inject constructor() {
128145
))
129146
}
130147

148+
/**
149+
* Store RF environment enriched data for a detection.
150+
*/
151+
fun putRfEnvironment(detectionId: String, data: EnrichedDetectorData.RfEnvironment) {
152+
put(detectionId, data)
153+
}
154+
131155
/**
132156
* Remove enriched data for a detection.
133157
*/
@@ -182,6 +206,8 @@ class EnrichedDataCache @Inject constructor() {
182206
var ultrasonicCount = 0
183207
var wifiCount = 0
184208
var satelliteCount = 0
209+
var bleCount = 0
210+
var rfCount = 0
185211

186212
cache.values.forEach { entry ->
187213
if (entry.isExpired()) expiredCount++
@@ -190,7 +216,11 @@ class EnrichedDataCache @Inject constructor() {
190216
is EnrichedDetectorData.Gnss -> gnssCount++
191217
is EnrichedDetectorData.Ultrasonic -> ultrasonicCount++
192218
is EnrichedDetectorData.WifiFollowing -> wifiCount++
219+
is EnrichedDetectorData.WifiSsidMatch -> wifiCount++
193220
is EnrichedDetectorData.Satellite -> satelliteCount++
221+
is EnrichedDetectorData.Ble -> bleCount++
222+
is EnrichedDetectorData.RfEnvironment -> rfCount++
223+
else -> {}
194224
}
195225
}
196226

@@ -201,7 +231,9 @@ class EnrichedDataCache @Inject constructor() {
201231
gnssEntries = gnssCount,
202232
ultrasonicEntries = ultrasonicCount,
203233
wifiEntries = wifiCount,
204-
satelliteEntries = satelliteCount
234+
satelliteEntries = satelliteCount,
235+
bleEntries = bleCount,
236+
rfEntries = rfCount
205237
)
206238
}
207239

@@ -212,6 +244,8 @@ class EnrichedDataCache @Inject constructor() {
212244
val gnssEntries: Int,
213245
val ultrasonicEntries: Int,
214246
val wifiEntries: Int,
215-
val satelliteEntries: Int
247+
val satelliteEntries: Int,
248+
val bleEntries: Int = 0,
249+
val rfEntries: Int = 0
216250
)
217251
}

0 commit comments

Comments
 (0)