android-sdd 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: root-detection
3
+ description: >
4
+ Root detection for Android security hardening.
5
+ Load this skill when detecting rooted devices, implementing multi-signal
6
+ root checks, deciding how to respond to rooted devices, or combining
7
+ root detection with other integrity checks.
8
+ ---
9
+
10
+ # Root Detection
11
+
12
+ ## Overview
13
+ Root detection identifies devices where the Android security model has been compromised. A rooted device can bypass app-level protections, read encrypted storage, hook function calls, and tamper with app behavior. Root detection is one layer in a defense-in-depth strategy — no single check is foolproof.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - **Multiple signals** — no single check is reliable; combine several
20
+ - **Fail-secure** — if detection is uncertain, treat as rooted in high-security contexts
21
+ - **Graceful degradation** — decide per-feature whether to block, warn, or restrict
22
+ - **Obfuscate detection logic** — easy-to-read detection code is easy to bypass
23
+ - **Server-side validation** — combine with Play Integrity API for stronger guarantees
24
+
25
+ ---
26
+
27
+ ## Multi-Signal Root Detection
28
+
29
+ ```kotlin
30
+ class RootDetector @Inject constructor(
31
+ @ApplicationContext private val context: Context
32
+ ) {
33
+ data class RootCheckResult(
34
+ val isRooted: Boolean,
35
+ val signals: List<String> // which signals triggered
36
+ )
37
+
38
+ fun check(): RootCheckResult {
39
+ val signals = mutableListOf<String>()
40
+
41
+ if (checkSuBinary()) signals.add("su_binary")
42
+ if (checkRootApps()) signals.add("root_apps")
43
+ if (checkDangerousProps()) signals.add("dangerous_props")
44
+ if (checkRwPaths()) signals.add("rw_system_paths")
45
+ if (checkTestKeys()) signals.add("test_keys")
46
+ if (checkMagiskFiles()) signals.add("magisk_files")
47
+
48
+ return RootCheckResult(
49
+ isRooted = signals.isNotEmpty(),
50
+ signals = signals
51
+ )
52
+ }
53
+
54
+ // ✅ Check for su binary in common locations
55
+ private fun checkSuBinary(): Boolean {
56
+ val paths = listOf(
57
+ "/system/bin/su", "/system/xbin/su", "/sbin/su",
58
+ "/system/su", "/system/bin/.ext/.su", "/system/usr/we-need-root/su-backup",
59
+ "/data/local/su", "/data/local/bin/su", "/data/local/xbin/su"
60
+ )
61
+ return paths.any { File(it).exists() }
62
+ }
63
+
64
+ // ✅ Check for known root management apps
65
+ private fun checkRootApps(): Boolean {
66
+ val rootPackages = listOf(
67
+ "com.topjohnwu.magisk",
68
+ "com.noshufou.android.su",
69
+ "com.koushikdutta.superuser",
70
+ "eu.chainfire.supersu",
71
+ "com.kingroot.kinguser",
72
+ "com.kingo.root",
73
+ "com.smedialink.oneclickroot"
74
+ )
75
+ return rootPackages.any { pkg ->
76
+ try {
77
+ context.packageManager.getPackageInfo(pkg, 0)
78
+ true
79
+ } catch (e: PackageManager.NameNotFoundException) {
80
+ false
81
+ }
82
+ }
83
+ }
84
+
85
+ // ✅ Check dangerous build properties
86
+ private fun checkDangerousProps(): Boolean {
87
+ val dangerousProps = mapOf(
88
+ "ro.debuggable" to "1",
89
+ "ro.secure" to "0",
90
+ "ro.build.type" to "eng"
91
+ )
92
+ return dangerousProps.any { (prop, dangerousValue) ->
93
+ readSystemProperty(prop) == dangerousValue
94
+ }
95
+ }
96
+
97
+ // ✅ Check if /system is writable (should always be read-only)
98
+ private fun checkRwPaths(): Boolean {
99
+ val rwPaths = listOf("/system", "/system/bin", "/system/sbin", "/system/xbin", "/vendor/bin")
100
+ return rwPaths.any { path ->
101
+ try {
102
+ val file = File(path)
103
+ file.exists() && file.canWrite()
104
+ } catch (e: Exception) {
105
+ false
106
+ }
107
+ }
108
+ }
109
+
110
+ // ✅ Check for test-keys build (non-official signing)
111
+ private fun checkTestKeys(): Boolean {
112
+ val buildTags = Build.TAGS
113
+ return buildTags != null && buildTags.contains("test-keys")
114
+ }
115
+
116
+ // ✅ Check for Magisk-specific files
117
+ private fun checkMagiskFiles(): Boolean {
118
+ val magiskPaths = listOf(
119
+ "/sbin/.magisk", "/sbin/.core/mirror",
120
+ "/sbin/.core/img", "/data/adb/magisk"
121
+ )
122
+ return magiskPaths.any { File(it).exists() }
123
+ }
124
+
125
+ private fun readSystemProperty(name: String): String? = try {
126
+ val process = Runtime.getRuntime().exec(arrayOf("getprop", name))
127
+ process.inputStream.bufferedReader().readLine()?.trim()
128
+ } catch (e: Exception) {
129
+ null
130
+ }
131
+ }
132
+ ```
133
+
134
+ ---
135
+
136
+ ## Play Integrity API (Recommended for Production)
137
+
138
+ ```kotlin
139
+ // ✅ Play Integrity API — server-side verification, harder to bypass
140
+ class IntegrityChecker @Inject constructor(
141
+ @ApplicationContext private val context: Context
142
+ ) {
143
+ suspend fun checkIntegrity(nonce: String): Result<String> = runCatching {
144
+ val integrityManager = IntegrityManagerFactory.create(context)
145
+ val request = IntegrityTokenRequest.builder()
146
+ .setNonce(nonce)
147
+ .build()
148
+
149
+ val response = integrityManager.requestIntegrityToken(request).await()
150
+ response.token() // send this token to your server for verification
151
+ }
152
+ }
153
+
154
+ // Server verifies the token via Google Play Integrity API
155
+ // Token contains: deviceIntegrity, appIntegrity, accountDetails
156
+ // deviceIntegrity.deviceRecognitionVerdict includes MEETS_STRONG_INTEGRITY
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Response Strategy
162
+
163
+ ```kotlin
164
+ // ✅ Decide response per security level
165
+ @HiltViewModel
166
+ class SecurityViewModel @Inject constructor(
167
+ private val rootDetector: RootDetector
168
+ ) : ViewModel() {
169
+
170
+ sealed interface SecurityState {
171
+ data object Safe : SecurityState
172
+ data class Compromised(val signals: List<String>) : SecurityState
173
+ }
174
+
175
+ val securityState: StateFlow<SecurityState> = flow {
176
+ val result = rootDetector.check()
177
+ emit(
178
+ if (result.isRooted) SecurityState.Compromised(result.signals)
179
+ else SecurityState.Safe
180
+ )
181
+ }.stateIn(viewModelScope, SharingStarted.Eagerly, SecurityState.Safe)
182
+ }
183
+
184
+ // ✅ UI response options (choose based on app sensitivity)
185
+ // Option 1: Block entirely (banking, health apps)
186
+ if (securityState is SecurityState.Compromised) {
187
+ RootedDeviceBlockScreen()
188
+ return
189
+ }
190
+
191
+ // Option 2: Warn and restrict features
192
+ if (securityState is SecurityState.Compromised) {
193
+ showWarning("Some features are unavailable on rooted devices")
194
+ disableSensitiveFeatures()
195
+ }
196
+
197
+ // Option 3: Log and monitor (lower security apps)
198
+ if (securityState is SecurityState.Compromised) {
199
+ analytics.logEvent("rooted_device_detected", securityState.signals)
200
+ }
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Anti-Patterns
206
+
207
+ - Relying on a single check — root tools specifically bypass known single checks
208
+ - Blocking without user explanation — frustrates legitimate users (developers, testers)
209
+ - Performing root check on main thread — run on IO dispatcher
210
+ - Storing root check result in plain SharedPreferences — a rooted device can modify it
211
+ - Skipping server-side validation for high-security operations — client checks are bypassable
212
+
213
+ ---
214
+
215
+ ## Related Skills
216
+ - `hook-detection` — detecting Frida and Xposed hooks
217
+ - `frida-detection` — Frida-specific detection techniques
218
+ - `reverse-engineering-resistance` — broader anti-tampering measures
219
+ - `obfuscation` — making detection logic harder to analyze
220
+ - `certificate-pinning` — network-level security complement
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: secure-networking
3
+ description: >
4
+ Secure networking practices for Android apps.
5
+ Load this skill when configuring TLS, implementing certificate pinning,
6
+ setting up Network Security Config, handling authentication headers securely,
7
+ preventing cleartext traffic, or hardening OkHttp/Retrofit for production.
8
+ ---
9
+
10
+ # Secure Networking
11
+
12
+ ## Overview
13
+ Secure networking ensures that data in transit is protected from interception and tampering. The key concerns are: enforcing TLS, pinning certificates against MITM attacks, protecting auth credentials in requests, and preventing accidental cleartext traffic.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - **TLS only** — no cleartext HTTP in production; enforce via Network Security Config
20
+ - **Certificate pinning** for high-security endpoints — prevents MITM even with compromised CAs
21
+ - **Auth tokens in headers** — never in URL query parameters (logged by servers/proxies)
22
+ - **Token refresh** handled transparently via OkHttp Authenticator
23
+ - **Sensitive headers stripped** on redirects — never forward auth to different domains
24
+
25
+ ---
26
+
27
+ ## Network Security Config
28
+
29
+ ```xml
30
+ <!-- res/xml/network_security_config.xml -->
31
+ <?xml version="1.0" encoding="utf-8"?>
32
+ <network-security-config>
33
+
34
+ <!-- ✅ Block all cleartext traffic in production -->
35
+ <base-config cleartextTrafficPermitted="false">
36
+ <trust-anchors>
37
+ <certificates src="system" />
38
+ </trust-anchors>
39
+ </base-config>
40
+
41
+ <!-- ✅ Allow cleartext only for debug builds (localhost) -->
42
+ <debug-overrides>
43
+ <trust-anchors>
44
+ <certificates src="system" />
45
+ <certificates src="user" /> <!-- allows Charles/Burp on debug -->
46
+ </trust-anchors>
47
+ </debug-overrides>
48
+
49
+ </network-security-config>
50
+ ```
51
+
52
+ ```xml
53
+ <!-- AndroidManifest.xml -->
54
+ <application
55
+ android:networkSecurityConfig="@xml/network_security_config"
56
+ ...>
57
+ ```
58
+
59
+ ---
60
+
61
+ ## OkHttp Security Configuration
62
+
63
+ ```kotlin
64
+ // ✅ Production OkHttpClient
65
+ fun buildSecureOkHttpClient(
66
+ authInterceptor: AuthInterceptor,
67
+ certificatePinner: CertificatePinner? = null
68
+ ): OkHttpClient {
69
+ return OkHttpClient.Builder()
70
+ .connectTimeout(30, TimeUnit.SECONDS)
71
+ .readTimeout(30, TimeUnit.SECONDS)
72
+ .writeTimeout(30, TimeUnit.SECONDS)
73
+ .addInterceptor(authInterceptor)
74
+ .apply { certificatePinner?.let { certificatePinner(it) } }
75
+ .authenticator(TokenRefreshAuthenticator())
76
+ .build()
77
+ }
78
+
79
+ // ✅ Auth interceptor — adds token to every request
80
+ class AuthInterceptor @Inject constructor(
81
+ private val securePreferences: SecurePreferences
82
+ ) : Interceptor {
83
+ override fun intercept(chain: Interceptor.Chain): Response {
84
+ val token = securePreferences.authToken
85
+ val request = if (token != null) {
86
+ chain.request().newBuilder()
87
+ .header("Authorization", "Bearer $token")
88
+ .build()
89
+ } else {
90
+ chain.request()
91
+ }
92
+ return chain.proceed(request)
93
+ }
94
+ }
95
+
96
+ // ✅ Token refresh on 401
97
+ class TokenRefreshAuthenticator @Inject constructor(
98
+ private val securePreferences: SecurePreferences,
99
+ private val authApi: AuthApiService
100
+ ) : Authenticator {
101
+ override fun authenticate(route: Route?, response: Response): Request? {
102
+ // Prevent infinite loops
103
+ if (response.request.header("Authorization") == null) return null
104
+ if (responseCount(response) >= 2) return null
105
+
106
+ val refreshToken = securePreferences.refreshToken ?: return null
107
+
108
+ val newToken = runBlocking {
109
+ authApi.refreshToken(refreshToken).getOrNull()
110
+ } ?: return null
111
+
112
+ securePreferences.authToken = newToken.accessToken
113
+ securePreferences.refreshToken = newToken.refreshToken
114
+
115
+ return response.request.newBuilder()
116
+ .header("Authorization", "Bearer ${newToken.accessToken}")
117
+ .build()
118
+ }
119
+
120
+ private fun responseCount(response: Response): Int {
121
+ var count = 1
122
+ var prior = response.priorResponse
123
+ while (prior != null) { count++; prior = prior.priorResponse }
124
+ return count
125
+ }
126
+ }
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Certificate Pinning
132
+
133
+ ```kotlin
134
+ // ✅ Certificate pinning via OkHttp
135
+ fun buildCertificatePinner(): CertificatePinner {
136
+ return CertificatePinner.Builder()
137
+ // Pin the leaf certificate + backup pin for rotation
138
+ .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
139
+ .add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") // backup
140
+ .build()
141
+ }
142
+
143
+ // ✅ Getting the pin value for your certificate:
144
+ // $ openssl s_client -connect api.example.com:443 | openssl x509 -pubkey -noout |
145
+ // openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Logging Interceptor (Debug Only)
151
+
152
+ ```kotlin
153
+ // ✅ Never log request bodies in release — may contain tokens or PII
154
+ val loggingInterceptor = HttpLoggingInterceptor().apply {
155
+ level = if (BuildConfig.DEBUG) {
156
+ HttpLoggingInterceptor.Level.BODY
157
+ } else {
158
+ HttpLoggingInterceptor.Level.NONE // ✅ no logging in production
159
+ }
160
+ }
161
+
162
+ // ✅ Redact sensitive headers in logs
163
+ val loggingInterceptor = HttpLoggingInterceptor().apply {
164
+ level = HttpLoggingInterceptor.Level.HEADERS
165
+ redactHeader("Authorization")
166
+ redactHeader("Cookie")
167
+ redactHeader("Set-Cookie")
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Hilt Setup
174
+
175
+ ```kotlin
176
+ @Module
177
+ @InstallIn(SingletonComponent::class)
178
+ object NetworkModule {
179
+
180
+ @Provides
181
+ @Singleton
182
+ fun provideCertificatePinner(): CertificatePinner = buildCertificatePinner()
183
+
184
+ @Provides
185
+ @Singleton
186
+ fun provideOkHttpClient(
187
+ authInterceptor: AuthInterceptor,
188
+ certificatePinner: CertificatePinner
189
+ ): OkHttpClient = buildSecureOkHttpClient(authInterceptor, certificatePinner)
190
+
191
+ @Provides
192
+ @Singleton
193
+ fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit =
194
+ Retrofit.Builder()
195
+ .baseUrl(BuildConfig.API_BASE_URL)
196
+ .client(okHttpClient)
197
+ .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
198
+ .build()
199
+ }
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Anti-Patterns
205
+
206
+ - Passing auth tokens as URL query params — visible in server logs, browser history, proxies
207
+ - `cleartextTrafficPermitted="true"` in production manifest — allows HTTP interception
208
+ - Logging request/response bodies in release — leaks tokens and PII to logcat
209
+ - No token refresh logic — forces users to re-login on every token expiry
210
+ - Trusting user-installed CAs in production — enables corporate proxy / MITM attacks
211
+ - Hard-coding API keys or secrets in source code — use BuildConfig from CI environment variables
212
+
213
+ ---
214
+
215
+ ## Related Skills
216
+ - `certificate-pinning` — in-depth pinning setup and rotation strategy
217
+ - `retrofit` — Retrofit configuration
218
+ - `okhttp` — OkHttp interceptors and configuration
219
+ - `authentication` — auth flow and token management
220
+ - `network-security-config` — XML-based network security policy
@@ -0,0 +1,182 @@
1
+ ---
2
+ name: alarmmanager
3
+ description: >
4
+ AlarmManager for time-based task scheduling in Android.
5
+ Load this skill when scheduling exact-time alarms, implementing
6
+ reminder notifications, triggering actions at a specific time,
7
+ or handling cases where WorkManager's timing precision is insufficient.
8
+ ---
9
+
10
+ # AlarmManager
11
+
12
+ ## Overview
13
+ AlarmManager schedules tasks to run at a specific time, even if the app is not running. It is the correct tool when **exact timing** matters — reminder alarms, calendar events, scheduled notifications. For flexible/deferrable work, WorkManager is preferred. Since Android 12, exact alarms require explicit permission.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Use AlarmManager only when **exact timing** is required — use WorkManager for flexible scheduling
20
+ - Request `SCHEDULE_EXACT_ALARM` or `USE_EXACT_ALARM` permission for exact alarms on Android 12+
21
+ - Always use `PendingIntent` with `FLAG_IMMUTABLE` — required on Android 12+
22
+ - Re-schedule alarms after device reboot via `BOOT_COMPLETED` receiver
23
+ - Cancel alarms when they are no longer needed — they persist across app restarts
24
+
25
+ ---
26
+
27
+ ## Permissions
28
+
29
+ ```xml
30
+ <!-- AndroidManifest.xml -->
31
+
32
+ <!-- For exact alarms — user must grant in settings (Android 12+) -->
33
+ <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
34
+
35
+ <!-- OR for specific use cases (clock, calendar apps) — always granted -->
36
+ <uses-permission android:name="android.permission.USE_EXACT_ALARM" />
37
+
38
+ <!-- For BroadcastReceiver to survive reboot -->
39
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Scheduling an Exact Alarm
45
+
46
+ ```kotlin
47
+ // ✅ Schedule exact alarm
48
+ class ReminderScheduler @Inject constructor(
49
+ @ApplicationContext private val context: Context
50
+ ) {
51
+ private val alarmManager = context.getSystemService(AlarmManager::class.java)
52
+
53
+ fun scheduleReminder(reminderId: Long, triggerAtMillis: Long) {
54
+ val intent = Intent(context, ReminderReceiver::class.java).apply {
55
+ putExtra(ReminderReceiver.EXTRA_REMINDER_ID, reminderId)
56
+ }
57
+
58
+ val pendingIntent = PendingIntent.getBroadcast(
59
+ context,
60
+ reminderId.toInt(),
61
+ intent,
62
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
63
+ )
64
+
65
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
66
+ if (alarmManager.canScheduleExactAlarms()) {
67
+ alarmManager.setExactAndAllowWhileIdle(
68
+ AlarmManager.RTC_WAKEUP,
69
+ triggerAtMillis,
70
+ pendingIntent
71
+ )
72
+ }
73
+ // else: redirect user to settings
74
+ } else {
75
+ alarmManager.setExactAndAllowWhileIdle(
76
+ AlarmManager.RTC_WAKEUP,
77
+ triggerAtMillis,
78
+ pendingIntent
79
+ )
80
+ }
81
+ }
82
+
83
+ fun cancelReminder(reminderId: Long) {
84
+ val intent = Intent(context, ReminderReceiver::class.java)
85
+ val pendingIntent = PendingIntent.getBroadcast(
86
+ context,
87
+ reminderId.toInt(),
88
+ intent,
89
+ PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
90
+ )
91
+ pendingIntent?.let { alarmManager.cancel(it) }
92
+ }
93
+ }
94
+ ```
95
+
96
+ ---
97
+
98
+ ## BroadcastReceiver
99
+
100
+ ```kotlin
101
+ // ✅ Receiver triggered by alarm
102
+ class ReminderReceiver : BroadcastReceiver() {
103
+ override fun onReceive(context: Context, intent: Intent) {
104
+ val reminderId = intent.getLongExtra(EXTRA_REMINDER_ID, -1L)
105
+ if (reminderId == -1L) return
106
+
107
+ // Show notification — short work only in onReceive
108
+ NotificationHelper.showReminder(context, reminderId)
109
+ }
110
+
111
+ companion object {
112
+ const val EXTRA_REMINDER_ID = "reminder_id"
113
+ }
114
+ }
115
+
116
+ // Register in manifest
117
+ // <receiver android:name=".ReminderReceiver" android:exported="false" />
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Reschedule After Reboot
123
+
124
+ ```kotlin
125
+ // ✅ Reboot receiver — reschedules all pending alarms
126
+ class BootReceiver : BroadcastReceiver() {
127
+ override fun onReceive(context: Context, intent: Intent) {
128
+ if (intent.action != Intent.ACTION_BOOT_COMPLETED) return
129
+
130
+ // Reschedule all pending reminders from local DB
131
+ val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
132
+ scope.launch {
133
+ val scheduler = ReminderScheduler(context)
134
+ reminderRepository.getPendingReminders().forEach { reminder ->
135
+ scheduler.scheduleReminder(reminder.id, reminder.triggerAt)
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ // Register in manifest
142
+ // <receiver android:name=".BootReceiver" android:exported="true">
143
+ // <intent-filter>
144
+ // <action android:name="android.intent.action.BOOT_COMPLETED" />
145
+ // </intent-filter>
146
+ // </receiver>
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Directing User to Exact Alarm Settings
152
+
153
+ ```kotlin
154
+ // ✅ On Android 12+ — direct user to grant exact alarm permission
155
+ fun requestExactAlarmPermission(activity: Activity) {
156
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
157
+ val alarmManager = activity.getSystemService(AlarmManager::class.java)
158
+ if (!alarmManager.canScheduleExactAlarms()) {
159
+ val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)
160
+ activity.startActivity(intent)
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Anti-Patterns
169
+
170
+ - Using AlarmManager for network sync or deferrable tasks — use WorkManager
171
+ - Not rescheduling alarms after reboot — alarms are lost on device restart
172
+ - Using `FLAG_MUTABLE` for PendingIntent on Android 12+ — SecurityException
173
+ - Using `set()` instead of `setExactAndAllowWhileIdle()` — alarm delayed in Doze mode
174
+ - Doing heavy work in `onReceive` — it runs on the main thread; delegate to a service or WorkManager
175
+
176
+ ---
177
+
178
+ ## Related Skills
179
+ - `workmanager` — preferred for deferrable background work
180
+ - `notification` — showing notifications from the alarm receiver
181
+ - `broadcast-receiver` — BroadcastReceiver fundamentals
182
+ - `background-processing` — choosing the right scheduling tool