diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle
index d2862d66..6b287fdd 100644
--- a/play-services-base/build.gradle
+++ b/play-services-base/build.gradle
@@ -39,6 +39,6 @@ dependencies {
api project(':play-services-tasks')
api project(':play-services-base-api')
- implementation 'androidx.fragment:fragment:1.3.3'
+ implementation 'androidx.fragment:fragment:1.3.5'
implementation 'com.google.android.gms:play-services-base:17.6.0'
}
\ No newline at end of file
diff --git a/play-services-core-proto/build.gradle b/play-services-core-proto/build.gradle
index 2cfb351e..facb7026 100644
--- a/play-services-core-proto/build.gradle
+++ b/play-services-core-proto/build.gradle
@@ -7,7 +7,7 @@ apply plugin: 'com.squareup.wire'
apply plugin: 'kotlin'
dependencies {
- implementation "com.squareup.wire:wire-runtime:$wireVersion"
+ implementation "com.squareup.wire:wire-runtime:3.6.1"
}
wire {
diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle
index d735a8ee..fe05de71 100644
--- a/play-services-core/build.gradle
+++ b/play-services-core/build.gradle
@@ -32,8 +32,8 @@ dependencies {
implementation project(':play-services-cast-api')
// AndroidX UI
- implementation "androidx.appcompat:appcompat:1.2.0"
- implementation 'androidx.mediarouter:mediarouter:1.2.3'
+ implementation 'androidx.appcompat:appcompat:1.3.0'
+ implementation 'androidx.mediarouter:mediarouter:1.2.4'
implementation "androidx.preference:preference-ktx:$preferenceVersion"
implementation "org.microg.gms:conscrypt-gmscore:2.5.1"
@@ -45,7 +45,7 @@ dependencies {
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
implementation "androidx.lifecycle:lifecycle-service:2.3.1"
- implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.31'
+ implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32'
api project(':play-services-basement')
}
diff --git a/play-services-core/microg-ui-tools/build.gradle b/play-services-core/microg-ui-tools/build.gradle
index 24c2d2be..4dc1aaa2 100644
--- a/play-services-core/microg-ui-tools/build.gradle
+++ b/play-services-core/microg-ui-tools/build.gradle
@@ -38,7 +38,7 @@ android {
}
dependencies {
- implementation "androidx.appcompat:appcompat:1.2.0"
+ implementation 'androidx.appcompat:appcompat:1.3.0'
implementation "androidx.preference:preference:$preferenceVersion"
}
diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml
index 38afc8cf..95cbc125 100644
--- a/play-services-core/src/main/AndroidManifest.xml
+++ b/play-services-core/src/main/AndroidManifest.xml
@@ -91,6 +91,13 @@
android:name="fake-signature"
android:value="@string/fake_signature" />
+
+
+
+
-
-
@@ -303,6 +308,12 @@
+
+
+
= Build.VERSION_CODES.O) {
+ if (isAuthVisible(this) && SDK_INT >= Build.VERSION_CODES.O) {
accountManager.setAccountVisibility(account, PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE, VISIBILITY_USER_MANAGED_VISIBLE);
}
retrieveGmsToken(account);
@@ -167,7 +168,7 @@ public class LoginActivity extends AssistantActivity {
}
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(HuaweiButtonPreference, true).apply();
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(LoginButtonPreference, true)) {
- LastCheckinInfo.ClearCheckinInfo(this);
+ LastCheckinInfo.clear(this);
CheckinClient.brandSpoof = true;
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(LoginButtonPreference, true).apply();
}
@@ -182,7 +183,7 @@ public class LoginActivity extends AssistantActivity {
if (state == 1) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(LoginButtonPreference, true).apply();
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(HuaweiButtonPreference, true)) {
- LastCheckinInfo.ClearCheckinInfo(this);
+ LastCheckinInfo.clear(this);
CheckinClient.brandSpoof = false;
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(HuaweiButtonPreference, true).apply();
}
@@ -253,7 +254,7 @@ public class LoginActivity extends AssistantActivity {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
- if (LastCheckinInfo.read(this).androidId == 0) {
+ if (LastCheckinInfo.read(this).getAndroidId() == 0) {
new Thread(() -> {
Runnable next;
next = checkin(false) ? this::loadLoginPage : () -> showError(R.string.auth_general_error_desc);
@@ -454,7 +455,7 @@ public class LoginActivity extends AssistantActivity {
@JavascriptInterface
public final String getAndroidId() {
- long androidId = LastCheckinInfo.read(LoginActivity.this).androidId;
+ long androidId = LastCheckinInfo.read(LoginActivity.this).getAndroidId();
Log.d(TAG, "JSBridge: getAndroidId " + androidId);
if (androidId == 0 || androidId == -1) return null;
return Long.toHexString(androidId);
diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java
index 49bf4d73..5527ac48 100755
--- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java
+++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java
@@ -81,8 +81,8 @@ public class CheckinClient {
LastCheckinInfo checkinInfo, Locale locale,
List accounts) {
CheckinRequest.Builder builder = new CheckinRequest.Builder()
- .accountCookie(new ArrayList())
- .androidId(checkinInfo.androidId)
+ .accountCookie(new ArrayList<>())
+ .androidId(checkinInfo.getAndroidId())
.checkin(new CheckinRequest.Checkin.Builder()
.build(new CheckinRequest.Checkin.Build.Builder()
.bootloader(brandSpoof ? "c2f2-0.2-5799621" : build.bootloader)
@@ -101,11 +101,11 @@ public class CheckinClient {
.build())
.cellOperator(phoneInfo.cellOperator)
.event(Collections.singletonList(new CheckinRequest.Checkin.Event.Builder()
- .tag(checkinInfo.androidId == 0 ? "event_log_start" : "system_update")
- .value(checkinInfo.androidId == 0 ? null : "1536,0,-1,NULL")
+ .tag(checkinInfo.getAndroidId() == 0 ? "event_log_start" : "system_update")
+ .value(checkinInfo.getAndroidId() == 0 ? null : "1536,0,-1,NULL")
.timeMs(new Date().getTime())
.build()))
- .lastCheckinMs(checkinInfo.lastCheckin)
+ .lastCheckinMs(checkinInfo.getLastCheckin())
.requestedGroup(TODO_LIST_STRING)
.roaming(phoneInfo.roaming)
.simOperator(phoneInfo.simOperator)
@@ -129,7 +129,7 @@ public class CheckinClient {
.touchScreen(deviceConfiguration.touchScreen)
.widthPixels(deviceConfiguration.widthPixels)
.build())
- .digest(checkinInfo.digest)
+ .digest(checkinInfo.getDigest())
.esn(deviceIdent.esn)
.fragment(0)
.locale(locale.toString())
@@ -150,8 +150,8 @@ public class CheckinClient {
builder.macAddress(Collections.singletonList(deviceIdent.wifiMac))
.macAddressType(Collections.singletonList("wifi"));
}
- if (checkinInfo.securityToken != 0) {
- builder.securityToken(checkinInfo.securityToken)
+ if (checkinInfo.getSecurityToken() != 0) {
+ builder.securityToken(checkinInfo.getSecurityToken())
.fragment(1);
}
return builder.build();
diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java
index 9775797e..018d9655 100755
--- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java
+++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java
@@ -39,9 +39,9 @@ public class CheckinManager {
@SuppressWarnings("MissingPermission")
public static synchronized LastCheckinInfo checkin(Context context, boolean force) throws IOException {
LastCheckinInfo info = LastCheckinInfo.read(context);
- if (!force && info.lastCheckin > System.currentTimeMillis() - MIN_CHECKIN_INTERVAL)
+ if (!force && info.getLastCheckin() > System.currentTimeMillis() - MIN_CHECKIN_INTERVAL)
return null;
- if (!CheckinPrefs.get(context).isEnabled())
+ if (!CheckinPrefs.isEnabled(context))
return null;
List accounts = new ArrayList<>();
AccountManager accountManager = AccountManager.get(context);
@@ -63,13 +63,7 @@ public class CheckinManager {
}
private static LastCheckinInfo handleResponse(Context context, CheckinResponse response) {
- LastCheckinInfo info = new LastCheckinInfo();
- info.androidId = response.androidId;
- info.lastCheckin = response.timeMs;
- info.securityToken = response.securityToken;
- info.digest = response.digest;
- info.versionInfo = response.versionInfo;
- info.deviceDataVersionInfo = response.deviceDataVersionInfo;
+ LastCheckinInfo info = new LastCheckinInfo(response);
info.write(context);
ContentResolver resolver = context.getContentResolver();
diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinPrefs.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinPrefs.java
deleted file mode 100755
index e5d64db4..00000000
--- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinPrefs.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2020, microG Project Team
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.microg.gms.checkin;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-
-import androidx.preference.PreferenceManager;
-import android.util.Log;
-
-import org.microg.gms.common.PackageUtils;
-
-import java.io.File;
-
-public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
- public static final String PREF_ENABLE_CHECKIN = "checkin_enable_service";
- private static CheckinPrefs INSTANCE;
-
- public static CheckinPrefs get(Context context) {
- if (INSTANCE == null) {
- PackageUtils.warnIfNotMainProcess(context, CheckinPrefs.class);
- if (context == null) return new CheckinPrefs(null);
- INSTANCE = new CheckinPrefs(context.getApplicationContext());
- }
- return INSTANCE;
- }
-
- private SharedPreferences preferences;
- private SharedPreferences systemDefaultPreferences;
- private boolean checkinEnabled = false;
-
- private CheckinPrefs(Context context) {
- if (context != null) {
- preferences = PreferenceManager.getDefaultSharedPreferences(context);
- 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();
- }
- }
-
- private boolean getSettingsBoolean(String key, boolean def) {
- if (systemDefaultPreferences != null) {
- def = systemDefaultPreferences.getBoolean(key, def);
- }
- return preferences.getBoolean(key, def);
- }
-
- private void update() {
- checkinEnabled = getSettingsBoolean(PREF_ENABLE_CHECKIN, true);
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- update();
- }
-
- public boolean isEnabled() {
- return checkinEnabled;
- }
-
- public static void setEnabled(Context context, boolean newStatus) {
- boolean changed = CheckinPrefs.get(context).isEnabled() != newStatus;
- PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(PREF_ENABLE_CHECKIN, newStatus).apply();
- if (!changed) return;
- if (newStatus) {
- context.sendOrderedBroadcast(new Intent(context, TriggerReceiver.class), null);
- }
- }
-}
diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java
index dd32c1e1..39f7ef70 100755
--- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java
+++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java
@@ -57,7 +57,7 @@ public class CheckinService extends IntentService {
private ICheckinService iface = new ICheckinService.Stub() {
@Override
public String getDeviceDataVersionInfo() throws RemoteException {
- return LastCheckinInfo.read(CheckinService.this).deviceDataVersionInfo;
+ return LastCheckinInfo.read(CheckinService.this).getDeviceDataVersionInfo();
}
};
@@ -70,10 +70,10 @@ public class CheckinService extends IntentService {
protected void onHandleIntent(Intent intent) {
try {
ForegroundServiceContext.completeForegroundService(this, intent, TAG);
- if (CheckinPrefs.get(this).isEnabled()) {
+ if (CheckinPrefs.isEnabled(this)) {
LastCheckinInfo info = CheckinManager.checkin(this, intent.getBooleanExtra(EXTRA_FORCE_CHECKIN, false));
if (info != null) {
- Log.d(TAG, "Checked in as " + Long.toHexString(info.androidId));
+ Log.d(TAG, "Checked in as " + Long.toHexString(info.getAndroidId()));
String accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
for (Account account : AccountManager.get(this).getAccountsByType(accountType)) {
PeopleManager.loadUserInfo(this, account);
@@ -86,7 +86,7 @@ public class CheckinService extends IntentService {
ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER);
if (receiver != null) {
Bundle bundle = new Bundle();
- bundle.putLong(EXTRA_NEW_CHECKIN_TIME, info.lastCheckin);
+ bundle.putLong(EXTRA_NEW_CHECKIN_TIME, info.getLastCheckin());
receiver.send(Activity.RESULT_OK, bundle);
}
}
@@ -115,6 +115,6 @@ public class CheckinService extends IntentService {
static void schedule(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(context, TriggerReceiver.class.getName().hashCode(), new Intent(context, TriggerReceiver.class), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
- alarmManager.set(AlarmManager.RTC, Math.max(LastCheckinInfo.read(context).lastCheckin + REGULAR_CHECKIN_INTERVAL, System.currentTimeMillis() + BACKUP_CHECKIN_DELAY), pendingIntent);
+ alarmManager.set(AlarmManager.RTC, Math.max(LastCheckinInfo.read(context).getLastCheckin() + REGULAR_CHECKIN_INTERVAL, System.currentTimeMillis() + BACKUP_CHECKIN_DELAY), pendingIntent);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/LastCheckinInfo.java b/play-services-core/src/main/java/org/microg/gms/checkin/LastCheckinInfo.java
deleted file mode 100755
index 1325cb38..00000000
--- a/play-services-core/src/main/java/org/microg/gms/checkin/LastCheckinInfo.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2013-2017 microG Project Team
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.microg.gms.checkin;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-public class LastCheckinInfo {
- public static final String PREFERENCES_NAME = "checkin";
- public static final String PREF_ANDROID_ID = "androidId";
- public static final String PREF_DIGEST = "digest";
- public static final String PREF_LAST_CHECKIN = "lastCheckin";
- public static final String PREF_SECURITY_TOKEN = "securityToken";
- public static final String PREF_VERSION_INFO = "versionInfo";
- public static final String PREF_DEVICE_DATA_VERSION_INFO = "deviceDataVersionInfo";
- public static final String INITIAL_DIGEST = "1-929a0dca0eee55513280171a8585da7dcd3700f8";
- public long lastCheckin;
- public long androidId;
- public long securityToken;
- public String digest;
- public String versionInfo;
- public String deviceDataVersionInfo;
-
- public static LastCheckinInfo read(Context context) {
- LastCheckinInfo info = new LastCheckinInfo();
- SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
- info.androidId = preferences.getLong(PREF_ANDROID_ID, 0);
- info.digest = preferences.getString(PREF_DIGEST, INITIAL_DIGEST);
- info.lastCheckin = preferences.getLong(PREF_LAST_CHECKIN, 0);
- info.securityToken = preferences.getLong(PREF_SECURITY_TOKEN, 0);
- info.versionInfo = preferences.getString(PREF_VERSION_INFO, "");
- info.deviceDataVersionInfo = preferences.getString(PREF_DEVICE_DATA_VERSION_INFO, "");
- return info;
- }
-
- public void write(Context context) {
- context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit()
- .putLong(PREF_ANDROID_ID, androidId)
- .putString(PREF_DIGEST, digest)
- .putLong(PREF_LAST_CHECKIN, lastCheckin)
- .putLong(PREF_SECURITY_TOKEN, securityToken)
- .putString(PREF_VERSION_INFO, versionInfo)
- .putString(PREF_DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo)
- .commit();
- }
-
- public static void ClearCheckinInfo(Context context) {
- context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit()
- .putLong(PREF_ANDROID_ID, 0)
- .putString(PREF_DIGEST, INITIAL_DIGEST)
- .putLong(PREF_LAST_CHECKIN, 0)
- .putLong(PREF_SECURITY_TOKEN, 0)
- .putString(PREF_VERSION_INFO, "")
- .putString(PREF_DEVICE_DATA_VERSION_INFO, "")
- .commit();
- }
-}
diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java b/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java
index e2bcca5d..da7c90c3 100755
--- a/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java
+++ b/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java
@@ -16,16 +16,21 @@
package org.microg.gms.checkin;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.util.Log;
import androidx.legacy.content.WakefulBroadcastReceiver;
import org.microg.gms.common.ForegroundServiceContext;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.os.Build.VERSION.SDK_INT;
import static org.microg.gms.checkin.CheckinService.EXTRA_FORCE_CHECKIN;
import static org.microg.gms.checkin.CheckinService.REGULAR_CHECKIN_INTERVAL;
@@ -37,8 +42,8 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
try {
boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction());
- if (CheckinPrefs.get(context).isEnabled() || force) {
- if (LastCheckinInfo.read(context).lastCheckin > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL && !force) {
+ if (CheckinPrefs.isEnabled(context) || force) {
+ if (LastCheckinInfo.read(context).getLastCheckin() > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL && !force) {
CheckinService.schedule(context);
return;
}
@@ -49,7 +54,15 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
Intent subIntent = new Intent(context, CheckinService.class);
subIntent.putExtra(EXTRA_FORCE_CHECKIN, force);
startWakefulService(new ForegroundServiceContext(context), subIntent);
- }
+ } else if (SDK_INT >= 23) {
+ // no network, register a network callback to retry when we have internet
+ NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ Intent i = new Intent(context, TriggerReceiver.class);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, i, FLAG_UPDATE_CURRENT);
+ cm.registerNetworkCallback(networkRequest, pendingIntent);
+ }
} else {
Log.d(TAG, "Ignoring " + intent + ": checkin is disabled");
}
diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java
deleted file mode 100644
index c80e80ab..00000000
--- a/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2013-2017 microG Project Team
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.microg.gms.gcm;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.util.Log;
-
-import androidx.preference.PreferenceManager;
-
-import org.microg.gms.common.PackageUtils;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
- public static final String PREF_FULL_LOG = "gcm_full_log";
- public static final String PREF_LAST_PERSISTENT_ID = "gcm_last_persistent_id";
- public static final String PREF_CONFIRM_NEW_APPS = "gcm_confirm_new_apps";
- public static final String PREF_ENABLE_GCM = "gcm_enable_mcs_service";
-
- public static final String PREF_NETWORK_MOBILE = "gcm_network_mobile";
- public static final String PREF_NETWORK_WIFI = "gcm_network_wifi";
- public static final String PREF_NETWORK_ROAMING = "gcm_network_roaming";
- public static final String PREF_NETWORK_OTHER = "gcm_network_other";
-
- public static final String PREF_LEARNT_MOBILE = "gcm_learnt_mobile";
- public static final String PREF_LEARNT_WIFI = "gcm_learnt_wifi";
- public static final String PREF_LEARNT_OTHER = "gcm_learnt_other";
-
- private static final int INTERVAL = 1 * 60 * 1000; // 1 minute
-
- private static GcmPrefs INSTANCE;
-
- public static GcmPrefs get(Context context) {
- if (INSTANCE == null) {
- PackageUtils.warnIfNotPersistentProcess(GcmPrefs.class);
- INSTANCE = new GcmPrefs(context.getApplicationContext());
- }
- return INSTANCE;
- }
-
- private boolean gcmLogEnabled = true;
- private String lastPersistedId = "";
- private boolean gcmEnabled = false;
-
- private int networkMobile = 0;
- private int networkWifi = 0;
- private int networkRoaming = 0;
- private int networkOther = 0;
-
- private int learntWifi = 300000;
- private int learntMobile = 300000;
- private int learntOther = 300000;
-
- private final Context context;
- private SharedPreferences preferences;
- private SharedPreferences systemDefaultPreferences;
-
- private GcmPrefs(Context context) {
- this.context = context;
- preferences = PreferenceManager.getDefaultSharedPreferences(context);
- 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();
- }
-
- private boolean getSettingsBoolean(String key, boolean def) {
- if (systemDefaultPreferences != null) {
- def = systemDefaultPreferences.getBoolean(key, def);
- }
- return preferences.getBoolean(key, def);
- }
-
- public void update() {
- gcmEnabled = getSettingsBoolean(PREF_ENABLE_GCM, true);
- gcmLogEnabled = getSettingsBoolean(PREF_FULL_LOG, true);
-
- lastPersistedId = preferences.getString(PREF_LAST_PERSISTENT_ID, "");
-
- networkMobile = Integer.parseInt(preferences.getString(PREF_NETWORK_MOBILE, "0"));
- networkWifi = Integer.parseInt(preferences.getString(PREF_NETWORK_WIFI, "0"));
- networkRoaming = Integer.parseInt(preferences.getString(PREF_NETWORK_ROAMING, "0"));
- networkOther = Integer.parseInt(preferences.getString(PREF_NETWORK_OTHER, "0"));
-
- learntMobile = preferences.getInt(PREF_LEARNT_MOBILE, INTERVAL);
- learntWifi = preferences.getInt(PREF_LEARNT_WIFI, INTERVAL);
- learntOther = preferences.getInt(PREF_LEARNT_OTHER, INTERVAL);
- }
-
- public String getNetworkPrefForInfo(NetworkInfo info) {
- if (info == null) return PREF_NETWORK_OTHER;
- if (info.isRoaming()) return PREF_NETWORK_ROAMING;
- switch (info.getType()) {
- case ConnectivityManager.TYPE_MOBILE:
- return PREF_NETWORK_MOBILE;
- case ConnectivityManager.TYPE_WIFI:
- return PREF_NETWORK_WIFI;
- default:
- return PREF_NETWORK_OTHER;
- }
- }
-
- public int getHeartbeatMsFor(NetworkInfo info) {
- return getHeartbeatMsFor(getNetworkPrefForInfo(info));
- }
-
- public int getMobileInterval() {
- return networkMobile;
- }
-
- public int getWifiInterval() {
- return networkWifi;
- }
-
- public int getRoamingInterval() {
- return networkRoaming;
- }
-
- public int getOtherInterval() {
- return networkOther;
- }
-
- public void setMobileInterval(int value) {
- this.networkMobile = value;
- preferences.edit().putString(PREF_NETWORK_MOBILE, Integer.toString(networkMobile)).apply();
- }
-
- public void setWifiInterval(int value) {
- this.networkWifi = value;
- preferences.edit().putString(PREF_NETWORK_WIFI, Integer.toString(networkWifi)).apply();
- }
-
- public void setRoamingInterval(int value) {
- this.networkRoaming = value;
- preferences.edit().putString(PREF_NETWORK_ROAMING, Integer.toString(networkRoaming)).apply();
- }
-
- public void setOtherInterval(int value) {
- this.networkOther = value;
- preferences.edit().putString(PREF_NETWORK_OTHER, Integer.toString(networkOther)).apply();
- }
-
- public int getHeartbeatMsFor(String pref) {
- if (PREF_NETWORK_ROAMING.equals(pref)) {
- if (networkRoaming != 0) return networkRoaming * 60000;
- else return learntMobile;
- } else if (PREF_NETWORK_MOBILE.equals(pref)) {
- if (networkMobile != 0) return networkMobile * 60000;
- else return learntMobile;
- } else if (PREF_NETWORK_WIFI.equals(pref)) {
- if (networkWifi != 0) return networkWifi * 60000;
- else return learntWifi;
- } else {
- if (networkOther != 0) return networkOther * 60000;
- else return learntOther;
- }
- }
-
- public int getNetworkValue(String pref) {
- switch (pref) {
- case PREF_NETWORK_MOBILE:
- return networkMobile;
- case PREF_NETWORK_ROAMING:
- return networkRoaming;
- case PREF_NETWORK_WIFI:
- return networkWifi;
- default:
- return networkOther;
- }
- }
-
- public void learnTimeout(String pref) {
- Log.d("GmsGcmPrefs", "learnTimeout: " + pref);
- switch (pref) {
- case PREF_NETWORK_MOBILE:
- case PREF_NETWORK_ROAMING:
- learntMobile *= 0.95;
- break;
- case PREF_NETWORK_WIFI:
- learntWifi *= 0.95;
- break;
- default:
- learntOther *= 0.95;
- break;
- }
- updateLearntValues();
- }
-
- public void learnReached(String pref, long time) {
- Log.d("GmsGcmPrefs", "learnReached: " + pref + " / " + time);
- switch (pref) {
- case PREF_NETWORK_MOBILE:
- case PREF_NETWORK_ROAMING:
- if (time > learntMobile / 4 * 3)
- learntMobile *= 1.02;
- break;
- case PREF_NETWORK_WIFI:
- if (time > learntWifi / 4 * 3)
- learntWifi *= 1.02;
- break;
- default:
- if (time > learntOther / 4 * 3)
- learntOther *= 1.02;
- break;
- }
- updateLearntValues();
- }
-
- private void updateLearntValues() {
- preferences.edit().putInt(PREF_LEARNT_MOBILE, INTERVAL).putInt(PREF_LEARNT_WIFI, INTERVAL).putInt(PREF_LEARNT_OTHER, INTERVAL).apply();
- }
-
- public int getLearntMobileInterval() {
- return learntMobile;
- }
-
- public int getLearntWifiInterval() {
- return learntWifi;
- }
-
- public int getLearntOtherInterval() {
- return learntOther;
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- update();
- }
-
- public boolean isEnabled() {
- return gcmEnabled;
- }
-
- public void setEnabled(boolean value) {
- boolean changed = gcmEnabled != value;
- preferences.edit().putBoolean(GcmPrefs.PREF_ENABLE_GCM, value).apply();
- if (!changed) return;
- if (!value) {
- McsService.stop(context);
- } else {
- context.sendBroadcast(new Intent(TriggerReceiver.FORCE_TRY_RECONNECT, null, context, TriggerReceiver.class));
- }
- }
-
- public boolean isEnabledFor(NetworkInfo info) {
- return isEnabled() && info != null && getHeartbeatMsFor(info) >= 0;
- }
-
- public boolean isGcmLogEnabled() {
- return gcmLogEnabled;
- }
-
- public List getLastPersistedIds() {
- if (lastPersistedId.isEmpty()) return Collections.emptyList();
- return Arrays.asList(lastPersistedId.split("\\|"));
- }
-
- public void extendLastPersistedId(String id) {
- if (!lastPersistedId.isEmpty()) lastPersistedId += "|";
- lastPersistedId += id;
- preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
- }
-
- public void clearLastPersistedId() {
- lastPersistedId = "";
- preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
- }
-}
diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java
index 8b3d45d7..498faea2 100644
--- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java
+++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java
@@ -73,6 +73,7 @@ import okio.ByteString;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.os.Build.VERSION.SDK_INT;
+import static org.microg.gms.common.PackageUtils.warnIfNotPersistentProcess;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE;
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_OVERRIDE;
@@ -234,6 +235,7 @@ public class McsService extends Service implements Handler.Callback {
}
public synchronized static boolean isConnected(Context context) {
+ warnIfNotPersistentProcess(McsService.class);
if (inputStream == null || !inputStream.isAlive() || outputStream == null || !outputStream.isAlive()) {
logd(null, "Connection is not enabled or dead.");
return false;
@@ -244,13 +246,14 @@ public class McsService extends Service implements Handler.Callback {
closeAll();
} else if (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime > 2 * heartbeatMs) {
logd(null, "No heartbeat for " + (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime) / 1000 + " seconds, connection assumed to be dead after " + 2 * heartbeatMs / 1000 + " seconds");
- GcmPrefs.get(context).learnTimeout(activeNetworkPref);
+ GcmPrefs.get(context).learnTimeout(context, activeNetworkPref);
return false;
}
return true;
}
public static long getStartTimestamp() {
+ warnIfNotPersistentProcess(McsService.class);
return startTimestamp;
}
@@ -460,7 +463,7 @@ public class McsService extends Service implements Handler.Callback {
private void handleLoginResponse(LoginResponse loginResponse) {
if (loginResponse.error == null) {
- GcmPrefs.get(this).clearLastPersistedId();
+ GcmPrefs.clearLastPersistedId(this);
logd(this, "Logged in");
wakeLock.release();
} else {
@@ -470,7 +473,7 @@ public class McsService extends Service implements Handler.Callback {
private void handleCloudMessage(DataMessageStanza message) {
if (message.persistent_id != null) {
- GcmPrefs.get(this).extendLastPersistedId(message.persistent_id);
+ GcmPrefs.get(this).extendLastPersistedId(this, message.persistent_id);
}
if (SELF_CATEGORY.equals(message.category)) {
handleSelfMessage(message);
@@ -488,7 +491,7 @@ public class McsService extends Service implements Handler.Callback {
}
private void handleHeartbeatAck(HeartbeatAck ack) {
- GcmPrefs.get(this).learnReached(activeNetworkPref, SystemClock.elapsedRealtime() - lastIncomingNetworkRealtime);
+ GcmPrefs.get(this).learnReached(this, activeNetworkPref, SystemClock.elapsedRealtime() - lastIncomingNetworkRealtime);
lastHeartbeatAckElapsedRealtime = SystemClock.elapsedRealtime();
wakeLock.release();
}
@@ -498,13 +501,13 @@ public class McsService extends Service implements Handler.Callback {
return new LoginRequest.Builder()
.adaptive_heartbeat(false)
.auth_service(LoginRequest.AuthService.ANDROID_ID)
- .auth_token(Long.toString(info.securityToken))
+ .auth_token(Long.toString(info.getSecurityToken()))
.id("android-" + SDK_INT)
.domain("mcs.android.com")
- .device_id("android-" + Long.toHexString(info.androidId))
+ .device_id("android-" + Long.toHexString(info.getAndroidId()))
.network_type(1)
- .resource(Long.toString(info.androidId))
- .user(Long.toString(info.androidId))
+ .resource(Long.toString(info.getAndroidId()))
+ .user(Long.toString(info.getAndroidId()))
.use_rmq2(true)
.setting(Collections.singletonList(new Setting.Builder().name("new_vc").value("1").build()))
.received_persistent_id(GcmPrefs.get(this).getLastPersistedIds())
diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java
index 3c1846f3..7dab2ca8 100644
--- a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java
+++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java
@@ -87,7 +87,7 @@ public class PushRegisterManager {
if (!request.delete) {
if (!prefs.isEnabled() ||
(app != null && !app.allowRegister) ||
- LastCheckinInfo.read(context).lastCheckin <= 0) {
+ LastCheckinInfo.read(context).getLastCheckin() <= 0) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE);
callback.onResult(bundle);
diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java b/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java
index e4cfb851..43f04028 100644
--- a/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java
+++ b/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java
@@ -70,8 +70,8 @@ public class RegisterRequest extends HttpFormClient.Request {
}
public RegisterRequest checkin(LastCheckinInfo lastCheckinInfo) {
- androidId = lastCheckinInfo.androidId;
- securityToken = lastCheckinInfo.securityToken;
+ androidId = lastCheckinInfo.getAndroidId();
+ securityToken = lastCheckinInfo.getSecurityToken();
return this;
}
diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java
index 77fa3eff..3be427c3 100644
--- a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java
+++ b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java
@@ -65,7 +65,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
McsService.resetCurrentDelay();
}
- if (LastCheckinInfo.read(context).androidId == 0) {
+ if (LastCheckinInfo.read(context).getAndroidId() == 0) {
Log.d(TAG, "Ignoring " + intent + ": need to checkin first.");
return;
}
diff --git a/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java b/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java
index 6b3d8b85..2382814d 100644
--- a/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java
+++ b/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java
@@ -55,14 +55,6 @@ public class GServicesProvider extends ContentProvider {
@Override
public boolean onCreate() {
- if (CheckinPrefs.get(getContext()).isEnabled()) {
- getContext().sendOrderedBroadcast(new Intent(getContext(), org.microg.gms.checkin.TriggerReceiver.class), null);
-
- if (GcmPrefs.get(getContext()).isEnabled()) {
- getContext().sendBroadcast(new Intent(org.microg.gms.gcm.TriggerReceiver.FORCE_TRY_RECONNECT, null, getContext(), org.microg.gms.gcm.TriggerReceiver.class));
- }
- }
-
databaseHelper = new DatabaseHelper(getContext());
return true;
}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt
new file mode 100644
index 00000000..f5797f56
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt
@@ -0,0 +1,23 @@
+package org.microg.gms.auth
+
+import android.content.Context
+import org.microg.gms.settings.SettingsContract
+import org.microg.gms.settings.SettingsContract.Auth
+
+object AuthPrefs {
+
+ @JvmStatic
+ fun isTrustGooglePermitted(context: Context): Boolean {
+ return SettingsContract.getSettings(context, Auth.CONTENT_URI, arrayOf(Auth.TRUST_GOOGLE)) { c ->
+ c.getInt(0) != 0
+ }
+ }
+
+ @JvmStatic
+ fun isAuthVisible(context: Context): Boolean {
+ return SettingsContract.getSettings(context, Auth.CONTENT_URI, arrayOf(Auth.VISIBLE)) { c ->
+ c.getInt(0) != 0
+ }
+ }
+
+}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPrefs.kt b/play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPrefs.kt
new file mode 100644
index 00000000..bd122c35
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPrefs.kt
@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: 2020, microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package org.microg.gms.checkin
+
+import android.content.Context
+import org.microg.gms.settings.SettingsContract
+import org.microg.gms.settings.SettingsContract.CheckIn
+
+object CheckinPrefs {
+
+ @JvmStatic
+ fun isEnabled(context: Context): Boolean {
+ val projection = arrayOf(CheckIn.ENABLED)
+ return SettingsContract.getSettings(context, CheckIn.CONTENT_URI, projection) { c ->
+ c.getInt(0) != 0
+ }
+ }
+
+}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/checkin/LastCheckinInfo.kt b/play-services-core/src/main/kotlin/org/microg/gms/checkin/LastCheckinInfo.kt
new file mode 100644
index 00000000..ccdb31af
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/checkin/LastCheckinInfo.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013-2017 microG Project Team
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.microg.gms.checkin
+
+import android.content.Context
+import org.microg.gms.settings.SettingsContract
+import org.microg.gms.settings.SettingsContract.CheckIn
+
+data class LastCheckinInfo(
+ val lastCheckin: Long,
+ val androidId: Long,
+ val securityToken: Long,
+ val digest: String,
+ val versionInfo: String,
+ val deviceDataVersionInfo: String,
+) {
+
+ constructor(r: CheckinResponse) : this(
+ lastCheckin = r.timeMs ?: 0L,
+ androidId = r.androidId ?: 0L,
+ securityToken = r.securityToken ?: 0L,
+ digest = r.digest ?: CheckIn.INITIAL_DIGEST,
+ versionInfo = r.versionInfo ?: "",
+ deviceDataVersionInfo = r.deviceDataVersionInfo ?: "",
+ )
+
+ companion object {
+ @JvmStatic
+ fun read(context: Context): LastCheckinInfo {
+ val projection = arrayOf(
+ CheckIn.ANDROID_ID,
+ CheckIn.DIGEST,
+ CheckIn.LAST_CHECK_IN,
+ CheckIn.SECURITY_TOKEN,
+ CheckIn.VERSION_INFO,
+ CheckIn.DEVICE_DATA_VERSION_INFO,
+ )
+ return SettingsContract.getSettings(context, CheckIn.CONTENT_URI, projection) { c ->
+ LastCheckinInfo(
+ androidId = c.getLong(0),
+ digest = c.getString(1),
+ lastCheckin = c.getLong(2),
+ securityToken = c.getLong(3),
+ versionInfo = c.getString(4),
+ deviceDataVersionInfo = c.getString(5),
+ )
+ }
+ }
+
+ @JvmStatic
+ fun clear(context: Context) = SettingsContract.setSettings(context, CheckIn.CONTENT_URI) {
+ put(CheckIn.ANDROID_ID, 0L)
+ put(CheckIn.DIGEST, CheckIn.INITIAL_DIGEST)
+ put(CheckIn.LAST_CHECK_IN, 0L)
+ put(CheckIn.SECURITY_TOKEN, 0L)
+ put(CheckIn.VERSION_INFO, "")
+ put(CheckIn.DEVICE_DATA_VERSION_INFO, "")
+ }
+ }
+
+ fun write(context: Context) = SettingsContract.setSettings(context, CheckIn.CONTENT_URI) {
+ put(CheckIn.ANDROID_ID, androidId)
+ put(CheckIn.DIGEST, digest)
+ put(CheckIn.LAST_CHECK_IN, lastCheckin)
+ put(CheckIn.SECURITY_TOKEN, securityToken)
+ put(CheckIn.VERSION_INFO, versionInfo)
+ put(CheckIn.DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo)
+ }
+}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt b/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt
index d8902b8f..e61eb4d1 100644
--- a/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt
@@ -5,89 +5,38 @@
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 kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.microg.gms.settings.SettingsContract.CheckIn
+import org.microg.gms.settings.SettingsContract.getSettings
+import org.microg.gms.settings.SettingsContract.setSettings
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)
+data class ServiceConfiguration(val enabled: Boolean) : Serializable
+
+suspend fun getCheckinServiceInfo(context: Context): ServiceInfo = withContext(Dispatchers.IO) {
+ val projection = arrayOf(CheckIn.ENABLED, CheckIn.LAST_CHECK_IN, CheckIn.ANDROID_ID)
+ getSettings(context, CheckIn.CONTENT_URI, projection) { c ->
+ ServiceInfo(
+ configuration = ServiceConfiguration(c.getInt(0) != 0),
+ lastCheckin = c.getLong(1),
+ androidId = c.getLong(2),
+ )
}
}
-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)
+suspend fun setCheckinServiceConfiguration(context: Context, configuration: ServiceConfiguration) = withContext(Dispatchers.IO) {
+ val serviceInfo = getCheckinServiceInfo(context)
+ if (serviceInfo.configuration == configuration) return@withContext
+ // enabled state is not already set, setting it now
+ setSettings(context, CheckIn.CONTENT_URI) {
+ put(CheckIn.ENABLED, configuration.enabled)
}
-
- 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)
- }
+ if (configuration.enabled) {
+ context.sendOrderedBroadcast(Intent(context, TriggerReceiver::class.java), null)
}
}
-
-
-
-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)
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/fonts/FontsProvider.kt b/play-services-core/src/main/kotlin/org/microg/gms/fonts/FontsProvider.kt
new file mode 100644
index 00000000..a15180c5
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/fonts/FontsProvider.kt
@@ -0,0 +1,97 @@
+/*
+ * SPDX-FileCopyrightText: 2020, microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.fonts
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.database.Cursor
+import android.database.MatrixCursor
+import android.net.Uri
+import android.os.Bundle
+import android.os.ParcelFileDescriptor
+import android.os.ParcelFileDescriptor.MODE_READ_ONLY
+import android.util.Log
+import java.io.File
+
+class FontsProvider : ContentProvider() {
+
+ override fun onCreate(): Boolean {
+ Log.d(TAG, "onCreate")
+ return true
+ }
+
+ override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
+ Log.d(TAG, "call $method $arg $extras")
+ return null
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array?,
+ selection: String?,
+ selectionArgs: Array?,
+ sortOrder: String?
+ ): Cursor {
+ Log.e(TAG, "query: $uri ${projection?.toList()} $selection")
+ val cursor = MatrixCursor(COLUMNS)
+ // We could also return an empty cursor here, but some apps have been reported to crash
+ // when their expected font is not returned by Google's font provider.
+ cursor.addRow(
+ arrayOf(
+ 1337L, // file_id
+ 0, // font_ttc_index
+ null, // font_variation_settings
+ 400, // font_weight
+ 0, // font_italic
+ 0, // result_code: RESULT_CODE_OK
+ )
+ )
+ return cursor
+ }
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri {
+ Log.d(TAG, "insert: $uri, $values")
+ return uri
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array?
+ ): Int {
+ Log.d(TAG, "update: $uri, $values, $selection, $selectionArgs")
+ return 0
+ }
+
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int {
+ Log.d(TAG, "delete: $uri, $selection, $selectionArgs")
+ return 0
+ }
+
+ override fun getType(uri: Uri): String {
+ Log.d(TAG, "getType: $uri")
+ return "font/ttf"
+ }
+
+ override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
+ Log.d(TAG, "openFile: $uri mode: $mode")
+ val file = File("/system/fonts/Roboto-Regular.ttf")
+ return ParcelFileDescriptor.open(file, MODE_READ_ONLY)
+ }
+
+ companion object {
+ private const val TAG = "FontsProvider"
+ private val COLUMNS = arrayOf(
+ "file_id",
+ "font_ttc_index",
+ "font_variation_settings",
+ "font_weight",
+ "font_italic",
+ "result_code"
+ )
+ }
+}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gcm/GcmPrefs.kt b/play-services-core/src/main/kotlin/org/microg/gms/gcm/GcmPrefs.kt
new file mode 100644
index 00000000..76240e9e
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/gcm/GcmPrefs.kt
@@ -0,0 +1,171 @@
+package org.microg.gms.gcm
+
+import android.content.Context
+import android.content.Intent
+import android.net.*
+import android.util.Log
+import org.microg.gms.gcm.TriggerReceiver.FORCE_TRY_RECONNECT
+import org.microg.gms.settings.SettingsContract
+import org.microg.gms.settings.SettingsContract.Gcm
+import org.microg.gms.settings.SettingsContract.setSettings
+
+@Suppress("Warnings")
+data class GcmPrefs(
+ val isGcmLogEnabled: Boolean,
+ val lastPersistedId: String?,
+ val gcmEnabled: Boolean,
+ val networkMobile: Int,
+ val networkWifi: Int,
+ val networkRoaming: Int,
+ val networkOther: Int,
+ val learntMobileInterval: Int,
+ val learntWifiInterval: Int,
+ val learntOtherInterval: Int,
+) {
+
+ val isEnabled: Boolean get() = gcmEnabled
+
+ val lastPersistedIds: List
+ get() = if (lastPersistedId.isNullOrEmpty()) emptyList() else lastPersistedId.split("\\|")
+
+ companion object {
+ const val PREF_NETWORK_MOBILE = Gcm.NETWORK_MOBILE
+ const val PREF_NETWORK_WIFI = Gcm.NETWORK_WIFI
+ const val PREF_NETWORK_ROAMING = Gcm.NETWORK_ROAMING
+ const val PREF_NETWORK_OTHER = Gcm.NETWORK_OTHER
+
+ private const val INTERVAL = 1 * 60 * 1000 // 1 minute
+
+ @JvmStatic
+ fun get(context: Context): GcmPrefs {
+ return SettingsContract.getSettings(context, Gcm.CONTENT_URI, Gcm.PROJECTION) { c ->
+ GcmPrefs(
+ isGcmLogEnabled = c.getInt(0) != 0,
+ lastPersistedId = c.getString(1),
+ gcmEnabled = c.getInt(3) != 0,
+ networkMobile = c.getInt(4),
+ networkWifi = c.getInt(5),
+ networkRoaming = c.getInt(6),
+ networkOther = c.getInt(7),
+ learntMobileInterval = c.getInt(8),
+ learntWifiInterval = c.getInt(9),
+ learntOtherInterval = c.getInt(10),
+ )
+ }
+ }
+
+ fun write(context: Context, config: ServiceConfiguration) {
+ val gcmPrefs = get(context)
+ setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.ENABLE_GCM, config.enabled)
+ put(Gcm.NETWORK_MOBILE, config.mobile)
+ put(Gcm.NETWORK_WIFI, config.wifi)
+ put(Gcm.NETWORK_ROAMING, config.roaming)
+ put(Gcm.NETWORK_OTHER, config.other)
+ }
+ gcmPrefs.setEnabled(context, config.enabled)
+ }
+
+ @JvmStatic
+ fun clearLastPersistedId(context: Context) {
+ setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.LAST_PERSISTENT_ID, "")
+ }
+ }
+ }
+
+ /**
+ * Call this whenever the enabled state of GCM has changed.
+ */
+ private fun setEnabled(context: Context, enabled: Boolean) {
+ if (gcmEnabled == enabled) return
+ if (enabled) {
+ val i = Intent(FORCE_TRY_RECONNECT, null, context, TriggerReceiver::class.java)
+ context.sendBroadcast(i)
+ } else {
+ McsService.stop(context)
+ }
+ }
+
+ @Suppress("DEPRECATION")
+ fun getNetworkPrefForInfo(info: NetworkInfo?): String {
+ if (info == null) return PREF_NETWORK_OTHER
+ return if (info.isRoaming) PREF_NETWORK_ROAMING else when (info.type) {
+ ConnectivityManager.TYPE_MOBILE -> PREF_NETWORK_MOBILE
+ ConnectivityManager.TYPE_WIFI -> PREF_NETWORK_WIFI
+ else -> PREF_NETWORK_OTHER
+ }
+ }
+
+ @Suppress("DEPRECATION")
+ fun getHeartbeatMsFor(info: NetworkInfo?): Int {
+ return getHeartbeatMsFor(getNetworkPrefForInfo(info))
+ }
+
+ fun getHeartbeatMsFor(pref: String): Int {
+ return if (PREF_NETWORK_ROAMING == pref) {
+ if (networkRoaming != 0) networkRoaming * 60000 else learntMobileInterval
+ } else if (PREF_NETWORK_MOBILE == pref) {
+ if (networkMobile != 0) networkMobile * 60000 else learntMobileInterval
+ } else if (PREF_NETWORK_WIFI == pref) {
+ if (networkWifi != 0) networkWifi * 60000 else learntWifiInterval
+ } else {
+ if (networkOther != 0) networkOther * 60000 else learntOtherInterval
+ }
+ }
+
+ fun learnTimeout(context: Context, pref: String) {
+ Log.d("GmsGcmPrefs", "learnTimeout: $pref")
+ when (pref) {
+ PREF_NETWORK_MOBILE, PREF_NETWORK_ROAMING -> setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.LEARNT_MOBILE, (learntMobileInterval * 0.95).toInt())
+ }
+ PREF_NETWORK_WIFI -> setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.LEARNT_WIFI, (learntWifiInterval * 0.95).toInt())
+ }
+ else -> setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.LEARNT_OTHER, (learntOtherInterval * 0.95).toInt())
+ }
+ }
+ }
+
+ fun learnReached(context: Context, pref: String, time: Long) {
+ Log.d("GmsGcmPrefs", "learnReached: $pref / $time")
+ when (pref) {
+ PREF_NETWORK_MOBILE, PREF_NETWORK_ROAMING -> {
+ if (time > learntMobileInterval / 4 * 3) {
+ setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.LEARNT_MOBILE, INTERVAL)
+ }
+ }
+ }
+ PREF_NETWORK_WIFI -> {
+ if (time > learntWifiInterval / 4 * 3) {
+ setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.LEARNT_WIFI, INTERVAL)
+ }
+ }
+ }
+ else -> {
+ if (time > learntOtherInterval / 4 * 3) {
+ setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.LEARNT_OTHER, INTERVAL)
+ }
+ }
+ }
+ }
+ }
+
+ @Suppress("DEPRECATION")
+ fun isEnabledFor(info: NetworkInfo?): Boolean {
+ return isEnabled && info != null && getHeartbeatMsFor(info) >= 0
+ }
+
+ fun extendLastPersistedId(context: Context, id: String) {
+ val newId = if (lastPersistedId.isNullOrEmpty()) id else "$lastPersistedId|$id"
+ setSettings(context, Gcm.CONTENT_URI) {
+ put(Gcm.LAST_PERSISTENT_ID, newId)
+ }
+ }
+
+}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt b/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt
index 00d0721e..9571e931 100644
--- a/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt
@@ -31,7 +31,7 @@ import kotlin.coroutines.suspendCoroutine
private const val TAG = "GmsGcmRegister"
private suspend fun ensureCheckinIsUpToDate(context: Context) {
- if (!CheckinPrefs.get(context).isEnabled) throw RuntimeException("Checkin disabled")
+ if (!CheckinPrefs.isEnabled(context)) throw RuntimeException("Checkin disabled")
val lastCheckin = LastCheckinInfo.read(context).lastCheckin
if (lastCheckin < System.currentTimeMillis() - CheckinService.MAX_VALID_CHECKIN_AGE) {
val resultData: Bundle = suspendCoroutine { continuation ->
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gcm/ServiceInfo.kt b/play-services-core/src/main/kotlin/org/microg/gms/gcm/ServiceInfo.kt
index b6717ee4..15569540 100644
--- a/play-services-core/src/main/kotlin/org/microg/gms/gcm/ServiceInfo.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/gcm/ServiceInfo.kt
@@ -10,52 +10,40 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
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, val learntMobileInterval: Int, val learntWifiInterval: Int, val learntOtherInterval: Int) : Serializable
-// TODO: Intervals
-data class ServiceConfiguration(val enabled: Boolean, val mobile: Int, val wifi: Int, val roaming: Int, val other: Int) : Serializable {
- fun saveToPrefs(context: Context) {
- GcmPrefs.get(context).apply {
- isEnabled = enabled
- mobileInterval = mobile
- wifiInterval = wifi
- roamingInterval = roaming
- otherInterval = other
- }
- }
-}
+data class ServiceConfiguration(val enabled: Boolean, val mobile: Int, val wifi: Int, val roaming: Int, val other: Int) : Serializable
-private fun GcmPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled, mobileInterval, wifiInterval, roamingInterval, otherInterval)
+private fun GcmPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled, networkMobile, networkWifi, networkRoaming, networkOther)
class ServiceInfoReceiver : BroadcastReceiver() {
- private fun sendInfoResponse(context: Context) {
- context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
- setPackage(context.packageName)
- val prefs = GcmPrefs.get(context)
- putExtra(EXTRA_SERVICE_INFO, ServiceInfo(prefs.toConfiguration(), McsService.isConnected(context), McsService.getStartTimestamp(), prefs.learntMobileInterval, prefs.learntWifiInterval, prefs.learntOtherInterval))
- }, 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)
+ context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
+ setPackage(context.packageName)
+ val prefs = GcmPrefs.get(context)
+ val info = ServiceInfo(
+ configuration = prefs.toConfiguration(),
+ connected = McsService.isConnected(context),
+ startTimestamp = McsService.getStartTimestamp(),
+ learntMobileInterval = prefs.learntMobileInterval,
+ learntWifiInterval = prefs.learntWifiInterval,
+ learntOtherInterval = prefs.learntOtherInterval
+ )
+ putExtra(EXTRA_SERVICE_INFO, info)
+ }, null)
} catch (e: Exception) {
Log.w(TAG, e)
}
@@ -87,12 +75,11 @@ private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context):
}
suspend fun getGcmServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
+ // this is still using a broadcast, because it calls into McsService in the persistent process
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)
\ No newline at end of file
+suspend fun setGcmServiceConfiguration(context: Context, configuration: ServiceConfiguration) = withContext(Dispatchers.IO) {
+ GcmPrefs.write(context, configuration)
+}
\ No newline at end of file
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt b/play-services-core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt
new file mode 100644
index 00000000..c3d517dd
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt
@@ -0,0 +1,97 @@
+package org.microg.gms.settings
+
+import android.content.ContentValues
+import android.content.Context
+import android.database.Cursor
+import android.net.Uri
+
+object SettingsContract {
+ const val AUTHORITY = "org.microg.gms.settings"
+ val AUTHORITY_URI: Uri = Uri.parse("content://$AUTHORITY")
+
+ object CheckIn {
+ private const val id = "check-in"
+ val CONTENT_URI: Uri = Uri.withAppendedPath(AUTHORITY_URI, id)
+ const val CONTENT_TYPE = "vnd.android.cursor.item/vnd.$AUTHORITY.$id"
+
+ const val ENABLED = "checkin_enable_service"
+ const val ANDROID_ID = "androidId"
+ const val DIGEST = "digest"
+ const val LAST_CHECK_IN = "lastCheckin"
+ const val SECURITY_TOKEN = "securityToken"
+ const val VERSION_INFO = "versionInfo"
+ const val DEVICE_DATA_VERSION_INFO = "deviceDataVersionInfo"
+
+ val PROJECTION = arrayOf(
+ ENABLED,
+ ANDROID_ID,
+ DIGEST,
+ LAST_CHECK_IN,
+ SECURITY_TOKEN,
+ VERSION_INFO,
+ DEVICE_DATA_VERSION_INFO,
+ )
+ const val PREFERENCES_NAME = "checkin"
+ const val INITIAL_DIGEST = "1-929a0dca0eee55513280171a8585da7dcd3700f8"
+ }
+
+ object Gcm {
+ private const val id = "gcm"
+ val CONTENT_URI: Uri = Uri.withAppendedPath(AUTHORITY_URI, id)
+ const val CONTENT_TYPE = "vnd.android.cursor.item/vnd.$AUTHORITY.$id"
+
+ const val FULL_LOG = "gcm_full_log"
+ const val LAST_PERSISTENT_ID = "gcm_last_persistent_id"
+ const val ENABLE_GCM = "gcm_enable_mcs_service"
+
+ const val NETWORK_MOBILE = "gcm_network_mobile"
+ const val NETWORK_WIFI = "gcm_network_wifi"
+ const val NETWORK_ROAMING = "gcm_network_roaming"
+ const val NETWORK_OTHER = "gcm_network_other"
+
+ const val LEARNT_MOBILE = "gcm_learnt_mobile"
+ const val LEARNT_WIFI = "gcm_learnt_wifi"
+ const val LEARNT_OTHER = "gcm_learnt_other"
+
+ val PROJECTION = arrayOf(
+ FULL_LOG,
+ LAST_PERSISTENT_ID,
+ ENABLE_GCM,
+ NETWORK_MOBILE,
+ NETWORK_WIFI,
+ NETWORK_ROAMING,
+ NETWORK_OTHER,
+ LEARNT_MOBILE,
+ LEARNT_WIFI,
+ LEARNT_OTHER,
+ )
+ }
+
+ object Auth {
+ private const val id = "auth"
+ val CONTENT_URI: Uri = Uri.withAppendedPath(AUTHORITY_URI, id)
+ const val CONTENT_TYPE = "vnd.android.cursor.item/vnd.$AUTHORITY.$id"
+
+ const val TRUST_GOOGLE = "auth_manager_trust_google"
+ const val VISIBLE = "auth_manager_visible"
+
+ val PROJECTION = arrayOf(
+ TRUST_GOOGLE,
+ VISIBLE,
+ )
+ }
+
+ fun getSettings(context: Context, uri: Uri, projection: Array?, f: (Cursor) -> T): T {
+ context.contentResolver.query(uri, projection, null, null, null).use { c ->
+ require(c != null) { "Cursor for query $uri ${projection?.toList()} was null" }
+ if (!c.moveToFirst()) error("Cursor for query $uri ${projection?.toList()} was empty")
+ return f.invoke(c)
+ }
+ }
+
+ fun setSettings(context: Context, uri: Uri, v: ContentValues.() -> Unit) {
+ val values = ContentValues().apply { v.invoke(this) }
+ val affected = context.contentResolver.update(uri, values, null, null)
+ require(affected == 1) { "Update for $uri with $values affected 0 rows"}
+ }
+}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt b/play-services-core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt
new file mode 100644
index 00000000..ddb55a6b
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt
@@ -0,0 +1,228 @@
+package org.microg.gms.settings
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.Context
+import android.content.Context.MODE_PRIVATE
+import android.content.SharedPreferences
+import android.database.Cursor
+import android.database.MatrixCursor
+import android.net.Uri
+import android.util.Log
+import androidx.preference.PreferenceManager
+import org.microg.gms.common.PackageUtils.warnIfNotMainProcess
+import org.microg.gms.settings.SettingsContract.AUTHORITY
+import org.microg.gms.settings.SettingsContract.Auth
+import org.microg.gms.settings.SettingsContract.CheckIn
+import org.microg.gms.settings.SettingsContract.Gcm
+import java.io.File
+
+/**
+ * All settings access should go through this [ContentProvider],
+ * because it provides safe access from different processes which normal [SharedPreferences] don't.
+ */
+class SettingsProvider : ContentProvider() {
+
+ private val preferences: SharedPreferences by lazy {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ }
+ private val checkInPrefs by lazy {
+ context!!.getSharedPreferences(CheckIn.PREFERENCES_NAME, MODE_PRIVATE)
+ }
+ private val systemDefaultPreferences: SharedPreferences? by lazy {
+ try {
+ Context::class.java.getDeclaredMethod(
+ "getSharedPreferences",
+ File::class.java,
+ Int::class.javaPrimitiveType
+ ).invoke(context, File("/system/etc/microg.xml"), MODE_PRIVATE) as SharedPreferences
+ } catch (ignored: Exception) {
+ null
+ }
+ }
+
+ override fun onCreate(): Boolean {
+ return true
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array?,
+ selection: String?,
+ selectionArgs: Array?,
+ sortOrder: String?
+ ): Cursor? = when (uri) {
+ CheckIn.CONTENT_URI -> queryCheckIn(projection ?: CheckIn.PROJECTION)
+ Gcm.CONTENT_URI -> queryGcm(projection ?: Gcm.PROJECTION)
+ Auth.CONTENT_URI -> queryAuth(projection ?: Auth.PROJECTION)
+ else -> null
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array?
+ ): Int {
+ warnIfNotMainProcess(context, this.javaClass)
+ if (values == null) return 0
+ when (uri) {
+ CheckIn.CONTENT_URI -> updateCheckIn(values)
+ Gcm.CONTENT_URI -> updateGcm(values)
+ Auth.CONTENT_URI -> updateAuth(values)
+ else -> return 0
+ }
+ return 1
+ }
+
+ private fun queryCheckIn(p: Array): Cursor = MatrixCursor(p).addRow(p) { key ->
+ when (key) {
+ CheckIn.ENABLED -> getSettingsBoolean(key, true)
+ CheckIn.ANDROID_ID -> checkInPrefs.getLong(key, 0)
+ CheckIn.DIGEST -> checkInPrefs.getString(key, CheckIn.INITIAL_DIGEST)
+ ?: CheckIn.INITIAL_DIGEST
+ CheckIn.LAST_CHECK_IN -> checkInPrefs.getLong(key, 0)
+ CheckIn.SECURITY_TOKEN -> checkInPrefs.getLong(key, 0)
+ CheckIn.VERSION_INFO -> checkInPrefs.getString(key, "") ?: ""
+ CheckIn.DEVICE_DATA_VERSION_INFO -> checkInPrefs.getString(key, "") ?: ""
+ else -> throw IllegalArgumentException()
+ }
+ }
+
+ private fun updateCheckIn(values: ContentValues) {
+ if (values.size() == 0) return
+ if (values.size() == 1 && values.containsKey(CheckIn.ENABLED)) {
+ // special case: only changing enabled state
+ updateCheckInEnabled(values.getAsBoolean(CheckIn.ENABLED))
+ return
+ }
+ val editor = checkInPrefs.edit()
+ values.valueSet().forEach { (key, value) ->
+ // TODO remove log
+ Log.e("TEST", "check-in update: $key = $value")
+ if (key == CheckIn.ENABLED) {
+ // special case: not saved in checkInPrefs
+ updateCheckInEnabled(value as Boolean)
+ }
+ when (key) {
+ CheckIn.ANDROID_ID -> editor.putLong(key, value as Long)
+ CheckIn.DIGEST -> editor.putString(key, value as String?)
+ CheckIn.LAST_CHECK_IN -> editor.putLong(key, value as Long)
+ CheckIn.SECURITY_TOKEN -> editor.putLong(key, value as Long)
+ CheckIn.VERSION_INFO -> editor.putString(key, value as String?)
+ CheckIn.DEVICE_DATA_VERSION_INFO -> editor.putString(key, value as String?)
+ }
+ }
+ editor.apply()
+ }
+
+ private fun updateCheckInEnabled(enabled: Boolean) {
+ preferences.edit()
+ .putBoolean(CheckIn.ENABLED, enabled)
+ .apply()
+ }
+
+ private fun queryGcm(p: Array): Cursor = MatrixCursor(p).addRow(p) { key ->
+ when (key) {
+ Gcm.ENABLE_GCM -> getSettingsBoolean(key, true)
+ Gcm.FULL_LOG -> getSettingsBoolean(key, true)
+
+ Gcm.LAST_PERSISTENT_ID -> preferences.getString(key, "") ?: ""
+
+ Gcm.NETWORK_MOBILE -> Integer.parseInt(preferences.getString(key, "0") ?: "0")
+ Gcm.NETWORK_WIFI -> Integer.parseInt(preferences.getString(key, "0") ?: "0")
+ Gcm.NETWORK_ROAMING -> Integer.parseInt(preferences.getString(key, "0") ?: "0")
+ Gcm.NETWORK_OTHER -> Integer.parseInt(preferences.getString(key, "0") ?: "0")
+
+ Gcm.LEARNT_MOBILE -> preferences.getInt(key, 300000)
+ Gcm.LEARNT_WIFI -> preferences.getInt(key, 300000)
+ Gcm.LEARNT_OTHER -> preferences.getInt(key, 300000)
+
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+
+ private fun updateGcm(values: ContentValues) {
+ if (values.size() == 0) return
+ val editor = preferences.edit()
+ values.valueSet().forEach { (key, value) ->
+ // TODO remove log
+ Log.e("TEST", "gcm update: $key = $value")
+ when (key) {
+ Gcm.ENABLE_GCM -> editor.putBoolean(key, value as Boolean)
+ Gcm.FULL_LOG -> editor.putBoolean(key, value as Boolean)
+
+ Gcm.LAST_PERSISTENT_ID -> editor.putString(key, value as String?)
+
+ Gcm.NETWORK_MOBILE -> editor.putString(key, (value as Int).toString())
+ Gcm.NETWORK_WIFI -> editor.putString(key, (value as Int).toString())
+ Gcm.NETWORK_ROAMING -> editor.putString(key, (value as Int).toString())
+ Gcm.NETWORK_OTHER -> editor.putString(key, (value as Int).toString())
+
+ Gcm.LEARNT_MOBILE -> editor.putInt(key, value as Int)
+ Gcm.LEARNT_WIFI -> editor.putInt(key, value as Int)
+ Gcm.LEARNT_OTHER -> editor.putInt(key, value as Int)
+
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+ editor.apply()
+ }
+
+ private fun queryAuth(p: Array): Cursor = MatrixCursor(p).addRow(p) { key ->
+ when (key) {
+ Auth.TRUST_GOOGLE -> getSettingsBoolean(key, true)
+ Auth.VISIBLE -> getSettingsBoolean(key, false)
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+
+ private fun updateAuth(values: ContentValues) {
+ if (values.size() == 0) return
+ val editor = preferences.edit()
+ values.valueSet().forEach { (key, value) ->
+ // TODO remove log
+ Log.e("TEST", "auth update: $key = $value")
+ when (key) {
+ Auth.TRUST_GOOGLE -> editor.putBoolean(key, value as Boolean)
+ Auth.VISIBLE -> editor.putBoolean(key, value as Boolean)
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+ editor.apply()
+ }
+
+ private fun MatrixCursor.addRow(
+ p: Array,
+ valueGetter: (String) -> Any
+ ): MatrixCursor {
+ val row = newRow()
+ for (key in p) row.add(valueGetter.invoke(key).apply {
+ // TODO remove log
+ Log.e("TEST", "$key = $this")
+ })
+ return this
+ }
+
+ override fun getType(uri: Uri): String {
+ return "vnd.android.cursor.item/vnd.$AUTHORITY.${uri.path}"
+ }
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int {
+ throw UnsupportedOperationException()
+ }
+
+ /**
+ * Returns the current setting of the given [key]
+ * using the default value from [systemDefaultPreferences] or [def] if not available.
+ * @return the current setting as [Int], because [ContentProvider] does not support [Boolean].
+ */
+ private fun getSettingsBoolean(key: String, def: Boolean): Int {
+ val default = systemDefaultPreferences?.getBoolean(key, def) ?: def
+ return if (preferences.getBoolean(key, default)) 1 else 0
+ }
+}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt
index d0c3f0ed..232298f8 100755
--- a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt
@@ -34,11 +34,12 @@ class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragmen
lifecycleScope.launchWhenResumed {
val info = getCheckinServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
- displayServiceInfo(setCheckinServiceConfiguration(requireContext(), newConfiguration))
+ setCheckinServiceConfiguration(requireContext(), newConfiguration)
+ displayServiceInfo(info.copy(configuration = newConfiguration))
}
}
- fun displayServiceInfo(serviceInfo: ServiceInfo) {
+ private fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.checkinEnabled = serviceInfo.configuration.enabled
}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt
index 0a490bd2..773578fa 100755
--- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt
@@ -16,6 +16,7 @@ import org.microg.gms.gcm.ServiceInfo
import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.gms.gcm.setGcmServiceConfiguration
+@Suppress("Warnings")
class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
lateinit var binding: PushNotificationFragmentBinding
@@ -33,11 +34,12 @@ class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
lifecycleScope.launchWhenResumed {
val info = getGcmServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
- displayServiceInfo(setGcmServiceConfiguration(requireContext(), newConfiguration))
+ setGcmServiceConfiguration(requireContext(), newConfiguration)
+ displayServiceInfo(info.copy(configuration = newConfiguration))
}
}
- fun displayServiceInfo(serviceInfo: ServiceInfo) {
+ private fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.gcmEnabled = serviceInfo.configuration.enabled
}
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt
index 141b33f5..dcea1a9c 100644
--- a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt
@@ -7,7 +7,9 @@ package org.microg.gms.ui
import android.content.ComponentName
import android.content.pm.PackageManager
+import android.content.Intent
import android.os.Bundle
+import android.util.Log
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
@@ -17,6 +19,9 @@ import com.mgoogle.android.gms.R
import org.microg.gms.checkin.CheckinClient
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.gcm.GcmDatabase
+import org.microg.gms.gcm.McsConstants.ACTION_RECONNECT
+import org.microg.gms.gcm.McsService
+import org.microg.gms.gcm.TriggerReceiver
import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.tools.ui.ResourceSettingsFragment
@@ -73,16 +78,22 @@ class SettingsFragment : ResourceSettingsFragment() {
}
private suspend fun updateDetails() {
- findPreference(PREF_GCM)?.summary = if (getGcmServiceInfo(requireContext()).configuration.enabled) {
+ val context = requireContext()
+ val gcmServiceInfo = getGcmServiceInfo(context)
+ if (gcmServiceInfo.configuration.enabled) {
val database = GcmDatabase(context)
val regCount = database.registrationList.size
+ // check if we are connected as we should be and re-connect if not
+ if (!gcmServiceInfo.connected) {
+ context.sendBroadcast(Intent(ACTION_RECONNECT, null, context, TriggerReceiver::class.java))
+ }
database.close()
getString(R.string.service_status_enabled_short) + " - " + resources.getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount)
} else {
getString(R.string.service_status_disabled_short)
}
- findPreference(PREF_CHECKIN)?.setSummary(if (getCheckinServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
+ findPreference(PREF_CHECKIN)?.setSummary(if (getCheckinServiceInfo(context).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
}
companion object {