Files
furbo-vpn-edition/app/src/main/java/com/streamplayer/DeviceRegistry.java
Renato a4e8deb45a Fix DNS issues: Add 4 DoH servers with fallback, remove ineffective DNSSetter
- Remove DNSSetter.java (System.setProperty doesn't affect Android DNS)
- Update NetworkUtils with 4 DNS over HTTPS providers:
  * Google DNS (8.8.8.8) - Primary
  * Cloudflare (1.1.1.1) - Secondary
  * AdGuard (94.140.14.14) - Tertiary
  * Quad9 (9.9.9.9) - Quaternary
- Update DeviceRegistry to use NetworkUtils client
- Update UpdateManager to use NetworkUtils client
- Remove DNSSetter call from PlayerActivity

This ensures the app works even when ISPs block specific DNS servers.
2026-02-12 21:53:44 -03:00

170 lines
6.2 KiB
Java

package com.streamplayer;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* Informa al dashboard qué dispositivos tienen instalada la app y permite bloquearlos remotamente.
*/
public class DeviceRegistry {
public interface Callback {
void onAllowed();
void onBlocked(String reason, String tokenPart);
void onError(String message);
}
private static final String TAG = "DeviceRegistry";
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
private final Context appContext;
private final OkHttpClient httpClient;
private final ExecutorService executorService;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
public DeviceRegistry(Context context) {
this.appContext = context.getApplicationContext();
// Usar NetworkUtils para obtener cliente con DNS over HTTPS configurado
this.httpClient = NetworkUtils.getClient();
this.executorService = Executors.newSingleThreadExecutor();
}
public void syncDevice(Callback callback) {
if (TextUtils.isEmpty(BuildConfig.DEVICE_REGISTRY_URL)) {
postAllowed(callback);
return;
}
executorService.execute(() -> {
try {
JSONObject payload = new JSONObject();
payload.put("deviceId", getDeviceId());
payload.put("deviceName", Build.MODEL);
payload.put("model", Build.MODEL);
payload.put("manufacturer", capitalize(Build.MANUFACTURER));
payload.put("osVersion", Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ")");
payload.put("appVersionName", BuildConfig.VERSION_NAME);
payload.put("appVersionCode", BuildConfig.VERSION_CODE);
String endpoint = sanitizeBaseUrl(BuildConfig.DEVICE_REGISTRY_URL) + "/api/devices/register";
RequestBody body = RequestBody.create(payload.toString(), JSON);
Request request = new Request.Builder()
.url(endpoint)
.post(body)
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("HTTP " + response.code());
}
String responseText = response.body().string();
// Validar que no sea HTML antes de parsear
if (responseText != null) {
String trimmed = responseText.trim();
if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
throw new IOException("El servidor devolvió HTML en lugar de JSON");
}
}
JSONObject json = new JSONObject(responseText);
JSONObject deviceJson = json.optJSONObject("device");
JSONObject verificationJson = json.optJSONObject("verification");
boolean blocked = json.optBoolean("blocked", false);
String reason = json.optString("message");
if (TextUtils.isEmpty(reason) && deviceJson != null) {
reason = deviceJson.optString("notes", "");
}
String tokenPart = "";
if (verificationJson != null) {
boolean verificationRequired = verificationJson.optBoolean("required", false);
blocked = blocked || verificationRequired;
tokenPart = verificationJson.optString("clientTokenPart", "");
}
if (blocked) {
postBlocked(callback, reason, tokenPart);
} else {
postAllowed(callback);
}
}
} catch (IOException | JSONException e) {
Log.w(TAG, "Device sync error", e);
postError(callback, e.getMessage());
}
});
}
private String sanitizeBaseUrl(String base) {
if (TextUtils.isEmpty(base)) {
return "";
}
if (base.endsWith("/")) {
return base.substring(0, base.length() - 1);
}
return base;
}
private String getDeviceId() {
String id = Settings.Secure.getString(appContext.getContentResolver(),
Settings.Secure.ANDROID_ID);
if (TextUtils.isEmpty(id)) {
id = Build.MODEL + "-" + Build.BOARD + "-" + BuildConfig.VERSION_CODE;
}
return id;
}
private String capitalize(String value) {
if (TextUtils.isEmpty(value)) {
return "";
}
return value.substring(0, 1).toUpperCase(Locale.getDefault())
+ value.substring(1);
}
public void release() {
executorService.shutdownNow();
}
private void postAllowed(Callback callback) {
if (callback == null) {
return;
}
mainHandler.post(callback::onAllowed);
}
private void postBlocked(Callback callback, String reason, String tokenPart) {
if (callback == null) {
return;
}
String reasonText = reason == null ? "" : reason;
String token = tokenPart == null ? "" : tokenPart;
mainHandler.post(() -> callback.onBlocked(reasonText, token));
}
private void postError(Callback callback, String message) {
if (callback == null) {
return;
}
mainHandler.post(() -> callback.onError(message));
}
}