diff --git a/build.gradle b/build.gradle index 6ab8cd6b..05e72a1f 100644 --- a/build.gradle +++ b/build.gradle @@ -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 } diff --git a/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/AppMetadata.aidl b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/AppMetadata.aidl new file mode 100644 index 00000000..005edc39 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/AppMetadata.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.measurement.internal; + +parcelable AppMetadata; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/ConditionalUserPropertyParcel.aidl b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/ConditionalUserPropertyParcel.aidl new file mode 100644 index 00000000..de59ae57 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/ConditionalUserPropertyParcel.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.measurement.internal; + +parcelable ConditionalUserPropertyParcel; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/EventParcel.aidl b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/EventParcel.aidl new file mode 100644 index 00000000..372d66f6 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/EventParcel.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.measurement.internal; + +parcelable EventParcel; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/IMeasurementService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/IMeasurementService.aidl index 692d414a..8a99173a 100644 --- a/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/IMeasurementService.aidl +++ b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/IMeasurementService.aidl @@ -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; -} \ No newline at end of file +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 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 zza(String p0, String p1, boolean p2, AppMetadata p3) = 13; +// List zza(String p0, String p1, String p2, boolean p3) = 14; +// List zza(String p0, String p1, AppMetadata p2) = 15; +// List zza(String p0, String p1, String p2) = 16; +// void zzd(AppMetadata p0) = 17; +// void zza(Bundle p0, AppMetadata p1) = 18; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/UserAttributeParcel.aidl b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/UserAttributeParcel.aidl new file mode 100644 index 00000000..efb3b721 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/measurement/internal/UserAttributeParcel.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.measurement.internal; + +parcelable UserAttributeParcel; diff --git a/play-services-api/src/main/java/com/google/android/gms/measurement/internal/AppMetadata.java b/play-services-api/src/main/java/com/google/android/gms/measurement/internal/AppMetadata.java new file mode 100644 index 00000000..754f31b5 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/measurement/internal/AppMetadata.java @@ -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 CREATOR = new AutoCreator<>(AppMetadata.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/measurement/internal/ConditionalUserPropertyParcel.java b/play-services-api/src/main/java/com/google/android/gms/measurement/internal/ConditionalUserPropertyParcel.java new file mode 100644 index 00000000..e0594ca8 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/measurement/internal/ConditionalUserPropertyParcel.java @@ -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 CREATOR = new AutoCreator<>(ConditionalUserPropertyParcel.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/measurement/internal/EventParcel.java b/play-services-api/src/main/java/com/google/android/gms/measurement/internal/EventParcel.java new file mode 100644 index 00000000..3ff7c796 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/measurement/internal/EventParcel.java @@ -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 CREATOR = new AutoCreator<>(EventParcel.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/measurement/internal/UserAttributeParcel.java b/play-services-api/src/main/java/com/google/android/gms/measurement/internal/UserAttributeParcel.java new file mode 100644 index 00000000..6070b923 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/measurement/internal/UserAttributeParcel.java @@ -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 CREATOR = new AutoCreator<>(UserAttributeParcel.class); +} diff --git a/play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java b/play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java index 1a019230..8ca5c490 100644 --- a/play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java +++ b/play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java @@ -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"; diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 08e23664..c09e7e2e 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -110,6 +110,8 @@ + + @@ -131,8 +133,7 @@ + android:name="org.microg.gms.gcm.PushRegisterService"> @@ -150,13 +151,14 @@ + android:name="org.microg.gms.gcm.SendReceiver"> + + @@ -209,7 +211,8 @@ + android:exported="true" + android:process=":ui"> @@ -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"> @@ -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" /> + android:exported="true" + android:process=":ui" /> @@ -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"> @@ -296,21 +304,22 @@ + android:process=":ui" /> + android:process=":ui" /> + android:process=":ui"> diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinPrefs.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinPrefs.java index 76309ade..a4de2630 100755 --- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinPrefs.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinPrefs.java @@ -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 diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/LastCheckinInfo.java b/play-services-core/src/main/java/org/microg/gms/checkin/LastCheckinInfo.java index 4cdfd88c..30e0a1af 100755 --- a/play-services-core/src/main/java/org/microg/gms/checkin/LastCheckinInfo.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/LastCheckinInfo.java @@ -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(); } } diff --git a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java index 40aa86ca..f726bcec 100644 --- a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java +++ b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java @@ -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 { diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java index 3dd67b53..bc524047 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java @@ -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 { diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java index 8b580504..246ceb5b 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java @@ -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(); } } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java index 300dac0c..0b3108dd 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java @@ -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"; } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java index acbc1de3..8142adfd 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java @@ -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,12 +345,20 @@ 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 { - ttl = Integer.parseInt(intent.getStringExtra(EXTRA_TTL)); - if (ttl < 0 || ttl > maxTtl) { + 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) { @@ -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,41 +535,52 @@ 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); - receiverPermission = name; - } catch (PackageManager.NameNotFoundException e) { - receiverPermission = null; + PermissionInfo info = getPackageManager().getPermissionInfo(name, 0); + if (info.packageName.equals(packageName)) { + receiverPermission = name; + } + } catch (Exception ignored) { + // Keep null, no valid permission found } - List infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER); - if (infos == null || infos.isEmpty()) { - logd("No target for message, wut?"); + 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 { - for (ResolveInfo resolveInfo : infos) { - logd("Target: " + resolveInfo); - Intent targetIntent = new Intent(intent); - 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"); - addPowerSaveTempWhitelistAppMethod.invoke(deviceIdleController, packageName, 10000, userId, "GCM Push"); + List infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER); + if (infos == null || infos.isEmpty()) { + logd(this, "No target for message, wut?"); + } else { + for (ResolveInfo resolveInfo : infos) { + 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(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); + } } - } catch (Exception e) { - Log.w(TAG, e); + // We don't need receiver permission for our own package + logd(this, "Deliver message to own receiver " + resolveInfo); + sendOrderedBroadcast(targetIntent, null); + } else if (resolveInfo.filter.hasCategory(packageName)) { + // Permission required + logd(this, "Deliver message to third-party receiver (with permission check)" + resolveInfo); + sendOrderedBroadcast(targetIntent, receiverPermission); } } - targetIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)); - if (resolveInfo.activityInfo.packageName.equals(packageName)) { - sendOrderedBroadcast(targetIntent, null); - } else if (receiverPermission != null) { - 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: diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java index e95c98f0..15aaecc1 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java @@ -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,40 +58,54 @@ 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); - Message response = Message.obtain(); - response.what = what; - response.arg1 = id; - response.setData(messageData); - try { - replyTo.send(response); - } catch (RemoteException e) { - Log.w(TAG, e); - } + private void sendReplyViaMessage(int what, int id, Messenger replyTo, Bundle messageData) { + Message response = Message.obtain(); + response.what = what; + response.arg1 = id; + response.setData(messageData); + try { + replyTo.send(response); + } catch (RemoteException e) { + Log.w(TAG, e); } } - private void replyError(int what, int id, Messenger replyTo, String errorMessage) { + 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 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,16 +148,46 @@ class PushRegisterHandler extends Handler { return; } - // TODO: We should checkin and/or ask for permission here. + Log.d(TAG, "handleMessage: package=" + packageName + " what=" + what + " id=" + id); - PushRegisterManager.completeRegisterRequest(context, database, - new RegisterRequest() - .build(Utils.getBuild(context)) - .sender(sender) - .checkin(LastCheckinInfo.read(context)) - .app(packageName) - .delete(delete) - .extraParams(subdata), - bundle -> sendReply(what, id, replyTo, bundle)); + 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() + .build(Utils.getBuild(context)) + .sender(sender) + .checkin(LastCheckinInfo.read(context)) + .app(packageName) + .delete(delete) + .extraParams(subdata), + 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); + } } } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java index 2d12deee..86abd04b 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java @@ -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)); diff --git a/play-services-core/src/main/java/org/microg/gms/measurement/MeasurementBrokerService.java b/play-services-core/src/main/java/org/microg/gms/measurement/MeasurementBrokerService.java index 04495420..4cd9a0f8 100644 --- a/play-services-core/src/main/java/org/microg/gms/measurement/MeasurementBrokerService.java +++ b/play-services-core/src/main/java/org/microg/gms/measurement/MeasurementBrokerService.java @@ -1,37 +1,39 @@ -/* - * Copyright (C) 2018 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. - */ +/* + * Copyright (C) 2018 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.measurement; +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; +import com.google.android.gms.common.internal.IGmsCallbacks; -import org.microg.gms.BaseService; -import org.microg.gms.common.GmsService; +import org.microg.gms.BaseService; +import org.microg.gms.common.GmsService; -public class MeasurementBrokerService extends BaseService { - public MeasurementBrokerService() { - super("GmsMeasureBrokerSvc", GmsService.MEASUREMENT); - } +public class MeasurementBrokerService extends BaseService { + public MeasurementBrokerService() { + super("GmsMeasureBrokerSvc", GmsService.MEASUREMENT); + } - @Override - public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException { - callback.onPostInitComplete(CommonStatusCodes.SUCCESS, new MeasurementServiceImpl().asBinder(), null); - } -} + @Override + public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException { + callback.onPostInitComplete(CommonStatusCodes.SUCCESS, new MeasurementServiceImpl().asBinder(), null); + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/measurement/MeasurementServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/measurement/MeasurementServiceImpl.java index bba81666..b6fbb62b 100644 --- a/play-services-core/src/main/java/org/microg/gms/measurement/MeasurementServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/measurement/MeasurementServiceImpl.java @@ -1,34 +1,63 @@ -/* - * Copyright (C) 2018 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. - */ +/* + * Copyright (C) 2018 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.measurement; +package org.microg.gms.measurement; -import android.os.Parcel; -import android.os.RemoteException; -import android.util.Log; +import android.os.Parcel; +import android.os.RemoteException; +import android.util.Log; -import com.google.android.gms.measurement.internal.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; +import com.google.android.gms.measurement.internal.IMeasurementService; -public class MeasurementServiceImpl extends IMeasurementService.Stub { - private static final String TAG = "GmsMeasureSvcImpl"; +public class MeasurementServiceImpl extends IMeasurementService.Stub { + private static final String TAG = "GmsMeasureSvcImpl"; - @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { - if (super.onTransact(code, data, reply, flags)) return true; - Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags); - return false; - } -} + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { + if (super.onTransact(code, data, reply, flags)) return true; + 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); + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/ui/GcmAdvancedFragment.java b/play-services-core/src/main/java/org/microg/gms/ui/GcmAdvancedFragment.java index a7d76f88..a7ff1080 100755 --- a/play-services-core/src/main/java/org/microg/gms/ui/GcmAdvancedFragment.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/GcmAdvancedFragment.java @@ -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; diff --git a/play-services-core/src/main/java/org/microg/gms/ui/SettingsFragment.java b/play-services-core/src/main/java/org/microg/gms/ui/SettingsFragment.java deleted file mode 100755 index f91de00f..00000000 --- a/play-services-core/src/main/java/org/microg/gms/ui/SettingsFragment.java +++ /dev/null @@ -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; - }); - } -} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt b/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt new file mode 100644 index 00000000..d8902b8f --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt @@ -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) diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gcm/ServiceInfo.kt b/play-services-core/src/main/kotlin/org/microg/gms/gcm/ServiceInfo.kt new file mode 100644 index 00000000..576a01f0 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/gcm/ServiceInfo.kt @@ -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) \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt index 74c4eb9b..d0c3f0ed 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt @@ -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())) } } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationPreferencesFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationPreferencesFragment.kt index 64a486ae..1dd406a1 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationPreferencesFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationPreferencesFragment.kt @@ -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)) - } else { - getString(R.string.checkin_not_registered) + 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 { + status.summary = getString(R.string.checkin_not_registered) + androidId.isVisible = false + } } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt index e98cf1da..0a490bd2 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt @@ -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 } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationPreferencesFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationPreferencesFragment.kt index e5b71f6e..b4b80167 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationPreferencesFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationPreferencesFragment.kt @@ -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,11 +65,14 @@ 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)) - } else { - getString(R.string.gcm_network_state_disconnected) + 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) + } } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt new file mode 100644 index 00000000..3f2f26e9 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt @@ -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(PREF_CHECKIN)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + findNavController().navigate(requireContext(), R.id.openCheckinSettings) + true + } + findPreference(PREF_GCM)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + findNavController().navigate(requireContext(), R.id.openGcmSettings) + true + } + findPreference(PREF_ABOUT)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + findNavController().navigate(requireContext(), R.id.openAbout) + true + } + findPreference(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(PREF_GCM)!!.summary = getString(R.string.service_status_enabled_short) + " - " + resources.getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount) + } else { + findPreference(PREF_GCM)!!.setSummary(R.string.service_status_disabled_short) + } + + findPreference(PREF_CHECKIN)!!.setSummary(if (getCheckinServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short) + } + + 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 + } +} diff --git a/play-services-core/src/main/res/values-it/strings.xml b/play-services-core/src/main/res/values-it/strings.xml index b0cedb74..b810c7f6 100755 --- a/play-services-core/src/main/res/values-it/strings.xml +++ b/play-services-core/src/main/res/values-it/strings.xml @@ -107,6 +107,7 @@ Questo potrĂ  richiedere un paio di minuti" Registra dispositivo 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. + ID Android Stato Automatico diff --git a/play-services-core/src/main/res/values/strings.xml b/play-services-core/src/main/res/values/strings.xml index 4b784ce6..0a8b25fa 100755 --- a/play-services-core/src/main/res/values/strings.xml +++ b/play-services-core/src/main/res/values/strings.xml @@ -107,6 +107,7 @@ This can take a couple of minutes." Register device 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. + Android ID Status Automatic diff --git a/play-services-core/src/main/res/xml/preferences_device_registration.xml b/play-services-core/src/main/res/xml/preferences_device_registration.xml index 92c2023f..d245d19c 100755 --- a/play-services-core/src/main/res/xml/preferences_device_registration.xml +++ b/play-services-core/src/main/res/xml/preferences_device_registration.xml @@ -14,6 +14,11 @@ android:selectable="false" android:title="@string/pref_info_status" tools:summary="Last registration: 13 hours ago" /> +