Overview
APK Extractor automatically saves WiFi-connected devices to devices.json for quick reconnection. This eliminates the need to re-enter pairing codes and ports every time you want to connect.
Saved device information includes:
- IP address
- Connection port
- Device label (manufacturer + model)
- Last connected timestamp
- Unique device ID
devices.json Structure
The devices.json file stores an array of device objects in JSON format.
File Location
Same directory as server.js (defined in server.js:11):
const DEVICES_FILE = path.join(__dirname, 'devices.json');
Example devices.json file:
[
{
"id": "1709856234567",
"label": "Samsung Galaxy S21",
"ip": "192.168.1.42",
"port": "37685",
"lastConnected": "2026-03-07T14:30:45.123Z"
},
{
"id": "1709856789012",
"label": "Xiaomi Redmi Note 10",
"ip": "192.168.1.58",
"port": "41234",
"lastConnected": "2026-03-06T09:15:22.456Z"
}
]
Field Descriptions
| Field | Type | Description |
|---|
id | string | Unique identifier (timestamp in milliseconds) |
label | string | Display name (brand + model or custom name) |
ip | string | Device IP address on local network |
port | string | ADB wireless connection port |
lastConnected | string | ISO 8601 timestamp of last connection |
The file is created automatically when you connect the first WiFi device. If it doesn’t exist, functions return an empty array.
Automatic Device Saving
Devices are saved automatically when you successfully connect via WiFi.
On WiFi Connect
When you use /api/adb/connect (server.js:598), the device is automatically saved:
app.post('/api/adb/connect', async (req, res) => {
const { ip, port, label } = req.body;
// ... connection logic ...
if (success) {
const devices = loadSavedDevices();
const existing = devices.find(d => d.ip === ip);
if (existing) {
// Update existing device
existing.port = port;
existing.lastConnected = new Date().toISOString();
if (label) existing.label = label;
} else {
// Add new device
let autoLabel = label || `${ip}:${port}`;
try {
const serial = `${ip}:${port}`;
const props = await runAdb(
`shell "getprop ro.product.manufacturer && getprop ro.product.model"`,
serial, 5000
);
const ls = props.trim().split('\n').map(l => l.trim());
if (ls[0] && ls[1] && ls[0] !== 'N/A') autoLabel = `${ls[0]} ${ls[1]}`;
} catch { }
devices.push({
id: Date.now().toString(),
label: autoLabel,
ip,
port,
lastConnected: new Date().toISOString(),
});
}
writeSavedDevices(devices);
}
});
Auto-Update on Reconnect
When listing devices (server.js:240), if a wireless device’s IP matches a saved device, the record is automatically updated:
// Auto-update saved device if IP matches
if (ip) {
try {
const saved = loadSavedDevices();
const existing = saved.find(d => d.ip === ip);
if (existing) {
existing.lastConnected = new Date().toISOString();
if (label !== serial) existing.label = label;
writeSavedDevices(saved);
}
} catch { }
}
WiFi ADB ports expire when you disable wireless debugging on the device. Saved ports may become invalid and require re-pairing.
Quick Reconnection
The saved devices feature enables one-click reconnection without entering pairing codes.
List Saved Devices
Retrieve all saved devices:
Response:
{
"devices": [
{
"id": "1709856234567",
"label": "Samsung Galaxy S21",
"ip": "192.168.1.42",
"port": "37685",
"lastConnected": "2026-03-07T14:30:45.123Z"
}
]
}
Implementation (server.js:640):
app.get('/api/saved-devices', (req, res) => {
res.json({ devices: loadSavedDevices() });
});
Reconnect to Saved Device
Simply use the saved IP and port with /api/adb/connect:
POST /api/adb/connect
Content-Type: application/json
{
"ip": "192.168.1.42",
"port": "37685"
}
If the port has expired, you’ll need to re-pair with the new port and code.
Managing Saved Devices
Add or Update Device
Manually add or update a device entry:
POST /api/saved-devices
Content-Type: application/json
{
"label": "My Phone",
"ip": "192.168.1.100",
"port": "12345"
}
Implementation (server.js:644):
app.post('/api/saved-devices', (req, res) => {
const { label, ip, port } = req.body;
if (!ip || !port) return res.status(400).json({ error: 'IP y puerto son requeridos' });
const devices = loadSavedDevices();
const existing = devices.find(d => d.ip === ip && d.port === port);
if (existing) {
if (label) existing.label = label;
existing.lastConnected = new Date().toISOString();
} else {
devices.push({
id: Date.now().toString(),
label: label || `${ip}:${port}`,
ip,
port,
lastConnected: new Date().toISOString(),
});
}
writeSavedDevices(devices);
res.json({ ok: true });
});
If a device with the same IP and port already exists, only the label and timestamp are updated.
Delete Saved Device
Remove a device by its unique ID:
DELETE /api/saved-devices/:id
Example:
DELETE /api/saved-devices/1709856234567
Implementation (server.js:665):
app.delete('/api/saved-devices/:id', (req, res) => {
const { id } = req.params;
let devices = loadSavedDevices();
devices = devices.filter(d => d.id !== id);
writeSavedDevices(devices);
res.json({ ok: true });
});
Port Changes
When wireless debugging restarts, the connection port changes. Update it:
POST /api/saved-devices
Content-Type: application/json
{
"ip": "192.168.1.42",
"port": "38192", // New port
"label": "Samsung Galaxy S21"
}
The API uses IP as the lookup key, so it updates the existing device’s port.
Custom Labels
You can override the auto-detected label with a custom name:
POST /api/saved-devices
Content-Type: application/json
{
"ip": "192.168.1.42",
"port": "37685",
"label": "Work Phone" // Custom label
}
CLI Saved Devices Management
The batch script provides a menu for managing saved devices (apk-downloader.bat:1068).
View Saved Devices
The script reads devices.json using PowerShell:
for /f "usebackq delims=" %%L in (`powershell -Command "$d=Get-Content '!DEV_FILE!' -Raw|ConvertFrom-Json;$i=0;foreach($dev in $d){$i++;Write-Output \"$i|$($dev.label)|$($dev.ip)|$($dev.port)|$($dev.id)\"}"`) do (
set "SLINE=%%L"
for /f "tokens=1-5 delims=|" %%a in ("!SLINE!") do (
set /a SAVED_COUNT=%%a
set "SAVED_LABEL_%%a=%%b"
set "SAVED_IP_%%a=%%c"
set "SAVED_PORT_%%a=%%d"
set "SAVED_ID_%%a=%%e"
)
)
Connect to Saved Device
Select a device and the script attempts connection (apk-downloader.bat:1123):
echo Conectando a !SC_IP!:!SC_PORT!...
"%ADB_EXE%" connect !SC_IP!:!SC_PORT! 2>&1 | findstr /i "connected" >nul 2>&1
if %errorlevel%==0 (
echo [OK] Conectado a !SC_LABEL!
) else (
echo [!] No se pudo conectar. Los puertos pueden haber caducado.
)
Delete Saved Device
Removes a device from the JSON file using PowerShell (apk-downloader.bat:1186):
powershell -Command "$f='!DEV_FILE!';$d=Get-Content $f -Raw|ConvertFrom-Json;$d=$d|Where-Object{$_.id -ne '!DEL_ID!'};if($d -eq $null){$d=@()};ConvertTo-Json @($d) -Depth 5|Set-Content $f"
Loading and Writing Functions
Load Saved Devices
Reads devices.json with in-memory caching (server.js:50):
function loadSavedDevices() {
if (_savedDevicesCache) return _savedDevicesCache;
try {
if (fs.existsSync(DEVICES_FILE)) {
_savedDevicesCache = JSON.parse(fs.readFileSync(DEVICES_FILE, 'utf8'));
return _savedDevicesCache;
}
} catch { }
_savedDevicesCache = [];
return _savedDevicesCache;
}
The cache (_savedDevicesCache) prevents reading the file on every API call. It’s updated when devices are written.
Write Saved Devices
Updates the file and cache (server.js:62):
function writeSavedDevices(devices) {
_savedDevicesCache = devices;
fs.writeFileSync(DEVICES_FILE, JSON.stringify(devices, null, 2), 'utf8');
}
The file is written with 2-space indentation for readability.
Port Expiration Handling
Critical: WiFi ADB ports expire when you:
- Disable wireless debugging
- Restart the device
- Toggle airplane mode
- Disconnect from WiFi network
When a saved port expires:
- Connection attempts will fail with timeout or “connection refused”
- You must re-enable wireless debugging on the device
- A new pairing code and ports are generated
- Re-pair using
/api/adb/pair with the new code
- Connect using
/api/adb/connect with the new port
- The saved device is automatically updated with the new port
CLI Re-pairing Flow
The batch script handles expired ports gracefully (apk-downloader.bat:1150):
if %errorlevel%==0 (
echo [OK] Conectado a !SC_LABEL!
else (
echo [!] No se pudo conectar. Los puertos pueden haber caducado.
echo ¿Deseas re-vincular este dispositivo? [S/N]
set /p RELINK_CHOICE=" "
if /i "!RELINK_CHOICE!"=="S" (
set /p NEW_PAIR_PORT=" Nuevo puerto de vinculacion: "
set /p NEW_CODE=" Codigo de vinculacion: "
"%ADB_EXE%" pair !SC_IP!:!NEW_PAIR_PORT! !NEW_CODE!
set /p NEW_CONN_PORT=" Nuevo puerto de conexion: "
"%ADB_EXE%" connect !SC_IP!:!NEW_CONN_PORT!
# Update port in devices.json
powershell -Command "...$e.port='!NEW_CONN_PORT!';..."
)
)
Best Practices
IP Address Stability
For reliable reconnection:
- Assign static IP to your device in router settings (DHCP reservation)
- Use static IP on the device’s WiFi settings
- Avoid letting IP addresses change due to DHCP lease expiration
Regular Cleanup
Periodically remove devices you no longer use:
DELETE /api/saved-devices/{old-device-id}
Backup devices.json
The file is a simple JSON array - back it up to preserve your saved devices:
cp devices.json devices.backup.json
Troubleshooting
Device Not Saving
If devices aren’t being saved after connection:
- Check
devices.json file permissions (must be writable)
- Verify the connection was successful (check response from
/api/adb/connect)
- Look for errors in server logs
Connection Fails for Saved Device
Possible causes:
- Port expired (re-pair required)
- Device IP changed (update IP in saved devices)
- Device disconnected from WiFi
- Wireless debugging disabled on device
Duplicate Devices
The API uses IP + port as the uniqueness key. If you connect to the same device with different ports, you may have duplicates. Delete the old entry manually.
If you manually edit devices.json and break the JSON syntax:
- The file will fail to parse and return an empty array
- Fix the JSON syntax or delete the file
- The file will be recreated on the next device save