Update to latest upstream

This commit is contained in:
Oizaro 2021-06-26 09:45:33 +02:00
parent ba1ee758be
commit 79f78b8d86
33 changed files with 860 additions and 624 deletions

View File

@ -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'
}

View File

@ -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 {

View File

@ -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')
}

View File

@ -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"
}

View File

@ -91,6 +91,13 @@
android:name="fake-signature"
android:value="@string/fake_signature" />
<!-- Internal Settings -->
<provider
android:name="org.microg.gms.settings.SettingsProvider"
android:authorities="org.microg.gms.settings"
android:exported="true" />
<!-- Services Framework -->
<provider
@ -117,8 +124,6 @@
</intent-filter>
</service>
<receiver android:name="org.microg.gms.checkin.ServiceInfoReceiver" />
<receiver android:name="org.microg.gms.checkin.TriggerReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
@ -303,6 +308,12 @@
</intent-filter>
</activity>
<!-- Fonts -->
<provider
android:name="org.microg.gms.fonts.FontsProvider"
android:authorities="com.mgoogle.android.gms.fonts"
android:exported="true"/>
<!-- microG Settings shown in Launcher -->
<activity-alias
android:name="org.microg.gms.ui.SettingsActivityLauncher"

View File

@ -23,21 +23,20 @@ import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import androidx.preference.PreferenceManager;
import org.microg.gms.common.PackageUtils;
import org.microg.gms.settings.SettingsContract;
import java.io.IOException;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static org.microg.gms.auth.AuthPrefs.isTrustGooglePermitted;
public class AuthManager {
private static final String TAG = "GmsAuthManager";
public static final String PERMISSION_TREE_BASE = "com.mgoogle.android.googleapps.permission.GOOGLE_AUTH.";
private static final String PREF_AUTH_TRUST_GOOGLE = "auth_manager_trust_google";
public static final String PREF_AUTH_VISIBLE = "auth_manager_visible";
public static final String PREF_AUTH_VISIBLE = SettingsContract.Auth.VISIBLE;
public static final int ONE_HOUR_IN_SECONDS = 60 * 60;
private final Context context;
@ -194,14 +193,6 @@ public class AuthManager {
}
}
public static boolean isTrustGooglePermitted(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_AUTH_TRUST_GOOGLE, true);
}
public static boolean isAuthVisible(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_AUTH_VISIBLE, false);
}
private boolean isSystemApp() {
try {
int flags = context.getPackageManager().getApplicationInfo(packageName, 0).flags;

View File

@ -113,7 +113,7 @@ public class AuthRequest extends HttpFormClient.Request {
public AuthRequest fromContext(Context context) {
build(Utils.getBuild(context));
locale(Utils.getLocale(context));
androidIdHex = Long.toHexString(LastCheckinInfo.read(context).androidId);
androidIdHex = Long.toHexString(LastCheckinInfo.read(context).getAndroidId());
return this;
}

View File

@ -74,6 +74,7 @@ import static android.view.KeyEvent.KEYCODE_BACK;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static org.microg.gms.auth.AuthPrefs.isAuthVisible;
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
import static org.microg.gms.common.Constants.GMS_VERSION_CODE;
@ -141,7 +142,7 @@ public class LoginActivity extends AssistantActivity {
AccountManager accountManager = AccountManager.get(this);
Account account = new Account(getIntent().getStringExtra(EXTRA_EMAIL), accountType);
accountManager.addAccountExplicitly(account, getIntent().getStringExtra(EXTRA_TOKEN), null);
if (AuthManager.isAuthVisible(this) && SDK_INT >= 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);

View File

@ -81,8 +81,8 @@ public class CheckinClient {
LastCheckinInfo checkinInfo, Locale locale,
List<Account> accounts) {
CheckinRequest.Builder builder = new CheckinRequest.Builder()
.accountCookie(new ArrayList<String>())
.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();

View File

@ -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<CheckinClient.Account> 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();

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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");
}

View File

@ -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<String> 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();
}
}

View File

@ -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())

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
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<out String>?
): Int {
Log.d(TAG, "update: $uri, $values, $selection, $selectionArgs")
return 0
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): 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"
)
}
}

View File

@ -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<String>
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)
}
}
}

View File

@ -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 ->

View File

@ -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)
suspend fun setGcmServiceConfiguration(context: Context, configuration: ServiceConfiguration) = withContext(Dispatchers.IO) {
GcmPrefs.write(context, configuration)
}

View File

@ -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 <T> getSettings(context: Context, uri: Uri, projection: Array<out String>?, 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"}
}
}

View File

@ -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<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
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<out String>?
): 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<out String>): 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<out String>): 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<out String>): 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<out String>,
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<out String>?): 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
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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<Preference>(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<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_CHECKIN)?.setSummary(if (getCheckinServiceInfo(context).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
}
companion object {