Add heartbeat feature for MCS

This commit is contained in:
mar-v-in 2015-03-23 02:14:07 +01:00
parent f8be4c534c
commit a1a27167e6
8 changed files with 96 additions and 53 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
gen/
bin/
build/
.gradle/
user.gradle
local.properties
.directory

View File

@ -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;
}
}

View File

@ -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) {
}

View File

@ -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;

View File

@ -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:

View File

@ -0,0 +1,4 @@
package org.microg.gms.gcm.mcs;
public class McsMessage {
}

View File

@ -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);

View File

@ -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());
}