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 41c54c0f..a0e47a05 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 @@ -18,7 +18,10 @@ package org.microg.gms.gcm; import android.content.Context; import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.preference.PreferenceManager; +import android.util.Log; import java.util.Arrays; import java.util.Collections; @@ -31,6 +34,15 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe public static final String PREF_CONFIRM_NEW_APPS = "gcm_confirm_new_apps"; public static final String PREF_ENABLE_GCM = "gcm_enable_mcs_service"; + public static final String PREF_NETWORK_MOBILE = "gcm_network_mobile"; + public static final String PREF_NETWORK_WIFI = "gcm_network_wifi"; + public static final String PREF_NETWORK_ROAMING = "gcm_network_roaming"; + public static final String PREF_NETWORK_OTHER = "gcm_network_other"; + + public static final String PREF_LEARNT_MOBILE = "gcm_learnt_mobile"; + public static final String PREF_LEARNT_WIFI = "gcm_learnt_wifi"; + public static final String PREF_LEARNT_OTHER = "gcm_learnt_other"; + private static GcmPrefs INSTANCE; public static GcmPrefs get(Context context) { @@ -47,6 +59,15 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe private boolean confirmNewApps = false; private boolean gcmEnabled = false; + private int networkMobile = 0; + private int networkWifi = 0; + private int networkRoaming = 0; + private int networkOther = 0; + + private int learntWifi = 300000; + private int learntMobile = 300000; + private int learntOther = 300000; + private SharedPreferences defaultPreferences; private GcmPrefs(Context context) { @@ -63,21 +84,115 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe lastPersistedId = defaultPreferences.getString(PREF_LAST_PERSISTENT_ID, ""); confirmNewApps = defaultPreferences.getBoolean(PREF_CONFIRM_NEW_APPS, false); gcmEnabled = defaultPreferences.getBoolean(PREF_ENABLE_GCM, 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")); + + learntMobile = defaultPreferences.getInt(PREF_LEARNT_MOBILE, 300000); + learntWifi = defaultPreferences.getInt(PREF_LEARNT_WIFI, 300000); + learntOther = defaultPreferences.getInt(PREF_LEARNT_OTHER, 300000); } public int getHeartbeatMs() { return heartbeatMs; } + public String getNetworkPrefForInfo(NetworkInfo info) { + if (info.isRoaming()) return PREF_NETWORK_ROAMING; + switch (info.getType()) { + case ConnectivityManager.TYPE_MOBILE: + return PREF_NETWORK_MOBILE; + case ConnectivityManager.TYPE_WIFI: + return PREF_NETWORK_WIFI; + default: + return PREF_NETWORK_OTHER; + } + } + + public int getHeartbeatMsFor(NetworkInfo info) { + return getHeartbeatMsFor(getNetworkPrefForInfo(info), false); + } + + public int getHeartbeatMsFor(String pref, boolean rawRoaming) { + if (PREF_NETWORK_ROAMING.equals(pref) && (rawRoaming || networkRoaming != 0)) { + return networkRoaming * 6000; + } else if (PREF_NETWORK_MOBILE.equals(pref)) { + if (networkMobile != 0) return networkMobile * 60000; + else return learntMobile; + } else if (PREF_NETWORK_WIFI.equals(pref)) { + if (networkWifi != 0) return networkWifi * 60000; + else return learntWifi; + } else { + if (networkOther != 0) return networkOther * 60000; + else return learntOther; + } + } + + public int getNetworkValue(String pref) { + switch (pref) { + case PREF_NETWORK_MOBILE: + return networkMobile; + case PREF_NETWORK_ROAMING: + return networkRoaming; + case PREF_NETWORK_WIFI: + return networkWifi; + default: + return networkOther; + } + } + + public void learnTimeout(String pref) { + Log.d("GmsGcmPrefs", "learnTimeout: " + pref); + switch (pref) { + case PREF_NETWORK_MOBILE: + case PREF_NETWORK_ROAMING: + learntMobile *= 0.95; + break; + case PREF_NETWORK_WIFI: + learntWifi *= 0.95; + break; + default: + learntOther *= 0.95; + break; + } + defaultPreferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply(); + } + + public void learnReached(String pref, long time) { + Log.d("GmsGcmPrefs", "learnReached: " + pref + " / " + time); + switch (pref) { + case PREF_NETWORK_MOBILE: + case PREF_NETWORK_ROAMING: + if (time > learntMobile / 4 * 3) + learntMobile *= 1.02; + break; + case PREF_NETWORK_WIFI: + if (time > learntWifi / 4 * 3) + learntWifi *= 1.02; + break; + default: + if (time > learntOther / 4 * 3) + learntOther *= 1.02; + break; + } + defaultPreferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply(); + } + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { update(); } - public boolean isGcmEnabled() { + public boolean isEnabled() { return gcmEnabled; } + public boolean isEnabledFor(NetworkInfo info) { + return isEnabled() && getHeartbeatMsFor(info) >= 0; + } + public boolean isGcmLogEnabled() { return gcmLogEnabled; } 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 91cb9824..96a9fa68 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 @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.net.ConnectivityManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -105,7 +106,9 @@ public class McsService extends Service implements Handler.Callback { private static final int WAKELOCK_TIMEOUT = 5000; private static long lastHeartbeatAckElapsedRealtime = -1; + private static long lastIncomingNetworkRealtime = 0; private static long startTimestamp = 0; + public static String activeNetworkPref = null; private static Socket sslSocket; private static McsInputStream inputStream; @@ -194,8 +197,12 @@ public class McsService extends Service implements Handler.Callback { return false; } // consider connection to be dead if we did not receive an ack within twice the heartbeat interval - if (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime > 2 * GcmPrefs.get(null).getHeartbeatMs()) { - logd("No heartbeat for " + (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime) / 1000 + " seconds, connection assumed to be dead after " + 2 * GcmPrefs.get(null).getHeartbeatMs() / 1000 + " seconds"); + int heartbeatMs = GcmPrefs.get(null).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); return false; } return true; @@ -215,15 +222,18 @@ public class McsService extends Service implements Handler.Callback { public void scheduleHeartbeat(Context context) { AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE); - logd("Scheduling heartbeat in " + GcmPrefs.get(this).getHeartbeatMs() / 1000 + " seconds..."); - int heartbeatMs = GcmPrefs.get(this).getHeartbeatMs(); + int heartbeatMs = GcmPrefs.get(this).getHeartbeatMsFor(activeNetworkPref, false); + if (heartbeatMs < 0) { + closeAll(); + } + logd("Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds..."); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { alarmManager.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent); } else { // with KitKat, the alarms become inexact by default, but with the newly available setWindow we can get inexact alarms with guarantees. - // Schedule the alarm to fire within the interval [heartbeatMs/2, heartbeatMs] - alarmManager.setWindow(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs / 2, heartbeatMs / 2, + // Schedule the alarm to fire within the interval [heartbeatMs/3*4, heartbeatMs] + alarmManager.setWindow(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs / 4 * 3, heartbeatMs / 4, heartbeatIntent); } @@ -356,6 +366,13 @@ public class McsService extends Service implements Handler.Callback { private synchronized void connect() { try { closeAll(); + ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + activeNetworkPref = GcmPrefs.get(this).getNetworkPrefForInfo(cm.getActiveNetworkInfo()); + if (!GcmPrefs.get(this).isEnabledFor(cm.getActiveNetworkInfo())) { + scheduleReconnect(this); + return; + } + logd("Starting MCS connection..."); Socket socket = new Socket(SERVICE_HOST, SERVICE_PORT); logd("Connected to " + SERVICE_HOST + ":" + SERVICE_PORT); @@ -368,6 +385,7 @@ public class McsService extends Service implements Handler.Callback { startTimestamp = System.currentTimeMillis(); lastHeartbeatAckElapsedRealtime = SystemClock.elapsedRealtime(); + lastIncomingNetworkRealtime = SystemClock.elapsedRealtime(); scheduleHeartbeat(this); } catch (Exception e) { Log.w(TAG, "Exception while connecting!", e); @@ -409,6 +427,7 @@ public class McsService extends Service implements Handler.Callback { } private void handleHeartbeatAck(HeartbeatAck ack) { + GcmPrefs.get(this).learnReached(activeNetworkPref, SystemClock.elapsedRealtime() - lastIncomingNetworkRealtime); lastHeartbeatAckElapsedRealtime = SystemClock.elapsedRealtime(); wakeLock.release(); } @@ -573,6 +592,7 @@ public class McsService extends Service implements Handler.Callback { Log.w(TAG, "Unknown message: " + message); } resetCurrentDelay(); + lastIncomingNetworkRealtime = SystemClock.elapsedRealtime(); } catch (Exception e) { rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, e)); } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java index 66b1562b..b1d749b1 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java @@ -101,7 +101,7 @@ public class PushRegisterService extends IntentService { protected void onHandleIntent(Intent intent) { Log.d(TAG, "onHandleIntent: " + intent); Log.d(TAG, "onHandleIntent: " + intent.getExtras()); - if (GcmPrefs.get(this).isGcmEnabled()) { + if (GcmPrefs.get(this).isEnabled()) { if (LastCheckinInfo.read(this).lastCheckin > 0) { try { if (ACTION_C2DM_UNREGISTER.equals(intent.getAction()) || 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 5d572240..9828c2be 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 @@ -34,6 +34,7 @@ import static org.microg.gms.gcm.McsConstants.EXTRA_REASON; public class TriggerReceiver extends WakefulBroadcastReceiver { private static final String TAG = "GmsGcmTrigger"; + public static final String FORCE_TRY_RECONNECT = "org.microg.gms.gcm.FORCE_TRY_RECONNECT"; private static boolean registered = false; /** @@ -50,40 +51,51 @@ public class TriggerReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()); + boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()) || FORCE_TRY_RECONNECT.equals(intent.getAction()); ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (GcmPrefs.get(context).isGcmEnabled() || force) { - if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { - McsService.resetCurrentDelay(); - } - if (LastCheckinInfo.read(context).androidId == 0) { - Log.d(TAG, "Ignoring " + intent + ": need to checkin first."); - return; - } + if (!GcmPrefs.get(context).isEnabled() && !force) { + Log.d(TAG, "Ignoring " + intent + ": gcm is disabled"); + return; + } - NetworkInfo networkInfo = cm.getActiveNetworkInfo(); - if (networkInfo != null && networkInfo.isConnected() || force || "android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) { - if (!McsService.isConnected() || force) { - Log.d(TAG, "Not connected to GCM but should be, asking the service to start up. Triggered by: " + intent); - startWakefulService(context, new Intent(ACTION_CONNECT, null, context, McsService.class) - .putExtra(EXTRA_REASON, intent)); - } else { - if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { - Log.d(TAG, "Ignoring " + intent + ": service is running. schedule reconnect instead."); - McsService.scheduleReconnect(context); - } else { - Log.d(TAG, "Ignoring " + intent + ": service is running. heartbeat instead."); - startWakefulService(context, new Intent(ACTION_HEARTBEAT, null, context, McsService.class) - .putExtra(EXTRA_REASON, intent)); - } - } - } else { + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { + McsService.resetCurrentDelay(); + } + + if (LastCheckinInfo.read(context).androidId == 0) { + Log.d(TAG, "Ignoring " + intent + ": need to checkin first."); + return; + } + + force |= "android.intent.action.BOOT_COMPLETED".equals(intent.getAction()); + + NetworkInfo networkInfo = cm.getActiveNetworkInfo(); + + if (!force) { + if (networkInfo == null || !networkInfo.isConnected()) { Log.d(TAG, "Ignoring " + intent + ": network is offline, scheduling new attempt."); McsService.scheduleReconnect(context); + return; + } else if (!GcmPrefs.get(context).isEnabledFor(networkInfo)) { + Log.d(TAG, "Ignoring " + intent + ": gcm is disabled for " + networkInfo.getTypeName()); + return; } + } + + if (!McsService.isConnected() || force) { + Log.d(TAG, "Not connected to GCM but should be, asking the service to start up. Triggered by: " + intent); + startWakefulService(context, new Intent(ACTION_CONNECT, null, context, McsService.class) + .putExtra(EXTRA_REASON, intent)); } else { - Log.d(TAG, "Ignoring " + intent + ": gcm is disabled"); + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { + Log.d(TAG, "Ignoring " + intent + ": service is running. schedule reconnect instead."); + McsService.scheduleReconnect(context); + } else { + Log.d(TAG, "Ignoring " + intent + ": service is running. heartbeat instead."); + startWakefulService(context, new Intent(ACTION_HEARTBEAT, null, context, McsService.class) + .putExtra(EXTRA_REASON, intent)); + } } } diff --git a/play-services-core/src/main/java/org/microg/gms/ui/Conditions.java b/play-services-core/src/main/java/org/microg/gms/ui/Conditions.java index e51151ad..5a8babde 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/Conditions.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/Conditions.java @@ -46,7 +46,7 @@ public class Conditions { @Override public boolean isActive(Context context) { if (SDK_INT < 23) return false; - if (!GcmPrefs.get(context).isGcmEnabled()) return false; + if (!GcmPrefs.get(context).isEnabled()) return false; PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); return !pm.isIgnoringBatteryOptimizations(context.getPackageName()); } 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 92771e87..64a1a518 100644 --- 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 @@ -16,19 +16,84 @@ package org.microg.gms.ui; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import android.support.v7.preference.Preference; import com.google.android.gms.R; +import org.microg.gms.gcm.GcmPrefs; +import org.microg.gms.gcm.McsService; +import org.microg.gms.gcm.TriggerReceiver; import org.microg.tools.ui.AbstractSettingsActivity; import org.microg.tools.ui.ResourceSettingsFragment; +import java.util.Objects; + public class GcmAdvancedFragment extends ResourceSettingsFragment { + private static String[] HEARTBEAT_PREFS = new String[]{GcmPrefs.PREF_NETWORK_MOBILE, GcmPrefs.PREF_NETWORK_ROAMING, GcmPrefs.PREF_NETWORK_WIFI, GcmPrefs.PREF_NETWORK_OTHER}; + public GcmAdvancedFragment() { preferencesResource = R.xml.preferences_gcm_advanced; } + @Override + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { + super.onCreatePreferencesFix(savedInstanceState, rootKey); + for (String pref : HEARTBEAT_PREFS) { + findPreference(pref).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + getPreferenceManager().getSharedPreferences().edit().putString(preference.getKey(), (String) newValue).apply(); + updateContent(); + if (newValue.equals("-1") && preference.getKey().equals(McsService.activeNetworkPref)) { + McsService.stop(getContext()); + } else if (!McsService.isConnected()) { + getContext().sendBroadcast(new Intent(TriggerReceiver.FORCE_TRY_RECONNECT, null, getContext(), TriggerReceiver.class)); + } + return true; + } + }); + } + updateContent(); + } + + @Override + public void onResume() { + super.onResume(); + updateContent(); + } + + private void updateContent() { + GcmPrefs prefs = GcmPrefs.get(getContext()); + for (String pref : HEARTBEAT_PREFS) { + Preference preference = findPreference(pref); + int state = prefs.getNetworkValue(pref); + if (state == 0) { + int heartbeat = prefs.getHeartbeatMsFor(preference.getKey(), true); + if (heartbeat == 0) { + preference.setSummary("ON / Automatic"); + } else { + preference.setSummary("ON / Automatic: " + getHeartbeatString(heartbeat)); + } + } else if (state == -1) { + preference.setSummary("OFF"); + } else { + preference.setSummary("ON / Manual: " + getHeartbeatString(state * 60000)); + } + } + } + + private String getHeartbeatString(int heartbeatMs) { + if (heartbeatMs < 120000) { + return (heartbeatMs / 1000) + " seconds"; + } + return (heartbeatMs / 60000) + " minutes"; + } + public static class AsActivity extends AbstractSettingsActivity { public AsActivity() { showHomeAsUp = true; diff --git a/play-services-core/src/main/java/org/microg/gms/ui/GcmFragment.java b/play-services-core/src/main/java/org/microg/gms/ui/GcmFragment.java index a050d7d1..0f9574b1 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/GcmFragment.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/GcmFragment.java @@ -39,6 +39,7 @@ import org.microg.gms.gcm.GcmDatabase; import org.microg.gms.gcm.GcmPrefs; import org.microg.gms.gcm.McsConstants; import org.microg.gms.gcm.McsService; +import org.microg.gms.gcm.TriggerReceiver; import org.microg.tools.ui.AbstractSettingsActivity; import org.microg.tools.ui.DimmableIconPreference; import org.microg.tools.ui.SwitchBarResourceSettingsFragment; @@ -70,7 +71,7 @@ public class GcmFragment extends SwitchBarResourceSettingsFragment { super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); - switchBar.setChecked(GcmPrefs.get(getContext()).isGcmEnabled()); + switchBar.setChecked(GcmPrefs.get(getContext()).isEnabled()); } @Override @@ -104,7 +105,7 @@ public class GcmFragment extends SwitchBarResourceSettingsFragment { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_ADVANCED: - Intent intent = new Intent(getContext(), GcmAdvancedFragment.AsActivity .class); + Intent intent = new Intent(getContext(), GcmAdvancedFragment.AsActivity.class); startActivity(intent); return true; default: @@ -118,7 +119,7 @@ public class GcmFragment extends SwitchBarResourceSettingsFragment { if (!isChecked) { McsService.stop(getContext()); } else { - getContext().startService(new Intent(McsConstants.ACTION_CONNECT, null, getContext(), McsService.class)); + getContext().sendBroadcast(new Intent(TriggerReceiver.FORCE_TRY_RECONNECT, null, getContext(), TriggerReceiver.class)); } updateContent(); } diff --git a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java b/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java index 7fbf61b8..dd49b2d4 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java @@ -72,7 +72,7 @@ public class SettingsActivity extends AbstractDashboardActivity { private void updateDetails() { findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext()))); - if (GcmPrefs.get(getContext()).isGcmEnabled()) { + if (GcmPrefs.get(getContext()).isEnabled()) { GcmDatabase database = new GcmDatabase(getContext()); int regCount = database.getRegistrationList().size(); database.close(); diff --git a/play-services-core/src/main/res/xml/preferences_gcm_advanced.xml b/play-services-core/src/main/res/xml/preferences_gcm_advanced.xml index b5ac7a87..87a6e7a4 100644 --- a/play-services-core/src/main/res/xml/preferences_gcm_advanced.xml +++ b/play-services-core/src/main/res/xml/preferences_gcm_advanced.xml @@ -17,12 +17,6 @@ - -