- 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.
170 lines
6.2 KiB
Java
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));
|
|
}
|
|
}
|