From a1a27167e66a151fda360323839b830a6ee14d9f Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Mon, 23 Mar 2015 02:14:07 +0100 Subject: [PATCH] Add heartbeat feature for MCS --- .gitignore | 3 +- .../java/org/microg/gms/gcm/GcmManager.java | 47 -------------- .../microg/gms/gcm/PushRegisterService.java | 26 +++++++- .../org/microg/gms/gcm/mcs/Constants.java | 1 + .../microg/gms/gcm/mcs/McsInputStream.java | 3 + .../org/microg/gms/gcm/mcs/McsMessage.java | 4 ++ .../microg/gms/gcm/mcs/McsOutputStream.java | 4 ++ .../org/microg/gms/gcm/mcs/McsService.java | 61 +++++++++++++++++-- 8 files changed, 96 insertions(+), 53 deletions(-) delete mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/GcmManager.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsMessage.java diff --git a/.gitignore b/.gitignore index 37358848..6265e60a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ gen/ bin/ build/ +.gradle/ user.gradle local.properties -.directory \ No newline at end of file +.directory diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmManager.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmManager.java deleted file mode 100644 index 07e74c76..00000000 --- a/play-services-core/src/main/java/org/microg/gms/gcm/GcmManager.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2013-2015 µg Project Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.microg.gms.gcm; - -import android.content.Context; -import android.util.Log; - -import org.microg.gms.checkin.LastCheckinInfo; -import org.microg.gms.common.PackageUtils; -import org.microg.gms.common.Utils; - -import java.io.IOException; - -public class GcmManager { - private static final String TAG = "GmsGcmManager"; - - public static String register(Context context, String app, String sender, String info) { - try { - return new RegisterRequest() - .build(Utils.getBuild(context)) - .sender(sender) - .info(info) - .checkin(LastCheckinInfo.read(context)) - .app(app, PackageUtils.firstSignatureDigest(context, app), PackageUtils.versionCode(context, app)) - .getResponse() - .token; - } catch (IOException e) { - Log.w(TAG, e); - } - - return null; - } -} 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 a2a9e0e5..68873611 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 @@ -18,6 +18,7 @@ package org.microg.gms.gcm; import android.app.IntentService; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; @@ -25,6 +26,12 @@ import android.os.Message; import android.os.Messenger; import android.util.Log; +import org.microg.gms.checkin.LastCheckinInfo; +import org.microg.gms.common.PackageUtils; +import org.microg.gms.common.Utils; + +import java.io.IOException; + public class PushRegisterService extends IntentService { private static final String TAG = "GmsGcmRegisterSvc"; @@ -66,7 +73,7 @@ public class PushRegisterService extends IntentService { Log.d(TAG, "register[req]: " + extras); Intent outIntent = new Intent("com.google.android.c2dm.intent.REGISTRATION"); outIntent.setPackage(app); - String regId = GcmManager.register(this, app, sender, null); + String regId = register(this, app, sender, null); if (regId != null) { outIntent.putExtra("registration_id", regId); } else { @@ -87,6 +94,23 @@ public class PushRegisterService extends IntentService { sendOrderedBroadcast(outIntent, null); } + public static String register(Context context, String app, String sender, String info) { + try { + return new RegisterRequest() + .build(Utils.getBuild(context)) + .sender(sender) + .info(info) + .checkin(LastCheckinInfo.read(context)) + .app(app, PackageUtils.firstSignatureDigest(context, app), PackageUtils.versionCode(context, app)) + .getResponse() + .token; + } catch (IOException e) { + Log.w(TAG, e); + } + + return null; + } + private void unregister(Intent intent) { } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/Constants.java b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/Constants.java index 27e20ad8..4b3ac468 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/Constants.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/Constants.java @@ -17,6 +17,7 @@ package org.microg.gms.gcm.mcs; public class Constants { + public static final int MCS_HEARTBEAT_PING_TAG = 0; public static final int MCS_HEARTBEAT_ACK_TAG = 1; public static final int MCS_LOGIN_REQUEST_TAG = 2; public static final int MCS_LOGIN_RESPONSE_TAG = 3; diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsInputStream.java b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsInputStream.java index 825f1d81..6b62d5f9 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsInputStream.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsInputStream.java @@ -28,6 +28,7 @@ import java.io.InputStream; import static org.microg.gms.gcm.mcs.Constants.MCS_CLOSE_TAG; import static org.microg.gms.gcm.mcs.Constants.MCS_DATA_MESSAGE_STANZA_TAG; import static org.microg.gms.gcm.mcs.Constants.MCS_HEARTBEAT_ACK_TAG; +import static org.microg.gms.gcm.mcs.Constants.MCS_HEARTBEAT_PING_TAG; import static org.microg.gms.gcm.mcs.Constants.MCS_IQ_STANZA_TAG; import static org.microg.gms.gcm.mcs.Constants.MCS_LOGIN_REQUEST_TAG; import static org.microg.gms.gcm.mcs.Constants.MCS_LOGIN_RESPONSE_TAG; @@ -94,6 +95,8 @@ public class McsInputStream { private static Message read(int mcsTag, byte[] bytes, int len) throws IOException { Wire wire = new Wire(); switch (mcsTag) { + case MCS_HEARTBEAT_PING_TAG: + return wire.parseFrom(bytes, 0, len, HeartbeatPing.class); case MCS_HEARTBEAT_ACK_TAG: return wire.parseFrom(bytes, 0, len, HeartbeatAck.class); case MCS_LOGIN_REQUEST_TAG: diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsMessage.java b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsMessage.java new file mode 100644 index 00000000..bb326afa --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsMessage.java @@ -0,0 +1,4 @@ +package org.microg.gms.gcm.mcs; + +public class McsMessage { +} diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsOutputStream.java b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsOutputStream.java index 3e719165..68202aae 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsOutputStream.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsOutputStream.java @@ -58,6 +58,10 @@ public class McsOutputStream { write(ack, MCS_HEARTBEAT_ACK_TAG); } + public void write(HeartbeatPing ping) throws IOException{ + write(ping, MCS_HEARTBEAT_PING_TAG); + } + public synchronized void write(Message message, int tag) throws IOException { if (!initialized) { Log.d(TAG, "Write MCS version code: " + version); diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsService.java index b04c0487..e8379c71 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsService.java @@ -29,6 +29,7 @@ import org.microg.gms.checkin.LastCheckinInfo; import java.io.IOException; import java.net.Socket; import java.util.Arrays; +import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.SSLContext; @@ -45,12 +46,16 @@ public class McsService extends IntentService { public static final String SELF_CATEGORY = "com.google.android.gsf.gtalkservice"; public static final String IDLE_NOTIFICATION = "IdleNotification"; public static final String FROM_FIELD = "gcm@android.com"; + public static final int HEARTBEAT_MS = 60000; private static AtomicBoolean connected = new AtomicBoolean(false); private Socket socket; private Socket sslSocket; private McsInputStream inputStream; private McsOutputStream outputStream; + private Thread connectionThread; + private Thread heartbeatThread; + private long lastMsgTime; public McsService() { super(TAG); @@ -59,17 +64,50 @@ public class McsService extends IntentService { @Override protected void onHandleIntent(Intent intent) { if (connected.compareAndSet(false, true)) { - new Thread(new Runnable() { + connectionThread = new Thread(new Runnable() { @Override public void run() { connect(); } - }).start(); + }); + connectionThread.start(); } else { Log.d(TAG, "MCS connection already started"); } } + private void heartbeatLoop() { + try { + while (!Thread.interrupted()) { + try { + long waitTime; + while ((waitTime = lastMsgTime + HEARTBEAT_MS - System.currentTimeMillis()) > 0) { + synchronized (heartbeatThread) { + Log.d(TAG, "Waiting for " + waitTime + "ms"); + heartbeatThread.wait(waitTime); + } + } + HeartbeatPing.Builder ping = new HeartbeatPing.Builder(); + if (inputStream.newStreamIdAvailable()) { + ping.last_stream_id_received(inputStream.getStreamId()); + } + outputStream.write(ping.build()); + lastMsgTime = System.currentTimeMillis(); + } catch (InterruptedException ie) { + Log.w(TAG, ie); + return; + } + } + } catch (Exception e) { + Log.w(TAG, e); + connectionThread.interrupt(); + } + if (heartbeatThread == Thread.currentThread()) { + heartbeatThread = null; + } + Log.d(TAG, "Heartbeating stopped"); + } + private void connect() { try { Log.d(TAG, "Starting MCS connection..."); @@ -86,6 +124,7 @@ public class McsService extends IntentService { boolean close = false; while (!close) { Message o = inputStream.read(); + lastMsgTime = System.currentTimeMillis(); if (o instanceof DataMessageStanza) { handleMessage((DataMessageStanza) o); } else if (o instanceof HeartbeatPing) { @@ -96,7 +135,7 @@ public class McsService extends IntentService { handleLoginresponse((LoginResponse) o); } } - socket.close(); + sslSocket.close(); } catch (Exception e) { Log.w(TAG, e); try { @@ -104,7 +143,12 @@ public class McsService extends IntentService { } catch (Exception ignored) { } } + if (heartbeatThread != null) { + heartbeatThread.interrupt(); + heartbeatThread = null; + } connected.set(false); + Log.d(TAG, "Connection closed"); } private void handleClose(Close close) throws IOException { @@ -118,6 +162,15 @@ public class McsService extends IntentService { } else { throw new IOException("Could not login: " + loginResponse.error); } + if (heartbeatThread == null) { + heartbeatThread = new Thread(new Runnable() { + @Override + public void run() { + heartbeatLoop(); + } + }); + heartbeatThread.start(); + } } private void handleMessage(DataMessageStanza message) throws IOException { @@ -181,7 +234,7 @@ public class McsService extends IntentService { .sent(System.currentTimeMillis() / 1000) .ttl(0) .category(SELF_CATEGORY) - .app_data(Arrays.asList(new AppData(IDLE_NOTIFICATION, "false"))); + .app_data(Collections.singletonList(new AppData(IDLE_NOTIFICATION, "false"))); if (inputStream.newStreamIdAvailable()) { msgResponse.last_stream_id_received(inputStream.getStreamId()); }