====== Troubleshooting Común en PAE ====== ===== Compilación ===== ==== Error: "Gradle sync failed" ==== **Síntomas**: Android Studio no puede sincronizar proyecto **Soluciones**: # 1. Limpiar caches ./gradlew clean # 2. Actualizar gradle wrapper ./gradlew wrapper --gradle-version=8.0 # 3. Actualizar dependencies ./gradlew --refresh-dependencies build # 4. Regenerar gradle files rm -rf .gradle rm build.gradle.kts.bak ./gradlew build ==== Error: "org.gradle.api.GradleException" ==== **Síntomas**: Excepción genérica de Gradle org.gradle.api.GradleException: org.gradle.workers.internal.DefaultWorkerExecutor$WorkerExecutionException **Deamoscrático**: # Compilar sin daemon ./gradlew --no-daemon clean build # O deshabilitar daemon echo "org.gradle.daemon=false" >> gradle.properties ==== Error: "Could not resolve dependency" ==== **Síntomas**: No encuentra librería Caused by: org.gradle.api.artifacts.ResolveException: Could not resolve: com.example:library:1.0.0 **Soluciones**: // Verificar todas las fuentes están disponibles repositories { google() mavenCentral() jitpack { url "https://jitpack.io" } } // Comprobar versión existe ./gradlew :dependencies | grep library: // Si es jitpack, verificar repo público ==== Error: "Compilation failed: Duplicate class kotlin.concurrent.LockedWrapper" ==== **Causa**: Múltiples versiones de stdlib **Solución**: configurations { all { exclude module: 'kotlin-stdlib-jdk7' exclude module: 'kotlin-stdlib' } } // O forzar versión dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib:2.3.10") { force = true } } ===== Runtime ===== ==== Error: "Manifest does not include the internet permission" ==== **Síntomas**: App no puede conectarse a internet **Causa**: Falta permiso en AndroidManifest **Solución**: ==== Error: "Permission Denial: reading..." ==== **Síntomas**: Acceso a datos denegado PermissionError: Permission Denial: reading ContentProvider requires android.permission.CAMERA **Solución**: Solicitar en runtime val permission = Manifest.permission.CAMERA if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(activity, arrayOf(permission), 100) } ==== Error: "Hardware not available: Camera2" ==== **Síntomas**: Camera2 API no disponible **Causa**: Dispositivo antigua o emulador sin soporte **Solución**: // Comprobar disponibilidad val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager val cameraIds = cameraManager.cameraIdList if (cameraIds.isEmpty()) { throw Exception("No cameras available") } ==== Error: "Bluetooth connection refused" ==== **Síntomas**: No puede conectar a balanza Bluetooth BluetoothSocket connect failed: BluetoothSocket.connect() called **Soluciones**: // 1. Verificar permisos val requiredPermissions = arrayOf( Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT ) // 2. Verificar dispositivo pareado val device = bluetoothAdapter.getRemoteDevice(macAddress) // 3. Usar UUID correcto (SPP) val uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") device.createRfcommSocketToServiceRecord(uuid) // 4. Reintentar con backoff for (attempt in 1..3) { try { socket.connect() break } catch (e: IOException) { if (attempt < 3) { delay(1000 * attempt) } else throw e } } ==== Error: "OutOfMemory - Java heap space" ==== **Síntomas**: App crashes por falta de memoria java.lang.OutOfMemoryError: Java heap space **Causa típica**: Cargando imágenes grandes sin comprimir **Soluciones**: // Comprimir Bitmap antes guardar fun saveBitmap(bitmap: Bitmap, file: File) { FileOutputStream(file).use { fos -> // Reducir calidad bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos) } } // Reducir tamaño de imagen fun downscaleBitmap(original: Bitmap, maxWidth: Int = 1280): Bitmap { val scale = maxWidth.toFloat() / original.width val newHeight = (original.height * scale).toInt() return Bitmap.createScaledBitmap(original, maxWidth, newHeight, true) } // Forzar garbage collection (último recurso) System.gc() ==== Error: "Database is locked" ==== **Síntomas**: SQLite no puede escribir android.database.sqlite.SQLiteDatabaseLockedException: database is locked **Causa**: Múltiples threads escribiendo **Soluciones**: // Usar WAL (Write-Ahead Logging) db.enableWriteAheadLogging() // O serializar acceso synchronized(dbLock) { repository.save(entity) } // O usar single-threaded scope withContext(Dispatchers.IO) { // serializa en Single thread repository.save(entity) } ==== Error: "State transition invalid" ==== **Síntomas**: StateManager rechaza transición IllegalStateException: Cannot transition from WaitingForWeight to SaveDelivery **Causa**: Flujo de estados incorrecto **Solución**: Verificar que estados sean consecutivos // ✅ Correcto: estados lineales WaitingForWeight → CaptureImages → CaptureFace → ... → SaveDelivery // ❌ Incorrecto: saltos aleatorios WaitingForWeight → SaveDelivery // Imposible ===== Networking ===== ==== Error: "UnknownHostException: backend.api.com" ==== **Síntomas**: No puede resolver DNS java.net.UnknownHostException: Unable to resolve host "backend.api.com" **Causa**: Internet no disponible o DNS fallido **Soluciones**: // Verificar conectividad val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val network = connectivityManager.activeNetwork val capabilities = connectivityManager.getNetworkCapabilities(network) val isConnected = capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true if (!isConnected) { showError("No hay conexión a internet") return } // Retry con backoff exponencial suspend fun retryWithBackoff( maxRetries: Int = 3, delayMs: Long = 1000, block: suspend () -> T ): T { repeat(maxRetries - 1) { try { return block() } catch (e: Exception) { delay(delayMs * (2 pow it)) } } return block() // última vez, dejar fallar } ==== Error: "Certificate verification failed" ==== **Síntomas**: HTTPS no confía en certificado javax.net.ssl.SSLHandshakeException: Certificate verification failed **Causa**: Certificado self-signed o expirado **Soluciones**: // Para testing (NO producción): val trustAllCerts = arrayOf( object : X509TrustManager { override fun checkClientTrusted(chain: Array, authType: String) {} override fun checkServerTrusted(chain: Array, authType: String) {} override fun getAcceptedIssuers(): Array? = null } ) // En producción: use certificate pinning (ver security-and-privacy.md) ==== Error: "P2P connection timeout" ==== **Síntomas**: Timeout conectando a máquina vía P2P java.net.SocketTimeoutException: timeout after 20000ms **Causa**: Máquina no responde o está fuera de rango **Soluciones**: // Aumentar timeout val okHttpClient = OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build() // Verificar conexión Wi-Fi Direct val wifiP2pManager = context.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager // Buscar dispositivos disponibles primero wifiP2pManager.discoverPeers(channel, object : WifiP2pManager.ActionListener { override fun onSuccess() { // Pedir lista de peers } override fun onFailure(reason: Int) { // No hay peers } }) ===== P2P / DirectLink ===== ==== Error: "Wi-Fi Direct not available on this device" ==== **Síntomas**: No se puede usar P2P DirectLinkException: Wi-Fi Direct not available **Causa**: Dispositivo viejo sin Wi-Fi Direct **Solución**: Usar fallback a hotspot try { return p2pManager.connectMachine(machineId) } catch (e: DirectLinkException) { // Fallback a hotspot return p2pManager.connectMachineHotspot(ssid) } ==== Error: "Peer not found in scan results" ==== **Síntomas**: No encuentra máquina en discoverable **Soluciones**: // 1. Verificar que máquina está en modo descubrible // (ejecutar en Machine: directlink.startAdvertising()) // 2. Re-escanear p2pManager.discoverableMachineIds() // 3. verificar que ambos están en mismo rango // Wi-Fi Direct: debe estar en rango de ~100m o menos // 4. Retry for (attempt in 1..5) { val peers = p2pManager.discoveredMachineCandidates() if (peers.isNotEmpty()) return delay(500) } ===== Base de datos ===== ==== Error: "VectorDB index corruption" ==== **Síntomas**: Búsqueda vectorial falla VectorDBException: Index corrupted - index dimensions mismatch **Solución**: // Reconstruir índice repository.rebuildVectorIndex() // O truncar si es necesario repository.beneficiaries.deleteAll() ==== Error: "No space left on device" ==== **Síntomas**: No puede guardar fotos o entregas java.io.IOException: No space left on device **Soluciones**: // Verificar espacio disponible val stat = StatFs(Environment.getDataDirectory().path) val availableBytes = stat.availableBytes if (availableBytes < 100_000_000) { // < 100 MB throw Exception("Espacio insuficiente") } // Limpiar fotos antiguas val photosDir = File(context.filesDir, "photos") photosDir.listFiles()?.forEach { file -> if (System.currentTimeMillis() - file.lastModified() > 30.days.inMilliseconds) { file.delete() } } ===== Testing ===== ==== Error: "Test timed out after 10000ms" ==== **Causa**: Corrutina no completa **Solución**: @Test fun testDeliverySync() = runBlocking { // Usar runBlocking val result = repository.syncDeliveries() assertEquals(true, result) } // O alargar timeout @Test(timeout = 30000) fun slowTest() { } ==== Error: "MockedObject(derivedFrom(...))" ==== **Causa**: Mock no configurado correctamente **Solución**: // Configure el mock antes de usar `when`(mockRepository.getDelivery(1)) .thenReturn(testDelivery) // Luego usar val delivery = mockRepository.getDelivery(1) ===== Logs y Debugging ===== ==== Buscar error específico ==== # Filtrar por tag adb logcat | grep "P2P_MANAGER" # Filtrar por nivel adb logcat "*:E" # solo errores # Con timestamp adb logcat -v threadtime | grep StateManager # Caché en archivo adb logcat > logcat.txt 2>&1 ==== Breakpoint en Android Studio ==== 1. Click en número de línea (marcar breakpoint) 2. Run → Debug 'AppName' 3. Pausará cuando llegue a línea 4. F9 para continuar 5. F10 for step over 6. F11 for step into ===== Performance ===== ==== App muy lenta ==== **Causas comunes**: - **Main thread blocking**: Operations IO/DB en UI thread - **Memory leak**: Retaining objetos grandes - **Excessive allocations**: Crear muchos objetos - **Unoptimized queries**: O(n²) en loops **Debug**: # Profiler CPU adb shell am profile start --sampling 1000 com.package # Profiler Memory adb shell dumpsys meminfo com.package # Traces adb shell am trace-ipc start adb shell am trace-ipc stop ===== Checklist de debugging ===== * [ ] ¿Está el dispositivo/emulador conectado? * [ ] ¿Android Studio está sincronizado? * [ ] ¿Build es reciente? * [ ] ¿Logcat limpio (sin warnings críticos)? * [ ] ¿Permisos solicitados en runtime? * [ ] ¿Conectado a internet/Bluetooth? * [ ] ¿Versión de Android compatible? * [ ] ¿No hay hardcoded paths incorrectos? ===== Recursos de ayuda ===== * [[https://developer.android.com/troubleshoot|Android Developers Troubleshooting]] * [[https://docs.gradle.org/current/userguide/troubleshooting.html|Gradle troubleshooting]] * [[https://kotlinlang.org/docs/troubleshooting.html|Common Kotlin issues]] * Stack Overflow: tag ''android'' + descripción del error