mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2024-11-19 02:29:25 +01:00
Refactor settings access to use a SettingsProvider
to enable safe usage of settings no matter which process is getting/setting them. Previously, different processes were accessing settings in an unsafe way and the warn methods were throwing Runtime exceptions which went largely unnoticed, but happened, especially on a fresh start of the OS. Change-Id: Ie4134e7be2a7ca4a373790f45fbcbd09bf02ad86
This commit is contained in:
parent
fbcb6066c3
commit
aa3a2d65cd
@ -0,0 +1,120 @@
|
||||
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 CONFIRM_NEW_APPS = "gcm_confirm_new_apps"
|
||||
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,
|
||||
CONFIRM_NEW_APPS,
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
object Exposure {
|
||||
private const val id = "exposureNotification"
|
||||
val CONTENT_URI: Uri = Uri.withAppendedPath(AUTHORITY_URI, id)
|
||||
const val CONTENT_TYPE = "vnd.android.cursor.item/vnd.$AUTHORITY.$id"
|
||||
|
||||
const val SCANNER_ENABLED = "exposure_scanner_enabled"
|
||||
const val LAST_CLEANUP = "exposure_last_cleanup"
|
||||
|
||||
val PROJECTION = arrayOf(
|
||||
SCANNER_ENABLED,
|
||||
LAST_CLEANUP,
|
||||
)
|
||||
}
|
||||
|
||||
object SafetyNet {
|
||||
private const val id = "safety-net"
|
||||
val CONTENT_URI: Uri = Uri.withAppendedPath(AUTHORITY_URI, id)
|
||||
const val CONTENT_TYPE = "vnd.android.cursor.item/vnd.$AUTHORITY.$id"
|
||||
}
|
||||
|
||||
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"}
|
||||
}
|
||||
|
||||
}
|
@ -129,6 +129,13 @@
|
||||
android:exported="true"
|
||||
android:permission="org.microg.gms.PROVISION" />
|
||||
|
||||
<!-- Internal Settings -->
|
||||
|
||||
<provider
|
||||
android:name="org.microg.gms.settings.SettingsProvider"
|
||||
android:authorities="org.microg.gms.settings"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- Location -->
|
||||
|
||||
<activity
|
||||
@ -198,8 +205,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" />
|
||||
|
@ -21,22 +21,22 @@ import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
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.google.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;
|
||||
@ -185,14 +185,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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ import android.accounts.AccountManager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
@ -36,7 +35,6 @@ import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
@ -72,6 +70,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;
|
||||
|
||||
@ -136,7 +135,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);
|
||||
@ -223,7 +222,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);
|
||||
@ -425,7 +424,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);
|
||||
|
@ -18,15 +18,12 @@ package org.microg.gms.checkin;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.wire.Wire;
|
||||
|
||||
import org.microg.gms.common.Build;
|
||||
import org.microg.gms.common.DeviceConfiguration;
|
||||
import org.microg.gms.common.DeviceIdentifier;
|
||||
import org.microg.gms.common.PhoneInfo;
|
||||
import org.microg.gms.common.Utils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@ -46,8 +43,8 @@ import java.util.zip.GZIPOutputStream;
|
||||
public class CheckinClient {
|
||||
private static final String TAG = "GmsCheckinClient";
|
||||
private static final Object TODO = null; // TODO
|
||||
private static final List<String> TODO_LIST_STRING = new ArrayList<String>(); // TODO
|
||||
private static final List<CheckinRequest.Checkin.Statistic> TODO_LIST_CHECKIN = new ArrayList<CheckinRequest.Checkin.Statistic>(); // TODO
|
||||
private static final List<String> TODO_LIST_STRING = new ArrayList<>(); // TODO
|
||||
private static final List<CheckinRequest.Checkin.Statistic> TODO_LIST_CHECKIN = new ArrayList<>(); // TODO
|
||||
private static final String SERVICE_URL = "https://android.clients.google.com/checkin";
|
||||
|
||||
public static CheckinResponse request(CheckinRequest request) throws IOException {
|
||||
@ -84,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(build.bootloader)
|
||||
@ -105,11 +102,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)
|
||||
@ -133,7 +130,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())
|
||||
@ -154,8 +151,8 @@ public class CheckinClient {
|
||||
builder.macAddress(Arrays.asList(deviceIdent.wifiMac))
|
||||
.macAddressType(Arrays.asList("wifi"));
|
||||
}
|
||||
if (checkinInfo.securityToken != 0) {
|
||||
builder.securityToken(checkinInfo.securityToken)
|
||||
if (checkinInfo.getSecurityToken() != 0) {
|
||||
builder.securityToken(checkinInfo.getSecurityToken())
|
||||
.fragment(1);
|
||||
}
|
||||
return builder.build();
|
||||
|
@ -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<CheckinClient.Account>();
|
||||
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();
|
||||
|
@ -1,75 +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 android.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, false);
|
||||
}
|
||||
|
||||
@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).commit();
|
||||
if (!changed) return;
|
||||
if (newStatus) {
|
||||
context.sendOrderedBroadcast(new Intent(context, TriggerReceiver.class), null);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +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();
|
||||
}
|
||||
}
|
@ -38,8 +38,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;
|
||||
}
|
||||
|
@ -1,305 +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.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
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 MIN_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
||||
private static final int MAX_INTERVAL = 30 * 60 * 1000; // 30 minutes
|
||||
|
||||
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 confirmNewApps = false;
|
||||
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, false);
|
||||
gcmLogEnabled = getSettingsBoolean(PREF_FULL_LOG, true);
|
||||
confirmNewApps = getSettingsBoolean(PREF_CONFIRM_NEW_APPS, false);
|
||||
|
||||
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, 300000);
|
||||
learntWifi = preferences.getInt(PREF_LEARNT_WIFI, 300000);
|
||||
learntOther = preferences.getInt(PREF_LEARNT_OTHER, 300000);
|
||||
}
|
||||
|
||||
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() {
|
||||
learntMobile = Math.max(MIN_INTERVAL, Math.min(learntMobile, MAX_INTERVAL));
|
||||
learntWifi = Math.max(MIN_INTERVAL, Math.min(learntWifi, MAX_INTERVAL));
|
||||
learntOther = Math.max(MIN_INTERVAL, Math.min(learntOther, MAX_INTERVAL));
|
||||
preferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).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 boolean isConfirmNewApps() {
|
||||
return confirmNewApps;
|
||||
}
|
||||
|
||||
public void setConfirmNewApps(boolean value) {
|
||||
confirmNewApps = value;
|
||||
preferences.edit().putBoolean(PREF_CONFIRM_NEW_APPS, value).apply();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@ -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())
|
||||
|
@ -87,8 +87,8 @@ public class PushRegisterManager {
|
||||
if (!request.delete) {
|
||||
if (!prefs.isEnabled() ||
|
||||
(app != null && !app.allowRegister) ||
|
||||
LastCheckinInfo.read(context).lastCheckin <= 0 ||
|
||||
(app == null && prefs.isConfirmNewApps())) {
|
||||
LastCheckinInfo.read(context).getLastCheckin() <= 0 ||
|
||||
(app == null && prefs.getConfirmNewApps())) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE);
|
||||
callback.onResult(bundle);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class MessageHandler extends ServerMessageListener {
|
||||
private String peerNodeId;
|
||||
|
||||
public MessageHandler(WearableImpl wearable, ConnectionConfiguration config) {
|
||||
this(wearable, config, new Build().model, config.nodeId, LastCheckinInfo.read(wearable.getContext()).androidId);
|
||||
this(wearable, config, new Build().model, config.nodeId, LastCheckinInfo.read(wearable.getContext()).getAndroidId());
|
||||
}
|
||||
|
||||
private MessageHandler(WearableImpl wearable, ConnectionConfiguration config, String name, String networkId, long androidId) {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -0,0 +1,181 @@
|
||||
package org.microg.gms.gcm
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkInfo
|
||||
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
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
data class GcmPrefs(
|
||||
val isGcmLogEnabled: Boolean,
|
||||
val lastPersistedId: String?,
|
||||
val confirmNewApps: Boolean,
|
||||
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_CONFIRM_NEW_APPS = Gcm.CONFIRM_NEW_APPS
|
||||
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 MIN_INTERVAL = 5 * 60 * 1000 // 5 minutes
|
||||
private const val MAX_INTERVAL = 30 * 60 * 1000 // 30 minutes
|
||||
|
||||
@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),
|
||||
confirmNewApps = c.getInt(2) != 0,
|
||||
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.CONFIRM_NEW_APPS, config.confirmNewApps)
|
||||
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) {
|
||||
val newInterval = (learntMobileInterval * 1.02).toInt()
|
||||
setSettings(context, Gcm.CONTENT_URI) {
|
||||
put(Gcm.LEARNT_MOBILE, max(MIN_INTERVAL, min(newInterval, MAX_INTERVAL)))
|
||||
}
|
||||
}
|
||||
}
|
||||
PREF_NETWORK_WIFI -> {
|
||||
if (time > learntWifiInterval / 4 * 3) {
|
||||
val newInterval = (learntWifiInterval * 1.02).toInt()
|
||||
setSettings(context, Gcm.CONTENT_URI) {
|
||||
put(Gcm.LEARNT_WIFI, max(MIN_INTERVAL, min(newInterval, MAX_INTERVAL)))
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if (time > learntOtherInterval / 4 * 3) {
|
||||
val newInterval = (learntOtherInterval * 1.02).toInt()
|
||||
setSettings(context, Gcm.CONTENT_URI) {
|
||||
put(Gcm.LEARNT_OTHER, max(MIN_INTERVAL, min(newInterval, MAX_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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -30,7 +30,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 ->
|
||||
@ -55,7 +55,7 @@ private suspend fun ensureCheckinIsUpToDate(context: Context) {
|
||||
private suspend fun ensureAppRegistrationAllowed(context: Context, database: GcmDatabase, packageName: String) {
|
||||
if (!GcmPrefs.get(context).isEnabled) throw RuntimeException("GCM disabled")
|
||||
val app = database.getApp(packageName)
|
||||
if (app == null && GcmPrefs.get(context).isConfirmNewApps) {
|
||||
if (app == null && GcmPrefs.get(context).confirmNewApps) {
|
||||
val accepted: Boolean = suspendCoroutine { continuation ->
|
||||
val i = Intent(context, AskPushPermission::class.java)
|
||||
i.putExtra(AskPushPermission.EXTRA_REQUESTED_PACKAGE, packageName)
|
||||
|
@ -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
|
||||
|
||||
data class ServiceConfiguration(val enabled: Boolean, val confirmNewApps: Boolean, val mobile: Int, val wifi: Int, val roaming: Int, val other: Int) : Serializable {
|
||||
fun saveToPrefs(context: Context) {
|
||||
GcmPrefs.get(context).apply {
|
||||
isEnabled = enabled
|
||||
isConfirmNewApps = confirmNewApps
|
||||
mobileInterval = mobile
|
||||
wifiInterval = wifi
|
||||
roamingInterval = roaming
|
||||
otherInterval = other
|
||||
}
|
||||
}
|
||||
}
|
||||
data class ServiceConfiguration(val enabled: Boolean, val confirmNewApps: Boolean, val mobile: Int, val wifi: Int, val roaming: Int, val other: Int) : Serializable
|
||||
|
||||
private fun GcmPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled, isConfirmNewApps, mobileInterval, wifiInterval, roamingInterval, otherInterval)
|
||||
private fun GcmPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled, confirmNewApps, 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)
|
||||
}
|
||||
|
@ -0,0 +1,256 @@
|
||||
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.Exposure
|
||||
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)
|
||||
Exposure.CONTENT_URI -> queryExposure(projection ?: Exposure.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)
|
||||
Exposure.CONTENT_URI -> updateExposure(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, false)
|
||||
Gcm.FULL_LOG -> getSettingsBoolean(key, true)
|
||||
Gcm.CONFIRM_NEW_APPS -> getSettingsBoolean(key, false)
|
||||
|
||||
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.CONFIRM_NEW_APPS -> 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 queryExposure(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key ->
|
||||
when (key) {
|
||||
Exposure.SCANNER_ENABLED -> getSettingsBoolean(key, false)
|
||||
Exposure.LAST_CLEANUP -> preferences.getLong(key, 0L)
|
||||
else -> throw IllegalArgumentException("Unknown key: $key")
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateExposure(values: ContentValues) {
|
||||
if (values.size() == 0) return
|
||||
val editor = preferences.edit()
|
||||
values.valueSet().forEach { (key, value) ->
|
||||
// TODO remove log
|
||||
Log.e("TEST", "exposure update: $key = $value")
|
||||
when (key) {
|
||||
Exposure.SCANNER_ENABLED -> editor.putBoolean(key, value as Boolean)
|
||||
Exposure.LAST_CLEANUP -> editor.putLong(key, value as Long)
|
||||
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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -33,11 +33,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
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,8 @@ class ExposureNotificationsFragment : Fragment(R.layout.exposure_notifications_f
|
||||
lifecycleScope.launchWhenResumed {
|
||||
val info = getExposureNotificationsServiceInfo(requireContext())
|
||||
val newConfiguration = info.configuration.copy(enabled = newStatus)
|
||||
displayServiceInfo(setExposureNotificationsServiceConfiguration(requireContext(), newConfiguration))
|
||||
setExposureNotificationsServiceConfiguration(requireContext(), newConfiguration)
|
||||
displayServiceInfo(info.copy(configuration = newConfiguration))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,10 +43,6 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name="org.microg.gms.nearby.exposurenotification.ServiceInfoReceiver"
|
||||
android:process=":persistent" />
|
||||
|
||||
<receiver
|
||||
android:name="org.microg.gms.nearby.exposurenotification.ServiceTrigger"
|
||||
android:process=":persistent">
|
||||
|
@ -7,22 +7,23 @@ package org.microg.gms.nearby.exposurenotification
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.microg.gms.common.PackageUtils
|
||||
import org.microg.gms.settings.SettingsContract.Exposure.CONTENT_URI
|
||||
import org.microg.gms.settings.SettingsContract.Exposure.LAST_CLEANUP
|
||||
import org.microg.gms.settings.SettingsContract.Exposure.SCANNER_ENABLED
|
||||
import org.microg.gms.settings.SettingsContract.getSettings
|
||||
import org.microg.gms.settings.SettingsContract.setSettings
|
||||
|
||||
class ExposurePreferences(private val context: Context) {
|
||||
private var preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
init {
|
||||
PackageUtils.warnIfNotPersistentProcess(ExposurePreferences::class.java)
|
||||
}
|
||||
|
||||
var enabled
|
||||
get() = preferences.getBoolean(PREF_SCANNER_ENABLED, false)
|
||||
get() = getSettings(context, CONTENT_URI, arrayOf(SCANNER_ENABLED)) { c ->
|
||||
c.getInt(0) != 0
|
||||
}
|
||||
set(newStatus) {
|
||||
val changed = enabled != newStatus
|
||||
preferences.edit().putBoolean(PREF_SCANNER_ENABLED, newStatus).commit()
|
||||
setSettings(context, CONTENT_URI) {
|
||||
put(SCANNER_ENABLED, newStatus)
|
||||
}
|
||||
if (!changed) return
|
||||
if (newStatus) {
|
||||
context.sendOrderedBroadcast(Intent(context, ServiceTrigger::class.java), null)
|
||||
@ -33,11 +34,11 @@ class ExposurePreferences(private val context: Context) {
|
||||
}
|
||||
|
||||
var lastCleanup
|
||||
get() = preferences.getLong(PREF_LAST_CLEANUP, 0)
|
||||
set(value) = preferences.edit().putLong(PREF_LAST_CLEANUP, value).apply()
|
||||
get() = getSettings(context, CONTENT_URI, arrayOf(LAST_CLEANUP)) { c ->
|
||||
c.getLong(0)
|
||||
}
|
||||
set(value) = setSettings(context, CONTENT_URI) {
|
||||
put(LAST_CLEANUP, value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREF_SCANNER_ENABLED = "exposure_scanner_enabled"
|
||||
private const val PREF_LAST_CLEANUP = "exposure_last_cleanup"
|
||||
}
|
||||
}
|
||||
|
@ -5,21 +5,10 @@
|
||||
|
||||
package org.microg.gms.nearby.exposurenotification
|
||||
|
||||
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 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.nearby.exposurenotification.SERVICE_INFO_REQUEST"
|
||||
private const val ACTION_UPDATE_CONFIGURATION = "org.microg.gms.nearby.exposurenotification.UPDATE_CONFIGURATION"
|
||||
private const val ACTION_SERVICE_INFO_RESPONSE = "org.microg.gms.nearby.exposurenotification.SERVICE_INFO_RESPONSE"
|
||||
private const val EXTRA_SERVICE_INFO = "org.microg.gms.nearby.exposurenotification.SERVICE_INFO"
|
||||
private const val EXTRA_CONFIGURATION = "org.microg.gms.nearby.exposurenotification.CONFIGURATION"
|
||||
|
||||
data class ServiceInfo(val configuration: ServiceConfiguration) : Serializable
|
||||
|
||||
@ -31,59 +20,12 @@ data class ServiceConfiguration(val enabled: Boolean) : Serializable {
|
||||
|
||||
private fun ExposurePreferences.toConfiguration(): ServiceConfiguration = ServiceConfiguration(enabled)
|
||||
|
||||
class ServiceInfoReceiver : BroadcastReceiver() {
|
||||
private fun sendInfoResponse(context: Context) {
|
||||
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
|
||||
setPackage(context.packageName)
|
||||
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(ExposurePreferences(context).toConfiguration()))
|
||||
}, null)
|
||||
suspend fun getExposureNotificationsServiceInfo(context: Context): ServiceInfo =
|
||||
withContext(Dispatchers.IO) {
|
||||
ServiceInfo(ExposurePreferences(context).toConfiguration())
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
suspend fun setExposureNotificationsServiceConfiguration(context: Context, configuration: ServiceConfiguration) =
|
||||
withContext(Dispatchers.IO) {
|
||||
ExposurePreferences(context).enabled = configuration.enabled
|
||||
}
|
||||
}
|
||||
|
||||
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 getExposureNotificationsServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
|
||||
Intent(context, ServiceInfoReceiver::class.java).apply {
|
||||
action = ACTION_SERVICE_INFO_REQUEST
|
||||
}, context)
|
||||
|
||||
suspend fun setExposureNotificationsServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
|
||||
Intent(context, ServiceInfoReceiver::class.java).apply {
|
||||
action = ACTION_UPDATE_CONFIGURATION
|
||||
putExtra(EXTRA_CONFIGURATION, configuration)
|
||||
}, context)
|
||||
|
Loading…
Reference in New Issue
Block a user