Update to latest upstream

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

View File

@ -39,6 +39,6 @@ dependencies {
api project(':play-services-tasks') api project(':play-services-tasks')
api project(':play-services-base-api') 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' implementation 'com.google.android.gms:play-services-base:17.6.0'
} }

View File

@ -7,7 +7,7 @@ apply plugin: 'com.squareup.wire'
apply plugin: 'kotlin' apply plugin: 'kotlin'
dependencies { dependencies {
implementation "com.squareup.wire:wire-runtime:$wireVersion" implementation "com.squareup.wire:wire-runtime:3.6.1"
} }
wire { wire {

View File

@ -32,8 +32,8 @@ dependencies {
implementation project(':play-services-cast-api') implementation project(':play-services-cast-api')
// AndroidX UI // AndroidX UI
implementation "androidx.appcompat:appcompat:1.2.0" implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.mediarouter:mediarouter:1.2.3' implementation 'androidx.mediarouter:mediarouter:1.2.4'
implementation "androidx.preference:preference-ktx:$preferenceVersion" implementation "androidx.preference:preference-ktx:$preferenceVersion"
implementation "org.microg.gms:conscrypt-gmscore:2.5.1" 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.navigation:navigation-ui-ktx:2.3.5'
implementation "androidx.lifecycle:lifecycle-service:2.3.1" 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') api project(':play-services-basement')
} }

View File

@ -38,7 +38,7 @@ android {
} }
dependencies { dependencies {
implementation "androidx.appcompat:appcompat:1.2.0" implementation 'androidx.appcompat:appcompat:1.3.0'
implementation "androidx.preference:preference:$preferenceVersion" implementation "androidx.preference:preference:$preferenceVersion"
} }

View File

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

View File

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

View File

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

View File

@ -74,6 +74,7 @@ import static android.view.KeyEvent.KEYCODE_BACK;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT; 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_PACKAGE_NAME;
import static org.microg.gms.common.Constants.GMS_VERSION_CODE; import static org.microg.gms.common.Constants.GMS_VERSION_CODE;
@ -141,7 +142,7 @@ public class LoginActivity extends AssistantActivity {
AccountManager accountManager = AccountManager.get(this); AccountManager accountManager = AccountManager.get(this);
Account account = new Account(getIntent().getStringExtra(EXTRA_EMAIL), accountType); Account account = new Account(getIntent().getStringExtra(EXTRA_EMAIL), accountType);
accountManager.addAccountExplicitly(account, getIntent().getStringExtra(EXTRA_TOKEN), null); 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); accountManager.setAccountVisibility(account, PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE, VISIBILITY_USER_MANAGED_VISIBLE);
} }
retrieveGmsToken(account); retrieveGmsToken(account);
@ -167,7 +168,7 @@ public class LoginActivity extends AssistantActivity {
} }
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(HuaweiButtonPreference, true).apply(); PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(HuaweiButtonPreference, true).apply();
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(LoginButtonPreference, true)) { if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(LoginButtonPreference, true)) {
LastCheckinInfo.ClearCheckinInfo(this); LastCheckinInfo.clear(this);
CheckinClient.brandSpoof = true; CheckinClient.brandSpoof = true;
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(LoginButtonPreference, true).apply(); PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(LoginButtonPreference, true).apply();
} }
@ -182,7 +183,7 @@ public class LoginActivity extends AssistantActivity {
if (state == 1) { if (state == 1) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(LoginButtonPreference, true).apply(); PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(LoginButtonPreference, true).apply();
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(HuaweiButtonPreference, true)) { if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(HuaweiButtonPreference, true)) {
LastCheckinInfo.ClearCheckinInfo(this); LastCheckinInfo.clear(this);
CheckinClient.brandSpoof = false; CheckinClient.brandSpoof = false;
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(HuaweiButtonPreference, true).apply(); PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(HuaweiButtonPreference, true).apply();
} }
@ -253,7 +254,7 @@ public class LoginActivity extends AssistantActivity {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo(); NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) { if (networkInfo != null && networkInfo.isConnected()) {
if (LastCheckinInfo.read(this).androidId == 0) { if (LastCheckinInfo.read(this).getAndroidId() == 0) {
new Thread(() -> { new Thread(() -> {
Runnable next; Runnable next;
next = checkin(false) ? this::loadLoginPage : () -> showError(R.string.auth_general_error_desc); next = checkin(false) ? this::loadLoginPage : () -> showError(R.string.auth_general_error_desc);
@ -454,7 +455,7 @@ public class LoginActivity extends AssistantActivity {
@JavascriptInterface @JavascriptInterface
public final String getAndroidId() { public final String getAndroidId() {
long androidId = LastCheckinInfo.read(LoginActivity.this).androidId; long androidId = LastCheckinInfo.read(LoginActivity.this).getAndroidId();
Log.d(TAG, "JSBridge: getAndroidId " + androidId); Log.d(TAG, "JSBridge: getAndroidId " + androidId);
if (androidId == 0 || androidId == -1) return null; if (androidId == 0 || androidId == -1) return null;
return Long.toHexString(androidId); return Long.toHexString(androidId);

View File

@ -81,8 +81,8 @@ public class CheckinClient {
LastCheckinInfo checkinInfo, Locale locale, LastCheckinInfo checkinInfo, Locale locale,
List<Account> accounts) { List<Account> accounts) {
CheckinRequest.Builder builder = new CheckinRequest.Builder() CheckinRequest.Builder builder = new CheckinRequest.Builder()
.accountCookie(new ArrayList<String>()) .accountCookie(new ArrayList<>())
.androidId(checkinInfo.androidId) .androidId(checkinInfo.getAndroidId())
.checkin(new CheckinRequest.Checkin.Builder() .checkin(new CheckinRequest.Checkin.Builder()
.build(new CheckinRequest.Checkin.Build.Builder() .build(new CheckinRequest.Checkin.Build.Builder()
.bootloader(brandSpoof ? "c2f2-0.2-5799621" : build.bootloader) .bootloader(brandSpoof ? "c2f2-0.2-5799621" : build.bootloader)
@ -101,11 +101,11 @@ public class CheckinClient {
.build()) .build())
.cellOperator(phoneInfo.cellOperator) .cellOperator(phoneInfo.cellOperator)
.event(Collections.singletonList(new CheckinRequest.Checkin.Event.Builder() .event(Collections.singletonList(new CheckinRequest.Checkin.Event.Builder()
.tag(checkinInfo.androidId == 0 ? "event_log_start" : "system_update") .tag(checkinInfo.getAndroidId() == 0 ? "event_log_start" : "system_update")
.value(checkinInfo.androidId == 0 ? null : "1536,0,-1,NULL") .value(checkinInfo.getAndroidId() == 0 ? null : "1536,0,-1,NULL")
.timeMs(new Date().getTime()) .timeMs(new Date().getTime())
.build())) .build()))
.lastCheckinMs(checkinInfo.lastCheckin) .lastCheckinMs(checkinInfo.getLastCheckin())
.requestedGroup(TODO_LIST_STRING) .requestedGroup(TODO_LIST_STRING)
.roaming(phoneInfo.roaming) .roaming(phoneInfo.roaming)
.simOperator(phoneInfo.simOperator) .simOperator(phoneInfo.simOperator)
@ -129,7 +129,7 @@ public class CheckinClient {
.touchScreen(deviceConfiguration.touchScreen) .touchScreen(deviceConfiguration.touchScreen)
.widthPixels(deviceConfiguration.widthPixels) .widthPixels(deviceConfiguration.widthPixels)
.build()) .build())
.digest(checkinInfo.digest) .digest(checkinInfo.getDigest())
.esn(deviceIdent.esn) .esn(deviceIdent.esn)
.fragment(0) .fragment(0)
.locale(locale.toString()) .locale(locale.toString())
@ -150,8 +150,8 @@ public class CheckinClient {
builder.macAddress(Collections.singletonList(deviceIdent.wifiMac)) builder.macAddress(Collections.singletonList(deviceIdent.wifiMac))
.macAddressType(Collections.singletonList("wifi")); .macAddressType(Collections.singletonList("wifi"));
} }
if (checkinInfo.securityToken != 0) { if (checkinInfo.getSecurityToken() != 0) {
builder.securityToken(checkinInfo.securityToken) builder.securityToken(checkinInfo.getSecurityToken())
.fragment(1); .fragment(1);
} }
return builder.build(); return builder.build();

View File

@ -39,9 +39,9 @@ public class CheckinManager {
@SuppressWarnings("MissingPermission") @SuppressWarnings("MissingPermission")
public static synchronized LastCheckinInfo checkin(Context context, boolean force) throws IOException { public static synchronized LastCheckinInfo checkin(Context context, boolean force) throws IOException {
LastCheckinInfo info = LastCheckinInfo.read(context); 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; return null;
if (!CheckinPrefs.get(context).isEnabled()) if (!CheckinPrefs.isEnabled(context))
return null; return null;
List<CheckinClient.Account> accounts = new ArrayList<>(); List<CheckinClient.Account> accounts = new ArrayList<>();
AccountManager accountManager = AccountManager.get(context); AccountManager accountManager = AccountManager.get(context);
@ -63,13 +63,7 @@ public class CheckinManager {
} }
private static LastCheckinInfo handleResponse(Context context, CheckinResponse response) { private static LastCheckinInfo handleResponse(Context context, CheckinResponse response) {
LastCheckinInfo info = new LastCheckinInfo(); LastCheckinInfo info = new LastCheckinInfo(response);
info.androidId = response.androidId;
info.lastCheckin = response.timeMs;
info.securityToken = response.securityToken;
info.digest = response.digest;
info.versionInfo = response.versionInfo;
info.deviceDataVersionInfo = response.deviceDataVersionInfo;
info.write(context); info.write(context);
ContentResolver resolver = context.getContentResolver(); ContentResolver resolver = context.getContentResolver();

View File

@ -1,76 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.checkin;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import androidx.preference.PreferenceManager;
import android.util.Log;
import org.microg.gms.common.PackageUtils;
import java.io.File;
public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String PREF_ENABLE_CHECKIN = "checkin_enable_service";
private static CheckinPrefs INSTANCE;
public static CheckinPrefs get(Context context) {
if (INSTANCE == null) {
PackageUtils.warnIfNotMainProcess(context, CheckinPrefs.class);
if (context == null) return new CheckinPrefs(null);
INSTANCE = new CheckinPrefs(context.getApplicationContext());
}
return INSTANCE;
}
private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private boolean checkinEnabled = false;
private CheckinPrefs(Context context) {
if (context != null) {
preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.registerOnSharedPreferenceChangeListener(this);
try {
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
} catch (Exception ignored) {
}
update();
}
}
private boolean getSettingsBoolean(String key, boolean def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getBoolean(key, def);
}
return preferences.getBoolean(key, def);
}
private void update() {
checkinEnabled = getSettingsBoolean(PREF_ENABLE_CHECKIN, true);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
update();
}
public boolean isEnabled() {
return checkinEnabled;
}
public static void setEnabled(Context context, boolean newStatus) {
boolean changed = CheckinPrefs.get(context).isEnabled() != newStatus;
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(PREF_ENABLE_CHECKIN, newStatus).apply();
if (!changed) return;
if (newStatus) {
context.sendOrderedBroadcast(new Intent(context, TriggerReceiver.class), null);
}
}
}

View File

@ -57,7 +57,7 @@ public class CheckinService extends IntentService {
private ICheckinService iface = new ICheckinService.Stub() { private ICheckinService iface = new ICheckinService.Stub() {
@Override @Override
public String getDeviceDataVersionInfo() throws RemoteException { 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) { protected void onHandleIntent(Intent intent) {
try { try {
ForegroundServiceContext.completeForegroundService(this, intent, TAG); 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)); LastCheckinInfo info = CheckinManager.checkin(this, intent.getBooleanExtra(EXTRA_FORCE_CHECKIN, false));
if (info != null) { 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; String accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
for (Account account : AccountManager.get(this).getAccountsByType(accountType)) { for (Account account : AccountManager.get(this).getAccountsByType(accountType)) {
PeopleManager.loadUserInfo(this, account); PeopleManager.loadUserInfo(this, account);
@ -86,7 +86,7 @@ public class CheckinService extends IntentService {
ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER); ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER);
if (receiver != null) { if (receiver != null) {
Bundle bundle = new Bundle(); 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); receiver.send(Activity.RESULT_OK, bundle);
} }
} }
@ -115,6 +115,6 @@ public class CheckinService extends IntentService {
static void schedule(Context context) { static void schedule(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 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); PendingIntent pendingIntent = PendingIntent.getService(context, TriggerReceiver.class.getName().hashCode(), new Intent(context, TriggerReceiver.class), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC, Math.max(LastCheckinInfo.read(context).lastCheckin + REGULAR_CHECKIN_INTERVAL, System.currentTimeMillis() + BACKUP_CHECKIN_DELAY), pendingIntent); alarmManager.set(AlarmManager.RTC, Math.max(LastCheckinInfo.read(context).getLastCheckin() + REGULAR_CHECKIN_INTERVAL, System.currentTimeMillis() + BACKUP_CHECKIN_DELAY), pendingIntent);
} }
} }

View File

@ -1,71 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.checkin;
import android.content.Context;
import android.content.SharedPreferences;
public class LastCheckinInfo {
public static final String PREFERENCES_NAME = "checkin";
public static final String PREF_ANDROID_ID = "androidId";
public static final String PREF_DIGEST = "digest";
public static final String PREF_LAST_CHECKIN = "lastCheckin";
public static final String PREF_SECURITY_TOKEN = "securityToken";
public static final String PREF_VERSION_INFO = "versionInfo";
public static final String PREF_DEVICE_DATA_VERSION_INFO = "deviceDataVersionInfo";
public static final String INITIAL_DIGEST = "1-929a0dca0eee55513280171a8585da7dcd3700f8";
public long lastCheckin;
public long androidId;
public long securityToken;
public String digest;
public String versionInfo;
public String deviceDataVersionInfo;
public static LastCheckinInfo read(Context context) {
LastCheckinInfo info = new LastCheckinInfo();
SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
info.androidId = preferences.getLong(PREF_ANDROID_ID, 0);
info.digest = preferences.getString(PREF_DIGEST, INITIAL_DIGEST);
info.lastCheckin = preferences.getLong(PREF_LAST_CHECKIN, 0);
info.securityToken = preferences.getLong(PREF_SECURITY_TOKEN, 0);
info.versionInfo = preferences.getString(PREF_VERSION_INFO, "");
info.deviceDataVersionInfo = preferences.getString(PREF_DEVICE_DATA_VERSION_INFO, "");
return info;
}
public void write(Context context) {
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit()
.putLong(PREF_ANDROID_ID, androidId)
.putString(PREF_DIGEST, digest)
.putLong(PREF_LAST_CHECKIN, lastCheckin)
.putLong(PREF_SECURITY_TOKEN, securityToken)
.putString(PREF_VERSION_INFO, versionInfo)
.putString(PREF_DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo)
.commit();
}
public static void ClearCheckinInfo(Context context) {
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit()
.putLong(PREF_ANDROID_ID, 0)
.putString(PREF_DIGEST, INITIAL_DIGEST)
.putLong(PREF_LAST_CHECKIN, 0)
.putLong(PREF_SECURITY_TOKEN, 0)
.putString(PREF_VERSION_INFO, "")
.putString(PREF_DEVICE_DATA_VERSION_INFO, "")
.commit();
}
}

View File

@ -16,16 +16,21 @@
package org.microg.gms.checkin; package org.microg.gms.checkin;
import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.util.Log; import android.util.Log;
import androidx.legacy.content.WakefulBroadcastReceiver; import androidx.legacy.content.WakefulBroadcastReceiver;
import org.microg.gms.common.ForegroundServiceContext; 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.EXTRA_FORCE_CHECKIN;
import static org.microg.gms.checkin.CheckinService.REGULAR_CHECKIN_INTERVAL; import static org.microg.gms.checkin.CheckinService.REGULAR_CHECKIN_INTERVAL;
@ -37,8 +42,8 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
try { try {
boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()); boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction());
if (CheckinPrefs.get(context).isEnabled() || force) { if (CheckinPrefs.isEnabled(context) || force) {
if (LastCheckinInfo.read(context).lastCheckin > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL && !force) { if (LastCheckinInfo.read(context).getLastCheckin() > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL && !force) {
CheckinService.schedule(context); CheckinService.schedule(context);
return; return;
} }
@ -49,7 +54,15 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
Intent subIntent = new Intent(context, CheckinService.class); Intent subIntent = new Intent(context, CheckinService.class);
subIntent.putExtra(EXTRA_FORCE_CHECKIN, force); subIntent.putExtra(EXTRA_FORCE_CHECKIN, force);
startWakefulService(new ForegroundServiceContext(context), subIntent); 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 { } else {
Log.d(TAG, "Ignoring " + intent + ": checkin is disabled"); Log.d(TAG, "Ignoring " + intent + ": checkin is disabled");
} }

View File

@ -1,291 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.gcm;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import androidx.preference.PreferenceManager;
import org.microg.gms.common.PackageUtils;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String PREF_FULL_LOG = "gcm_full_log";
public static final String PREF_LAST_PERSISTENT_ID = "gcm_last_persistent_id";
public static final String PREF_CONFIRM_NEW_APPS = "gcm_confirm_new_apps";
public static final String PREF_ENABLE_GCM = "gcm_enable_mcs_service";
public static final String PREF_NETWORK_MOBILE = "gcm_network_mobile";
public static final String PREF_NETWORK_WIFI = "gcm_network_wifi";
public static final String PREF_NETWORK_ROAMING = "gcm_network_roaming";
public static final String PREF_NETWORK_OTHER = "gcm_network_other";
public static final String PREF_LEARNT_MOBILE = "gcm_learnt_mobile";
public static final String PREF_LEARNT_WIFI = "gcm_learnt_wifi";
public static final String PREF_LEARNT_OTHER = "gcm_learnt_other";
private static final int INTERVAL = 1 * 60 * 1000; // 1 minute
private static GcmPrefs INSTANCE;
public static GcmPrefs get(Context context) {
if (INSTANCE == null) {
PackageUtils.warnIfNotPersistentProcess(GcmPrefs.class);
INSTANCE = new GcmPrefs(context.getApplicationContext());
}
return INSTANCE;
}
private boolean gcmLogEnabled = true;
private String lastPersistedId = "";
private boolean gcmEnabled = false;
private int networkMobile = 0;
private int networkWifi = 0;
private int networkRoaming = 0;
private int networkOther = 0;
private int learntWifi = 300000;
private int learntMobile = 300000;
private int learntOther = 300000;
private final Context context;
private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private GcmPrefs(Context context) {
this.context = context;
preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.registerOnSharedPreferenceChangeListener(this);
try {
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
} catch (Exception ignored) {
}
update();
}
private boolean getSettingsBoolean(String key, boolean def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getBoolean(key, def);
}
return preferences.getBoolean(key, def);
}
public void update() {
gcmEnabled = getSettingsBoolean(PREF_ENABLE_GCM, true);
gcmLogEnabled = getSettingsBoolean(PREF_FULL_LOG, true);
lastPersistedId = preferences.getString(PREF_LAST_PERSISTENT_ID, "");
networkMobile = Integer.parseInt(preferences.getString(PREF_NETWORK_MOBILE, "0"));
networkWifi = Integer.parseInt(preferences.getString(PREF_NETWORK_WIFI, "0"));
networkRoaming = Integer.parseInt(preferences.getString(PREF_NETWORK_ROAMING, "0"));
networkOther = Integer.parseInt(preferences.getString(PREF_NETWORK_OTHER, "0"));
learntMobile = preferences.getInt(PREF_LEARNT_MOBILE, INTERVAL);
learntWifi = preferences.getInt(PREF_LEARNT_WIFI, INTERVAL);
learntOther = preferences.getInt(PREF_LEARNT_OTHER, INTERVAL);
}
public String getNetworkPrefForInfo(NetworkInfo info) {
if (info == null) return PREF_NETWORK_OTHER;
if (info.isRoaming()) return PREF_NETWORK_ROAMING;
switch (info.getType()) {
case ConnectivityManager.TYPE_MOBILE:
return PREF_NETWORK_MOBILE;
case ConnectivityManager.TYPE_WIFI:
return PREF_NETWORK_WIFI;
default:
return PREF_NETWORK_OTHER;
}
}
public int getHeartbeatMsFor(NetworkInfo info) {
return getHeartbeatMsFor(getNetworkPrefForInfo(info));
}
public int getMobileInterval() {
return networkMobile;
}
public int getWifiInterval() {
return networkWifi;
}
public int getRoamingInterval() {
return networkRoaming;
}
public int getOtherInterval() {
return networkOther;
}
public void setMobileInterval(int value) {
this.networkMobile = value;
preferences.edit().putString(PREF_NETWORK_MOBILE, Integer.toString(networkMobile)).apply();
}
public void setWifiInterval(int value) {
this.networkWifi = value;
preferences.edit().putString(PREF_NETWORK_WIFI, Integer.toString(networkWifi)).apply();
}
public void setRoamingInterval(int value) {
this.networkRoaming = value;
preferences.edit().putString(PREF_NETWORK_ROAMING, Integer.toString(networkRoaming)).apply();
}
public void setOtherInterval(int value) {
this.networkOther = value;
preferences.edit().putString(PREF_NETWORK_OTHER, Integer.toString(networkOther)).apply();
}
public int getHeartbeatMsFor(String pref) {
if (PREF_NETWORK_ROAMING.equals(pref)) {
if (networkRoaming != 0) return networkRoaming * 60000;
else return learntMobile;
} else if (PREF_NETWORK_MOBILE.equals(pref)) {
if (networkMobile != 0) return networkMobile * 60000;
else return learntMobile;
} else if (PREF_NETWORK_WIFI.equals(pref)) {
if (networkWifi != 0) return networkWifi * 60000;
else return learntWifi;
} else {
if (networkOther != 0) return networkOther * 60000;
else return learntOther;
}
}
public int getNetworkValue(String pref) {
switch (pref) {
case PREF_NETWORK_MOBILE:
return networkMobile;
case PREF_NETWORK_ROAMING:
return networkRoaming;
case PREF_NETWORK_WIFI:
return networkWifi;
default:
return networkOther;
}
}
public void learnTimeout(String pref) {
Log.d("GmsGcmPrefs", "learnTimeout: " + pref);
switch (pref) {
case PREF_NETWORK_MOBILE:
case PREF_NETWORK_ROAMING:
learntMobile *= 0.95;
break;
case PREF_NETWORK_WIFI:
learntWifi *= 0.95;
break;
default:
learntOther *= 0.95;
break;
}
updateLearntValues();
}
public void learnReached(String pref, long time) {
Log.d("GmsGcmPrefs", "learnReached: " + pref + " / " + time);
switch (pref) {
case PREF_NETWORK_MOBILE:
case PREF_NETWORK_ROAMING:
if (time > learntMobile / 4 * 3)
learntMobile *= 1.02;
break;
case PREF_NETWORK_WIFI:
if (time > learntWifi / 4 * 3)
learntWifi *= 1.02;
break;
default:
if (time > learntOther / 4 * 3)
learntOther *= 1.02;
break;
}
updateLearntValues();
}
private void updateLearntValues() {
preferences.edit().putInt(PREF_LEARNT_MOBILE, INTERVAL).putInt(PREF_LEARNT_WIFI, INTERVAL).putInt(PREF_LEARNT_OTHER, INTERVAL).apply();
}
public int getLearntMobileInterval() {
return learntMobile;
}
public int getLearntWifiInterval() {
return learntWifi;
}
public int getLearntOtherInterval() {
return learntOther;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
update();
}
public boolean isEnabled() {
return gcmEnabled;
}
public void setEnabled(boolean value) {
boolean changed = gcmEnabled != value;
preferences.edit().putBoolean(GcmPrefs.PREF_ENABLE_GCM, value).apply();
if (!changed) return;
if (!value) {
McsService.stop(context);
} else {
context.sendBroadcast(new Intent(TriggerReceiver.FORCE_TRY_RECONNECT, null, context, TriggerReceiver.class));
}
}
public boolean isEnabledFor(NetworkInfo info) {
return isEnabled() && info != null && getHeartbeatMsFor(info) >= 0;
}
public boolean isGcmLogEnabled() {
return gcmLogEnabled;
}
public List<String> getLastPersistedIds() {
if (lastPersistedId.isEmpty()) return Collections.emptyList();
return Arrays.asList(lastPersistedId.split("\\|"));
}
public void extendLastPersistedId(String id) {
if (!lastPersistedId.isEmpty()) lastPersistedId += "|";
lastPersistedId += id;
preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
}
public void clearLastPersistedId() {
lastPersistedId = "";
preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
}
}

View File

@ -73,6 +73,7 @@ import okio.ByteString;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.os.Build.VERSION.SDK_INT; 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.ACTION_C2DM_RECEIVE;
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_OVERRIDE; 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) { public synchronized static boolean isConnected(Context context) {
warnIfNotPersistentProcess(McsService.class);
if (inputStream == null || !inputStream.isAlive() || outputStream == null || !outputStream.isAlive()) { if (inputStream == null || !inputStream.isAlive() || outputStream == null || !outputStream.isAlive()) {
logd(null, "Connection is not enabled or dead."); logd(null, "Connection is not enabled or dead.");
return false; return false;
@ -244,13 +246,14 @@ public class McsService extends Service implements Handler.Callback {
closeAll(); closeAll();
} else if (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime > 2 * heartbeatMs) { } 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"); 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 false;
} }
return true; return true;
} }
public static long getStartTimestamp() { public static long getStartTimestamp() {
warnIfNotPersistentProcess(McsService.class);
return startTimestamp; return startTimestamp;
} }
@ -460,7 +463,7 @@ public class McsService extends Service implements Handler.Callback {
private void handleLoginResponse(LoginResponse loginResponse) { private void handleLoginResponse(LoginResponse loginResponse) {
if (loginResponse.error == null) { if (loginResponse.error == null) {
GcmPrefs.get(this).clearLastPersistedId(); GcmPrefs.clearLastPersistedId(this);
logd(this, "Logged in"); logd(this, "Logged in");
wakeLock.release(); wakeLock.release();
} else { } else {
@ -470,7 +473,7 @@ public class McsService extends Service implements Handler.Callback {
private void handleCloudMessage(DataMessageStanza message) { private void handleCloudMessage(DataMessageStanza message) {
if (message.persistent_id != null) { 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)) { if (SELF_CATEGORY.equals(message.category)) {
handleSelfMessage(message); handleSelfMessage(message);
@ -488,7 +491,7 @@ public class McsService extends Service implements Handler.Callback {
} }
private void handleHeartbeatAck(HeartbeatAck ack) { 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(); lastHeartbeatAckElapsedRealtime = SystemClock.elapsedRealtime();
wakeLock.release(); wakeLock.release();
} }
@ -498,13 +501,13 @@ public class McsService extends Service implements Handler.Callback {
return new LoginRequest.Builder() return new LoginRequest.Builder()
.adaptive_heartbeat(false) .adaptive_heartbeat(false)
.auth_service(LoginRequest.AuthService.ANDROID_ID) .auth_service(LoginRequest.AuthService.ANDROID_ID)
.auth_token(Long.toString(info.securityToken)) .auth_token(Long.toString(info.getSecurityToken()))
.id("android-" + SDK_INT) .id("android-" + SDK_INT)
.domain("mcs.android.com") .domain("mcs.android.com")
.device_id("android-" + Long.toHexString(info.androidId)) .device_id("android-" + Long.toHexString(info.getAndroidId()))
.network_type(1) .network_type(1)
.resource(Long.toString(info.androidId)) .resource(Long.toString(info.getAndroidId()))
.user(Long.toString(info.androidId)) .user(Long.toString(info.getAndroidId()))
.use_rmq2(true) .use_rmq2(true)
.setting(Collections.singletonList(new Setting.Builder().name("new_vc").value("1").build())) .setting(Collections.singletonList(new Setting.Builder().name("new_vc").value("1").build()))
.received_persistent_id(GcmPrefs.get(this).getLastPersistedIds()) .received_persistent_id(GcmPrefs.get(this).getLastPersistedIds())

View File

@ -87,7 +87,7 @@ public class PushRegisterManager {
if (!request.delete) { if (!request.delete) {
if (!prefs.isEnabled() || if (!prefs.isEnabled() ||
(app != null && !app.allowRegister) || (app != null && !app.allowRegister) ||
LastCheckinInfo.read(context).lastCheckin <= 0) { LastCheckinInfo.read(context).getLastCheckin() <= 0) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE); bundle.putString(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE);
callback.onResult(bundle); callback.onResult(bundle);

View File

@ -70,8 +70,8 @@ public class RegisterRequest extends HttpFormClient.Request {
} }
public RegisterRequest checkin(LastCheckinInfo lastCheckinInfo) { public RegisterRequest checkin(LastCheckinInfo lastCheckinInfo) {
androidId = lastCheckinInfo.androidId; androidId = lastCheckinInfo.getAndroidId();
securityToken = lastCheckinInfo.securityToken; securityToken = lastCheckinInfo.getSecurityToken();
return this; return this;
} }

View File

@ -65,7 +65,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
McsService.resetCurrentDelay(); McsService.resetCurrentDelay();
} }
if (LastCheckinInfo.read(context).androidId == 0) { if (LastCheckinInfo.read(context).getAndroidId() == 0) {
Log.d(TAG, "Ignoring " + intent + ": need to checkin first."); Log.d(TAG, "Ignoring " + intent + ": need to checkin first.");
return; return;
} }

View File

@ -55,14 +55,6 @@ public class GServicesProvider extends ContentProvider {
@Override @Override
public boolean onCreate() { 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()); databaseHelper = new DatabaseHelper(getContext());
return true; return true;
} }

View File

@ -0,0 +1,23 @@
package org.microg.gms.auth
import android.content.Context
import org.microg.gms.settings.SettingsContract
import org.microg.gms.settings.SettingsContract.Auth
object AuthPrefs {
@JvmStatic
fun isTrustGooglePermitted(context: Context): Boolean {
return SettingsContract.getSettings(context, Auth.CONTENT_URI, arrayOf(Auth.TRUST_GOOGLE)) { c ->
c.getInt(0) != 0
}
}
@JvmStatic
fun isAuthVisible(context: Context): Boolean {
return SettingsContract.getSettings(context, Auth.CONTENT_URI, arrayOf(Auth.VISIBLE)) { c ->
c.getInt(0) != 0
}
}
}

View File

@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.checkin
import android.content.Context
import org.microg.gms.settings.SettingsContract
import org.microg.gms.settings.SettingsContract.CheckIn
object CheckinPrefs {
@JvmStatic
fun isEnabled(context: Context): Boolean {
val projection = arrayOf(CheckIn.ENABLED)
return SettingsContract.getSettings(context, CheckIn.CONTENT_URI, projection) { c ->
c.getInt(0) != 0
}
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.checkin
import android.content.Context
import org.microg.gms.settings.SettingsContract
import org.microg.gms.settings.SettingsContract.CheckIn
data class LastCheckinInfo(
val lastCheckin: Long,
val androidId: Long,
val securityToken: Long,
val digest: String,
val versionInfo: String,
val deviceDataVersionInfo: String,
) {
constructor(r: CheckinResponse) : this(
lastCheckin = r.timeMs ?: 0L,
androidId = r.androidId ?: 0L,
securityToken = r.securityToken ?: 0L,
digest = r.digest ?: CheckIn.INITIAL_DIGEST,
versionInfo = r.versionInfo ?: "",
deviceDataVersionInfo = r.deviceDataVersionInfo ?: "",
)
companion object {
@JvmStatic
fun read(context: Context): LastCheckinInfo {
val projection = arrayOf(
CheckIn.ANDROID_ID,
CheckIn.DIGEST,
CheckIn.LAST_CHECK_IN,
CheckIn.SECURITY_TOKEN,
CheckIn.VERSION_INFO,
CheckIn.DEVICE_DATA_VERSION_INFO,
)
return SettingsContract.getSettings(context, CheckIn.CONTENT_URI, projection) { c ->
LastCheckinInfo(
androidId = c.getLong(0),
digest = c.getString(1),
lastCheckin = c.getLong(2),
securityToken = c.getLong(3),
versionInfo = c.getString(4),
deviceDataVersionInfo = c.getString(5),
)
}
}
@JvmStatic
fun clear(context: Context) = SettingsContract.setSettings(context, CheckIn.CONTENT_URI) {
put(CheckIn.ANDROID_ID, 0L)
put(CheckIn.DIGEST, CheckIn.INITIAL_DIGEST)
put(CheckIn.LAST_CHECK_IN, 0L)
put(CheckIn.SECURITY_TOKEN, 0L)
put(CheckIn.VERSION_INFO, "")
put(CheckIn.DEVICE_DATA_VERSION_INFO, "")
}
}
fun write(context: Context) = SettingsContract.setSettings(context, CheckIn.CONTENT_URI) {
put(CheckIn.ANDROID_ID, androidId)
put(CheckIn.DIGEST, digest)
put(CheckIn.LAST_CHECK_IN, lastCheckin)
put(CheckIn.SECURITY_TOKEN, securityToken)
put(CheckIn.VERSION_INFO, versionInfo)
put(CheckIn.DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo)
}
}

View File

@ -5,89 +5,38 @@
package org.microg.gms.checkin package org.microg.gms.checkin
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import kotlinx.coroutines.Dispatchers
import android.util.Log 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 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 ServiceInfo(val configuration: ServiceConfiguration, val lastCheckin: Long, val androidId: Long) : Serializable
data class ServiceConfiguration(val enabled: Boolean) : Serializable { data class ServiceConfiguration(val enabled: Boolean) : Serializable
fun saveToPrefs(context: Context) {
CheckinPrefs.setEnabled(context, enabled) 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) suspend fun setCheckinServiceConfiguration(context: Context, configuration: ServiceConfiguration) = withContext(Dispatchers.IO) {
val serviceInfo = getCheckinServiceInfo(context)
class ServiceInfoReceiver : BroadcastReceiver() { if (serviceInfo.configuration == configuration) return@withContext
private fun sendInfoResponse(context: Context) { // enabled state is not already set, setting it now
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply { setSettings(context, CheckIn.CONTENT_URI) {
setPackage(context.packageName) put(CheckIn.ENABLED, configuration.enabled)
val checkinInfo = LastCheckinInfo.read(context)
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(CheckinPrefs.get(context).toConfiguration(), checkinInfo.lastCheckin, checkinInfo.androidId))
}, null)
} }
if (configuration.enabled) {
override fun onReceive(context: Context, intent: Intent) { context.sendOrderedBroadcast(Intent(context, TriggerReceiver::class.java), null)
try {
when (intent.action) {
ACTION_UPDATE_CONFIGURATION -> {
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context)
}
}
sendInfoResponse(context)
} catch (e: Exception) {
Log.w(TAG, e)
}
} }
} }
private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context): ServiceInfo = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.unregisterReceiver(this)
val serviceInfo = try {
intent.getSerializableExtra(EXTRA_SERVICE_INFO) as ServiceInfo
} catch (e: Exception) {
it.resumeWithException(e)
return
}
try {
it.resume(serviceInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}, IntentFilter(ACTION_SERVICE_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(intent, null)
} catch (e: Exception) {
it.resumeWithException(e)
}
}
suspend fun getCheckinServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_SERVICE_INFO_REQUEST
}, context)
suspend fun setCheckinServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_UPDATE_CONFIGURATION
putExtra(EXTRA_CONFIGURATION, configuration)
}, context)

View File

@ -0,0 +1,97 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.fonts
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.os.ParcelFileDescriptor.MODE_READ_ONLY
import android.util.Log
import java.io.File
class FontsProvider : ContentProvider() {
override fun onCreate(): Boolean {
Log.d(TAG, "onCreate")
return true
}
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
Log.d(TAG, "call $method $arg $extras")
return null
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor {
Log.e(TAG, "query: $uri ${projection?.toList()} $selection")
val cursor = MatrixCursor(COLUMNS)
// We could also return an empty cursor here, but some apps have been reported to crash
// when their expected font is not returned by Google's font provider.
cursor.addRow(
arrayOf(
1337L, // file_id
0, // font_ttc_index
null, // font_variation_settings
400, // font_weight
0, // font_italic
0, // result_code: RESULT_CODE_OK
)
)
return cursor
}
override fun insert(uri: Uri, values: ContentValues?): Uri {
Log.d(TAG, "insert: $uri, $values")
return uri
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
Log.d(TAG, "update: $uri, $values, $selection, $selectionArgs")
return 0
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
Log.d(TAG, "delete: $uri, $selection, $selectionArgs")
return 0
}
override fun getType(uri: Uri): String {
Log.d(TAG, "getType: $uri")
return "font/ttf"
}
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
Log.d(TAG, "openFile: $uri mode: $mode")
val file = File("/system/fonts/Roboto-Regular.ttf")
return ParcelFileDescriptor.open(file, MODE_READ_ONLY)
}
companion object {
private const val TAG = "FontsProvider"
private val COLUMNS = arrayOf(
"file_id",
"font_ttc_index",
"font_variation_settings",
"font_weight",
"font_italic",
"result_code"
)
}
}

View File

@ -0,0 +1,171 @@
package org.microg.gms.gcm
import android.content.Context
import android.content.Intent
import android.net.*
import android.util.Log
import org.microg.gms.gcm.TriggerReceiver.FORCE_TRY_RECONNECT
import org.microg.gms.settings.SettingsContract
import org.microg.gms.settings.SettingsContract.Gcm
import org.microg.gms.settings.SettingsContract.setSettings
@Suppress("Warnings")
data class GcmPrefs(
val isGcmLogEnabled: Boolean,
val lastPersistedId: String?,
val gcmEnabled: Boolean,
val networkMobile: Int,
val networkWifi: Int,
val networkRoaming: Int,
val networkOther: Int,
val learntMobileInterval: Int,
val learntWifiInterval: Int,
val learntOtherInterval: Int,
) {
val isEnabled: Boolean get() = gcmEnabled
val lastPersistedIds: List<String>
get() = if (lastPersistedId.isNullOrEmpty()) emptyList() else lastPersistedId.split("\\|")
companion object {
const val PREF_NETWORK_MOBILE = Gcm.NETWORK_MOBILE
const val PREF_NETWORK_WIFI = Gcm.NETWORK_WIFI
const val PREF_NETWORK_ROAMING = Gcm.NETWORK_ROAMING
const val PREF_NETWORK_OTHER = Gcm.NETWORK_OTHER
private const val INTERVAL = 1 * 60 * 1000 // 1 minute
@JvmStatic
fun get(context: Context): GcmPrefs {
return SettingsContract.getSettings(context, Gcm.CONTENT_URI, Gcm.PROJECTION) { c ->
GcmPrefs(
isGcmLogEnabled = c.getInt(0) != 0,
lastPersistedId = c.getString(1),
gcmEnabled = c.getInt(3) != 0,
networkMobile = c.getInt(4),
networkWifi = c.getInt(5),
networkRoaming = c.getInt(6),
networkOther = c.getInt(7),
learntMobileInterval = c.getInt(8),
learntWifiInterval = c.getInt(9),
learntOtherInterval = c.getInt(10),
)
}
}
fun write(context: Context, config: ServiceConfiguration) {
val gcmPrefs = get(context)
setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.ENABLE_GCM, config.enabled)
put(Gcm.NETWORK_MOBILE, config.mobile)
put(Gcm.NETWORK_WIFI, config.wifi)
put(Gcm.NETWORK_ROAMING, config.roaming)
put(Gcm.NETWORK_OTHER, config.other)
}
gcmPrefs.setEnabled(context, config.enabled)
}
@JvmStatic
fun clearLastPersistedId(context: Context) {
setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.LAST_PERSISTENT_ID, "")
}
}
}
/**
* Call this whenever the enabled state of GCM has changed.
*/
private fun setEnabled(context: Context, enabled: Boolean) {
if (gcmEnabled == enabled) return
if (enabled) {
val i = Intent(FORCE_TRY_RECONNECT, null, context, TriggerReceiver::class.java)
context.sendBroadcast(i)
} else {
McsService.stop(context)
}
}
@Suppress("DEPRECATION")
fun getNetworkPrefForInfo(info: NetworkInfo?): String {
if (info == null) return PREF_NETWORK_OTHER
return if (info.isRoaming) PREF_NETWORK_ROAMING else when (info.type) {
ConnectivityManager.TYPE_MOBILE -> PREF_NETWORK_MOBILE
ConnectivityManager.TYPE_WIFI -> PREF_NETWORK_WIFI
else -> PREF_NETWORK_OTHER
}
}
@Suppress("DEPRECATION")
fun getHeartbeatMsFor(info: NetworkInfo?): Int {
return getHeartbeatMsFor(getNetworkPrefForInfo(info))
}
fun getHeartbeatMsFor(pref: String): Int {
return if (PREF_NETWORK_ROAMING == pref) {
if (networkRoaming != 0) networkRoaming * 60000 else learntMobileInterval
} else if (PREF_NETWORK_MOBILE == pref) {
if (networkMobile != 0) networkMobile * 60000 else learntMobileInterval
} else if (PREF_NETWORK_WIFI == pref) {
if (networkWifi != 0) networkWifi * 60000 else learntWifiInterval
} else {
if (networkOther != 0) networkOther * 60000 else learntOtherInterval
}
}
fun learnTimeout(context: Context, pref: String) {
Log.d("GmsGcmPrefs", "learnTimeout: $pref")
when (pref) {
PREF_NETWORK_MOBILE, PREF_NETWORK_ROAMING -> setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.LEARNT_MOBILE, (learntMobileInterval * 0.95).toInt())
}
PREF_NETWORK_WIFI -> setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.LEARNT_WIFI, (learntWifiInterval * 0.95).toInt())
}
else -> setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.LEARNT_OTHER, (learntOtherInterval * 0.95).toInt())
}
}
}
fun learnReached(context: Context, pref: String, time: Long) {
Log.d("GmsGcmPrefs", "learnReached: $pref / $time")
when (pref) {
PREF_NETWORK_MOBILE, PREF_NETWORK_ROAMING -> {
if (time > learntMobileInterval / 4 * 3) {
setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.LEARNT_MOBILE, INTERVAL)
}
}
}
PREF_NETWORK_WIFI -> {
if (time > learntWifiInterval / 4 * 3) {
setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.LEARNT_WIFI, INTERVAL)
}
}
}
else -> {
if (time > learntOtherInterval / 4 * 3) {
setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.LEARNT_OTHER, INTERVAL)
}
}
}
}
}
@Suppress("DEPRECATION")
fun isEnabledFor(info: NetworkInfo?): Boolean {
return isEnabled && info != null && getHeartbeatMsFor(info) >= 0
}
fun extendLastPersistedId(context: Context, id: String) {
val newId = if (lastPersistedId.isNullOrEmpty()) id else "$lastPersistedId|$id"
setSettings(context, Gcm.CONTENT_URI) {
put(Gcm.LAST_PERSISTENT_ID, newId)
}
}
}

View File

@ -31,7 +31,7 @@ import kotlin.coroutines.suspendCoroutine
private const val TAG = "GmsGcmRegister" private const val TAG = "GmsGcmRegister"
private suspend fun ensureCheckinIsUpToDate(context: Context) { 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 val lastCheckin = LastCheckinInfo.read(context).lastCheckin
if (lastCheckin < System.currentTimeMillis() - CheckinService.MAX_VALID_CHECKIN_AGE) { if (lastCheckin < System.currentTimeMillis() - CheckinService.MAX_VALID_CHECKIN_AGE) {
val resultData: Bundle = suspendCoroutine { continuation -> val resultData: Bundle = suspendCoroutine { continuation ->

View File

@ -10,52 +10,40 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.util.Log import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.Serializable import java.io.Serializable
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
private const val ACTION_SERVICE_INFO_REQUEST = "org.microg.gms.gcm.SERVICE_INFO_REQUEST" 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 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_SERVICE_INFO = "org.microg.gms.gcm.SERVICE_INFO"
private const val EXTRA_CONFIGURATION = "org.microg.gms.gcm.CONFIGURATION"
private const val TAG = "GmsGcmStatusInfo" 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 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
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
}
}
}
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() { 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) { override fun onReceive(context: Context, intent: Intent) {
try { try {
when (intent.action) { context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
ACTION_UPDATE_CONFIGURATION -> { setPackage(context.packageName)
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context) val prefs = GcmPrefs.get(context)
} val info = ServiceInfo(
} configuration = prefs.toConfiguration(),
sendInfoResponse(context) 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) { } catch (e: Exception) {
Log.w(TAG, e) Log.w(TAG, e)
} }
@ -87,12 +75,11 @@ private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context):
} }
suspend fun getGcmServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver( 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 { Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_SERVICE_INFO_REQUEST action = ACTION_SERVICE_INFO_REQUEST
}, context) }, context)
suspend fun setGcmServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver( suspend fun setGcmServiceConfiguration(context: Context, configuration: ServiceConfiguration) = withContext(Dispatchers.IO) {
Intent(context, ServiceInfoReceiver::class.java).apply { GcmPrefs.write(context, configuration)
action = ACTION_UPDATE_CONFIGURATION }
putExtra(EXTRA_CONFIGURATION, configuration)
}, context)

View File

@ -0,0 +1,97 @@
package org.microg.gms.settings
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.net.Uri
object SettingsContract {
const val AUTHORITY = "org.microg.gms.settings"
val AUTHORITY_URI: Uri = Uri.parse("content://$AUTHORITY")
object CheckIn {
private const val id = "check-in"
val CONTENT_URI: Uri = Uri.withAppendedPath(AUTHORITY_URI, id)
const val CONTENT_TYPE = "vnd.android.cursor.item/vnd.$AUTHORITY.$id"
const val ENABLED = "checkin_enable_service"
const val ANDROID_ID = "androidId"
const val DIGEST = "digest"
const val LAST_CHECK_IN = "lastCheckin"
const val SECURITY_TOKEN = "securityToken"
const val VERSION_INFO = "versionInfo"
const val DEVICE_DATA_VERSION_INFO = "deviceDataVersionInfo"
val PROJECTION = arrayOf(
ENABLED,
ANDROID_ID,
DIGEST,
LAST_CHECK_IN,
SECURITY_TOKEN,
VERSION_INFO,
DEVICE_DATA_VERSION_INFO,
)
const val PREFERENCES_NAME = "checkin"
const val INITIAL_DIGEST = "1-929a0dca0eee55513280171a8585da7dcd3700f8"
}
object Gcm {
private const val id = "gcm"
val CONTENT_URI: Uri = Uri.withAppendedPath(AUTHORITY_URI, id)
const val CONTENT_TYPE = "vnd.android.cursor.item/vnd.$AUTHORITY.$id"
const val FULL_LOG = "gcm_full_log"
const val LAST_PERSISTENT_ID = "gcm_last_persistent_id"
const val ENABLE_GCM = "gcm_enable_mcs_service"
const val NETWORK_MOBILE = "gcm_network_mobile"
const val NETWORK_WIFI = "gcm_network_wifi"
const val NETWORK_ROAMING = "gcm_network_roaming"
const val NETWORK_OTHER = "gcm_network_other"
const val LEARNT_MOBILE = "gcm_learnt_mobile"
const val LEARNT_WIFI = "gcm_learnt_wifi"
const val LEARNT_OTHER = "gcm_learnt_other"
val PROJECTION = arrayOf(
FULL_LOG,
LAST_PERSISTENT_ID,
ENABLE_GCM,
NETWORK_MOBILE,
NETWORK_WIFI,
NETWORK_ROAMING,
NETWORK_OTHER,
LEARNT_MOBILE,
LEARNT_WIFI,
LEARNT_OTHER,
)
}
object Auth {
private const val id = "auth"
val CONTENT_URI: Uri = Uri.withAppendedPath(AUTHORITY_URI, id)
const val CONTENT_TYPE = "vnd.android.cursor.item/vnd.$AUTHORITY.$id"
const val TRUST_GOOGLE = "auth_manager_trust_google"
const val VISIBLE = "auth_manager_visible"
val PROJECTION = arrayOf(
TRUST_GOOGLE,
VISIBLE,
)
}
fun <T> getSettings(context: Context, uri: Uri, projection: Array<out String>?, f: (Cursor) -> T): T {
context.contentResolver.query(uri, projection, null, null, null).use { c ->
require(c != null) { "Cursor for query $uri ${projection?.toList()} was null" }
if (!c.moveToFirst()) error("Cursor for query $uri ${projection?.toList()} was empty")
return f.invoke(c)
}
}
fun setSettings(context: Context, uri: Uri, v: ContentValues.() -> Unit) {
val values = ContentValues().apply { v.invoke(this) }
val affected = context.contentResolver.update(uri, values, null, null)
require(affected == 1) { "Update for $uri with $values affected 0 rows"}
}
}

View File

@ -0,0 +1,228 @@
package org.microg.gms.settings
import android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.util.Log
import androidx.preference.PreferenceManager
import org.microg.gms.common.PackageUtils.warnIfNotMainProcess
import org.microg.gms.settings.SettingsContract.AUTHORITY
import org.microg.gms.settings.SettingsContract.Auth
import org.microg.gms.settings.SettingsContract.CheckIn
import org.microg.gms.settings.SettingsContract.Gcm
import java.io.File
/**
* All settings access should go through this [ContentProvider],
* because it provides safe access from different processes which normal [SharedPreferences] don't.
*/
class SettingsProvider : ContentProvider() {
private val preferences: SharedPreferences by lazy {
PreferenceManager.getDefaultSharedPreferences(context)
}
private val checkInPrefs by lazy {
context!!.getSharedPreferences(CheckIn.PREFERENCES_NAME, MODE_PRIVATE)
}
private val systemDefaultPreferences: SharedPreferences? by lazy {
try {
Context::class.java.getDeclaredMethod(
"getSharedPreferences",
File::class.java,
Int::class.javaPrimitiveType
).invoke(context, File("/system/etc/microg.xml"), MODE_PRIVATE) as SharedPreferences
} catch (ignored: Exception) {
null
}
}
override fun onCreate(): Boolean {
return true
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? = when (uri) {
CheckIn.CONTENT_URI -> queryCheckIn(projection ?: CheckIn.PROJECTION)
Gcm.CONTENT_URI -> queryGcm(projection ?: Gcm.PROJECTION)
Auth.CONTENT_URI -> queryAuth(projection ?: Auth.PROJECTION)
else -> null
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
warnIfNotMainProcess(context, this.javaClass)
if (values == null) return 0
when (uri) {
CheckIn.CONTENT_URI -> updateCheckIn(values)
Gcm.CONTENT_URI -> updateGcm(values)
Auth.CONTENT_URI -> updateAuth(values)
else -> return 0
}
return 1
}
private fun queryCheckIn(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key ->
when (key) {
CheckIn.ENABLED -> getSettingsBoolean(key, true)
CheckIn.ANDROID_ID -> checkInPrefs.getLong(key, 0)
CheckIn.DIGEST -> checkInPrefs.getString(key, CheckIn.INITIAL_DIGEST)
?: CheckIn.INITIAL_DIGEST
CheckIn.LAST_CHECK_IN -> checkInPrefs.getLong(key, 0)
CheckIn.SECURITY_TOKEN -> checkInPrefs.getLong(key, 0)
CheckIn.VERSION_INFO -> checkInPrefs.getString(key, "") ?: ""
CheckIn.DEVICE_DATA_VERSION_INFO -> checkInPrefs.getString(key, "") ?: ""
else -> throw IllegalArgumentException()
}
}
private fun updateCheckIn(values: ContentValues) {
if (values.size() == 0) return
if (values.size() == 1 && values.containsKey(CheckIn.ENABLED)) {
// special case: only changing enabled state
updateCheckInEnabled(values.getAsBoolean(CheckIn.ENABLED))
return
}
val editor = checkInPrefs.edit()
values.valueSet().forEach { (key, value) ->
// TODO remove log
Log.e("TEST", "check-in update: $key = $value")
if (key == CheckIn.ENABLED) {
// special case: not saved in checkInPrefs
updateCheckInEnabled(value as Boolean)
}
when (key) {
CheckIn.ANDROID_ID -> editor.putLong(key, value as Long)
CheckIn.DIGEST -> editor.putString(key, value as String?)
CheckIn.LAST_CHECK_IN -> editor.putLong(key, value as Long)
CheckIn.SECURITY_TOKEN -> editor.putLong(key, value as Long)
CheckIn.VERSION_INFO -> editor.putString(key, value as String?)
CheckIn.DEVICE_DATA_VERSION_INFO -> editor.putString(key, value as String?)
}
}
editor.apply()
}
private fun updateCheckInEnabled(enabled: Boolean) {
preferences.edit()
.putBoolean(CheckIn.ENABLED, enabled)
.apply()
}
private fun queryGcm(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key ->
when (key) {
Gcm.ENABLE_GCM -> getSettingsBoolean(key, true)
Gcm.FULL_LOG -> getSettingsBoolean(key, true)
Gcm.LAST_PERSISTENT_ID -> preferences.getString(key, "") ?: ""
Gcm.NETWORK_MOBILE -> Integer.parseInt(preferences.getString(key, "0") ?: "0")
Gcm.NETWORK_WIFI -> Integer.parseInt(preferences.getString(key, "0") ?: "0")
Gcm.NETWORK_ROAMING -> Integer.parseInt(preferences.getString(key, "0") ?: "0")
Gcm.NETWORK_OTHER -> Integer.parseInt(preferences.getString(key, "0") ?: "0")
Gcm.LEARNT_MOBILE -> preferences.getInt(key, 300000)
Gcm.LEARNT_WIFI -> preferences.getInt(key, 300000)
Gcm.LEARNT_OTHER -> preferences.getInt(key, 300000)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
private fun updateGcm(values: ContentValues) {
if (values.size() == 0) return
val editor = preferences.edit()
values.valueSet().forEach { (key, value) ->
// TODO remove log
Log.e("TEST", "gcm update: $key = $value")
when (key) {
Gcm.ENABLE_GCM -> editor.putBoolean(key, value as Boolean)
Gcm.FULL_LOG -> editor.putBoolean(key, value as Boolean)
Gcm.LAST_PERSISTENT_ID -> editor.putString(key, value as String?)
Gcm.NETWORK_MOBILE -> editor.putString(key, (value as Int).toString())
Gcm.NETWORK_WIFI -> editor.putString(key, (value as Int).toString())
Gcm.NETWORK_ROAMING -> editor.putString(key, (value as Int).toString())
Gcm.NETWORK_OTHER -> editor.putString(key, (value as Int).toString())
Gcm.LEARNT_MOBILE -> editor.putInt(key, value as Int)
Gcm.LEARNT_WIFI -> editor.putInt(key, value as Int)
Gcm.LEARNT_OTHER -> editor.putInt(key, value as Int)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
editor.apply()
}
private fun queryAuth(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key ->
when (key) {
Auth.TRUST_GOOGLE -> getSettingsBoolean(key, true)
Auth.VISIBLE -> getSettingsBoolean(key, false)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
private fun updateAuth(values: ContentValues) {
if (values.size() == 0) return
val editor = preferences.edit()
values.valueSet().forEach { (key, value) ->
// TODO remove log
Log.e("TEST", "auth update: $key = $value")
when (key) {
Auth.TRUST_GOOGLE -> editor.putBoolean(key, value as Boolean)
Auth.VISIBLE -> editor.putBoolean(key, value as Boolean)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
editor.apply()
}
private fun MatrixCursor.addRow(
p: Array<out String>,
valueGetter: (String) -> Any
): MatrixCursor {
val row = newRow()
for (key in p) row.add(valueGetter.invoke(key).apply {
// TODO remove log
Log.e("TEST", "$key = $this")
})
return this
}
override fun getType(uri: Uri): String {
return "vnd.android.cursor.item/vnd.$AUTHORITY.${uri.path}"
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
throw UnsupportedOperationException()
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
throw UnsupportedOperationException()
}
/**
* Returns the current setting of the given [key]
* using the default value from [systemDefaultPreferences] or [def] if not available.
* @return the current setting as [Int], because [ContentProvider] does not support [Boolean].
*/
private fun getSettingsBoolean(key: String, def: Boolean): Int {
val default = systemDefaultPreferences?.getBoolean(key, def) ?: def
return if (preferences.getBoolean(key, default)) 1 else 0
}
}

View File

@ -34,11 +34,12 @@ class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragmen
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val info = getCheckinServiceInfo(requireContext()) val info = getCheckinServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus) 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 binding.checkinEnabled = serviceInfo.configuration.enabled
} }

View File

@ -16,6 +16,7 @@ import org.microg.gms.gcm.ServiceInfo
import org.microg.gms.gcm.getGcmServiceInfo import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.gms.gcm.setGcmServiceConfiguration import org.microg.gms.gcm.setGcmServiceConfiguration
@Suppress("Warnings")
class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) { class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
lateinit var binding: PushNotificationFragmentBinding lateinit var binding: PushNotificationFragmentBinding
@ -33,11 +34,12 @@ class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val info = getGcmServiceInfo(requireContext()) val info = getGcmServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus) 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 binding.gcmEnabled = serviceInfo.configuration.enabled
} }

View File

@ -7,7 +7,9 @@ package org.microg.gms.ui
import android.content.ComponentName import android.content.ComponentName
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.preference.Preference 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.CheckinClient
import org.microg.gms.checkin.getCheckinServiceInfo import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.gcm.GcmDatabase 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.gms.gcm.getGcmServiceInfo
import org.microg.tools.ui.ResourceSettingsFragment import org.microg.tools.ui.ResourceSettingsFragment
@ -73,16 +78,22 @@ class SettingsFragment : ResourceSettingsFragment() {
} }
private suspend fun updateDetails() { private suspend fun updateDetails() {
findPreference<Preference>(PREF_GCM)?.summary = if (getGcmServiceInfo(requireContext()).configuration.enabled) { val context = requireContext()
val gcmServiceInfo = getGcmServiceInfo(context)
if (gcmServiceInfo.configuration.enabled) {
val database = GcmDatabase(context) val database = GcmDatabase(context)
val regCount = database.registrationList.size 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() database.close()
getString(R.string.service_status_enabled_short) + " - " + resources.getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount) getString(R.string.service_status_enabled_short) + " - " + resources.getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount)
} else { } else {
getString(R.string.service_status_disabled_short) getString(R.string.service_status_disabled_short)
} }
findPreference<Preference>(PREF_CHECKIN)?.setSummary(if (getCheckinServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short) findPreference<Preference>(PREF_CHECKIN)?.setSummary(if (getCheckinServiceInfo(context).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
} }
companion object { companion object {