mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2024-12-24 03:35:50 +01:00
0.2.12.203315 Upstream
This commit is contained in:
parent
0a4fd6158a
commit
3f3b4ca69f
@ -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
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
package com.google.android.gms.measurement.internal;
|
||||
|
||||
parcelable AppMetadata;
|
@ -0,0 +1,3 @@
|
||||
package com.google.android.gms.measurement.internal;
|
||||
|
||||
parcelable ConditionalUserPropertyParcel;
|
@ -0,0 +1,3 @@
|
||||
package com.google.android.gms.measurement.internal;
|
||||
|
||||
parcelable EventParcel;
|
@ -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;
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
package com.google.android.gms.measurement.internal;
|
||||
|
||||
parcelable UserAttributeParcel;
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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";
|
||||
|
@ -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" />
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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<ResolveInfo> 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<ResolveInfo> 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:
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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)
|
@ -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)
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user