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 {