Skip to content

Commit

Permalink
Add basic MCS implementation to realize C2DM (used by GCM)
Browse files Browse the repository at this point in the history
- Can be started using `am startservice com.google.android.gms/org.microg.gms.gcm.mcs.McsService` on device
- Does not send Heartbeats -> Connection will be closed after 30 minutes
- No automatic reconnect on connection closure
- Only push, no bidirectional communication
  • Loading branch information
mar-v-in committed Mar 22, 2015
1 parent b150e73 commit 93c3cbb
Show file tree
Hide file tree
Showing 21 changed files with 2,935 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.mcs;

public class Constants {
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;
public static final int MCS_CLOSE_TAG = 4;
public static final int MCS_IQ_STANZA_TAG = 7;
public static final int MCS_DATA_MESSAGE_STANZA_TAG = 8;

public static final int MCS_VERSION_CODE = 41;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* 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.mcs;

import android.util.Base64;
import android.util.Log;

import com.squareup.wire.Message;
import com.squareup.wire.Wire;

import java.io.IOException;
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_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;

public class McsInputStream {
private static final String TAG = "GmsGcmMcsInput";

private final InputStream is;
private boolean initialized;
private int version = -1;
private int lastStreamIdReported = -1;
private int streamId = 0;

public McsInputStream(InputStream is) {
this(is, false);
}

public McsInputStream(InputStream is, boolean initialized) {
this.is = is;
this.initialized = initialized;
}

public int getStreamId() {
lastStreamIdReported = streamId;
return streamId;
}

public boolean newStreamIdAvailable() {
return lastStreamIdReported != streamId;
}

public int getVersion() {
ensureVersionRead();
return version;
}

private void ensureVersionRead() {
if (!initialized) {
try {
version = is.read();
Log.d(TAG, "Reading from MCS version=" + version);
initialized = true;
} catch (IOException e) {
Log.w(TAG, e);
}
}
}

public synchronized Message read() throws IOException {
ensureVersionRead();
int mcsTag = is.read();
int mcsSize = readVarint();
Log.d(TAG, "Reading from MCS tag=" + mcsTag + " size=" + mcsSize);
byte[] bytes = new byte[mcsSize];
int len = 0;
while ((len += is.read(bytes, len, mcsSize - len)) < mcsSize) ;
Log.d(TAG, "Reading from MCS: " + Base64.encodeToString(bytes, 0));
Message read = read(mcsTag, bytes, len);
Log.d(TAG, "Read from MCS: " + read);
streamId++;
return read;
}

private static Message read(int mcsTag, byte[] bytes, int len) throws IOException {
Wire wire = new Wire();
switch (mcsTag) {
case MCS_HEARTBEAT_ACK_TAG:
return wire.parseFrom(bytes, 0, len, HeartbeatAck.class);
case MCS_LOGIN_REQUEST_TAG:
return wire.parseFrom(bytes, 0, len, LoginRequest.class);
case MCS_LOGIN_RESPONSE_TAG:
return wire.parseFrom(bytes, 0, len, LoginResponse.class);
case MCS_CLOSE_TAG:
return wire.parseFrom(bytes, 0, len, Close.class);
case MCS_IQ_STANZA_TAG:
return wire.parseFrom(bytes, 0, len, IqStanza.class);
case MCS_DATA_MESSAGE_STANZA_TAG:
return wire.parseFrom(bytes, 0, len, DataMessageStanza.class);
default:
return null;
}
}

private int readVarint() throws IOException {
int res = 0;
int s = 0;
int b = 0x80;
while ((b & 0x80) == 0x80) {
b = is.read();
res |= (b & 0x7F) << s;
s += 7;
}
return res;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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.mcs;

import android.util.Log;

import com.squareup.wire.Message;

import java.io.IOException;
import java.io.OutputStream;

import static org.microg.gms.gcm.mcs.Constants.*;

public class McsOutputStream {
private static final String TAG = "GmsGcmMcsOutput";

private final OutputStream os;
private boolean initialized;
private int version = MCS_VERSION_CODE;
private int streamId = 0;

public McsOutputStream(OutputStream os) {
this(os, false);
}

public McsOutputStream(OutputStream os, boolean initialized) {
this.os = os;
this.initialized = initialized;
}

public int getStreamId() {
return streamId;
}

public void write(DataMessageStanza message) throws IOException {
write(message, MCS_DATA_MESSAGE_STANZA_TAG);
}

public void write(LoginRequest loginRequest) throws IOException {
write(loginRequest, MCS_LOGIN_REQUEST_TAG);
}

public void write(HeartbeatAck ack) throws IOException {
write(ack, MCS_HEARTBEAT_ACK_TAG);
}

public synchronized void write(Message message, int tag) throws IOException {
if (!initialized) {
Log.d(TAG, "Write MCS version code: " + version);
os.write(version);
initialized = true;
}
Log.d(TAG, "Write to MCS: " + message);
os.write(tag);
writeVarint(os, message.getSerializedSize());
os.write(message.toByteArray());
os.flush();
streamId++;
}

private void writeVarint(OutputStream os, int value) throws IOException {
while (true) {
if ((value & ~0x7FL) == 0) {
os.write(value);
return;
} else {
os.write((value & 0x7F) | 0x80);
value >>>= 7;
}
}
}
}
Loading

0 comments on commit 93c3cbb

Please sign in to comment.