Fix UI mismatching actual configuration due to multiprocess

This commit is contained in:
Marvin W 2020-09-02 21:41:15 +02:00
parent 3ef330ad7b
commit d641ca7e7e
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
23 changed files with 608 additions and 259 deletions

View File

@ -193,6 +193,8 @@
</intent-filter> </intent-filter>
</service> </service>
<receiver android:name="org.microg.gms.checkin.ServiceInfoReceiver" />
<receiver android:name="org.microg.gms.checkin.TriggerReceiver"> <receiver android:name="org.microg.gms.checkin.TriggerReceiver">
<intent-filter> <intent-filter>
<action android:name="android.server.checkin.CHECKIN" /> <action android:name="android.server.checkin.CHECKIN" />
@ -238,7 +240,7 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name="org.microg.gms.gcm.StatusInfoProvider" /> <receiver android:name="org.microg.gms.gcm.ServiceInfoReceiver" />
<receiver android:name="org.microg.gms.gcm.TriggerReceiver"> <receiver android:name="org.microg.gms.gcm.TriggerReceiver">
<intent-filter> <intent-filter>
@ -627,6 +629,8 @@
</intent-filter> </intent-filter>
</service> </service>
<receiver android:name="org.microg.gms.snet.ServiceInfoReceiver" />
<service android:name="org.microg.gms.snet.SafetyNetClientService"> <service android:name="org.microg.gms.snet.SafetyNetClientService">
<intent-filter> <intent-filter>
<action android:name="com.google.android.gms.safetynet.service.START" /> <action android:name="com.google.android.gms.safetynet.service.START" />

View File

@ -9,6 +9,11 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log;
import org.microg.gms.common.PackageUtils;
import java.io.File;
public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeListener { public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String PREF_ENABLE_CHECKIN = "checkin_enable_service"; public static final String PREF_ENABLE_CHECKIN = "checkin_enable_service";
@ -16,6 +21,9 @@ public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeL
public static CheckinPrefs get(Context context) { public static CheckinPrefs get(Context context) {
if (INSTANCE == null) { if (INSTANCE == null) {
if (!context.getPackageName().equals(PackageUtils.getProcessName())) {
Log.w("Preferences", CheckinPrefs.class.getName() + " initialized outside main process", new RuntimeException());
}
if (context == null) return new CheckinPrefs(null); if (context == null) return new CheckinPrefs(null);
INSTANCE = new CheckinPrefs(context.getApplicationContext()); INSTANCE = new CheckinPrefs(context.getApplicationContext());
} }
@ -23,18 +31,30 @@ public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeL
} }
private SharedPreferences preferences; private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private boolean checkinEnabled = false; private boolean checkinEnabled = false;
private CheckinPrefs(Context context) { private CheckinPrefs(Context context) {
if (context != null) { if (context != null) {
preferences = PreferenceManager.getDefaultSharedPreferences(context); preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.registerOnSharedPreferenceChangeListener(this); preferences.registerOnSharedPreferenceChangeListener(this);
try {
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
} catch (Exception ignored) {
}
update(); update();
} }
} }
private boolean getSettingsBoolean(String key, boolean def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getBoolean(key, def);
}
return preferences.getBoolean(key, def);
}
private void update() { private void update() {
checkinEnabled = preferences.getBoolean(PREF_ENABLE_CHECKIN, false); checkinEnabled = getSettingsBoolean(PREF_ENABLE_CHECKIN, false);
} }
@Override @Override

View File

@ -55,6 +55,6 @@ public class LastCheckinInfo {
.putLong(PREF_SECURITY_TOKEN, securityToken) .putLong(PREF_SECURITY_TOKEN, securityToken)
.putString(PREF_VERSION_INFO, versionInfo) .putString(PREF_VERSION_INFO, versionInfo)
.putString(PREF_DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo) .putString(PREF_DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo)
.apply(); .commit();
} }
} }

View File

@ -21,6 +21,7 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
import java.util.ArrayList; import java.util.ArrayList;
@ -65,6 +66,9 @@ public class GcmDatabase extends SQLiteOpenHelper {
public GcmDatabase(Context context) { public GcmDatabase(Context context) {
super(context, DB_NAME, null, DB_VERSION); super(context, DB_NAME, null, DB_VERSION);
this.context = context; this.context = context;
if (Build.VERSION.SDK_INT >= 16) {
this.setWriteAheadLoggingEnabled(true);
}
} }
public static class App { public static class App {

View File

@ -24,6 +24,9 @@ import android.net.NetworkInfo;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import org.microg.gms.common.PackageUtils;
import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -50,6 +53,9 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
public static GcmPrefs get(Context context) { public static GcmPrefs get(Context context) {
if (INSTANCE == null) { if (INSTANCE == null) {
if (!context.getPackageName().equals(PackageUtils.getProcessName())) {
Log.w("Preferences", GcmPrefs.class.getName() + " initialized outside main process", new RuntimeException());
}
if (context == null) return new GcmPrefs(null); if (context == null) return new GcmPrefs(null);
INSTANCE = new GcmPrefs(context.getApplicationContext()); INSTANCE = new GcmPrefs(context.getApplicationContext());
} }
@ -70,30 +76,43 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
private int learntMobile = 300000; private int learntMobile = 300000;
private int learntOther = 300000; private int learntOther = 300000;
private SharedPreferences defaultPreferences; private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private GcmPrefs(Context context) { private GcmPrefs(Context context) {
if (context != null) { if (context != null) {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context); preferences = PreferenceManager.getDefaultSharedPreferences(context);
defaultPreferences.registerOnSharedPreferenceChangeListener(this); preferences.registerOnSharedPreferenceChangeListener(this);
try {
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
} catch (Exception ignored) {
}
update(); update();
} }
} }
private boolean getSettingsBoolean(String key, boolean def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getBoolean(key, def);
}
return preferences.getBoolean(key, def);
}
public void update() { public void update() {
gcmLogEnabled = defaultPreferences.getBoolean(PREF_FULL_LOG, true); gcmEnabled = getSettingsBoolean(PREF_ENABLE_GCM, false);
lastPersistedId = defaultPreferences.getString(PREF_LAST_PERSISTENT_ID, ""); gcmLogEnabled = getSettingsBoolean(PREF_FULL_LOG, true);
confirmNewApps = defaultPreferences.getBoolean(PREF_CONFIRM_NEW_APPS, false); confirmNewApps = getSettingsBoolean(PREF_CONFIRM_NEW_APPS, false);
gcmEnabled = defaultPreferences.getBoolean(PREF_ENABLE_GCM, false);
networkMobile = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_MOBILE, "0")); lastPersistedId = preferences.getString(PREF_LAST_PERSISTENT_ID, "");
networkWifi = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_WIFI, "0"));
networkRoaming = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_ROAMING, "0"));
networkOther = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_OTHER, "0"));
learntMobile = defaultPreferences.getInt(PREF_LEARNT_MOBILE, 300000); networkMobile = Integer.parseInt(preferences.getString(PREF_NETWORK_MOBILE, "0"));
learntWifi = defaultPreferences.getInt(PREF_LEARNT_WIFI, 300000); networkWifi = Integer.parseInt(preferences.getString(PREF_NETWORK_WIFI, "0"));
learntOther = defaultPreferences.getInt(PREF_LEARNT_OTHER, 300000); networkRoaming = Integer.parseInt(preferences.getString(PREF_NETWORK_ROAMING, "0"));
networkOther = Integer.parseInt(preferences.getString(PREF_NETWORK_OTHER, "0"));
learntMobile = preferences.getInt(PREF_LEARNT_MOBILE, 300000);
learntWifi = preferences.getInt(PREF_LEARNT_WIFI, 300000);
learntOther = preferences.getInt(PREF_LEARNT_OTHER, 300000);
} }
public String getNetworkPrefForInfo(NetworkInfo info) { public String getNetworkPrefForInfo(NetworkInfo info) {
@ -182,7 +201,7 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
learntMobile = Math.max(MIN_INTERVAL, Math.min(learntMobile, MAX_INTERVAL)); learntMobile = Math.max(MIN_INTERVAL, Math.min(learntMobile, MAX_INTERVAL));
learntWifi = Math.max(MIN_INTERVAL, Math.min(learntWifi, MAX_INTERVAL)); learntWifi = Math.max(MIN_INTERVAL, Math.min(learntWifi, MAX_INTERVAL));
learntOther = Math.max(MIN_INTERVAL, Math.min(learntOther, MAX_INTERVAL)); learntOther = Math.max(MIN_INTERVAL, Math.min(learntOther, MAX_INTERVAL));
defaultPreferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply(); preferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply();
} }
@Override @Override
@ -223,11 +242,11 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
public void extendLastPersistedId(String id) { public void extendLastPersistedId(String id) {
if (!lastPersistedId.isEmpty()) lastPersistedId += "|"; if (!lastPersistedId.isEmpty()) lastPersistedId += "|";
lastPersistedId += id; lastPersistedId += id;
defaultPreferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply(); preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
} }
public void clearLastPersistedId() { public void clearLastPersistedId() {
lastPersistedId = ""; lastPersistedId = "";
defaultPreferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply(); preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
} }
} }

View File

@ -176,8 +176,8 @@ public class McsService extends Service implements Handler.Callback {
} }
} }
private static void logd(String msg) { private static void logd(Context context, String msg) {
if (GcmPrefs.get(null).isGcmLogEnabled()) Log.d(TAG, msg); if (context == null || GcmPrefs.get(context).isGcmLogEnabled()) Log.d(TAG, msg);
} }
@Override @Override
@ -237,7 +237,7 @@ public class McsService extends Service implements Handler.Callback {
public synchronized static boolean isConnected() { public synchronized static boolean isConnected() {
if (inputStream == null || !inputStream.isAlive() || outputStream == null || !outputStream.isAlive()) { if (inputStream == null || !inputStream.isAlive() || outputStream == null || !outputStream.isAlive()) {
logd("Connection is not enabled or dead."); logd(null, "Connection is not enabled or dead.");
return false; return false;
} }
// consider connection to be dead if we did not receive an ack within twice the heartbeat interval // consider connection to be dead if we did not receive an ack within twice the heartbeat interval
@ -245,7 +245,7 @@ public class McsService extends Service implements Handler.Callback {
if (heartbeatMs < 0) { if (heartbeatMs < 0) {
closeAll(); closeAll();
} else if (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime > 2 * heartbeatMs) { } else if (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime > 2 * heartbeatMs) {
logd("No heartbeat for " + (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime) / 1000 + " seconds, connection assumed to be dead after " + 2 * heartbeatMs / 1000 + " seconds"); logd(null, "No heartbeat for " + (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime) / 1000 + " seconds, connection assumed to be dead after " + 2 * heartbeatMs / 1000 + " seconds");
GcmPrefs.get(null).learnTimeout(activeNetworkPref); GcmPrefs.get(null).learnTimeout(activeNetworkPref);
return false; return false;
} }
@ -259,7 +259,7 @@ public class McsService extends Service implements Handler.Callback {
public static void scheduleReconnect(Context context) { public static void scheduleReconnect(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE); AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
long delay = getCurrentDelay(); long delay = getCurrentDelay();
logd("Scheduling reconnect in " + delay / 1000 + " seconds..."); logd(context, "Scheduling reconnect in " + delay / 1000 + " seconds...");
PendingIntent pi = PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0); PendingIntent pi = PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi); alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi);
@ -275,7 +275,7 @@ public class McsService extends Service implements Handler.Callback {
if (heartbeatMs < 0) { if (heartbeatMs < 0) {
closeAll(); closeAll();
} }
logd("Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds..."); logd(context, "Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds...");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// This is supposed to work even when running in idle and without battery optimization disabled // This is supposed to work even when running in idle and without battery optimization disabled
alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent); alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent);
@ -449,11 +449,11 @@ public class McsService extends Service implements Handler.Callback {
return; return;
} }
logd("Starting MCS connection..."); logd(this, "Starting MCS connection...");
Socket socket = new Socket(SERVICE_HOST, SERVICE_PORT); Socket socket = new Socket(SERVICE_HOST, SERVICE_PORT);
logd("Connected to " + SERVICE_HOST + ":" + SERVICE_PORT); logd(this, "Connected to " + SERVICE_HOST + ":" + SERVICE_PORT);
sslSocket = SSLContext.getDefault().getSocketFactory().createSocket(socket, SERVICE_HOST, SERVICE_PORT, true); sslSocket = SSLContext.getDefault().getSocketFactory().createSocket(socket, SERVICE_HOST, SERVICE_PORT, true);
logd("Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT); logd(this, "Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT);
inputStream = new McsInputStream(sslSocket.getInputStream(), rootHandler); inputStream = new McsInputStream(sslSocket.getInputStream(), rootHandler);
outputStream = new McsOutputStream(sslSocket.getOutputStream(), rootHandler); outputStream = new McsOutputStream(sslSocket.getOutputStream(), rootHandler);
inputStream.start(); inputStream.start();
@ -476,7 +476,7 @@ public class McsService extends Service implements Handler.Callback {
private void handleLoginResponse(LoginResponse loginResponse) { private void handleLoginResponse(LoginResponse loginResponse) {
if (loginResponse.error == null) { if (loginResponse.error == null) {
GcmPrefs.get(this).clearLastPersistedId(); GcmPrefs.get(this).clearLastPersistedId();
logd("Logged in"); logd(this, "Logged in");
wakeLock.release(); wakeLock.release();
} else { } else {
throw new RuntimeException("Could not login: " + loginResponse.error); throw new RuntimeException("Could not login: " + loginResponse.error);
@ -561,13 +561,13 @@ public class McsService extends Service implements Handler.Callback {
if (receiverPermission == null) { if (receiverPermission == null) {
// Without receiver permission, we only restrict by package name // Without receiver permission, we only restrict by package name
logd("Deliver message to all receivers in package " + packageName); logd(this, "Deliver message to all receivers in package " + packageName);
intent.setPackage(packageName); intent.setPackage(packageName);
sendOrderedBroadcast(intent, null); sendOrderedBroadcast(intent, null);
} else { } else {
List<ResolveInfo> infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER); List<ResolveInfo> infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER);
if (infos == null || infos.isEmpty()) { if (infos == null || infos.isEmpty()) {
logd("No target for message, wut?"); logd(this, "No target for message, wut?");
} else { } else {
for (ResolveInfo resolveInfo : infos) { for (ResolveInfo resolveInfo : infos) {
Intent targetIntent = new Intent(intent); Intent targetIntent = new Intent(intent);
@ -578,7 +578,7 @@ public class McsService extends Service implements Handler.Callback {
try { try {
if (getUserIdMethod != null && addPowerSaveTempWhitelistAppMethod != null && deviceIdleController != null) { if (getUserIdMethod != null && addPowerSaveTempWhitelistAppMethod != null && deviceIdleController != null) {
int userId = (int) getUserIdMethod.invoke(null, getPackageManager().getApplicationInfo(packageName, 0).uid); int userId = (int) getUserIdMethod.invoke(null, getPackageManager().getApplicationInfo(packageName, 0).uid);
logd("Adding app " + packageName + " for userId " + userId + " to the temp whitelist"); logd(this, "Adding app " + packageName + " for userId " + userId + " to the temp whitelist");
addPowerSaveTempWhitelistAppMethod.invoke(deviceIdleController, packageName, 10000, userId, "GCM Push"); addPowerSaveTempWhitelistAppMethod.invoke(deviceIdleController, packageName, 10000, userId, "GCM Push");
} }
} catch (Exception e) { } catch (Exception e) {
@ -586,11 +586,11 @@ public class McsService extends Service implements Handler.Callback {
} }
} }
// We don't need receiver permission for our own package // We don't need receiver permission for our own package
logd("Deliver message to own receiver " + resolveInfo); logd(this, "Deliver message to own receiver " + resolveInfo);
sendOrderedBroadcast(targetIntent, null); sendOrderedBroadcast(targetIntent, null);
} else if (resolveInfo.filter.hasCategory(packageName)) { } else if (resolveInfo.filter.hasCategory(packageName)) {
// Permission required // Permission required
logd("Deliver message to third-party receiver (with permission check)" + resolveInfo); logd(this, "Deliver message to third-party receiver (with permission check)" + resolveInfo);
sendOrderedBroadcast(targetIntent, receiverPermission); sendOrderedBroadcast(targetIntent, receiverPermission);
} }
} }
@ -640,21 +640,21 @@ public class McsService extends Service implements Handler.Callback {
return true; return true;
case MSG_INPUT_ERROR: case MSG_INPUT_ERROR:
case MSG_OUTPUT_ERROR: case MSG_OUTPUT_ERROR:
logd("I/O error: " + msg.obj); logd(this, "I/O error: " + msg.obj);
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, msg.obj)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, msg.obj));
return true; return true;
case MSG_TEARDOWN: case MSG_TEARDOWN:
logd("Teardown initiated, reason: " + msg.obj); logd(this, "Teardown initiated, reason: " + msg.obj);
handleTeardown(msg); handleTeardown(msg);
return true; return true;
case MSG_CONNECT: case MSG_CONNECT:
logd("Connect initiated, reason: " + msg.obj); logd(this, "Connect initiated, reason: " + msg.obj);
if (!isConnected()) { if (!isConnected()) {
connect(); connect();
} }
return true; return true;
case MSG_HEARTBEAT: case MSG_HEARTBEAT:
logd("Heartbeat initiated, reason: " + msg.obj); logd(this, "Heartbeat initiated, reason: " + msg.obj);
if (isConnected()) { if (isConnected()) {
HeartbeatPing.Builder ping = new HeartbeatPing.Builder(); HeartbeatPing.Builder ping = new HeartbeatPing.Builder();
if (inputStream.newStreamIdAvailable()) { if (inputStream.newStreamIdAvailable()) {
@ -663,12 +663,12 @@ public class McsService extends Service implements Handler.Callback {
send(MCS_HEARTBEAT_PING_TAG, ping.build()); send(MCS_HEARTBEAT_PING_TAG, ping.build());
scheduleHeartbeat(this); scheduleHeartbeat(this);
} else { } else {
logd("Ignoring heartbeat, not connected!"); logd(this, "Ignoring heartbeat, not connected!");
scheduleReconnect(this); scheduleReconnect(this);
} }
return true; return true;
case MSG_ACK: case MSG_ACK:
logd("Ack initiated, reason: " + msg.obj); logd(this, "Ack initiated, reason: " + msg.obj);
if (isConnected()) { if (isConnected()) {
IqStanza.Builder iq = new IqStanza.Builder() IqStanza.Builder iq = new IqStanza.Builder()
.type(IqStanza.IqType.SET) .type(IqStanza.IqType.SET)
@ -680,11 +680,11 @@ public class McsService extends Service implements Handler.Callback {
} }
send(MCS_IQ_STANZA_TAG, iq.build()); send(MCS_IQ_STANZA_TAG, iq.build());
} else { } else {
logd("Ignoring ack, not connected!"); logd(this, "Ignoring ack, not connected!");
} }
return true; return true;
case MSG_OUTPUT_READY: case MSG_OUTPUT_READY:
logd("Sending login request..."); logd(this, "Sending login request...");
send(MCS_LOGIN_REQUEST_TAG, buildLoginRequest()); send(MCS_LOGIN_REQUEST_TAG, buildLoginRequest());
return true; return true;
case MSG_OUTPUT_DONE: case MSG_OUTPUT_DONE:

View File

@ -45,7 +45,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
public synchronized static void register(Context context) { public synchronized static void register(Context context) {
if (SDK_INT >= N && !registered) { if (SDK_INT >= N && !registered) {
IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"); IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
context.registerReceiver(new TriggerReceiver(), intentFilter); context.getApplicationContext().registerReceiver(new TriggerReceiver(), intentFilter);
registered = true; registered = true;
} }
} }

View File

@ -19,6 +19,11 @@ package org.microg.gms.snet;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log;
import org.microg.gms.common.PackageUtils;
import java.io.File;
public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChangeListener { public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String OFFICIAL_ATTEST_BASE_URL = "https://www.googleapis.com/androidcheck/v1/attestations/attest"; private static final String OFFICIAL_ATTEST_BASE_URL = "https://www.googleapis.com/androidcheck/v1/attestations/attest";
@ -33,6 +38,9 @@ public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChang
public static SafetyNetPrefs get(Context context) { public static SafetyNetPrefs get(Context context) {
if (INSTANCE == null) { if (INSTANCE == null) {
if (!context.getPackageName().equals(PackageUtils.getProcessName())) {
Log.w("Preferences", SafetyNetPrefs.class.getName() + " initialized outside main process", new RuntimeException());
}
if (context == null) return new SafetyNetPrefs(null); if (context == null) return new SafetyNetPrefs(null);
INSTANCE = new SafetyNetPrefs(context.getApplicationContext()); INSTANCE = new SafetyNetPrefs(context.getApplicationContext());
} }
@ -45,22 +53,41 @@ public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChang
private boolean thirdParty; private boolean thirdParty;
private String customUrl; private String customUrl;
private SharedPreferences defaultPreferences; private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private SafetyNetPrefs(Context context) { private SafetyNetPrefs(Context context) {
if (context != null) { if (context != null) {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context); preferences = PreferenceManager.getDefaultSharedPreferences(context);
defaultPreferences.registerOnSharedPreferenceChangeListener(this); preferences.registerOnSharedPreferenceChangeListener(this);
try {
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
} catch (Exception ignored) {
}
update(); update();
} }
} }
private boolean getSettingsBoolean(String key, boolean def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getBoolean(key, def);
}
return preferences.getBoolean(key, def);
}
private String getSettingsString(String key, String def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getString(key, def);
}
return preferences.getString(key, def);
}
public void update() { public void update() {
disabled = defaultPreferences.getBoolean(PREF_SNET_DISABLED, true); disabled = getSettingsBoolean(PREF_SNET_DISABLED, true);
official = defaultPreferences.getBoolean(PREF_SNET_OFFICIAL, false); official = getSettingsBoolean(PREF_SNET_OFFICIAL, false);
selfSigned = defaultPreferences.getBoolean(PREF_SNET_SELF_SIGNED, false); selfSigned = getSettingsBoolean(PREF_SNET_SELF_SIGNED, false);
thirdParty = defaultPreferences.getBoolean(PREF_SNET_THIRD_PARTY, false); thirdParty = getSettingsBoolean(PREF_SNET_THIRD_PARTY, false);
customUrl = defaultPreferences.getString(PREF_SNET_CUSTOM_URL, null); customUrl = getSettingsString(PREF_SNET_CUSTOM_URL, null);
} }
@Override @Override
@ -73,11 +100,13 @@ public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChang
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
defaultPreferences.edit().putBoolean(PREF_SNET_DISABLED, !enabled).apply(); SharedPreferences.Editor edit = preferences.edit();
edit.putBoolean(PREF_SNET_DISABLED, !enabled);
if (enabled && !isEnabled()) { if (enabled && !isEnabled()) {
official = true; official = true;
defaultPreferences.edit().putBoolean(PREF_SNET_OFFICIAL, true).apply(); edit.putBoolean(PREF_SNET_OFFICIAL, true);
} }
edit.commit();
} }
public boolean isSelfSigned() { public boolean isSelfSigned() {

View File

@ -1,115 +0,0 @@
package org.microg.gms.ui;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.navigation.NavOptions;
import androidx.navigation.fragment.NavHostFragment;
import com.google.android.gms.R;
import org.microg.gms.checkin.CheckinPrefs;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.GcmPrefs;
import org.microg.gms.nearby.exposurenotification.ExposurePreferences;
import org.microg.gms.snet.SafetyNetPrefs;
import org.microg.tools.ui.ResourceSettingsFragment;
public class SettingsFragment extends ResourceSettingsFragment {
public static final String PREF_ABOUT = "pref_about";
public static final String PREF_GCM = "pref_gcm";
public static final String PREF_SNET = "pref_snet";
public static final String PREF_UNIFIEDNLP = "pref_unifiednlp";
public static final String PREF_CHECKIN = "pref_checkin";
public static final String PREF_EXPOSURE = "pref_exposure";
public SettingsFragment() {
preferencesResource = R.xml.preferences_start;
}
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
updateDetails();
}
@Override
public void onResume() {
super.onResume();
updateDetails();
}
private void updateDetails() {
findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext())));
findPreference(PREF_ABOUT).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openAbout, null);
return true;
});
if (GcmPrefs.get(getContext()).isEnabled()) {
GcmDatabase database = new GcmDatabase(getContext());
int regCount = database.getRegistrationList().size();
database.close();
findPreference(PREF_GCM).setSummary(getString(R.string.service_status_enabled_short) + " - " + getResources().getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount));
} else {
findPreference(PREF_GCM).setSummary(R.string.service_status_disabled_short);
}
findPreference(PREF_GCM).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openGcmSettings, null);
return true;
});
if (SafetyNetPrefs.get(getContext()).isEnabled()) {
String snet_info = "";
if (SafetyNetPrefs.get(getContext()).isOfficial()) {
snet_info = getString(R.string.pref_snet_status_official_info);
} else if (SafetyNetPrefs.get(getContext()).isSelfSigned()) {
snet_info = getString(R.string.pref_snet_status_self_signed_info);
} else if (SafetyNetPrefs.get(getContext()).isThirdParty()) {
snet_info = getString(R.string.pref_snet_status_third_party_info);
}
findPreference(PREF_SNET).setSummary(getString(R.string.service_status_enabled_short));
} else {
findPreference(PREF_SNET).setSummary(R.string.service_status_disabled_short);
}
findPreference(PREF_SNET).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openSafetyNetSettings, null);
return true;
});
// Preferences unifiedNlPrefs = new Preferences(getContext());
// int backendCount = TextUtils.isEmpty(unifiedNlPrefs.getLocationBackends()) ? 0 :
// Preferences.splitBackendString(unifiedNlPrefs.getLocationBackends()).length;
// backendCount += TextUtils.isEmpty(unifiedNlPrefs.getGeocoderBackends()) ? 0 :
// Preferences.splitBackendString(unifiedNlPrefs.getGeocoderBackends()).length;
// findPreference(PREF_UNIFIEDNLP).setSummary(getResources().getQuantityString(R.plurals.pref_unifiednlp_summary, backendCount, backendCount));
findPreference(PREF_UNIFIEDNLP).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openUnifiedNlpSettings, null);
return true;
});
if (Build.VERSION.SDK_INT >= 21) {
findPreference(PREF_EXPOSURE).setVisible(true);
if (new ExposurePreferences(getContext()).getScannerEnabled()) {
findPreference(PREF_EXPOSURE).setSummary(getString(R.string.service_status_enabled_short));
} else {
findPreference(PREF_EXPOSURE).setSummary(R.string.service_status_disabled_short);
}
findPreference(PREF_EXPOSURE).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openExposureNotificationSettings, null);
return true;
});
} else {
findPreference(PREF_EXPOSURE).setVisible(false);
}
boolean checkinEnabled = CheckinPrefs.get(getContext()).isEnabled();
findPreference(PREF_CHECKIN).setSummary(checkinEnabled ? R.string.service_status_enabled_short : R.string.service_status_disabled_short);
findPreference(PREF_CHECKIN).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openCheckinSettings, null);
return true;
});
}
}

View File

@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.checkin
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import java.io.Serializable
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
private const val ACTION_SERVICE_INFO_REQUEST = "org.microg.gms.checkin.SERVICE_INFO_REQUEST"
private const val ACTION_UPDATE_CONFIGURATION = "org.microg.gms.checkin.UPDATE_CONFIGURATION"
private const val ACTION_SERVICE_INFO_RESPONSE = "org.microg.gms.checkin.SERVICE_INFO_RESPONSE"
private const val EXTRA_SERVICE_INFO = "org.microg.gms.checkin.SERVICE_INFO"
private const val EXTRA_CONFIGURATION = "org.microg.gms.checkin.CONFIGURATION"
private const val TAG = "GmsCheckinStatusInfo"
data class ServiceInfo(val configuration: ServiceConfiguration, val lastCheckin: Long, val androidId: Long) : Serializable
data class ServiceConfiguration(val enabled: Boolean) : Serializable {
fun saveToPrefs(context: Context) {
CheckinPrefs.setEnabled(context, enabled)
}
}
private fun CheckinPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled)
class ServiceInfoReceiver : BroadcastReceiver() {
private fun sendInfoResponse(context: Context) {
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
setPackage(context.packageName)
val checkinInfo = LastCheckinInfo.read(context)
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(CheckinPrefs.get(context).toConfiguration(), checkinInfo.lastCheckin, checkinInfo.androidId))
}, null)
}
override fun onReceive(context: Context, intent: Intent) {
try {
when (intent.action) {
ACTION_UPDATE_CONFIGURATION -> {
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context)
}
}
sendInfoResponse(context)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context): ServiceInfo = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.unregisterReceiver(this)
val serviceInfo = try {
intent.getSerializableExtra(EXTRA_SERVICE_INFO) as ServiceInfo
} catch (e: Exception) {
it.resumeWithException(e)
return
}
try {
it.resume(serviceInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}, IntentFilter(ACTION_SERVICE_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(intent, null)
} catch (e: Exception) {
it.resumeWithException(e)
}
}
suspend fun getCheckinServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_SERVICE_INFO_REQUEST
}, context)
suspend fun setCheckinServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_UPDATE_CONFIGURATION
putExtra(EXTRA_CONFIGURATION, configuration)
}, context)

View File

@ -0,0 +1,92 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.gcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import java.io.Serializable
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
private const val ACTION_SERVICE_INFO_REQUEST = "org.microg.gms.gcm.SERVICE_INFO_REQUEST"
private const val ACTION_UPDATE_CONFIGURATION = "org.microg.gms.gcm.UPDATE_CONFIGURATION"
private const val ACTION_SERVICE_INFO_RESPONSE = "org.microg.gms.gcm.SERVICE_INFO_RESPONSE"
private const val EXTRA_SERVICE_INFO = "org.microg.gms.gcm.SERVICE_INFO"
private const val EXTRA_CONFIGURATION = "org.microg.gms.gcm.CONFIGURATION"
private const val TAG = "GmsGcmStatusInfo"
data class ServiceInfo(val configuration: ServiceConfiguration, val connected: Boolean, val startTimestamp: Long) : Serializable
// TODO: Intervals
data class ServiceConfiguration(val enabled: Boolean, val confirmNewApps: Boolean) : Serializable {
fun saveToPrefs(context: Context) {
GcmPrefs.setEnabled(context, enabled)
// TODO: confirm new apps
}
}
private fun GcmPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled, isConfirmNewApps)
class ServiceInfoReceiver : BroadcastReceiver() {
private fun sendInfoResponse(context: Context) {
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
setPackage(context.packageName)
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(GcmPrefs.get(context).toConfiguration(), McsService.isConnected(), McsService.getStartTimestamp()))
}, null)
}
override fun onReceive(context: Context, intent: Intent) {
try {
when (intent.action) {
ACTION_UPDATE_CONFIGURATION -> {
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context)
}
}
sendInfoResponse(context)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context): ServiceInfo = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.unregisterReceiver(this)
val serviceInfo = try {
intent.getSerializableExtra(EXTRA_SERVICE_INFO) as ServiceInfo
} catch (e: Exception) {
it.resumeWithException(e)
return
}
try {
it.resume(serviceInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}, IntentFilter(ACTION_SERVICE_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(intent, null)
} catch (e: Exception) {
it.resumeWithException(e)
}
}
suspend fun getGcmServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_SERVICE_INFO_REQUEST
}, context)
suspend fun setGcmServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_UPDATE_CONFIGURATION
putExtra(EXTRA_CONFIGURATION, configuration)
}, context)

View File

@ -1,53 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.gcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import java.io.Serializable
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
private const val ACTION_STATUS_INFO_REQUEST = "org.microg.gms.STATUS_INFO_REQUEST"
private const val ACTION_STATUS_INFO_RESPONSE = "org.microg.gms.STATUS_INFO_RESPONSE"
private const val EXTRA_STATUS_INFO = "org.microg.gms.STATUS_INFO"
private const val TAG = "GmsGcmStatusInfo"
data class StatusInfo(val connected: Boolean, val startTimestamp: Long) : Serializable
class StatusInfoProvider : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
try {
context.sendOrderedBroadcast(Intent(ACTION_STATUS_INFO_RESPONSE).apply {
setPackage(context.packageName)
putExtra(EXTRA_STATUS_INFO, StatusInfo(McsService.isConnected(), McsService.getStartTimestamp()))
}, null)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
suspend fun getStatusInfo(context: Context): StatusInfo? = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
try {
it.resume(intent?.getSerializableExtra(EXTRA_STATUS_INFO) as? StatusInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
context.unregisterReceiver(this)
}
}, IntentFilter(ACTION_STATUS_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(Intent(context, StatusInfoProvider::class.java), null)
} catch (e: Exception) {
it.resume(null)
}
}

View File

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.snet
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import java.io.Serializable
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
private const val ACTION_SERVICE_INFO_REQUEST = "org.microg.gms.snet.SERVICE_INFO_REQUEST"
private const val ACTION_UPDATE_CONFIGURATION = "org.microg.gms.snet.UPDATE_CONFIGURATION"
private const val ACTION_SERVICE_INFO_RESPONSE = "org.microg.gms.snet.SERVICE_INFO_RESPONSE"
private const val EXTRA_SERVICE_INFO = "org.microg.gms.snet.SERVICE_INFO"
private const val EXTRA_CONFIGURATION = "org.microg.gms.snet.CONFIGURATION"
private const val TAG = "GmsSafetyNetStatusInfo"
data class ServiceInfo(val configuration: ServiceConfiguration) : Serializable
data class ServiceConfiguration(val enabled: Boolean) : Serializable {
fun saveToPrefs(context: Context) {
SafetyNetPrefs.get(context).isEnabled = enabled
}
}
private fun SafetyNetPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled)
class ServiceInfoReceiver : BroadcastReceiver() {
private fun sendInfoResponse(context: Context) {
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
setPackage(context.packageName)
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(SafetyNetPrefs.get(context).toConfiguration()))
}, null)
}
override fun onReceive(context: Context, intent: Intent) {
try {
when (intent.action) {
ACTION_UPDATE_CONFIGURATION -> {
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context)
}
}
sendInfoResponse(context)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context): ServiceInfo = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.unregisterReceiver(this)
val serviceInfo = try {
intent.getSerializableExtra(EXTRA_SERVICE_INFO) as ServiceInfo
} catch (e: Exception) {
it.resumeWithException(e)
return
}
try {
it.resume(serviceInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}, IntentFilter(ACTION_SERVICE_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(intent, null)
} catch (e: Exception) {
it.resumeWithException(e)
}
}
suspend fun getSafetyNetServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_SERVICE_INFO_REQUEST
}, context)
suspend fun setSafetyNetServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_UPDATE_CONFIGURATION
putExtra(EXTRA_CONFIGURATION, configuration)
}, context)

View File

@ -13,7 +13,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.gms.R import com.google.android.gms.R
import com.google.android.gms.databinding.DeviceRegistrationFragmentBinding import com.google.android.gms.databinding.DeviceRegistrationFragmentBinding
import org.microg.gms.checkin.CheckinPrefs import org.microg.gms.checkin.ServiceInfo
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.checkin.setCheckinServiceConfiguration
class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragment) { class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragment) {
private lateinit var binding: DeviceRegistrationFragmentBinding private lateinit var binding: DeviceRegistrationFragmentBinding
@ -22,17 +24,28 @@ class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragmen
binding = DeviceRegistrationFragmentBinding.inflate(inflater, container, false) binding = DeviceRegistrationFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback { binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) { override fun onChecked(newStatus: Boolean) {
CheckinPrefs.setEnabled(context, newStatus) setEnabled(newStatus)
binding.checkinEnabled = newStatus
} }
} }
return binding.root return binding.root
} }
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getCheckinServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setCheckinServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.checkinEnabled = serviceInfo.configuration.enabled
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
binding.checkinEnabled = CheckinPrefs.get(context).isEnabled displayServiceInfo(getCheckinServiceInfo(requireContext()))
} }
} }
} }

View File

@ -8,16 +8,17 @@ package org.microg.gms.ui
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.text.format.DateUtils import android.text.format.DateUtils
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceCategory import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.google.android.gms.R import com.google.android.gms.R
import org.microg.gms.checkin.CheckinPrefs import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.checkin.LastCheckinInfo
class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() { class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var statusCategory: PreferenceCategory private lateinit var statusCategory: PreferenceCategory
private lateinit var status: Preference private lateinit var status: Preference
private lateinit var androidId: Preference
private val handler = Handler() private val handler = Handler()
private val updateRunnable = Runnable { updateStatus() } private val updateRunnable = Runnable { updateStatus() }
@ -28,6 +29,7 @@ class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
override fun onBindPreferences() { override fun onBindPreferences() {
statusCategory = preferenceScreen.findPreference("prefcat_device_registration_status") ?: statusCategory statusCategory = preferenceScreen.findPreference("prefcat_device_registration_status") ?: statusCategory
status = preferenceScreen.findPreference("pref_device_registration_status") ?: status status = preferenceScreen.findPreference("pref_device_registration_status") ?: status
androidId = preferenceScreen.findPreference("pref_device_registration_android_id") ?: androidId
} }
override fun onResume() { override fun onResume() {
@ -42,12 +44,17 @@ class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() { private fun updateStatus() {
handler.postDelayed(updateRunnable, UPDATE_INTERVAL) handler.postDelayed(updateRunnable, UPDATE_INTERVAL)
statusCategory.isVisible = CheckinPrefs.get(context).isEnabled lifecycleScope.launchWhenResumed {
val checkinInfo = LastCheckinInfo.read(requireContext()) val serviceInfo = getCheckinServiceInfo(requireContext())
status.summary = if (checkinInfo.lastCheckin > 0) { statusCategory.isVisible = serviceInfo.configuration.enabled
getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(checkinInfo.lastCheckin, System.currentTimeMillis(), 0)) if (serviceInfo.lastCheckin > 0) {
} else { status.summary = getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0))
getString(R.string.checkin_not_registered) androidId.isVisible = true
androidId.summary = serviceInfo.androidId.toString(16)
} else {
status.summary = getString(R.string.checkin_not_registered)
androidId.isVisible = false
}
} }
} }

View File

@ -13,7 +13,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.gms.R import com.google.android.gms.R
import com.google.android.gms.databinding.ExposureNotificationsFragmentBinding import com.google.android.gms.databinding.ExposureNotificationsFragmentBinding
import org.microg.gms.nearby.exposurenotification.ExposurePreferences import org.microg.gms.nearby.exposurenotification.ServiceInfo
import org.microg.gms.nearby.exposurenotification.getExposureNotificationsServiceInfo
import org.microg.gms.nearby.exposurenotification.setExposureNotificationsServiceConfiguration
class ExposureNotificationsFragment : Fragment(R.layout.exposure_notifications_fragment) { class ExposureNotificationsFragment : Fragment(R.layout.exposure_notifications_fragment) {
private lateinit var binding: ExposureNotificationsFragmentBinding private lateinit var binding: ExposureNotificationsFragmentBinding
@ -22,17 +24,28 @@ class ExposureNotificationsFragment : Fragment(R.layout.exposure_notifications_f
binding = ExposureNotificationsFragmentBinding.inflate(inflater, container, false) binding = ExposureNotificationsFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback { binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) { override fun onChecked(newStatus: Boolean) {
ExposurePreferences(requireContext()).scannerEnabled = newStatus setEnabled(newStatus)
binding.scannerEnabled = newStatus
} }
} }
return binding.root return binding.root
} }
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getExposureNotificationsServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setExposureNotificationsServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.scannerEnabled = serviceInfo.configuration.enabled
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
binding.scannerEnabled = ExposurePreferences(requireContext()).scannerEnabled displayServiceInfo(getExposureNotificationsServiceInfo(requireContext()))
} }
} }
} }

View File

@ -17,7 +17,7 @@ import com.google.android.gms.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.microg.gms.nearby.exposurenotification.ExposureDatabase import org.microg.gms.nearby.exposurenotification.ExposureDatabase
import org.microg.gms.nearby.exposurenotification.ExposurePreferences import org.microg.gms.nearby.exposurenotification.getExposureNotificationsServiceInfo
class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() { class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var exposureEnableInfo: Preference private lateinit var exposureEnableInfo: Preference
@ -62,9 +62,9 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() { private fun updateStatus() {
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
handler.postDelayed(updateStatusRunnable, UPDATE_STATUS_INTERVAL) handler.postDelayed(updateStatusRunnable, UPDATE_STATUS_INTERVAL)
val preferences = ExposurePreferences(requireContext()) val enabled = getExposureNotificationsServiceInfo(requireContext()).configuration.enabled
exposureEnableInfo.isVisible = !preferences.scannerEnabled exposureEnableInfo.isVisible = !enabled
advertisingId.isVisible = preferences.advertiserEnabled advertisingId.isVisible = enabled
} }
} }

View File

@ -11,8 +11,10 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.gms.R import com.google.android.gms.R
import com.google.android.gms.databinding.PushNotificationFragmentBinding import com.google.android.gms.databinding.PushNotificationFragmentBinding
import org.microg.gms.checkin.CheckinPrefs import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.gcm.GcmPrefs import org.microg.gms.gcm.ServiceInfo
import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.gms.gcm.setGcmServiceConfiguration
class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) { class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
lateinit var binding: PushNotificationFragmentBinding lateinit var binding: PushNotificationFragmentBinding
@ -21,18 +23,29 @@ class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
binding = PushNotificationFragmentBinding.inflate(inflater, container, false) binding = PushNotificationFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback { binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) { override fun onChecked(newStatus: Boolean) {
GcmPrefs.setEnabled(context, newStatus) setEnabled(newStatus)
binding.gcmEnabled = newStatus
} }
} }
return binding.root return binding.root
} }
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getGcmServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setGcmServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.gcmEnabled = serviceInfo.configuration.enabled
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
binding.gcmEnabled = GcmPrefs.get(context).isEnabled displayServiceInfo(getGcmServiceInfo(requireContext()))
binding.checkinEnabled = CheckinPrefs.get(context).isEnabled binding.checkinEnabled = getCheckinServiceInfo(requireContext()).configuration.enabled
} }
} }

View File

@ -18,9 +18,7 @@ import com.google.android.gms.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.microg.gms.gcm.GcmDatabase import org.microg.gms.gcm.GcmDatabase
import org.microg.gms.gcm.GcmPrefs import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.gms.gcm.McsService
import org.microg.gms.gcm.getStatusInfo
class PushNotificationPreferencesFragment : PreferenceFragmentCompat() { class PushNotificationPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var pushStatusCategory: PreferenceCategory private lateinit var pushStatusCategory: PreferenceCategory
@ -67,9 +65,9 @@ class PushNotificationPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() { private fun updateStatus() {
handler.postDelayed(updateRunnable, UPDATE_INTERVAL) handler.postDelayed(updateRunnable, UPDATE_INTERVAL)
pushStatusCategory.isVisible = GcmPrefs.get(context).isEnabled
lifecycleScope.launchWhenStarted { lifecycleScope.launchWhenStarted {
val statusInfo = getStatusInfo(requireContext()) val statusInfo = getGcmServiceInfo(requireContext())
pushStatusCategory.isVisible = statusInfo.configuration.enabled
pushStatus.summary = if (statusInfo != null && statusInfo.connected) { pushStatus.summary = if (statusInfo != null && statusInfo.connected) {
getString(R.string.gcm_network_state_connected, DateUtils.getRelativeTimeSpanString(statusInfo.startTimestamp, System.currentTimeMillis(), 0)) getString(R.string.gcm_network_state_connected, DateUtils.getRelativeTimeSpanString(statusInfo.startTimestamp, System.currentTimeMillis(), 0))
} else { } else {

View File

@ -8,11 +8,14 @@ package org.microg.gms.ui
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.gms.R import com.google.android.gms.R
import com.google.android.gms.databinding.SafetyNetFragmentBinding import com.google.android.gms.databinding.SafetyNetFragmentBinding
import org.microg.gms.checkin.CheckinPrefs import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.snet.SafetyNetPrefs import org.microg.gms.snet.ServiceInfo
import org.microg.gms.snet.getSafetyNetServiceInfo
import org.microg.gms.snet.setSafetyNetServiceConfiguration
class SafetyNetFragment : Fragment(R.layout.safety_net_fragment) { class SafetyNetFragment : Fragment(R.layout.safety_net_fragment) {
@ -22,17 +25,30 @@ class SafetyNetFragment : Fragment(R.layout.safety_net_fragment) {
binding = SafetyNetFragmentBinding.inflate(inflater, container, false) binding = SafetyNetFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback { binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) { override fun onChecked(newStatus: Boolean) {
SafetyNetPrefs.get(requireContext()).isEnabled = newStatus setEnabled(newStatus)
binding.safetynetEnabled = newStatus
} }
} }
return binding.root return binding.root
} }
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getSafetyNetServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setSafetyNetServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.safetynetEnabled = serviceInfo.configuration.enabled
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
binding.checkinEnabled = CheckinPrefs.get(requireContext()).isEnabled lifecycleScope.launchWhenResumed {
binding.safetynetEnabled = SafetyNetPrefs.get(requireContext()).isEnabled binding.checkinEnabled = getCheckinServiceInfo(requireContext()).configuration.enabled
displayServiceInfo(getSafetyNetServiceInfo(requireContext()))
}
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {

View File

@ -0,0 +1,100 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.ui
import android.os.Build
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import com.google.android.gms.R
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.gcm.GcmDatabase
import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.gms.nearby.exposurenotification.getExposureNotificationsServiceInfo
import org.microg.gms.snet.getSafetyNetServiceInfo
import org.microg.nlp.client.UnifiedLocationClient
import org.microg.tools.ui.ResourceSettingsFragment
class SettingsFragment : ResourceSettingsFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
super.onCreatePreferences(savedInstanceState, rootKey)
findPreference<Preference>(PREF_CHECKIN)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openCheckinSettings)
true
}
findPreference<Preference>(PREF_GCM)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openGcmSettings)
true
}
findPreference<Preference>(PREF_SNET)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openSafetyNetSettings)
true
}
findPreference<Preference>(PREF_UNIFIEDNLP)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openUnifiedNlpSettings)
true
}
findPreference<Preference>(PREF_EXPOSURE)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openExposureNotificationSettings)
true
}
findPreference<Preference>(PREF_ABOUT)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openAbout)
true
}
findPreference<Preference>(PREF_ABOUT)!!.summary = getString(R.string.about_version_str, AboutFragment.getSelfVersion(context))
}
override fun onResume() {
super.onResume()
lifecycleScope.launchWhenResumed {
updateDetails()
}
}
private suspend fun updateDetails() {
if (getGcmServiceInfo(requireContext()).configuration.enabled) {
val database = GcmDatabase(context)
val regCount = database.registrationList.size
database.close()
findPreference<Preference>(PREF_GCM)!!.summary = getString(R.string.service_status_enabled_short) + " - " + resources.getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount)
} else {
findPreference<Preference>(PREF_GCM)!!.setSummary(R.string.service_status_disabled_short)
}
findPreference<Preference>(PREF_CHECKIN)!!.setSummary(if (getCheckinServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
findPreference<Preference>(PREF_SNET)!!.setSummary(if (getSafetyNetServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
val backendCount = UnifiedLocationClient[requireContext()].getLocationBackends().size + UnifiedLocationClient[requireContext()].getGeocoderBackends().size
findPreference<Preference>(PREF_UNIFIEDNLP)!!.summary = resources.getQuantityString(R.plurals.pref_unifiednlp_summary, backendCount, backendCount);
if (Build.VERSION.SDK_INT >= 21) {
findPreference<Preference>(PREF_EXPOSURE)!!.isVisible = true
if (getExposureNotificationsServiceInfo(requireContext()).configuration.enabled) {
findPreference<Preference>(PREF_EXPOSURE)!!.summary = getString(R.string.service_status_enabled_short)
} else {
findPreference<Preference>(PREF_EXPOSURE)!!.setSummary(R.string.service_status_disabled_short)
}
} else {
findPreference<Preference>(PREF_EXPOSURE)!!.isVisible = false
}
}
companion object {
const val PREF_ABOUT = "pref_about"
const val PREF_GCM = "pref_gcm"
const val PREF_SNET = "pref_snet"
const val PREF_UNIFIEDNLP = "pref_unifiednlp"
const val PREF_CHECKIN = "pref_checkin"
const val PREF_EXPOSURE = "pref_exposure"
}
init {
preferencesResource = R.xml.preferences_start
}
}

View File

@ -140,6 +140,7 @@ This can take a couple of minutes."</string>
<string name="pref_auth_visible_summary">When enabled, all applications on this device will be able to see email address of your Google Accounts without prior authorization.</string> <string name="pref_auth_visible_summary">When enabled, all applications on this device will be able to see email address of your Google Accounts without prior authorization.</string>
<string name="pref_checkin_enable_summary">Registers your device to Google services and creates a unique device identifier. microG strips identifying bits other than your Google account name from registration data.</string> <string name="pref_checkin_enable_summary">Registers your device to Google services and creates a unique device identifier. microG strips identifying bits other than your Google account name from registration data.</string>
<string name="pref_device_registration_android_id">Android ID</string>
<string name="checkin_not_registered">Not registered</string> <string name="checkin_not_registered">Not registered</string>
<string name="checkin_last_registration">Last registration: <xliff:g example="Yesterday, 02:20 PM">%1$s</xliff:g></string> <string name="checkin_last_registration">Last registration: <xliff:g example="Yesterday, 02:20 PM">%1$s</xliff:g></string>

View File

@ -14,6 +14,11 @@
android:selectable="false" android:selectable="false"
android:title="@string/pref_info_status" android:title="@string/pref_info_status"
tools:summary="Last registration: 13 hours ago" /> tools:summary="Last registration: 13 hours ago" />
<Preference
android:key="pref_device_registration_android_id"
android:selectable="false"
android:title="@string/pref_device_registration_android_id"
tools:summary="1953a59d1c1b7e4b" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:layout="@layout/preference_category_no_label"> <PreferenceCategory android:layout="@layout/preference_category_no_label">
<org.microg.gms.ui.TextPreference <org.microg.gms.ui.TextPreference