0.2.12.203315 Upstream

This commit is contained in:
Oizaro 2020-09-11 10:11:10 +02:00
parent 0a4fd6158a
commit 3f3b4ca69f
35 changed files with 786 additions and 289 deletions

View File

@ -49,8 +49,8 @@ allprojects {
apply plugin: 'idea'
group = 'org.microg.gms'
version = "0.2.11.20500"
ext.appVersionCode = 202500022
version = "0.2.12.203315"
ext.appVersionCode = 203315022
ext.isReleaseVersion = false
}

View File

@ -0,0 +1,3 @@
package com.google.android.gms.measurement.internal;
parcelable AppMetadata;

View File

@ -0,0 +1,3 @@
package com.google.android.gms.measurement.internal;
parcelable ConditionalUserPropertyParcel;

View File

@ -0,0 +1,3 @@
package com.google.android.gms.measurement.internal;
parcelable EventParcel;

View File

@ -1,5 +1,25 @@
package com.google.android.gms.measurement.internal;
interface IMeasurementService {
import com.google.android.gms.measurement.internal.AppMetadata;
import com.google.android.gms.measurement.internal.ConditionalUserPropertyParcel;
import com.google.android.gms.measurement.internal.EventParcel;
interface IMeasurementService {
void f1(in EventParcel p0, in AppMetadata p1) = 0;
// void zza(UserAttributeParcel p0, AppMetadata p1) = 1;
void f4(in AppMetadata p0) = 3;
// void zza(EventParcel p0, String p1, String p2) = 4;
// void zzb(AppMetadata p0) = 5;
// List<UserAttributeParcel> zza(AppMetadata p0, boolean p1) = 6;
// byte[] zza(EventParcel p0, String p1) = 8;
void f10(long p0, String p1, String p2, String p3) = 9;
String f11(in AppMetadata p0) = 10;
void f12(in ConditionalUserPropertyParcel p0, in AppMetadata p1) = 11;
// void zza(ConditionalUserPropertyParcel p0) = 12;
// List<UserAttributeParcelzkr> zza(String p0, String p1, boolean p2, AppMetadata p3) = 13;
// List<UserAttributeParcel> zza(String p0, String p1, String p2, boolean p3) = 14;
// List<ConditionalUserPropertyParcel> zza(String p0, String p1, AppMetadata p2) = 15;
// List<ConditionalUserPropertyParcel> zza(String p0, String p1, String p2) = 16;
// void zzd(AppMetadata p0) = 17;
// void zza(Bundle p0, AppMetadata p1) = 18;
}

View File

@ -0,0 +1,3 @@
package com.google.android.gms.measurement.internal;
parcelable UserAttributeParcel;

View File

@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.measurement.internal;
import org.microg.safeparcel.AutoSafeParcelable;
public class AppMetadata extends AutoSafeParcelable {
@Field(2)
public String packageName;
@Field(4)
public String versionName;
@Field(11)
public long versionCode;
public static final Creator<AppMetadata> CREATOR = new AutoCreator<>(AppMetadata.class);
}

View File

@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.measurement.internal;
import org.microg.safeparcel.AutoSafeParcelable;
public class ConditionalUserPropertyParcel extends AutoSafeParcelable {
public static final Creator<ConditionalUserPropertyParcel> CREATOR = new AutoCreator<>(ConditionalUserPropertyParcel.class);
}

View File

@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.measurement.internal;
import org.microg.safeparcel.AutoSafeParcelable;
public class EventParcel extends AutoSafeParcelable {
public static final Creator<EventParcel> CREATOR = new AutoCreator<>(EventParcel.class);
}

View File

@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.measurement.internal;
import org.microg.safeparcel.AutoSafeParcelable;
public class UserAttributeParcel extends AutoSafeParcelable {
public static final Creator<UserAttributeParcel> CREATOR = new AutoCreator<>(UserAttributeParcel.class);
}

View File

@ -30,6 +30,7 @@ public final class GcmConstants {
public static final String ACTION_INSTANCE_ID = "com.google.android.gms.iid.InstanceID";
public static final String EXTRA_APP = "app";
public static final String EXTRA_APP_OVERRIDE = "org.microg.gms.gcm.APP_OVERRIDE";
public static final String EXTRA_APP_ID = "appid";
public static final String EXTRA_APP_VERSION_CODE = "app_ver";
public static final String EXTRA_APP_VERSION_NAME = "app_ver_name";

View File

@ -110,6 +110,8 @@
</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.server.checkin.CHECKIN" />
@ -131,8 +133,7 @@
<!-- Cloud Messaging -->
<service
android:name="org.microg.gms.gcm.PushRegisterService"
android:permission="com.mgoogle.android.c2dm.permission.RECEIVE">
android:name="org.microg.gms.gcm.PushRegisterService">
<intent-filter>
<action android:name="com.mgoogle.android.c2dm.intent.REGISTER" />
<action android:name="com.mgoogle.android.c2dm.intent.UNREGISTER" />
@ -150,13 +151,14 @@
<service android:name="org.microg.gms.gcm.McsService" />
<receiver
android:name="org.microg.gms.gcm.SendReceiver"
android:permission="com.mgoogle.android.c2dm.permission.RECEIVE">
android:name="org.microg.gms.gcm.SendReceiver">
<intent-filter>
<action android:name="com.google.android.gcm.intent.SEND" />
</intent-filter>
</receiver>
<receiver android:name="org.microg.gms.gcm.ServiceInfoReceiver" />
<receiver android:name="org.microg.gms.gcm.TriggerReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
@ -209,7 +211,8 @@
<activity
android:name="org.microg.tools.AccountPickerActivity"
android:excludeFromRecents="true"
android:exported="true">
android:exported="true"
android:process=":ui">
<intent-filter>
<action android:name="com.google.android.gms.common.account.CHOOSE_ACCOUNT" />
@ -221,6 +224,7 @@
android:name="org.microg.gms.auth.login.LoginActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:process=":ui"
android:theme="@style/Theme.LoginBlue">
<intent-filter>
<action android:name="com.google.android.gms.auth.login.LOGIN" />
@ -233,6 +237,7 @@
android:name="org.microg.gms.auth.AskPermissionActivity"
android:excludeFromRecents="true"
android:exported="true"
android:process=":ui"
android:theme="@style/Theme.AppCompat.DayNight.Dialog" />
<service
@ -241,7 +246,8 @@
<activity
android:name="com.google.android.gms.auth.TokenActivity"
android:exported="true" />
android:exported="true"
android:process=":ui" />
<provider
android:name="org.microg.gms.auth.AccountContentProvider"
@ -263,6 +269,7 @@
android:name="org.microg.gms.ui.SettingsActivity"
android:icon="@mipmap/ic_launcher"
android:label="@string/gms_settings_name"
android:process=":ui"
android:roundIcon="@mipmap/ic_launcher"
android:theme="@style/Theme.AppCompat.DayNight">
<intent-filter>
@ -276,6 +283,7 @@
android:name="org.microg.gms.ui.SettingsActivityLink"
android:icon="@drawable/microg_light_color_24"
android:label="@string/gms_settings_name"
android:process=":ui"
android:targetActivity="org.microg.gms.ui.SettingsActivity"
android:theme="@style/Theme.AppCompat.DayNight">
<intent-filter>
@ -296,21 +304,22 @@
<activity
android:name="org.microg.gms.ui.AskPushPermission"
android:excludeFromRecents="true"
android:process=":ui"
android:theme="@style/Theme.AppCompat.DayNight.Dialog.Alert" />
<activity
android:name="org.microg.gms.ui.AboutFragment$AsActivity"
android:label="@string/pref_about_title"
android:theme="@style/Theme.AppCompat.DayNight" />
android:process=":ui" />
<activity
android:name="org.microg.gms.ui.GoogleMoreFragment$AsActivity"
android:label="@string/gms_settings_name"
android:theme="@style/Theme.AppCompat.DayNight" />
android:process=":ui" />
<activity
android:name="org.microg.gms.ui.AccountSettingsActivity"
android:theme="@style/Theme.AppCompat.DayNight">
android:process=":ui">
<intent-filter>
<action android:name="com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS" />
<action android:name="com.google.android.gms.accountsettings.PRIVACY_SETTINGS" />

View File

@ -10,6 +10,11 @@ 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";
@ -17,6 +22,9 @@ public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeL
public static CheckinPrefs get(Context context) {
if (INSTANCE == null) {
if (!context.getPackageName().equals(PackageUtils.getProcessName())) {
Log.w("Preferences", CheckinPrefs.class.getName() + " initialized outside main process", new RuntimeException());
}
if (context == null) return new CheckinPrefs(null);
INSTANCE = new CheckinPrefs(context.getApplicationContext());
}
@ -24,18 +32,30 @@ public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeL
}
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 = preferences.getBoolean(PREF_ENABLE_CHECKIN, true);
checkinEnabled = getSettingsBoolean(PREF_ENABLE_CHECKIN, false);
}
@Override

View File

@ -55,6 +55,6 @@ public class LastCheckinInfo {
.putLong(PREF_SECURITY_TOKEN, securityToken)
.putString(PREF_VERSION_INFO, versionInfo)
.putString(PREF_DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo)
.apply();
.commit();
}
}

View File

@ -17,6 +17,7 @@
package org.microg.gms.common;
import android.app.ActivityManager;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.PackageInfo;
@ -26,6 +27,7 @@ import android.os.Binder;
import androidx.annotation.Nullable;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@ -240,6 +242,19 @@ public class PackageUtils {
}
}
public static String getProcessName() {
if (android.os.Build.VERSION.SDK_INT >= 28)
return Application.getProcessName();
try {
Class<?> activityThread = Class.forName("android.app.ActivityThread");
String methodName = android.os.Build.VERSION.SDK_INT >= 18 ? "currentProcessName" : "currentPackageName";
Method getProcessName = activityThread.getDeclaredMethod(methodName);
return (String) getProcessName.invoke(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String sha1sum(byte[] bytes) {
MessageDigest md;
try {

View File

@ -21,6 +21,7 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.text.TextUtils;
import java.util.ArrayList;
@ -65,6 +66,9 @@ public class GcmDatabase extends SQLiteOpenHelper {
public GcmDatabase(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.context = context;
if (Build.VERSION.SDK_INT >= 16) {
this.setWriteAheadLoggingEnabled(true);
}
}
public static class App {

View File

@ -25,6 +25,9 @@ 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;
@ -51,6 +54,9 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
public static GcmPrefs get(Context context) {
if (INSTANCE == null) {
if (!context.getPackageName().equals(PackageUtils.getProcessName())) {
Log.w("Preferences", GcmPrefs.class.getName() + " initialized outside main process", new RuntimeException());
}
if (context == null) return new GcmPrefs(null);
INSTANCE = new GcmPrefs(context.getApplicationContext());
}
@ -71,30 +77,43 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
private int learntMobile = 300000;
private int learntOther = 300000;
private SharedPreferences defaultPreferences;
private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private GcmPrefs(Context context) {
if (context != null) {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
defaultPreferences.registerOnSharedPreferenceChangeListener(this);
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() {
gcmLogEnabled = defaultPreferences.getBoolean(PREF_FULL_LOG, true);
lastPersistedId = defaultPreferences.getString(PREF_LAST_PERSISTENT_ID, "");
confirmNewApps = defaultPreferences.getBoolean(PREF_CONFIRM_NEW_APPS, false);
gcmEnabled = defaultPreferences.getBoolean(PREF_ENABLE_GCM, true);
gcmEnabled = getSettingsBoolean(PREF_ENABLE_GCM, false);
gcmLogEnabled = getSettingsBoolean(PREF_FULL_LOG, true);
confirmNewApps = getSettingsBoolean(PREF_CONFIRM_NEW_APPS, false);
networkMobile = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_MOBILE, "0"));
networkWifi = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_WIFI, "0"));
networkRoaming = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_ROAMING, "0"));
networkOther = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_OTHER, "0"));
lastPersistedId = preferences.getString(PREF_LAST_PERSISTENT_ID, "");
learntMobile = defaultPreferences.getInt(PREF_LEARNT_MOBILE, 300000);
learntWifi = defaultPreferences.getInt(PREF_LEARNT_WIFI, 300000);
learntOther = defaultPreferences.getInt(PREF_LEARNT_OTHER, 300000);
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) {
@ -183,7 +202,7 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
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));
defaultPreferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply();
preferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply();
}
@Override
@ -224,11 +243,11 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
public void extendLastPersistedId(String id) {
if (!lastPersistedId.isEmpty()) lastPersistedId += "|";
lastPersistedId += id;
defaultPreferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
}
public void clearLastPersistedId() {
lastPersistedId = "";
defaultPreferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
}
}

View File

@ -36,10 +36,12 @@ public final class McsConstants {
public static final int MSG_TEARDOWN = 30;
public static final int MSG_CONNECT = 40;
public static final int MSG_HEARTBEAT = 41;
public static final int MSG_ACK = 42;
public static String ACTION_CONNECT = "org.microg.gms.gcm.mcs.CONNECT";
public static String ACTION_RECONNECT = "org.microg.gms.gcm.mcs.RECONNECT";
public static String ACTION_HEARTBEAT = "org.microg.gms.gcm.mcs.HEARTBEAT";
public static String ACTION_SEND = "org.microg.gms.gcm.mcs.SEND";
public static String ACTION_ACK = "org.microg.gms.gcm.mcs.ACK";
public static String EXTRA_REASON = "org.microg.gms.gcm.mcs.REASON";
}

View File

@ -26,6 +26,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.net.ConnectivityManager;
import android.os.Build;
@ -52,8 +53,10 @@ import org.microg.gms.common.PackageUtils;
import org.microg.gms.gcm.mcs.AppData;
import org.microg.gms.gcm.mcs.Close;
import org.microg.gms.gcm.mcs.DataMessageStanza;
import org.microg.gms.gcm.mcs.Extension;
import org.microg.gms.gcm.mcs.HeartbeatAck;
import org.microg.gms.gcm.mcs.HeartbeatPing;
import org.microg.gms.gcm.mcs.IqStanza;
import org.microg.gms.gcm.mcs.LoginRequest;
import org.microg.gms.gcm.mcs.LoginResponse;
import org.microg.gms.gcm.mcs.Setting;
@ -65,6 +68,7 @@ import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLContext;
@ -74,6 +78,7 @@ import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.os.Build.VERSION.SDK_INT;
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;
import static org.microg.gms.gcm.GcmConstants.EXTRA_COLLAPSE_KEY;
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM;
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
@ -82,6 +87,7 @@ import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_FROM;
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO;
import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL;
import static org.microg.gms.gcm.McsConstants.ACTION_ACK;
import static org.microg.gms.gcm.McsConstants.ACTION_CONNECT;
import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT;
import static org.microg.gms.gcm.McsConstants.ACTION_RECONNECT;
@ -91,8 +97,10 @@ import static org.microg.gms.gcm.McsConstants.MCS_CLOSE_TAG;
import static org.microg.gms.gcm.McsConstants.MCS_DATA_MESSAGE_STANZA_TAG;
import static org.microg.gms.gcm.McsConstants.MCS_HEARTBEAT_ACK_TAG;
import static org.microg.gms.gcm.McsConstants.MCS_HEARTBEAT_PING_TAG;
import static org.microg.gms.gcm.McsConstants.MCS_IQ_STANZA_TAG;
import static org.microg.gms.gcm.McsConstants.MCS_LOGIN_REQUEST_TAG;
import static org.microg.gms.gcm.McsConstants.MCS_LOGIN_RESPONSE_TAG;
import static org.microg.gms.gcm.McsConstants.MSG_ACK;
import static org.microg.gms.gcm.McsConstants.MSG_CONNECT;
import static org.microg.gms.gcm.McsConstants.MSG_HEARTBEAT;
import static org.microg.gms.gcm.McsConstants.MSG_INPUT;
@ -119,6 +127,7 @@ public class McsService extends Service implements Handler.Callback {
private static long lastIncomingNetworkRealtime = 0;
private static long startTimestamp = 0;
public static String activeNetworkPref = null;
private AtomicInteger nextMessageId = new AtomicInteger(0x1000000);
private static Socket sslSocket;
private static McsInputStream inputStream;
@ -167,8 +176,8 @@ public class McsService extends Service implements Handler.Callback {
}
}
private static void logd(String msg) {
if (GcmPrefs.get(null).isGcmLogEnabled()) Log.d(TAG, msg);
private static void logd(Context context, String msg) {
if (context == null || GcmPrefs.get(context).isGcmLogEnabled()) Log.d(TAG, msg);
}
@Override
@ -226,18 +235,18 @@ public class McsService extends Service implements Handler.Callback {
return null;
}
public synchronized static boolean isConnected() {
public synchronized static boolean isConnected(Context context) {
if (inputStream == null || !inputStream.isAlive() || outputStream == null || !outputStream.isAlive()) {
logd("Connection is not enabled or dead.");
logd(null, "Connection is not enabled or dead.");
return false;
}
// consider connection to be dead if we did not receive an ack within twice the heartbeat interval
int heartbeatMs = GcmPrefs.get(null).getHeartbeatMsFor(activeNetworkPref, false);
int heartbeatMs = GcmPrefs.get(context).getHeartbeatMsFor(activeNetworkPref, false);
if (heartbeatMs < 0) {
closeAll();
} else if (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime > 2 * heartbeatMs) {
logd("No heartbeat for " + (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime) / 1000 + " seconds, connection assumed to be dead after " + 2 * heartbeatMs / 1000 + " seconds");
GcmPrefs.get(null).learnTimeout(activeNetworkPref);
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);
return false;
}
return true;
@ -250,7 +259,7 @@ public class McsService extends Service implements Handler.Callback {
public static void scheduleReconnect(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
long delay = getCurrentDelay();
logd("Scheduling reconnect in " + delay / 1000 + " seconds...");
logd(context, "Scheduling reconnect in " + delay / 1000 + " seconds...");
PendingIntent pi = PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi);
@ -266,7 +275,7 @@ public class McsService extends Service implements Handler.Callback {
if (heartbeatMs < 0) {
closeAll();
}
logd("Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds...");
logd(context, "Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds...");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// This is supposed to work even when running in idle and without battery optimization disabled
alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent);
@ -307,6 +316,8 @@ public class McsService extends Service implements Handler.Callback {
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_HEARTBEAT, reason));
} else if (ACTION_SEND.equals(intent.getAction())) {
handleSendMessage(intent);
} else if (ACTION_ACK.equals(intent.getAction())) {
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_ACK, reason));
}
WakefulBroadcastReceiver.completeWakefulIntent(intent);
} else if (connectIntent == null) {
@ -334,14 +345,22 @@ public class McsService extends Service implements Handler.Callback {
Log.w(TAG, "Failed to send message, missing package name");
return;
}
if (packageName.equals(getPackageName()) && intent.hasExtra(EXTRA_APP_OVERRIDE)) {
packageName = intent.getStringExtra(EXTRA_APP_OVERRIDE);
intent.removeExtra(EXTRA_APP_OVERRIDE);
}
intent.removeExtra(EXTRA_APP);
int ttl;
try {
if (intent.hasExtra(EXTRA_TTL)) {
ttl = Integer.parseInt(intent.getStringExtra(EXTRA_TTL));
if (ttl < 0 || ttl > maxTtl) {
ttl = maxTtl;
}
} else {
ttl = maxTtl;
}
} catch (NumberFormatException e) {
// TODO: error TtlUnsupported
Log.w(TAG, e);
@ -390,7 +409,8 @@ public class McsService extends Service implements Handler.Callback {
try {
DataMessageStanza msg = new DataMessageStanza.Builder()
.sent(System.currentTimeMillis() / 1000L)
.id(messageId)
.id(Integer.toHexString(nextMessageId.incrementAndGet()))
.persistent_id(messageId)
.token(collapseKey)
.from(from)
.reg_id(registrationId)
@ -416,11 +436,11 @@ public class McsService extends Service implements Handler.Callback {
return;
}
logd("Starting MCS connection...");
logd(this, "Starting MCS connection...");
Socket socket = new Socket(SERVICE_HOST, SERVICE_PORT);
logd("Connected to " + SERVICE_HOST + ":" + SERVICE_PORT);
logd(this, "Connected to " + SERVICE_HOST + ":" + SERVICE_PORT);
sslSocket = SSLContext.getDefault().getSocketFactory().createSocket(socket, SERVICE_HOST, SERVICE_PORT, true);
logd("Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT);
logd(this, "Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT);
inputStream = new McsInputStream(sslSocket.getInputStream(), rootHandler);
outputStream = new McsOutputStream(sslSocket.getOutputStream(), rootHandler);
inputStream.start();
@ -443,7 +463,7 @@ public class McsService extends Service implements Handler.Callback {
private void handleLoginResponse(LoginResponse loginResponse) {
if (loginResponse.error == null) {
GcmPrefs.get(this).clearLastPersistedId();
logd("Logged in");
logd(this, "Logged in");
wakeLock.release();
} else {
throw new RuntimeException("Could not login: " + loginResponse.error);
@ -500,9 +520,11 @@ public class McsService extends Service implements Handler.Callback {
Intent intent = new Intent();
intent.setAction(ACTION_C2DM_RECEIVE);
intent.setPackage(packageName);
intent.putExtra(EXTRA_FROM, msg.from);
intent.putExtra(EXTRA_MESSAGE_ID, msg.id);
if (msg.persistent_id != null) {
intent.putExtra(EXTRA_MESSAGE_ID, msg.persistent_id);
}
if (app.wakeForDelivery) {
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
} else {
@ -513,40 +535,51 @@ public class McsService extends Service implements Handler.Callback {
intent.putExtra(appData.key, appData.value);
}
String receiverPermission;
String receiverPermission = null;
try {
String name = packageName + ".permission.C2D_MESSAGE";
getPackageManager().getPermissionInfo(name, 0);
PermissionInfo info = getPackageManager().getPermissionInfo(name, 0);
if (info.packageName.equals(packageName)) {
receiverPermission = name;
} catch (PackageManager.NameNotFoundException e) {
receiverPermission = null;
}
} catch (Exception ignored) {
// Keep null, no valid permission found
}
if (receiverPermission == null) {
// Without receiver permission, we only restrict by package name
logd(this, "Deliver message to all receivers in package " + packageName);
intent.setPackage(packageName);
sendOrderedBroadcast(intent, null);
} else {
List<ResolveInfo> infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER);
if (infos == null || infos.isEmpty()) {
logd("No target for message, wut?");
logd(this, "No target for message, wut?");
} else {
for (ResolveInfo resolveInfo : infos) {
logd("Target: " + resolveInfo);
Intent targetIntent = new Intent(intent);
targetIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name));
if (resolveInfo.activityInfo.packageName.equals(packageName)) {
// Wake up the package itself
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && app.wakeForDelivery) {
try {
if (getUserIdMethod != null && addPowerSaveTempWhitelistAppMethod != null && deviceIdleController != null) {
int userId = (int) getUserIdMethod.invoke(null, getPackageManager().getApplicationInfo(packageName, 0).uid);
logd("Adding app " + packageName + " for userId " + userId + " to the temp whitelist");
logd(this, "Adding app " + packageName + " for userId " + userId + " to the temp whitelist");
addPowerSaveTempWhitelistAppMethod.invoke(deviceIdleController, packageName, 10000, userId, "GCM Push");
}
} catch (Exception e) {
Log.w(TAG, e);
}
}
targetIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name));
if (resolveInfo.activityInfo.packageName.equals(packageName)) {
// We don't need receiver permission for our own package
logd(this, "Deliver message to own receiver " + resolveInfo);
sendOrderedBroadcast(targetIntent, null);
} else if (receiverPermission != null) {
} else if (resolveInfo.filter.hasCategory(packageName)) {
// Permission required
logd(this, "Deliver message to third-party receiver (with permission check)" + resolveInfo);
sendOrderedBroadcast(targetIntent, receiverPermission);
} else {
Log.w(TAG, resolveInfo.activityInfo.packageName + "/" + resolveInfo.activityInfo.name + " matches for C2D message to " + packageName + " but corresponding permission was not declared");
}
}
}
}
@ -556,6 +589,7 @@ public class McsService extends Service implements Handler.Callback {
for (AppData appData : msg.app_data) {
if (IDLE_NOTIFICATION.equals(appData.key)) {
DataMessageStanza.Builder msgResponse = new DataMessageStanza.Builder()
.id(Integer.toHexString(nextMessageId.incrementAndGet()))
.from(FROM_FIELD)
.sent(System.currentTimeMillis() / 1000)
.ttl(0)
@ -593,22 +627,22 @@ public class McsService extends Service implements Handler.Callback {
return true;
case MSG_INPUT_ERROR:
case MSG_OUTPUT_ERROR:
logd("I/O error: " + msg.obj);
logd(this, "I/O error: " + msg.obj);
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, msg.obj));
return true;
case MSG_TEARDOWN:
logd("Teardown initiated, reason: " + msg.obj);
logd(this, "Teardown initiated, reason: " + msg.obj);
handleTeardown(msg);
return true;
case MSG_CONNECT:
logd("Connect initiated, reason: " + msg.obj);
if (!isConnected()) {
logd(this, "Connect initiated, reason: " + msg.obj);
if (!isConnected(this)) {
connect();
}
return true;
case MSG_HEARTBEAT:
logd("Heartbeat initiated, reason: " + msg.obj);
if (isConnected()) {
logd(this, "Heartbeat initiated, reason: " + msg.obj);
if (isConnected(this)) {
HeartbeatPing.Builder ping = new HeartbeatPing.Builder();
if (inputStream.newStreamIdAvailable()) {
ping.last_stream_id_received(inputStream.getStreamId());
@ -616,12 +650,28 @@ public class McsService extends Service implements Handler.Callback {
send(MCS_HEARTBEAT_PING_TAG, ping.build());
scheduleHeartbeat(this);
} else {
logd("Ignoring heartbeat, not connected!");
logd(this, "Ignoring heartbeat, not connected!");
scheduleReconnect(this);
}
return true;
case MSG_ACK:
logd(this, "Ack initiated, reason: " + msg.obj);
if (isConnected(this)) {
IqStanza.Builder iq = new IqStanza.Builder()
.type(IqStanza.IqType.SET)
.id("")
.extension(new Extension.Builder().id(13).data(ByteString.EMPTY).build()) // StreamAck
.status(0L);
if (inputStream.newStreamIdAvailable()) {
iq.last_stream_id_received(inputStream.getStreamId());
}
send(MCS_IQ_STANZA_TAG, iq.build());
} else {
logd(this, "Ignoring ack, not connected!");
}
return true;
case MSG_OUTPUT_READY:
logd("Sending login request...");
logd(this, "Sending login request...");
send(MCS_LOGIN_REQUEST_TAG, buildLoginRequest());
return true;
case MSG_OUTPUT_DONE:

View File

@ -28,13 +28,17 @@ import android.os.RemoteException;
import android.util.Log;
import org.microg.gms.checkin.LastCheckinInfo;
import org.microg.gms.common.ForegroundServiceContext;
import org.microg.gms.common.PackageUtils;
import org.microg.gms.common.Utils;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
import static org.microg.gms.gcm.GcmConstants.ERROR_SERVICE_NOT_AVAILABLE;
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_ERROR;
import static org.microg.gms.gcm.McsConstants.ACTION_ACK;
import static org.microg.gms.gcm.McsConstants.ACTION_SEND;
class PushRegisterHandler extends Handler {
private static final String TAG = "GmsGcmRegisterHdl";
@ -54,20 +58,7 @@ class PushRegisterHandler extends Handler {
return super.sendMessageAtTime(msg, uptimeMillis);
}
private void sendReply(int what, int id, Messenger replyTo, Bundle data) {
if (what == 0) {
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
outIntent.putExtras(data);
Message message = Message.obtain();
message.obj = outIntent;
try {
replyTo.send(message);
} catch (RemoteException e) {
Log.w(TAG, e);
}
} else {
Bundle messageData = new Bundle();
messageData.putBundle("data", data);
private void sendReplyViaMessage(int what, int id, Messenger replyTo, Bundle messageData) {
Message response = Message.obtain();
response.what = what;
response.arg1 = id;
@ -78,16 +69,43 @@ class PushRegisterHandler extends Handler {
Log.w(TAG, e);
}
}
private void sendReplyViaIntent(Intent outIntent, Messenger replyTo) {
Message message = Message.obtain();
message.obj = outIntent;
try {
replyTo.send(message);
} catch (RemoteException e) {
Log.w(TAG, e);
}
}
private void replyError(int what, int id, Messenger replyTo, String errorMessage) {
private void sendReply(int what, int id, Messenger replyTo, Bundle data, boolean oneWay) {
if (what == 0) {
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
outIntent.putExtras(data);
sendReplyViaIntent(outIntent, replyTo);
return;
}
Bundle messageData = new Bundle();
messageData.putBundle("data", data);
sendReplyViaMessage(what, id, replyTo, messageData);
}
private void replyError(int what, int id, Messenger replyTo, String errorMessage, boolean oneWay) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_ERROR, errorMessage);
sendReply(what, id, replyTo, bundle);
sendReply(what, id, replyTo, bundle, oneWay);
}
private void replyNotAvailable(int what, int id, Messenger replyTo) {
replyError(what, id, replyTo, ERROR_SERVICE_NOT_AVAILABLE);
replyError(what, id, replyTo, ERROR_SERVICE_NOT_AVAILABLE, false);
}
private PendingIntent getSelfAuthIntent() {
Intent intent = new Intent();
intent.setPackage("com.google.example.invalidpackage");
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
@Override
@ -119,15 +137,9 @@ class PushRegisterHandler extends Handler {
return;
}
Bundle data = msg.getData();
if (data.getBoolean("oneWay", false)) {
Log.w(TAG, "oneWay requested");
return;
}
String packageName = data.getString("pkg");
Bundle subdata = data.getBundle("data");
String sender = subdata.getString("sender");
boolean delete = subdata.get("delete") != null;
try {
PackageUtils.checkPackageUid(context, packageName, callingUid);
@ -136,7 +148,16 @@ class PushRegisterHandler extends Handler {
return;
}
Log.d(TAG, "handleMessage: package=" + packageName + " what=" + what + " id=" + id);
boolean oneWay = data.getBoolean("oneWay", false);
switch (what) {
case 0:
case 1:
// TODO: We should checkin and/or ask for permission here.
String sender = subdata.getString("sender");
boolean delete = subdata.get("delete") != null;
PushRegisterManager.completeRegisterRequest(context, database,
new RegisterRequest()
@ -146,6 +167,27 @@ class PushRegisterHandler extends Handler {
.app(packageName)
.delete(delete)
.extraParams(subdata),
bundle -> sendReply(what, id, replyTo, bundle));
bundle -> sendReply(what, id, replyTo, bundle, oneWay));
break;
case 2:
String messageId = subdata.getString("google.message_id");
Log.d(TAG, "Ack " + messageId + " for " + packageName);
Intent i = new Intent(context, McsService.class);
i.setAction(ACTION_ACK);
i.putExtra(EXTRA_APP, getSelfAuthIntent());
new ForegroundServiceContext(context).startService(i);
break;
default:
Bundle bundle = new Bundle();
bundle.putBoolean("unsupported", true);
sendReplyViaMessage(what, id, replyTo, bundle);
return;
}
if (oneWay) {
Bundle bundle = new Bundle();
bundle.putBoolean("ack", true);
sendReplyViaMessage(what, id, replyTo, bundle);
}
}
}

View File

@ -45,7 +45,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
public synchronized static void register(Context context) {
if (SDK_INT >= N && !registered) {
IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
context.registerReceiver(new TriggerReceiver(), intentFilter);
context.getApplicationContext().registerReceiver(new TriggerReceiver(), intentFilter);
registered = true;
}
}
@ -86,7 +86,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
}
}
if (!McsService.isConnected() || force) {
if (!McsService.isConnected(context) || force) {
Log.d(TAG, "Not connected to GCM but should be, asking the service to start up. Triggered by: " + intent);
startWakefulService(new ForegroundServiceContext(context), new Intent(ACTION_CONNECT, null, context, McsService.class)
.putExtra(EXTRA_REASON, intent));

View File

@ -17,8 +17,10 @@
package org.microg.gms.measurement;
import android.os.RemoteException;
import android.util.Log;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;

View File

@ -20,6 +20,9 @@ import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import com.google.android.gms.measurement.internal.AppMetadata;
import com.google.android.gms.measurement.internal.ConditionalUserPropertyParcel;
import com.google.android.gms.measurement.internal.EventParcel;
import com.google.android.gms.measurement.internal.IMeasurementService;
public class MeasurementServiceImpl extends IMeasurementService.Stub {
@ -31,4 +34,30 @@ public class MeasurementServiceImpl extends IMeasurementService.Stub {
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
return false;
}
@Override
public void f1(EventParcel p0, AppMetadata p1) throws RemoteException {
Log.d(TAG, "f1: " + p1.packageName);
}
@Override
public void f4(AppMetadata p0) throws RemoteException {
Log.d(TAG, "f4: " + p0.packageName);
}
@Override
public void f10(long p0, String p1, String p2, String p3) throws RemoteException {
Log.d(TAG, "f10: " + p1);
}
@Override
public String f11(AppMetadata p0) throws RemoteException {
Log.d(TAG, "f11: " + p0.packageName);
return null;
}
@Override
public void f12(ConditionalUserPropertyParcel p0, AppMetadata p1) throws RemoteException {
Log.d(TAG, "f12: " + p1.packageName);
}
}

View File

@ -46,7 +46,7 @@ public class GcmAdvancedFragment extends ResourceSettingsFragment {
updateContent();
if (newValue.equals("-1") && preference.getKey().equals(McsService.activeNetworkPref)) {
McsService.stop(getContext());
} else if (!McsService.isConnected()) {
} else if (!McsService.isConnected(getContext())) {
getContext().sendBroadcast(new Intent(TriggerReceiver.FORCE_TRY_RECONNECT, null, getContext(), TriggerReceiver.class));
}
return true;

View File

@ -1,78 +0,0 @@
package org.microg.gms.ui;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.navigation.fragment.NavHostFragment;
import androidx.preference.Preference;
import androidx.preference.SwitchPreferenceCompat;
import com.google.android.gms.cast.media.CastMediaRouteProviderService;
import com.mgoogle.android.gms.R;
import org.microg.gms.checkin.CheckinPrefs;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.GcmPrefs;
import org.microg.tools.ui.ResourceSettingsFragment;
public class SettingsFragment extends ResourceSettingsFragment {
public static final String PREF_ABOUT = "pref_about";
public static final String PREF_GCM = "pref_gcm";
public static final String PREF_CHECKIN = "pref_checkin";
public static final String PREF_CAST_DOUBLE_FIX_ENABLED = "pref_cast_double_fix_enabled";
public SettingsFragment() {
preferencesResource = R.xml.preferences_start;
}
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
updateDetails();
}
@Override
public void onResume() {
super.onResume();
updateDetails();
}
private void updateDetails() {
findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext())));
findPreference(PREF_ABOUT).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openAbout, null);
return true;
});
findPreference(PREF_CAST_DOUBLE_FIX_ENABLED).setOnPreferenceChangeListener((preference, newValue) -> {
boolean isEnabled = (boolean) newValue;
getContext().getPackageManager().setComponentEnabledSetting(
new ComponentName(getContext().getApplicationContext(), CastMediaRouteProviderService.class),
isEnabled ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED : PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
return true;
});
if (GcmPrefs.get(getContext()).isEnabled()) {
GcmDatabase database = new GcmDatabase(getContext());
int regCount = database.getRegistrationList().size();
database.close();
findPreference(PREF_GCM).setSummary(getString(R.string.service_status_enabled_short) + " - " + getResources().getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount));
} else {
findPreference(PREF_GCM).setSummary(R.string.service_status_disabled_short);
}
findPreference(PREF_GCM).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openGcmSettings, null);
return true;
});
boolean checkinEnabled = CheckinPrefs.get(getContext()).isEnabled();
findPreference(PREF_CHECKIN).setSummary(checkinEnabled ? R.string.service_status_enabled_short : R.string.service_status_disabled_short);
findPreference(PREF_CHECKIN).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openCheckinSettings, null);
return true;
});
}
}

View File

@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
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 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)
}
}
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)
}
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)
}
}
}
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,92 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.gcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
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) : Serializable
// TODO: Intervals
data class ServiceConfiguration(val enabled: Boolean, val confirmNewApps: Boolean) : Serializable {
fun saveToPrefs(context: Context) {
GcmPrefs.setEnabled(context, enabled)
// TODO: confirm new apps
}
}
private fun GcmPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled, isConfirmNewApps)
class ServiceInfoReceiver : BroadcastReceiver() {
private fun sendInfoResponse(context: Context) {
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
setPackage(context.packageName)
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(GcmPrefs.get(context).toConfiguration(), McsService.isConnected(context), McsService.getStartTimestamp()))
}, 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)
} 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 getGcmServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
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)

View File

@ -13,7 +13,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.mgoogle.android.gms.R
import com.mgoogle.android.gms.databinding.DeviceRegistrationFragmentBinding
import org.microg.gms.checkin.CheckinPrefs
import org.microg.gms.checkin.ServiceInfo
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.checkin.setCheckinServiceConfiguration
class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragment) {
private lateinit var binding: DeviceRegistrationFragmentBinding
@ -22,17 +24,29 @@ class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragmen
binding = DeviceRegistrationFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) {
CheckinPrefs.setEnabled(context, newStatus)
binding.checkinEnabled = newStatus
setEnabled(newStatus)
}
}
return binding.root
}
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getCheckinServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setCheckinServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.checkinEnabled = serviceInfo.configuration.enabled
}
override fun onResume() {
super.onResume()
lifecycleScope.launchWhenResumed {
binding.checkinEnabled = CheckinPrefs.get(context).isEnabled
displayServiceInfo(getCheckinServiceInfo(requireContext()))
}
}
}

View File

@ -8,16 +8,17 @@ package org.microg.gms.ui
import android.os.Bundle
import android.os.Handler
import android.text.format.DateUtils
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import com.mgoogle.android.gms.R
import org.microg.gms.checkin.CheckinPrefs
import org.microg.gms.checkin.LastCheckinInfo
import org.microg.gms.checkin.getCheckinServiceInfo
class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var statusCategory: PreferenceCategory
private lateinit var status: Preference
private lateinit var androidId: Preference
private val handler = Handler()
private val updateRunnable = Runnable { updateStatus() }
@ -28,6 +29,7 @@ class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
override fun onBindPreferences() {
statusCategory = preferenceScreen.findPreference("prefcat_device_registration_status") ?: statusCategory
status = preferenceScreen.findPreference("pref_device_registration_status") ?: status
androidId = preferenceScreen.findPreference("pref_device_registration_android_id") ?: androidId
}
override fun onResume() {
@ -42,12 +44,17 @@ class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() {
handler.postDelayed(updateRunnable, UPDATE_INTERVAL)
statusCategory.isVisible = CheckinPrefs.get(context).isEnabled
val checkinInfo = LastCheckinInfo.read(requireContext())
status.summary = if (checkinInfo.lastCheckin > 0) {
getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(checkinInfo.lastCheckin, System.currentTimeMillis(), 0))
lifecycleScope.launchWhenResumed {
val serviceInfo = getCheckinServiceInfo(requireContext())
statusCategory.isVisible = serviceInfo.configuration.enabled
if (serviceInfo.lastCheckin > 0) {
status.summary = getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0))
androidId.isVisible = true
androidId.summary = serviceInfo.androidId.toString(16)
} else {
getString(R.string.checkin_not_registered)
status.summary = getString(R.string.checkin_not_registered)
androidId.isVisible = false
}
}
}

View File

@ -11,8 +11,10 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.mgoogle.android.gms.R
import com.mgoogle.android.gms.databinding.PushNotificationFragmentBinding
import org.microg.gms.checkin.CheckinPrefs
import org.microg.gms.gcm.GcmPrefs
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.gcm.ServiceInfo
import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.gms.gcm.setGcmServiceConfiguration
class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
lateinit var binding: PushNotificationFragmentBinding
@ -21,18 +23,29 @@ class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
binding = PushNotificationFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) {
GcmPrefs.setEnabled(context, newStatus)
binding.gcmEnabled = newStatus
setEnabled(newStatus)
}
}
return binding.root
}
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getGcmServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setGcmServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.gcmEnabled = serviceInfo.configuration.enabled
}
override fun onResume() {
super.onResume()
lifecycleScope.launchWhenResumed {
binding.gcmEnabled = GcmPrefs.get(context).isEnabled
binding.checkinEnabled = CheckinPrefs.get(context).isEnabled
displayServiceInfo(getGcmServiceInfo(requireContext()))
binding.checkinEnabled = getCheckinServiceInfo(requireContext()).configuration.enabled
}
}

View File

@ -18,8 +18,7 @@ import com.mgoogle.android.gms.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.microg.gms.gcm.GcmDatabase
import org.microg.gms.gcm.GcmPrefs
import org.microg.gms.gcm.McsService
import org.microg.gms.gcm.getGcmServiceInfo
class PushNotificationPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var pushStatusCategory: PreferenceCategory
@ -66,13 +65,16 @@ class PushNotificationPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() {
handler.postDelayed(updateRunnable, UPDATE_INTERVAL)
pushStatusCategory.isVisible = GcmPrefs.get(context).isEnabled
pushStatus.summary = if (McsService.isConnected()) {
getString(R.string.gcm_network_state_connected, DateUtils.getRelativeTimeSpanString(McsService.getStartTimestamp(), System.currentTimeMillis(), 0))
lifecycleScope.launchWhenStarted {
val statusInfo = getGcmServiceInfo(requireContext())
pushStatusCategory.isVisible = statusInfo.configuration.enabled
pushStatus.summary = if (statusInfo != null && statusInfo.connected) {
getString(R.string.gcm_network_state_connected, DateUtils.getRelativeTimeSpanString(statusInfo.startTimestamp, System.currentTimeMillis(), 0))
} else {
getString(R.string.gcm_network_state_disconnected)
}
}
}
private fun updateContent() {
lifecycleScope.launchWhenResumed {

View File

@ -0,0 +1,67 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.ui
import android.os.Build
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import com.mgoogle.android.gms.R
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.gcm.GcmDatabase
import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.tools.ui.ResourceSettingsFragment
class SettingsFragment : ResourceSettingsFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
super.onCreatePreferences(savedInstanceState, rootKey)
findPreference<Preference>(PREF_CHECKIN)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openCheckinSettings)
true
}
findPreference<Preference>(PREF_GCM)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openGcmSettings)
true
}
findPreference<Preference>(PREF_ABOUT)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openAbout)
true
}
findPreference<Preference>(PREF_ABOUT)!!.summary = getString(R.string.about_version_str, AboutFragment.getSelfVersion(context))
}
override fun onResume() {
super.onResume()
lifecycleScope.launchWhenResumed {
updateDetails()
}
}
private suspend fun updateDetails() {
if (getGcmServiceInfo(requireContext()).configuration.enabled) {
val database = GcmDatabase(context)
val regCount = database.registrationList.size
database.close()
findPreference<Preference>(PREF_GCM)!!.summary = getString(R.string.service_status_enabled_short) + " - " + resources.getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount)
} else {
findPreference<Preference>(PREF_GCM)!!.setSummary(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)
}
companion object {
const val PREF_ABOUT = "pref_about"
const val PREF_GCM = "pref_gcm"
const val PREF_CHECKIN = "pref_checkin"
}
init {
preferencesResource = R.xml.preferences_start
}
}

View File

@ -107,6 +107,7 @@ Questo potrà richiedere un paio di minuti"</string>
<string name="pref_switcher_title">Registra dispositivo</string>
<string name="pref_checkin_enable_summary">Registra il tuo dispositivo sui servizi Google e crea un identificatore univoco del dispositivo. Verranno rimossi dai Servizi Vanced microG alcuni bit utili per identificare i dati di registrazione, oltre al nome dell\'account Google.</string>
<string name="pref_device_registration_android_id">ID Android</string>
<string name="pref_info_status">Stato</string>
<string name="gcm_status_pref_auto">Automatico</string>

View File

@ -107,6 +107,7 @@ This can take a couple of minutes."</string>
<string name="pref_switcher_title">Register device</string>
<string name="pref_checkin_enable_summary">Registers your device to Google services and creates a unique device identifier. Vanced microG strips identifying bits other than your Google account name from registration data.</string>
<string name="pref_device_registration_android_id">Android ID</string>
<string name="pref_info_status">Status</string>
<string name="gcm_status_pref_auto">Automatic</string>

View File

@ -14,6 +14,11 @@
android:selectable="false"
android:title="@string/pref_info_status"
tools:summary="Last registration: 13 hours ago" />
<Preference
android:key="pref_device_registration_android_id"
android:selectable="false"
android:title="@string/pref_device_registration_android_id"
tools:summary="1953a59d1c1b7e4b" />
</PreferenceCategory>
<PreferenceCategory android:layout="@layout/preference_category_no_label">
<Preference